Performance tuning

Updated at:

This topic describes performance tuning recommendations for the PolarDB-X intelligent search engine in terms of index design, data ingestion, and queries.

Index design optimization

Shard count planning

A shard is the basic unit of data distribution and parallel processing. Planning the shard count properly is critical to performance.

Data volume

Recommended shard count

Description

< 10 GB

1

A single shard is sufficient. Avoid the overhead of over-sharding.

10 GB to 50 GB

2 to 3

A moderate shard count balances query parallelism and management overhead.

50 GB to 200 GB

3 to 5

Keep each shard between 30 GB and 50 GB.

> 200 GB

Calculate based on 30 GB to 50 GB per shard

Avoid oversized single shards that increase query latency.

Key principles:

  • Keep each shard between 30 GB and 50 GB.

  • The shard count should not exceed the node count multiplied by 20.

  • The shard count cannot be modified after index creation, so plan ahead.

  • Too many shards increase cluster metadata overhead and coordination cost.

Replica count selection

Scenario

Replica count

Description

Development and testing

0

Save resources. High availability is not required.

Standard production

1

Ensure high availability. Tolerates 1 node failure.

High availability requirement

2

Tolerates up to 2 simultaneous node failures.

Read-intensive workloads

1 to 2

Replicas help distribute read traffic.

Note

The replica count can be adjusted dynamically without rebuilding the index.

Mapping design best practices

curl -XPUT "http://<Search engine address>:<port>/my-index" \
  -u "<username>:<password>" -k \
  -H "Content-Type: application/json" \
  -d '{
    "settings": { "index.mapping.total_fields.limit": 1000 },
    "mappings": {
      "dynamic": "strict",
      "properties": {
        "title":      { "type": "text",    "analyzer": "ik_max_word" },
        "status":     { "type": "keyword" },
        "price":      { "type": "float" },
        "created_at": { "type": "date" }
      }
    }
  }'
  • Set "dynamic": "strict" to disallow automatically adding undefined fields and prevent mapping bloat.

  • For fields that do not need to be searched, set "index": false to reduce indexing overhead.

  • Do not add a keyword subfield to text fields that are not used for aggregation or sorting.

  • Use keyword instead of text for exact-match fields.

  • Choose the appropriate numeric type for numeric fields. Do not use long for all numbers.

Disable unnecessary features

Setting

Effect

When to disable

norms: false

Disable length normalization factor

Fields not used for relevance-based sorting

doc_values: false

Disable columnar storage

Fields not used for sorting or aggregation

index: false

Disable indexing

Fields that only need to be stored but not searched

store: false

Do not store separately

Already contained in _source; no separate storage is needed

Write performance optimization

Bulk write configuration

  • Size per _bulk request: 5 MB to 15 MB. Larger sizes cause memory pressure.

  • Documents per request: 1,000 to 5,000, depending on document size.

  • Concurrent write threads: 2 to 4, adjusted based on the CPU core count of the node.

Index settings tuning during writes

Before importing a large batch of data, temporarily adjust index settings to boost write throughput:

# Before writing: disable replicas and increase the refresh interval
curl -XPUT "http://<Search engine address>:<port>/my-index/_settings" \
  -u "<username>:<password>" -k \
  -H "Content-Type: application/json" \
  -d '{
    "number_of_replicas":            0,
    "refresh_interval":              "-1",
    "translog.durability":           "async",
    "translog.flush_threshold_size": "1gb"
  }'

# === Run the bulk data import ===

# After writing: restore normal settings
curl -XPUT "http://<Search engine address>:<port>/my-index/_settings" \
  -u "<username>:<password>" -k \
  -H "Content-Type: application/json" \
  -d '{
    "number_of_replicas":  1,
    "refresh_interval":    "1s",
    "translog.durability": "request"
  }'

curl -XPOST "http://<Search engine address>:<port>/my-index/_refresh"                        -u "<username>:<password>" -k
curl -XPOST "http://<Search engine address>:<port>/my-index/_forcemerge?max_num_segments=1"  -u "<username>:<password>" -k

Query performance optimization

Avoid deep pagination

// Not recommended: deep pagination performs poorly
{ "from": 10000, "size": 10 }

// Recommended: use search_after
{
  "size": 10,
  "sort": [ { "created_at": "desc" }, { "_id": "asc" } ],
  "search_after": ["2025-05-25T10:00:00Z", "doc_id_123"]
}

Use filter context appropriately

A filter clause does not participate in scoring and can leverage caching, delivering better performance than must. Place any condition that does not need to affect relevance scoring inside a filter.

Limit returned fields

{
  "query": { "match": { "content": "search" } },
  "_source": ["title", "summary", "created_at"],
  "size": 10
}

Avoid returning large fields such as full-text content or vector embedding. Return only the fields required for display.

Avoid expensive queries

  • Leading wildcard *abc: use a reverse token filter or ngram.

  • Complex script_score: use precomputed fields.

  • Deeply nested nested query: denormalize the schema.

  • Large numbers of should clauses (> 100): use a terms query.

  • match_all with a large size: use pagination or scroll.

Vector search optimization

Vector dimension selection

Model

Dimensions

Memory per million documents

Suitable scenarios

Small model

128 to 256

~0.5 GB to 1 GB

Lightweight scenarios such as recommendation and classification

General-purpose model

768

~3 GB

General-purpose semantic search

Large model

1024 to 1536

~4 GB to 6 GB

High-precision semantic or multimodal search

HNSW parameter tuning

Parameter

Default

Description

ef_construction

100

Recommended value is 128 to 512. Increases accuracy but also increases indexing time.

m

16

Recommended value is 8 to 32. Represents the maximum number of connections per node.

ef_search

100

Set dynamically through query parameters.

Key metrics to monitor

# Node resource usage
curl -XGET "http://<Search engine address>:<port>/_cat/nodes?v&h=name,heap.percent,ram.percent,cpu,disk.used_percent" \
  -u "<username>:<password>" -k

# Thread pool (pay attention to rejected)
curl -XGET "http://<Search engine address>:<port>/_cat/thread_pool?v&h=node_name,name,active,queue,rejected" \
  -u "<username>:<password>" -k

Performance alert threshold reference:

  • CPU utilization > 80% warning / > 90% critical: scale out or optimize queries.

  • Disk utilization > 75% warning / > 85% critical: a full disk turns indexes read-only.