{
  "schema_version": 2,
  "id": "develop/ai/search-and-query/best-practices/scalable-query-best-practices",
  "title": "Best practices for Redis Search performance",
  "url": "https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/scalable-query-best-practices/",
  "summary": "",
  "aliases": [
    "/develop/interact/search-and-query/best-practices/scalable-query-best-practices"
  ],
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-09-11T15:33:04+01:00",
  "page_type": "content",
  "content_hash": "3c386c3cad51e38d5a279532c02ed8350fb7b42e6d8ca2ab1f05a46d1a74c36e",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "> [!NOTE]\n> If you're using Redis Software or Redis Cloud, see the [best practices for scalable Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/scalable-query-best-practices) page."
    },
    {
      "id": "checklist",
      "title": "Checklist",
      "role": "content",
      "text": "Below are some basic steps to ensure good performance of Redis Search .\n\n* Create a Redis data model with your query patterns in mind.\n* Ensure the Redis architecture has been sized for the expected load using the [sizing calculator](https://redis.io/redisearch-sizing-calculator/).\n* Provision Redis nodes with sufficient resources (RAM, CPU, network) to support the expected maximum load.\n* Review [`FT.INFO`](https://redis.io/docs/latest/commands/ft.info) and [`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile) outputs for anomalies and/or errors.\n* Conduct load testing in a test environment with real-world queries and a load generated by either [memtier_benchmark](https://github.com/redislabs/memtier_benchmark) or a custom load application."
    },
    {
      "id": "indexing-considerations",
      "title": "Indexing considerations",
      "role": "content",
      "text": ""
    },
    {
      "id": "general",
      "title": "General",
      "role": "content",
      "text": "- Favor [`TAG`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#tag-fields) over [`NUMERIC`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#numeric-fields) for use cases that only require matching.\n- Favor [`TAG`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#tag-fields) over [`TEXT`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#text-fields) for use cases that don’t require full-text capabilities (pure match)."
    },
    {
      "id": "non-threaded-search",
      "title": "Non-threaded search",
      "role": "content",
      "text": "- Put only those fields used in your queries in the index.\n- Only make fields [`SORTABLE`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting) if they are used in [`SORTBY`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting#specifying-sortby)\nqueries.\n- Use [`DIALECT 2`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2)."
    },
    {
      "id": "threaded-query-performance-factor-or-qpf-search",
      "title": "Threaded (query performance factor or QPF) search",
      "role": "content",
      "text": "- Put both query fields and any projected fields (`RETURN` or `LOAD`) in the index.\n- Set all fields to `SORTABLE`.\n- Set TAG fields to [UNF](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting#normalization-unf-option).\n- Optional: Set `TEXT` fields to `NOSTEM` if the use case will support it.\n- Use [`DIALECT 2`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2)."
    },
    {
      "id": "query-optimization",
      "title": "Query optimization",
      "role": "content",
      "text": "- Avoid returning large result sets.  Use `CURSOR` or `LIMIT`.\n- Avoid wildcard searches.\n- Avoid projecting all fields (e.g., `LOAD *`). Project only those fields that are part of the index schema.\n- If queries are long-running, enable threading (query performance factor) to reduce contention for the main Redis thread."
    },
    {
      "id": "validate-performance-ftprofile",
      "title": "Validate performance (`FT.PROFILE`)",
      "role": "content",
      "text": "You can analyze [`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile) output to gain insights about query execution.\nThe following informational items are available for analysis:\n\n- Total execution time\n- Execution time per shard\n- Coordination time (for multi-sharded environments)\n- Breakdown of the query into fundamental components, such as `UNION` and `INTERSECT`\n- Warnings, such as `TIMEOUT`"
    },
    {
      "id": "anti-patterns",
      "title": "Anti-patterns",
      "role": "content",
      "text": "When designing and querying indexes in Redis Search, certain practices can hinder performance, scalability, and maintainability. Below are some common anti-patterns to avoid:\n\n- **Large documents**: storing excessively large documents in Redis makes data retrieval slower and increases memory usage. Break data into smaller, focused records whenever possible.\n- **Deeply-nested fields**: retrieving or indexing deeply-nested JSON fields is computationally expensive. Use a flatter schema for better performance.\n- **Large result sets**: fetching unnecessarily large result sets puts a strain on memory and network resources. Limit results to only what is needed.\n- **Wildcarding**: using wildcard patterns indiscriminately in queries can lead to large and inefficient scans, especially if the index size is significant.\n- **Large projections**: including excessive fields in query results increases memory overhead and slows down query execution. Limit projections to essential fields.\n\nThe following examples depict an anti-pattern index schema and query, followed by corrected versions designed for scalability with Redis Search."
    },
    {
      "id": "anti-pattern-index-schema",
      "title": "Anti-pattern index schema",
      "role": "content",
      "text": "The following schema introduces challenges for scalability and performance:\n\n[code example]\n\nIssues:\n\n- Minimal schema definition: the schema is sparse and lacks fields like `lastName`, `id`, and `version` that might be frequently queried. This results in additional operations to fetch these fields separately, reducing efficiency.\n- Missing `SORTABLE` flag for text fields: sorting operations on unsortable fields require full-text processing, which is slow.\n- Wildcard indexing: `$.tags.*` creates a broad index that can lead to excessive memory usage and reduced query performance."
    },
    {
      "id": "anti-pattern-query",
      "title": "Anti-pattern query",
      "role": "content",
      "text": "The following query is inefficient and not optimized for vertical scaling:\n\n[code example]\nIssues:\n\n- Wildcard projection (`LOAD *`): retrieving all fields in the result set is inefficient and increases memory usage, especially if the documents are large.\n- Unnecessary fields: fields that aren't required for the current operation are still fetched, slowing down execution.\n- Lack of advanced query syntax: without specifying a query dialect or leveraging features like tagging, the query may perform unnecessary computations."
    },
    {
      "id": "improved-index-schema",
      "title": "Improved index schema",
      "role": "content",
      "text": "Here’s an optimized schema that adheres to best practices for vertical scaling:\n\n[code example]\n\nImprovements:\n\n- `NOSTEM` for text fields: prevents stemming on fields like `firstName` and `lastName` to allow for exact matches (e.g., \"Smith\" stays \"Smith\").\n- Expanded schema: adds commonly queried fields like `lastName`, `id`, and `version`, making queries more efficient by reducing the need for post-query data retrieval.\n- `TAG` fields: `id` and `ver` are defined as `TAG` fields to support fast filtering with exact matches.\n- `SORTABLE` for all relevant fields: ensures that sorting operations are efficient without requiring full-text scanning.\n\nYou might be wondering why `$.tags.* as t NUMERIC SORTABLE` is acceptable in the improved schema and it wasn't previously.\nThe inclusion of `$.tags.*` is acceptable when:\n\n- It has a clear purpose: it is actively used in queries, such as filtering on numeric ranges or matching specific values.\n- Other fields in the schema complement it: these fields reduce over-reliance on `$.tags.*` for all query operations, distributing the load more evenly.\n- Projections and limits are managed carefully: queries that use `$.tags.*` should avoid loading unnecessary fields or returning excessively large result sets."
    },
    {
      "id": "improved-query",
      "title": "Improved query",
      "role": "content",
      "text": "The following query is better suited for vertical scaling:\n\n[code example]\n\nImprovements:\n\n- Targeted projection: the `LOAD` clause specifies only essential fields (`id, t, name, lastname, loc, ver`), reducing memory and network overhead.\n- Limited results: the `LIMIT` clause ensures the query retrieves only the first 10 results, avoiding large result sets.\n- [`DIALECT 2`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2): enables the latest Redis Search syntax and features, ensuring compatibility with modern capabilities."
    }
  ],
  "examples": [
    {
      "id": "anti-pattern-index-schema-ex0",
      "language": "sh",
      "code": "FT.CREATE jsonidx:profiles ON JSON PREFIX 1 profiles: \n          SCHEMA $.tags.* as t NUMERIC SORTABLE \n                 $.firstName as name TEXT \n                 $.location as loc GEO",
      "section_id": "anti-pattern-index-schema"
    },
    {
      "id": "anti-pattern-query-ex0",
      "language": "sh",
      "code": "FT.AGGREGATE jsonidx:profiles '@t:[1299 1299]' LOAD * LIMIT 0 10",
      "section_id": "anti-pattern-query"
    },
    {
      "id": "improved-index-schema-ex0",
      "language": "sh",
      "code": "FT.CREATE jsonidx:profiles ON JSON PREFIX 1 profiles: \n          SCHEMA $.tags.* as t NUMERIC SORTABLE \n                 $.firstName as name TEXT NOSTEM SORTABLE \n                 $.lastName as lastname TEXT NOSTEM SORTABLE \n                 $.location as loc GEO SORTABLE \n                 $.id as id TAG SORTABLE UNF \n                 $.ver as ver TAG SORTABLE UNF",
      "section_id": "improved-index-schema"
    },
    {
      "id": "improved-query-ex0",
      "language": "sh",
      "code": "FT.AGGREGATE jsonidx:profiles '@t:[1299 1299]' \n                LOAD 6 id t name lastname loc ver \n                LIMIT 0 10\n                DIALECT 2",
      "section_id": "improved-query"
    }
  ]
}
