Optimizing Elasticsearch Indexes for 1C-Bitrix
Optimizing Elasticsearch Indexes for the Bitrix Catalog and Search
Elasticsearch works "out of the box" — until the first serious load hits. On a catalog of 100,000+ products with active re-indexing via 1C synchronization, the typical symptoms of degradation are: heap memory consistently at 80%, GC pauses of 200–500 ms, search latency growing to 2–5 seconds, and merge throttling blocking indexing.
Optimizing ES indexes means working at several levels: sharding configuration, merge policy, fielddata and doc values, refresh interval, and force merge for completed indexes.
Diagnosing Cluster and Index Health
Start with a health assessment:
# Cluster status
GET /_cluster/health?pretty
# Index statistics
GET /bitrix_catalog/_stats?pretty
# Hot threads (what is loading the CPU)
GET /_nodes/hot_threads
# Memory usage
GET /_nodes/stats/jvm?pretty
Key diagnostic metrics:
-
jvm.mem.heap_used_percent— if consistently >75%, either increase the heap or reduce fielddata -
indices.segments.count— a high segment count slows down search; a sign of suboptimal merging -
indices.merges.current_size_in_bytes— active merge in production during peak load
Configuring Sharding
The most common mistake is too many shards. Each shard is a Lucene index with approximately 50 MB of heap overhead for metadata. 100 shards = 5 GB of heap just for metadata.
For a Bitrix catalog, the rule is: 1 shard per 20–40 GB of data, no more than 3–5 shards for a typical store:
PUT /bitrix_catalog
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
The number of primary shards cannot be changed after index creation — you need to create a new index and reindex via the Reindex API. Plan correctly from the start.
Optimizing the Refresh Interval
By default, ES refreshes the index every second — new documents become available for search within 1 s. This is expensive during bulk indexing:
PUT /bitrix_catalog/_settings
{
"index": {
"refresh_interval": "30s"
}
}
During 1C synchronization (bulk indexing), disable refresh entirely:
{ "index": { "refresh_interval": "-1" } }
After completion — restore the setting and call POST /bitrix_catalog/_refresh manually.
Optimizing the Merge Policy
PUT /bitrix_catalog/_settings
{
"index.merge.policy.max_merged_segment": "5gb",
"index.merge.policy.segments_per_tier": 10,
"index.merge.scheduler.max_thread_count": 1
}
max_thread_count: 1 for HDD servers — merging on multiple threads kills I/O. On NVMe you can use 2–4.
Optimizing Fielddata and Doc Values
Fielddata is loaded into heap during aggregations and sorting on text fields. For catalog faceted filters, use exclusively keyword with doc values (stored on disk, not in heap):
PUT /bitrix_catalog/_mapping
{
"properties": {
"brand": {
"type": "keyword",
"doc_values": true,
"eager_global_ordinals": true
}
}
}
eager_global_ordinals: true for high-cardinality filter fields (brand, category) — builds ordinals at refresh time rather than on the first aggregation request. Eliminates "cold start" after nightly re-indexing.
Force Merge for Static Indexes
If the catalog changes overnight (1C synchronization once per day), the daytime index can be merged into a single segment:
POST /bitrix_catalog/_forcemerge?max_num_segments=1
This is a heavy operation — run only during a maintenance window. One segment = maximum search speed, minimum overhead.
Configuring Indexing from Bitrix
On the PHP side, use the Bulk API with an optimal batch size for bulk indexing:
$batchSize = 500; // optimal for products with descriptions
$body = [];
foreach ($products as $product) {
$body[] = ['index' => ['_index' => 'bitrix_catalog', '_id' => $product['ID']]];
$body[] = $this->prepareDocument($product);
}
$client->bulk(['body' => $body]);
Determine the batch size empirically: too small — many round-trips, too large — GC pressure. Typically 200–500 documents for products with descriptions.
Post-Optimization Monitoring
Connect ES metrics to Prometheus via elasticsearch_exporter and alert on:
- heap_used_percent > 80% for 5+ minutes
- GC time > 1 second per minute
- search latency p99 > 500 ms
- unassigned shards > 0
Result
Comprehensive index optimization reduces heap utilization by 30–50%, eliminates GC pauses under normal load, reduces search query time from 1–3 s to 50–150 ms, and speeds up nightly catalog re-indexing by 3–5x.







