|
| 1 | +--- |
| 2 | +Title: Best practices for Redis Query Engine performance |
| 3 | +alwaysopen: false |
| 4 | +categories: |
| 5 | +- docs |
| 6 | +- develop |
| 7 | +- stack |
| 8 | +- oss |
| 9 | +- kubernetes |
| 10 | +- clients |
| 11 | +linkTitle: RQE performance |
| 12 | +weight: 1 |
| 13 | +--- |
| 14 | + |
| 15 | +{{< note >}} |
| 16 | +If you're using Redis Software or Redis Cloud, see the [best practices for scalable Redis Query Engine]({{< relref "/operate/oss_and_stack/stack-with-enterprise/search/scalable-query-best-practices" >}}) page. |
| 17 | +{{< /note >}} |
| 18 | + |
| 19 | +## Checklist |
| 20 | +Below are basic steps to ensure good performance of Redis Query Engine. |
| 21 | + |
| 22 | +* Create a Redis data model with your query patterns in mind. |
| 23 | +* Ensure the Redis architecture has been sized for the expected load using the [sizing calculator](https://redis.io/redisearch-sizing-calculator/). |
| 24 | +* Provision Redis nodes with sufficient resources (RAM, CPU, network) to support the expected maximum load. |
| 25 | +* Review [`FT.INFO`]({{< baseurl >}}/commands/ft.info) and [`FT.PROFILE`]({{< baseurl >}}/commands/ft.profile) outputs for anomalies and/or errors. |
| 26 | +* 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. |
| 27 | + |
| 28 | +## Indexing considerations |
| 29 | + |
| 30 | +### General |
| 31 | +- Favor [`TAG`]({{< relref "/develop/interact/search-and-query/basic-constructs/field-and-type-options#tag-fields" >}}) over [`NUMERIC`]({{< relref "/develop/interact/search-and-query/basic-constructs/field-and-type-options#numeric-fields" >}}) for use cases that only require matching. |
| 32 | +- Favor [`TAG`]({{< relref "/develop/interact/search-and-query/basic-constructs/field-and-type-options#tag-fields" >}}) over [`TEXT`]({{< relref "/develop/interact/search-and-query/basic-constructs/field-and-type-options#text-fields" >}}) for use cases that don’t require full-text capabilities (pure match). |
| 33 | + |
| 34 | +### Non-threaded search |
| 35 | +- Put only those fields used in your queries in the index. |
| 36 | +- Only make fields [`SORTABLE`]({{< relref "/develop/interact/search-and-query/advanced-concepts/sorting" >}}) if they are used in [`SORTBY`]({{< relref "/develop/interact/search-and-query/advanced-concepts/sorting#specifying-sortby" >}}) |
| 37 | +queries. |
| 38 | +- Use [`DIALECT 4`]({{< relref "/develop/interact/search-and-query/advanced-concepts/dialects#dialect-4" >}}). |
| 39 | + |
| 40 | +### Threaded (query performance factor or QPF) search |
| 41 | +- Put both query fields and any projected fields (`RETURN` or `LOAD`) in the index. |
| 42 | +- Set all fields to `SORTABLE`. |
| 43 | +- Set TAG fields to [UNF]({{< relref "/develop/interact/search-and-query/advanced-concepts/sorting#normalization-unf-option" >}}). |
| 44 | +- Optional: Set `TEXT` fields to `NOSTEM` if the use case will support it. |
| 45 | +- Use [`DIALECT 4`]({{< relref "/develop/interact/search-and-query/advanced-concepts/dialects#dialect-4" >}}). |
| 46 | + |
| 47 | +## Query optimization |
| 48 | + |
| 49 | +- Avoid returning large result sets. Use `CURSOR` or `LIMIT`. |
| 50 | +- Avoid wildcard searches. |
| 51 | +- Avoid projecting all fields (e.g., `LOAD *`). Project only those fields that are part of the index schema. |
| 52 | +- If queries are long-running, enable threading (query performance factor) to reduce contention for the main Redis thread. |
| 53 | + |
| 54 | +## Validate performance (`FT.PROFILE`) |
| 55 | + |
| 56 | +You can analyze [`FT.PROFILE`]({{< baseurl >}}/commands/ft.profile) output to gain insights about query execution. |
| 57 | +The following informational items are available for analysis: |
| 58 | + |
| 59 | +- Total execution time |
| 60 | +- Execution time per shard |
| 61 | +- Coordination time (for multi-sharded environments) |
| 62 | +- Breakdown of the query into fundamental components, such as `UNION` and `INTERSECT` |
| 63 | +- Warnings, such as `TIMEOUT` |
| 64 | + |
| 65 | +## Anti-patterns |
| 66 | + |
| 67 | +The following items are anti-patterns for RQE: |
| 68 | + |
| 69 | +- Large documents |
| 70 | +- Deeply-nested fields |
| 71 | +- Large result sets |
| 72 | +- Wildcarding |
| 73 | +- Large projections |
| 74 | + |
| 75 | +The following examples depict an anti-pattern index schema and query, followed by a corrected index schema and query, which allows for scalability with the Redis Query Engine. |
| 76 | + |
| 77 | +### Anti-pattern index schema |
| 78 | + |
| 79 | +The following index schema is not optimized for vertical scaling: |
| 80 | + |
| 81 | +```sh |
| 82 | +FT.CREATE jsonidx:profiles ON JSON PREFIX 1 profiles: |
| 83 | + SCHEMA $.tags.* as t NUMERIC SORTABLE |
| 84 | + $.firstName as name TEXT |
| 85 | + $.location as loc GEO |
| 86 | +``` |
| 87 | + |
| 88 | +### Anti-pattern query |
| 89 | + |
| 90 | +The following query is not optimized for vertical scaling: |
| 91 | + |
| 92 | +```sh |
| 93 | +FT.AGGREGATE jsonidx:profiles '@t:[1299 1299]' LOAD * LIMIT 0 10 |
| 94 | +``` |
| 95 | + |
| 96 | +### Improved index schema |
| 97 | + |
| 98 | +Here's an improved index schema that follows best practices for vertical scaling: |
| 99 | + |
| 100 | +```sh |
| 101 | +FT.CREATE jsonidx:profiles ON JSON PREFIX 1 profiles: |
| 102 | + SCHEMA $.tags.* as t NUMERIC SORTABLE |
| 103 | + $.firstName as name TEXT NOSTEM SORTABLE |
| 104 | + $.lastName as lastname TEXT NOSTEM SORTABLE |
| 105 | + $.location as loc GEO SORTABLE |
| 106 | + $.id as id TAG SORTABLE UNF |
| 107 | + $.ver as ver TAG SORTABLE UNF |
| 108 | +``` |
| 109 | + |
| 110 | +### Improved query |
| 111 | + |
| 112 | +Here's an improved query that follows best practices for vertical scaling: |
| 113 | + |
| 114 | +```sh |
| 115 | +FT.AGGREGATE jsonidx:profiles '@t:[1299 1299]' |
| 116 | + LOAD 6 id t nam" lastname loc ver |
| 117 | + LIMIT 0 10 |
| 118 | + DIALECT 3 |
| 119 | +``` |
0 commit comments