文章目录
0. 前言
有关读写大部分的内容,前面的章节已经介绍完毕。
该章节会着重介绍日常开发过程中,如何优化我们的读写操作。
1. 写优化
1.1 使用 bulk API
不同的 ES 集群配置,批量写入的性能不同,应在单个分片的单个节点上运行基准测试。先尝试 100 个文档,然后是 200 个,依次类推。
1.2 提高索引的 refresh_interval 间隔
适当的增加索引的 refresh_interval
可以提高写入速度。
默认情况下,ES 每秒刷新 1 次。
该值决定了,写入的数据多少秒后可被搜索到。
PUT test21_refresh_interval
{
"settings": {
"index": {
"refresh_interval": "30s"
}
}
}
1.3 禁用内存交换
修改 elasticsearch.yml
bootstrap.memory_lock: true
1.4 至少一半的内存留给机器
文件系统缓存用于缓冲 I/O
操作。将运行 ES 机器至少一半内存分配给文件系统缓存
1.5 ID 自动生成
当显示声明 ID 时,ES 需要检查其他分片上是否存在相同的 ID 字段。随着文档数量的增多,该操作成本会变高。
自动生成 ID,ES 可以跳过该操作。
例如,以下为自动生成ID
POST test21_auto_id/_doc
{
"name": "elasticsearch"
}
以下为显示声明ID
PUT test21_auto_id/_doc/1
{
"name": "hello"
}
1.6 使用更快的硬件
如果可以,尽可能使用 SSD
,或者其他更快的硬件
1.7 禁用你不用的功能
如果你确定该字段不需要排序、聚合,那么可以禁掉 doc_values
。
例如
PUT test_doc_value
{
"mappings": {
"properties": {
"name": {
"type": "keyword",
"doc_values": false
}
}
}
}
如果你确定该字段不需要被搜索,那么可以禁掉 index
。
例如
PUT test_index
{
"mappings": {
"properties": {
"age": {
"type": "integer" ,
"index": false
}
}
}
}
如果你确定你的对象字段,仅需要被存储,可以禁掉 enabled
。
例如
PUT test_enable
{
"mappings": {
"properties": {
"relation": {
"type": "object",
"enabled": false
}
}
}
}
1.8 字符串不要使用动态映射
默认情况下,string
会同时生成 text
keyword
2 个字段。
你需要确认该字段是否需要被全文搜索。如果不需要,则将该字段类型设置为 keyword
。
2. 读优化
2.1 至少一半的内存留给机器
文件系统缓存用于缓冲 I/O
操作。将运行 ES 机器至少一半内存分配给文件系统缓存
2.2 使用更快的硬件
如果可以,尽可能使用 SSD
。或者其他更快的硬件
2.3 恰当的文档模型
文档在建模时,尽可能的将所有的字段都放在一个索引上。即可能的冗余,以提高查询的性能。
2.4 搜索字段尽可能的少
常见的是使用 multi_match
搜索多个字段,搜索的字段越多,花费的时间也就越长。
如果这多个字段在权重上没有要求,可以考虑使用 copy_to
到一个字段上,搜索 copy_to
后的新字段。
例如
PUT test20_copyto
{
"mappings": {
"properties": {
"first_name": {
"type": "text",
"copy_to": "full_name"
},
"last_name": {
"type": "text",
"copy_to": "full_name"
},
"full_name": {
"type": "text"
}
}
}
}
GET test20_copyto/_search
{
"query": {
"match": {
"full_name": "hello"
}
}
}
2.5 仅返回你所需要的字段
仅返回你所需要的字段,来节约传输的带宽,加快访问速度。
例如
GET test21/_search
{
"query": {
"match_all": {}
},
"_source": {
"includes": ["name", "age"]
}
}
2.6 避免返回大结果集
不要一次性返回大量的结果集。例如 from + size
特别大。这种情况下,应该要考虑使用 search_after(PIT)
或者 scroll
API。
2.7 使用 keyword
如果数值类型的字段不需要范围查询,那么可以将其修改为 keyword
字段,以加快查询性能。
2.8 避免使用脚本
尽可能避免使用脚本查询。如果可以,尽可能在写入时将字段处理好,使其在搜索时不需要使用脚本
2.9 日期搜索四舍五入
在前面的章节,我们有提到过,不需要算分的字段可以使用 filter
,这样可以加速查询。
但是如果查询对象是日期的话,那么缓存效果会很差,因为日期会一直变化。
我们可以通过查询四舍五入的日期,来优化该操作
例如
PUT test21_date/_doc/1
{
"my_date": "2024-10-06T16:30:55.328Z"
}
GET test21_date/_search
{
"query": {
"constant_score": {
"filter": {
"range": {
"my_date": {
"gte": "now-1h",
"lte": "now"
}
}
}
}
}
}
上述的查询,我们可以转换为以下查询,使得可以缓存近 1 分钟内相同的查询请求
GET test21_date/_search
{
"query": {
"constant_score": {
"filter": {
"range": {
"my_date": {
"gte": "now-1h/m",
"lte": "now/m"
}
}
}
}
}
}
2.10 选择合适的副本数
假如 ES 集群有 num_nodes
个节点,总共有 num_primaries
个主分片,并且希望能够一次处理 max_failures
个节点故障。则合适的副本数量为:
max(max_failures, ceil(num_nodes / num_primaries) - 1)