search-speed.asciidoc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. [[tune-for-search-speed]]
  2. == Tune for search speed
  3. [float]
  4. === Give memory to the filesystem cache
  5. Elasticsearch heavily relies on the filesystem cache in order to make search
  6. fast. In general, you should make sure that at least half the available memory
  7. goes to the filesystem cache so that Elasticsearch can keep hot regions of the
  8. index in physical memory.
  9. [float]
  10. === Use faster hardware
  11. If your search is I/O bound, you should investigate giving more memory to the
  12. filesystem cache (see above) or buying faster drives. In particular SSD drives
  13. are known to perform better than spinning disks. Always use local storage,
  14. remote filesystems such as `NFS` or `SMB` should be avoided. Also beware of
  15. virtualized storage such as Amazon's `Elastic Block Storage`. Virtualized
  16. storage works very well with Elasticsearch, and it is appealing since it is so
  17. fast and simple to set up, but it is also unfortunately inherently slower on an
  18. ongoing basis when compared to dedicated local storage. If you put an index on
  19. `EBS`, be sure to use provisioned IOPS otherwise operations could be quickly
  20. throttled.
  21. If your search is CPU-bound, you should investigate buying faster CPUs.
  22. [float]
  23. === Document modeling
  24. Documents should be modeled so that search-time operations are as cheap as possible.
  25. In particular, joins should be avoided. <<nested,`nested`>> can make queries
  26. several times slower and <<mapping-parent-field,parent-child>> relations can make
  27. queries hundreds of times slower. So if the same questions can be answered without
  28. joins by denormalizing documents, significant speedups can be expected.
  29. [float]
  30. === Search as few fields as possible
  31. The more fields a <<query-dsl-query-string-query,`query_string`>> or
  32. <<query-dsl-multi-match-query,`multi_match`>> query targets, the slower it is.
  33. A common technique to improve search speed over multiple fields is to copy
  34. their values into a single field at index time, and then use this field at
  35. search time. This can be automated with the <<copy-to,`copy-to`>> directive of
  36. mappings without having to change the source of documents. Here is an example
  37. of an index containing movies that optimizes queries that search over both the
  38. name and the plot of the movie by indexing both values into the `name_and_plot`
  39. field.
  40. [source,js]
  41. --------------------------------------------------
  42. PUT movies
  43. {
  44. "mappings": {
  45. "_doc": {
  46. "properties": {
  47. "name_and_plot": {
  48. "type": "text"
  49. },
  50. "name": {
  51. "type": "text",
  52. "copy_to": "name_and_plot"
  53. },
  54. "plot": {
  55. "type": "text",
  56. "copy_to": "name_and_plot"
  57. }
  58. }
  59. }
  60. }
  61. }
  62. --------------------------------------------------
  63. // CONSOLE
  64. [float]
  65. === Pre-index data
  66. You should leverage patterns in your queries to optimize the way data is indexed.
  67. For instance, if all your documents have a `price` field and most queries run
  68. <<search-aggregations-bucket-range-aggregation,`range`>> aggregations on a fixed
  69. list of ranges, you could make this aggregation faster by pre-indexing the ranges
  70. into the index and using a <<search-aggregations-bucket-terms-aggregation,`terms`>>
  71. aggregations.
  72. For instance, if documents look like:
  73. [source,js]
  74. --------------------------------------------------
  75. PUT index/_doc/1
  76. {
  77. "designation": "spoon",
  78. "price": 13
  79. }
  80. --------------------------------------------------
  81. // CONSOLE
  82. and search requests look like:
  83. [source,js]
  84. --------------------------------------------------
  85. GET index/_search
  86. {
  87. "aggs": {
  88. "price_ranges": {
  89. "range": {
  90. "field": "price",
  91. "ranges": [
  92. { "to": 10 },
  93. { "from": 10, "to": 100 },
  94. { "from": 100 }
  95. ]
  96. }
  97. }
  98. }
  99. }
  100. --------------------------------------------------
  101. // CONSOLE
  102. // TEST[continued]
  103. Then documents could be enriched by a `price_range` field at index time, which
  104. should be mapped as a <<keyword,`keyword`>>:
  105. [source,js]
  106. --------------------------------------------------
  107. PUT index
  108. {
  109. "mappings": {
  110. "_doc": {
  111. "properties": {
  112. "price_range": {
  113. "type": "keyword"
  114. }
  115. }
  116. }
  117. }
  118. }
  119. PUT index/_doc/1
  120. {
  121. "designation": "spoon",
  122. "price": 13,
  123. "price_range": "10-100"
  124. }
  125. --------------------------------------------------
  126. // CONSOLE
  127. And then search requests could aggregate this new field rather than running a
  128. `range` aggregation on the `price` field.
  129. [source,js]
  130. --------------------------------------------------
  131. GET index/_search
  132. {
  133. "aggs": {
  134. "price_ranges": {
  135. "terms": {
  136. "field": "price_range"
  137. }
  138. }
  139. }
  140. }
  141. --------------------------------------------------
  142. // CONSOLE
  143. // TEST[continued]
  144. [float]
  145. === Consider mapping identifiers as `keyword`
  146. The fact that some data is numeric does not mean it should always be mapped as a
  147. <<number,numeric field>>. The way that Elasticsearch indexes numbers optimizes
  148. for `range` queries while `keyword` fields are better at `term` queries. Typically,
  149. fields storing identifiers such as an `ISBN` or any number identifying a record
  150. from another database are rarely used in `range` queries or aggregations. This is
  151. why they might benefit from being mapped as <<keyword,`keyword`>> rather than as
  152. `integer` or `long`.
  153. [float]
  154. === Avoid scripts
  155. In general, scripts should be avoided. If they are absolutely needed, you
  156. should prefer the `painless` and `expressions` engines.
  157. [float]
  158. === Search rounded dates
  159. Queries on date fields that use `now` are typically not cacheable since the
  160. range that is being matched changes all the time. However switching to a
  161. rounded date is often acceptable in terms of user experience, and has the
  162. benefit of making better use of the query cache.
  163. For instance the below query:
  164. [source,js]
  165. --------------------------------------------------
  166. PUT index/_doc/1
  167. {
  168. "my_date": "2016-05-11T16:30:55.328Z"
  169. }
  170. GET index/_search
  171. {
  172. "query": {
  173. "constant_score": {
  174. "filter": {
  175. "range": {
  176. "my_date": {
  177. "gte": "now-1h",
  178. "lte": "now"
  179. }
  180. }
  181. }
  182. }
  183. }
  184. }
  185. --------------------------------------------------
  186. // CONSOLE
  187. could be replaced with the following query:
  188. [source,js]
  189. --------------------------------------------------
  190. GET index/_search
  191. {
  192. "query": {
  193. "constant_score": {
  194. "filter": {
  195. "range": {
  196. "my_date": {
  197. "gte": "now-1h/m",
  198. "lte": "now/m"
  199. }
  200. }
  201. }
  202. }
  203. }
  204. }
  205. --------------------------------------------------
  206. // CONSOLE
  207. // TEST[continued]
  208. In that case we rounded to the minute, so if the current time is `16:31:29`,
  209. the range query will match everything whose value of the `my_date` field is
  210. between `15:31:00` and `16:31:59`. And if several users run a query that
  211. contains this range in the same minute, the query cache could help speed things
  212. up a bit. The longer the interval that is used for rounding, the more the query
  213. cache can help, but beware that too aggressive rounding might also hurt user
  214. experience.
  215. NOTE: It might be tempting to split ranges into a large cacheable part and
  216. smaller not cacheable parts in order to be able to leverage the query cache,
  217. as shown below:
  218. [source,js]
  219. --------------------------------------------------
  220. GET index/_search
  221. {
  222. "query": {
  223. "constant_score": {
  224. "filter": {
  225. "bool": {
  226. "should": [
  227. {
  228. "range": {
  229. "my_date": {
  230. "gte": "now-1h",
  231. "lte": "now-1h/m"
  232. }
  233. }
  234. },
  235. {
  236. "range": {
  237. "my_date": {
  238. "gt": "now-1h/m",
  239. "lt": "now/m"
  240. }
  241. }
  242. },
  243. {
  244. "range": {
  245. "my_date": {
  246. "gte": "now/m",
  247. "lte": "now"
  248. }
  249. }
  250. }
  251. ]
  252. }
  253. }
  254. }
  255. }
  256. }
  257. --------------------------------------------------
  258. // CONSOLE
  259. // TEST[continued]
  260. However such practice might make the query run slower in some cases since the
  261. overhead introduced by the `bool` query may defeat the savings from better
  262. leveraging the query cache.
  263. [float]
  264. === Force-merge read-only indices
  265. Indices that are read-only would benefit from being
  266. <<indices-forcemerge,merged down to a single segment>>. This is typically the
  267. case with time-based indices: only the index for the current time frame is
  268. getting new documents while older indices are read-only.
  269. IMPORTANT: Don't force-merge indices that are still being written to -- leave
  270. merging to the background merge process.
  271. [float]
  272. === Warm up global ordinals
  273. Global ordinals are a data-structure that is used in order to run
  274. <<search-aggregations-bucket-terms-aggregation,`terms`>> aggregations on
  275. <<keyword,`keyword`>> fields. They are loaded lazily in memory because
  276. Elasticsearch does not know which fields will be used in `terms` aggregations
  277. and which fields won't. You can tell Elasticsearch to load global ordinals
  278. eagerly at refresh-time by configuring mappings as described below:
  279. [source,js]
  280. --------------------------------------------------
  281. PUT index
  282. {
  283. "mappings": {
  284. "_doc": {
  285. "properties": {
  286. "foo": {
  287. "type": "keyword",
  288. "eager_global_ordinals": true
  289. }
  290. }
  291. }
  292. }
  293. }
  294. --------------------------------------------------
  295. // CONSOLE
  296. [float]
  297. === Warm up the filesystem cache
  298. If the machine running Elasticsearch is restarted, the filesystem cache will be
  299. empty, so it will take some time before the operating system loads hot regions
  300. of the index into memory so that search operations are fast. You can explicitly
  301. tell the operating system which files should be loaded into memory eagerly
  302. depending on the file extension using the <<file-system,`index.store.preload`>>
  303. setting.
  304. WARNING: Loading data into the filesystem cache eagerly on too many indices or
  305. too many files will make search _slower_ if the filesystem cache is not large
  306. enough to hold all the data. Use with caution.
  307. [float]
  308. === Use index sorting to speed up conjunctions
  309. <<index-modules-index-sorting,Index sorting>> can be useful in order to make
  310. conjunctions faster at the cost of slightly slower indexing. Read more about it
  311. in the <<index-modules-index-sorting-conjunctions,index sorting documentation>>.
  312. [float]
  313. === Use `preference` to optimize cache utilization
  314. There are multiple caches that can help with search performance, such as the
  315. https://en.wikipedia.org/wiki/Page_cache[filesystem cache], the
  316. <<shard-request-cache,request cache>> or the <<query-cache,query cache>>. Yet
  317. all these caches are maintained at the node level, meaning that if you run the
  318. same request twice in a row, have 1 <<glossary-replica-shard,replica>> or more
  319. and use https://en.wikipedia.org/wiki/Round-robin_DNS[round-robin], the default
  320. routing algorithm, then those two requests will go to different shard copies,
  321. preventing node-level caches from helping.
  322. Since it is common for users of a search application to run similar requests
  323. one after another, for instance in order to analyze a narrower subset of the
  324. index, using a preference value that identifies the current user or session
  325. could help optimize usage of the caches.
  326. [float]
  327. === Replicas might help with throughput, but not always
  328. In addition to improving resiliency, replicas can help improve throughput. For
  329. instance if you have a single-shard index and three nodes, you will need to
  330. set the number of replicas to 2 in order to have 3 copies of your shard in
  331. total so that all nodes are utilized.
  332. Now imagine that you have a 2-shards index and two nodes. In one case, the
  333. number of replicas is 0, meaning that each node holds a single shard. In the
  334. second case the number of replicas is 1, meaning that each node has two shards.
  335. Which setup is going to perform best in terms of search performance? Usually,
  336. the setup that has fewer shards per node in total will perform better. The
  337. reason for that is that it gives a greater share of the available filesystem
  338. cache to each shard, and the filesystem cache is probably Elasticsearch's
  339. number 1 performance factor. At the same time, beware that a setup that does
  340. not have replicas is subject to failure in case of a single node failure, so
  341. there is a trade-off between throughput and availability.
  342. So what is the right number of replicas? If you have a cluster that has
  343. `num_nodes` nodes, `num_primaries` primary shards _in total_ and if you want to
  344. be able to cope with `max_failures` node failures at once at most, then the
  345. right number of replicas for you is
  346. `max(max_failures, ceil(num_nodes / num_primaries) - 1)`.