推荐一个同步Mysql数据到Elasticsearch的工具

把Mysql的数据同步到Elasticsearch是个很常见的需求,但在Github里找到的同步工具用起来或多或少都有些别扭。 例如:某记录内容为"aaa|bbb|ccc",将其按|分割成数组同步到es,这样的简单任务都难以实现,再加上配置繁琐,文档语焉不详... 所以我写了个同步工具MysqlsMom:力求用最简单的配置完成复杂的同步任务。目前除了我所在的部门,也有越来越多的互联网公司在生产环境中使用该工具了。 欢迎各位大佬进行试用并提出意见,任何建议、鼓励、批评都受到欢迎。 github: https://github.com/m358807551/mysqlsmom MysqlsMom.jpeg

简介:同步mysql数据到elasticsearch的工具; QQ、微信:358807551

特点

  1. 纯Python编写;
  2. 有全量、增量更新两种模式;
  3. 全量更新只占用少量内存;支持通过sql语句同步数据;
  4. 增量更新自动断点续传;
  5. 取自mysql的数据可经过一系列自定义函数的处理后再同步至elasticsearch;
  6. 能用非常简单的配置完成复杂的同步任务;

环境

  • python2.7;
  • 如需增量同步,需要mysql开启binlog(binlog-format=row)且本地开启redis;

快速开始

全量同步MySql数据到es

  1. clone 项目到本地;

  2. 安装依赖;

    cd mysqlsmom
    pip install -r requirements.txt

    默认支持 elasticsearch-2.4版本,支持其它版本请运行(将5.4换成需要的elasticsearch版本)

    pip install --upgrade elasticsearch==5.4
  3. 编辑 ./config/example_init.py,按注释提示修改配置;

    # coding=utf-8
    
    STREAM = "INIT"
    
    # 修改数据库连接
    CONNECTION = {
       'host': '127.0.0.1',
       'port': 3306,
       'user': 'root',
       'passwd': ''
    }
    
    # 修改elasticsearch节点
    NODES = [{"host": "127.0.0.1", "port": 9200}]
    
    TASKS = [
       {
           "stream": {
               "database": "test_db",  # 在此数据库执行sql语句
               "sql": "select * from person"  # 将该sql语句选中的数据同步到 elasticsearch
           },
           "jobs": [
               {
                   "actions": ["insert", "update"],
                   "pipeline": [
                       {"set_id": {"field": "id"}}  # 默认设置 id字段的值 为elasticsearch中的文档id
                   ],
                   "dest": {
                       "es": {
                           "action": "upsert",
                           "index": "test_index",   # 设置 index
                           "type": "test",          # 设置 type
                           "nodes": NODES
                       }
                   }
               }
           ]
       }
    ]
  4. 运行

    cd mysqlsmom
    python mysqlsmom.py ./config/example_init.py

    等待同步完成即可;

增量同步MySql数据到es

  1. 确保要增量同步的MySql数据库开启binlog,且本地开启redis(为了存储最后一次读到的binlog文件名及读到的位置。未来可能支持本地文件存储该信息。)

  2. 下载项目到本地,且安装好依赖后,编辑 ./config/example_init.py,按注释提示修改配置;

    # coding=utf-8
    
    STREAM = "BINLOG"
    SERVER_ID = 99  # 确保每个用于binlog同步的配置文件的SERVER_ID不同;
    SLAVE_UUID = __name__
    
    # 配置开启binlog权限的MySql连接
    BINLOG_CONNECTION = {
       'host': '127.0.0.1',
       'port': 3306,
       'user': 'root',
       'passwd': ''
    }
    
    # 配置es节点
    NODES = [{"host": "127.0.0.1", "port": 9200}]
    
    TASKS = [
       {
           "stream": {
               "database": "test_db",  # [table]所在的数据库
               "table": "person"  # 监控该表的binlog
           },
           "jobs": [
               {
                   "actions": ["insert", "update"],
                   "pipeline": [
                       {"only_fields": {"fields": ["id", "name", "age"]}},  # 只同步这些字段到es,注释掉该行则同步全部字段的值到es
                       {"set_id": {"field": "id"}}  # 设置es中文档_id的值取自 id(或根据需要更改)字段
                   ],
                   "dest": {
                       "es": {
                           "action": "upsert",
                           "index": "test_index",  # 设置 index
                           "type": "test",         # 设置 type
                           "nodes": NODES
                       }
                   }
               }
           ]
       }
    ]
  3. 运行

    cd mysqlsmom
    python mysqlsmom.py ./config/example_binlog.py

    该进程会一直运行,实时同步新增和更改后的数据到elasticsearch;

    注意:第一次运行该进程时不会同步MySql中已存在的数据,从第二次运行开始,将接着上次同步停止时的位置继续同步;

    同步旧数据请看全量同步MySql数据到es

组织架构

all.png

Pipeline

如果需要从Mysql获取数据再进行特殊处理再同步到elasticsearch,pipeline组件会派上用场。

无论数据来自于全量同步的Sql语句或是通过实时分析binlog。

例如:

  • 只同步某些字段到es

    "pipeline": [
    {"only_fields": {"fields": ["id", "name"]}}, # 只同步 id 和 name字段
      ...
    ]
  • 重命名字段

    "pipeline": [
    {"replace_fields": {"name": ["name1", "name2"]}}, # 将name重命名为name1和name2
      ...
    ]
  • 甚至可以执行跨库数据库查询

    "pipeline": [
    {
        "do_sql": {
            "database": "db2",
            "connection": CONNECTION2,
            "sql": "select company, personid from company_manager where personid = {id}"  # id 的值会自动替换
        }
    }
      ...
    ]

支持编写自定义函数,只需在 row_handlers.py 中加入,之后可在pipeline中配置调用。

row_handlers.py中预定义了一些数据处理函数,但可能需要自定义的情况更多。

常见问题

能否把数据同步到多个es索引?

目前增量同步支持,只需修改配置文件中的[dest]

"dest": [
        {
            "es": {
            "action": "upsert",
            "index": "index1",  # 同步到 es index1.type1
            "type": "type1",
            "nodes": NODES
            }
        },
        {
            "es": {
            "action": "upsert",
            "index": "index2",  # 同时同步到 es index1.type1
            "type": "type2",
            "nodes": NODES
            }
        }
 ]

全量同步很快会支持该功能;

为什么我的增量同步不及时?

  1. 连接本地数据库增量同步不及时

    该情况暂未收到过反馈,如能复现请联系作者。

  2. 连接线上数据库发现增量同步不及时

    2.1 推荐使用内网IP连接数据库。连接线上数据库(如开启在阿里、腾讯服务器上的Mysql)时,推荐使用内网IP地址,因为外网IP会受到带宽等限制导致获取binlog数据速度受限,最终可能造成同步延时。

待改进

  1. 据部分用户反馈,全量同步百万级以上的数据性能不佳。

未完待续

文档近期会大幅度更新完善,任何问题、建议都收到欢迎,请在issues留言,会在24小时内回复;或联系QQ、微信: 358807551;

继续阅读 »

把Mysql的数据同步到Elasticsearch是个很常见的需求,但在Github里找到的同步工具用起来或多或少都有些别扭。 例如:某记录内容为"aaa|bbb|ccc",将其按|分割成数组同步到es,这样的简单任务都难以实现,再加上配置繁琐,文档语焉不详... 所以我写了个同步工具MysqlsMom:力求用最简单的配置完成复杂的同步任务。目前除了我所在的部门,也有越来越多的互联网公司在生产环境中使用该工具了。 欢迎各位大佬进行试用并提出意见,任何建议、鼓励、批评都受到欢迎。 github: https://github.com/m358807551/mysqlsmom MysqlsMom.jpeg

简介:同步mysql数据到elasticsearch的工具; QQ、微信:358807551

特点

  1. 纯Python编写;
  2. 有全量、增量更新两种模式;
  3. 全量更新只占用少量内存;支持通过sql语句同步数据;
  4. 增量更新自动断点续传;
  5. 取自mysql的数据可经过一系列自定义函数的处理后再同步至elasticsearch;
  6. 能用非常简单的配置完成复杂的同步任务;

环境

  • python2.7;
  • 如需增量同步,需要mysql开启binlog(binlog-format=row)且本地开启redis;

快速开始

全量同步MySql数据到es

  1. clone 项目到本地;

  2. 安装依赖;

    cd mysqlsmom
    pip install -r requirements.txt

    默认支持 elasticsearch-2.4版本,支持其它版本请运行(将5.4换成需要的elasticsearch版本)

    pip install --upgrade elasticsearch==5.4
  3. 编辑 ./config/example_init.py,按注释提示修改配置;

    # coding=utf-8
    
    STREAM = "INIT"
    
    # 修改数据库连接
    CONNECTION = {
       'host': '127.0.0.1',
       'port': 3306,
       'user': 'root',
       'passwd': ''
    }
    
    # 修改elasticsearch节点
    NODES = [{"host": "127.0.0.1", "port": 9200}]
    
    TASKS = [
       {
           "stream": {
               "database": "test_db",  # 在此数据库执行sql语句
               "sql": "select * from person"  # 将该sql语句选中的数据同步到 elasticsearch
           },
           "jobs": [
               {
                   "actions": ["insert", "update"],
                   "pipeline": [
                       {"set_id": {"field": "id"}}  # 默认设置 id字段的值 为elasticsearch中的文档id
                   ],
                   "dest": {
                       "es": {
                           "action": "upsert",
                           "index": "test_index",   # 设置 index
                           "type": "test",          # 设置 type
                           "nodes": NODES
                       }
                   }
               }
           ]
       }
    ]
  4. 运行

    cd mysqlsmom
    python mysqlsmom.py ./config/example_init.py

    等待同步完成即可;

增量同步MySql数据到es

  1. 确保要增量同步的MySql数据库开启binlog,且本地开启redis(为了存储最后一次读到的binlog文件名及读到的位置。未来可能支持本地文件存储该信息。)

  2. 下载项目到本地,且安装好依赖后,编辑 ./config/example_init.py,按注释提示修改配置;

    # coding=utf-8
    
    STREAM = "BINLOG"
    SERVER_ID = 99  # 确保每个用于binlog同步的配置文件的SERVER_ID不同;
    SLAVE_UUID = __name__
    
    # 配置开启binlog权限的MySql连接
    BINLOG_CONNECTION = {
       'host': '127.0.0.1',
       'port': 3306,
       'user': 'root',
       'passwd': ''
    }
    
    # 配置es节点
    NODES = [{"host": "127.0.0.1", "port": 9200}]
    
    TASKS = [
       {
           "stream": {
               "database": "test_db",  # [table]所在的数据库
               "table": "person"  # 监控该表的binlog
           },
           "jobs": [
               {
                   "actions": ["insert", "update"],
                   "pipeline": [
                       {"only_fields": {"fields": ["id", "name", "age"]}},  # 只同步这些字段到es,注释掉该行则同步全部字段的值到es
                       {"set_id": {"field": "id"}}  # 设置es中文档_id的值取自 id(或根据需要更改)字段
                   ],
                   "dest": {
                       "es": {
                           "action": "upsert",
                           "index": "test_index",  # 设置 index
                           "type": "test",         # 设置 type
                           "nodes": NODES
                       }
                   }
               }
           ]
       }
    ]
  3. 运行

    cd mysqlsmom
    python mysqlsmom.py ./config/example_binlog.py

    该进程会一直运行,实时同步新增和更改后的数据到elasticsearch;

    注意:第一次运行该进程时不会同步MySql中已存在的数据,从第二次运行开始,将接着上次同步停止时的位置继续同步;

    同步旧数据请看全量同步MySql数据到es

组织架构

all.png

Pipeline

如果需要从Mysql获取数据再进行特殊处理再同步到elasticsearch,pipeline组件会派上用场。

无论数据来自于全量同步的Sql语句或是通过实时分析binlog。

例如:

  • 只同步某些字段到es

    "pipeline": [
    {"only_fields": {"fields": ["id", "name"]}}, # 只同步 id 和 name字段
      ...
    ]
  • 重命名字段

    "pipeline": [
    {"replace_fields": {"name": ["name1", "name2"]}}, # 将name重命名为name1和name2
      ...
    ]
  • 甚至可以执行跨库数据库查询

    "pipeline": [
    {
        "do_sql": {
            "database": "db2",
            "connection": CONNECTION2,
            "sql": "select company, personid from company_manager where personid = {id}"  # id 的值会自动替换
        }
    }
      ...
    ]

支持编写自定义函数,只需在 row_handlers.py 中加入,之后可在pipeline中配置调用。

row_handlers.py中预定义了一些数据处理函数,但可能需要自定义的情况更多。

常见问题

能否把数据同步到多个es索引?

目前增量同步支持,只需修改配置文件中的[dest]

"dest": [
        {
            "es": {
            "action": "upsert",
            "index": "index1",  # 同步到 es index1.type1
            "type": "type1",
            "nodes": NODES
            }
        },
        {
            "es": {
            "action": "upsert",
            "index": "index2",  # 同时同步到 es index1.type1
            "type": "type2",
            "nodes": NODES
            }
        }
 ]

全量同步很快会支持该功能;

为什么我的增量同步不及时?

  1. 连接本地数据库增量同步不及时

    该情况暂未收到过反馈,如能复现请联系作者。

  2. 连接线上数据库发现增量同步不及时

    2.1 推荐使用内网IP连接数据库。连接线上数据库(如开启在阿里、腾讯服务器上的Mysql)时,推荐使用内网IP地址,因为外网IP会受到带宽等限制导致获取binlog数据速度受限,最终可能造成同步延时。

待改进

  1. 据部分用户反馈,全量同步百万级以上的数据性能不佳。

未完待续

文档近期会大幅度更新完善,任何问题、建议都收到欢迎,请在issues留言,会在24小时内回复;或联系QQ、微信: 358807551;

收起阅读 »

Mmap fs可能让大索引访问变得缓慢

在一年多以前,我写过Elasticsearch 5 入坑指南一文,其中提到将生产的某个ES集群从2.4升级到5.0以后, 冷数据结点搜索性能变差,对大索引进行搜索的时候,io read会长时间飙高,导致系统load很重,甚至到无法响应的程度。
 
通过进一步分析,用Linux下的Sar -B命令,可以看到有大量的数据被pagein到内存。 虽然通过“试”的方法,定位到这个问题和5.0开始使用的mmap fs有关联,并且通过更改为nio fs以后得到解决,但问题的底层根源一直没找到。 
 
近期有空重新去看了一下这个问题, 在Github上发现一个对os底层更熟悉的人提交并分析了类似的问题 Avoid file cache trashing on Linux with mmapfs by using madvise 。  细读之后,感觉该文抓到了问题的本质,以下基于该文做个总结:
  1. mmap fs对比nio fs,省去了磁盘io上的系统调用,并且不需要在jvm内部做io缓存,也减轻了GC压力。 所以通常来说,mmapfs的性能应该更高。 这也是为什么lucene推荐使用mmap fs,并且ES从5.0开始做为默认的store配置的原因。
  2. 然而,mmap系统调用,在内核层面默认会有一个2MB的预读大小设置,也就是说,当映射了一个大文件以后,即使读取其中1k个字节,mmap也会预读取2MB的数据到缓存。 这种策略是基于文件的访问大多数是顺序的假设。
  3. 在ES这个特定的应用场景,如果某数据结点上索引不是很大,系统剩余缓存也足够,一般不会有问题。但是如果是大数据应用场景,典型的如海量的日志ELK应用,则可能对大索引的搜索聚合,产生较多的随机磁盘访问。 从而mmap的预读策略,可能会导致大量的无用数据从磁盘读取到系统缓存。 在系统可用的缓存不是非常宽裕的情况下,某些极端场景下,会导致热数据被过于频繁的踢出内存,再反复读入,让磁盘IO不堪重负。
  4. Lucene有一个NativePosixUtil.madvise(buffer,NativePosixUtil.RANDOM)的native调用,可以用于指导内核对mmap过的文件做读取的时候,禁用预读。 上文作者将该调用hack进lucene代码,做搜索对比测试。 结论是对于磁盘io和cache的消耗,niofs都要好于mmapfs,而patch过的mmapfs则比niofs更好。
  5. 作者的测试仅限于搜索,对于其他类型的io操作,如写入,merge没有做过详尽测试,因此不清楚利弊。
  6. ES官方开发人员认为这是一个有趣的发现,值得深入去探究。对于用户报告的mmap fs性能比nio fs更差的问题,猜测可能是在大索引读取的场景下,预读带来的额外开销,抵消了相对niofs节省的系统调用等开销。 
  7. ES官方提到Lucene已经有一种类似功能的store,叫做NativeUnixDirectory(显然ES目前还没有对这种store的支持),用户动手能力强的话,应该可以利用这个store自己写一个ES plugin。 另外提到JAVA 10提供了O_DIRECT to streams / channels ,似乎官方打算等这个出来以后再看怎么处理这个问题。
  8. 要注意,这个预读是mmap层面的,和块设备的预读是两回事。 我们曾经尝试过使用 blockdev --setra 这个linux命令取消块设备预读,证实无法解决这个问题。

 
结论: 如果ES结点上会存放海量的索引数据,经常会有大索引(如1TB+)的搜索聚合操作,使用NIOFS会更安全,可以避免很多怪异的性能问题。
 
继续阅读 »
在一年多以前,我写过Elasticsearch 5 入坑指南一文,其中提到将生产的某个ES集群从2.4升级到5.0以后, 冷数据结点搜索性能变差,对大索引进行搜索的时候,io read会长时间飙高,导致系统load很重,甚至到无法响应的程度。
 
通过进一步分析,用Linux下的Sar -B命令,可以看到有大量的数据被pagein到内存。 虽然通过“试”的方法,定位到这个问题和5.0开始使用的mmap fs有关联,并且通过更改为nio fs以后得到解决,但问题的底层根源一直没找到。 
 
近期有空重新去看了一下这个问题, 在Github上发现一个对os底层更熟悉的人提交并分析了类似的问题 Avoid file cache trashing on Linux with mmapfs by using madvise 。  细读之后,感觉该文抓到了问题的本质,以下基于该文做个总结:
  1. mmap fs对比nio fs,省去了磁盘io上的系统调用,并且不需要在jvm内部做io缓存,也减轻了GC压力。 所以通常来说,mmapfs的性能应该更高。 这也是为什么lucene推荐使用mmap fs,并且ES从5.0开始做为默认的store配置的原因。
  2. 然而,mmap系统调用,在内核层面默认会有一个2MB的预读大小设置,也就是说,当映射了一个大文件以后,即使读取其中1k个字节,mmap也会预读取2MB的数据到缓存。 这种策略是基于文件的访问大多数是顺序的假设。
  3. 在ES这个特定的应用场景,如果某数据结点上索引不是很大,系统剩余缓存也足够,一般不会有问题。但是如果是大数据应用场景,典型的如海量的日志ELK应用,则可能对大索引的搜索聚合,产生较多的随机磁盘访问。 从而mmap的预读策略,可能会导致大量的无用数据从磁盘读取到系统缓存。 在系统可用的缓存不是非常宽裕的情况下,某些极端场景下,会导致热数据被过于频繁的踢出内存,再反复读入,让磁盘IO不堪重负。
  4. Lucene有一个NativePosixUtil.madvise(buffer,NativePosixUtil.RANDOM)的native调用,可以用于指导内核对mmap过的文件做读取的时候,禁用预读。 上文作者将该调用hack进lucene代码,做搜索对比测试。 结论是对于磁盘io和cache的消耗,niofs都要好于mmapfs,而patch过的mmapfs则比niofs更好。
  5. 作者的测试仅限于搜索,对于其他类型的io操作,如写入,merge没有做过详尽测试,因此不清楚利弊。
  6. ES官方开发人员认为这是一个有趣的发现,值得深入去探究。对于用户报告的mmap fs性能比nio fs更差的问题,猜测可能是在大索引读取的场景下,预读带来的额外开销,抵消了相对niofs节省的系统调用等开销。 
  7. ES官方提到Lucene已经有一种类似功能的store,叫做NativeUnixDirectory(显然ES目前还没有对这种store的支持),用户动手能力强的话,应该可以利用这个store自己写一个ES plugin。 另外提到JAVA 10提供了O_DIRECT to streams / channels ,似乎官方打算等这个出来以后再看怎么处理这个问题。
  8. 要注意,这个预读是mmap层面的,和块设备的预读是两回事。 我们曾经尝试过使用 blockdev --setra 这个linux命令取消块设备预读,证实无法解决这个问题。

 
结论: 如果ES结点上会存放海量的索引数据,经常会有大索引(如1TB+)的搜索聚合操作,使用NIOFS会更安全,可以避免很多怪异的性能问题。
  收起阅读 »

_nodes/stats thread_pool 中的bulk 在版本6.3.0中没有了,是哪个版本中取消了吗?

_nodes/stats thread_pool   中的bulk 在版本6.3.0中没有了,是哪个版本中取消了吗?
_nodes/stats thread_pool   中的bulk 在版本6.3.0中没有了,是哪个版本中取消了吗?

我曲解Elasticsearch了吗?

我曲解了Elasticsearch,我以为是每个节点可以存放不同的数据,哈哈哈😄。既然不是这样,引发了我另一个思考,说是Elasticsearch能处理TB以及PB的数据,这样的话,一台存放PB级数据的机器该是个多“可怕”的配置。每个节点的数据都一样,这是真正意义的分布式吗?我觉得按Elasticsearch的概念只是利用了节点的硬件资源。我真心希望我的理解是错的,这样我将欢欣鼓舞。
继续阅读 »
我曲解了Elasticsearch,我以为是每个节点可以存放不同的数据,哈哈哈😄。既然不是这样,引发了我另一个思考,说是Elasticsearch能处理TB以及PB的数据,这样的话,一台存放PB级数据的机器该是个多“可怕”的配置。每个节点的数据都一样,这是真正意义的分布式吗?我觉得按Elasticsearch的概念只是利用了节点的硬件资源。我真心希望我的理解是错的,这样我将欢欣鼓舞。 收起阅读 »

聊聊ELASTICSEARCH的集群状态的管理和维护

注意 本文参考的ES 5.0.1的源码

1. 查看集群状态

可以通过API查看集群状态

GET /_cluster/state

大致可以得到如下内容 cluster_state version 集群状态数字版本号 每次更新version + 1 集群重启version不会置0(只会单调增)

state_uuid 是集群状态字符串版本号(UUID)

master_node master节点

nodes 该版本中的所有节点信息

routing_tablerouting_nodes 都是描述不同index上的shard在node上的分布关系

2. 集群状态的维护

可以阅读 .//core/src/main/java/org/elasticsearch/cluster/ClusterState.java

  • 2.1 ClusterState是不可变对象,每次状态变更都会产生新的ClusterState,它们拥有不同的版本号
  • 2.2 在ES中, 集群状态由Master维护,并且只能由master节点更新集群状态
  • 2.3 更新完成后,Master会把新版本的集群状态推送给集群的其它所有节点 对于publish,Master节点会根据它已知其它节点所拥有的集群状态版本,决定是执行 sendFullClusterState()还是sendClusterStateDiff(),  前者是全量推送,后者增量推送。

  • 2.4 对于使用Zen Discovery的情况,只要有minimumMasterNodes响应了Master节点的publish消息, 那么这次的commit就算成功

  The cluster state can be updated only on the master node. All updates are performed by on a single thread and controlled by the {@link ClusterService}. After every update the {@link Discovery#publish} method publishes new version of the cluster state to all other nodes in the cluster.   In the Zen Discovery it is handled in the {@link PublishClusterStateAction#publish} method

  • 2.5 另外需要补充的是Elasticsearch使用Gossip + Bully算法进行选主。Bully算法在具体实现中,不是简单选取节点ID小的节点, 首先要先比较ClusterState的版本。版本高的优先当选。(各个版本实现有不同,但都需要考虑候选节点的集群状态版本)
        /**
         * Elects a new master out of the possible nodes, returning it. Returns <tt>null</tt>
         * if no master has been elected.
         */
        public MasterCandidate electMaster(Collection<MasterCandidate> candidates) {
            assert hasEnoughCandidates(candidates);
            List<MasterCandidate> sortedCandidates = new ArrayList<>(candidates);
            sortedCandidates.sort(MasterCandidate::compare);
            return sortedCandidates.get(0);
        }
            /**
             * compares two candidates to indicate which the a better master.
             * A higher cluster state version is better
             *
             * @return -1 if c1 is a batter candidate, 1 if c2.
             */
            public static int compare(MasterCandidate c1, MasterCandidate c2) {
                // we explicitly swap c1 and c2 here. the code expects "better" is lower in a sorted
                // list, so if c2 has a higher cluster state version, it needs to come first.
                int ret = Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);
                if (ret == 0) {
                    ret = compareNodes(c1.getNode(), c2.getNode());
                }
                return ret;
            }
        }

参考资料

  1. cluster-state

欢迎光临我的个人博客 萌叔 | http://vearne.cc

继续阅读 »

注意 本文参考的ES 5.0.1的源码

1. 查看集群状态

可以通过API查看集群状态

GET /_cluster/state

大致可以得到如下内容 cluster_state version 集群状态数字版本号 每次更新version + 1 集群重启version不会置0(只会单调增)

state_uuid 是集群状态字符串版本号(UUID)

master_node master节点

nodes 该版本中的所有节点信息

routing_tablerouting_nodes 都是描述不同index上的shard在node上的分布关系

2. 集群状态的维护

可以阅读 .//core/src/main/java/org/elasticsearch/cluster/ClusterState.java

  • 2.1 ClusterState是不可变对象,每次状态变更都会产生新的ClusterState,它们拥有不同的版本号
  • 2.2 在ES中, 集群状态由Master维护,并且只能由master节点更新集群状态
  • 2.3 更新完成后,Master会把新版本的集群状态推送给集群的其它所有节点 对于publish,Master节点会根据它已知其它节点所拥有的集群状态版本,决定是执行 sendFullClusterState()还是sendClusterStateDiff(),  前者是全量推送,后者增量推送。

  • 2.4 对于使用Zen Discovery的情况,只要有minimumMasterNodes响应了Master节点的publish消息, 那么这次的commit就算成功

  The cluster state can be updated only on the master node. All updates are performed by on a single thread and controlled by the {@link ClusterService}. After every update the {@link Discovery#publish} method publishes new version of the cluster state to all other nodes in the cluster.   In the Zen Discovery it is handled in the {@link PublishClusterStateAction#publish} method

  • 2.5 另外需要补充的是Elasticsearch使用Gossip + Bully算法进行选主。Bully算法在具体实现中,不是简单选取节点ID小的节点, 首先要先比较ClusterState的版本。版本高的优先当选。(各个版本实现有不同,但都需要考虑候选节点的集群状态版本)
        /**
         * Elects a new master out of the possible nodes, returning it. Returns <tt>null</tt>
         * if no master has been elected.
         */
        public MasterCandidate electMaster(Collection<MasterCandidate> candidates) {
            assert hasEnoughCandidates(candidates);
            List<MasterCandidate> sortedCandidates = new ArrayList<>(candidates);
            sortedCandidates.sort(MasterCandidate::compare);
            return sortedCandidates.get(0);
        }
            /**
             * compares two candidates to indicate which the a better master.
             * A higher cluster state version is better
             *
             * @return -1 if c1 is a batter candidate, 1 if c2.
             */
            public static int compare(MasterCandidate c1, MasterCandidate c2) {
                // we explicitly swap c1 and c2 here. the code expects "better" is lower in a sorted
                // list, so if c2 has a higher cluster state version, it needs to come first.
                int ret = Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);
                if (ret == 0) {
                    ret = compareNodes(c1.getNode(), c2.getNode());
                }
                return ret;
            }
        }

参考资料

  1. cluster-state

欢迎光临我的个人博客 萌叔 | http://vearne.cc

收起阅读 »

如何解决ES的性能问题

Part4:如何解决ES的性能问题
本文是对一篇外文博客的翻译

This post is the final part of a 4-part series on monitoring Elasticsearch performance. Part 1 provides an overview of Elasticsearch and its key performance metrics, Part 2 explains how to collect these metrics, and Part 3describes how to monitor Elasticsearch with Datadog.

这篇文章是监控ES性能系列文章的最后一部分。第1部分概述了ES及其关键性能指标,第2部分解释了如何收集这些指标,第3部分描述了如何使用Datadog监视ES。

Like a car, Elasticsearch was designed to allow its users to get up and running quickly, without having to understand all of its inner workings. However, it’s only a matter of time before you run into engine trouble here or there. This article will walk through five common Elasticsearch challenges, and how to deal with them.

就像汽车一样,用户可以在无需了解其所有内部工作原理的情况下,快速地站起来并运行。然而,在这里或那里遇到引擎故障只是时间问题。本文将介绍五种常见的ES的挑战,以及如何处理它们。

Problem #1: My cluster status is red or yellow. What should I do?
问题#1:我的集群状态是红色或黄色。我应该做什么?


21.png


If you recall from Part 1, cluster status is reported as red if one or more primary shards (and its replicas) is missing, and yellow if one or more replica shards is missing. Normally, this happens when a node drops off the cluster for whatever reason (hardware failure, long garbage collection time, etc.). Once the node recovers, its shards will remain in an initializing state before they transition back to active status.

回顾第1部分,如果丢失一个或多个主分片(及其副本),集群状态将报告为红色;如果丢失一个或多个副本分片,则报告为黄色。通常,这种情况发生在节点出于某些原因(硬件故障、长时间的垃圾收集时间等)退出集群时。一旦节点恢复,它的分片在转换会活跃状态之前将保持初始化状态。

The number of initializing shards typically peaks when a node rejoins the cluster, and then drops back down as the shards transition into an active state, as shown in the graph below.

初始化碎片的数量通常在节点重新加入集群时达到峰值,然后随着分片转换为活跃状态而下降,如下图所示。

22.png


During this initialization period, your cluster state may transition from green to yellow or red until the shards on the recovering node regain active status. In many cases, a brief status change to yellow or red may not require any action on your part.

在此初始化期间,集群状态可能从绿色转变为黄色或红色,直到恢复节点上的分片重新恢复到活跃状态。在很多情况下,一个简短的状态变化为黄色或红色可能不需要你的任何行动。

23.png


However, if you notice that your cluster status is lingering in red or yellow state for an extended period of time, verify that the cluster is recognizing the correct number of Elasticsearch nodes, either by consulting Datadog’s dashboard or by querying the Cluster Health API detailed in Part 2.

但是,如果您注意到您的集群状态在红色或黄色状态中徘徊了很长一段时间,请通过查阅Datadog的仪表板或查询第2部分中详细介绍的集群健康API来验证集群是否识别了正确的ES节点数量。

24.png


If the number of active nodes is lower than expected, it means that at least one of your nodes lost its connection and hasn’t been able to rejoin the cluster. To find out which node(s) left the cluster, check the logs (located by default in the logs folder of your Elasticsearch home directory) for a line similar to the following:

如果活动节点的数量低于预期,则意味着至少有一个节点失去了连接,无法重新加入集群。要找出离开集群的节点,请检查日志(默认位于您的Elasticsearch home目录的logs文件夹中),查找与以下内容类似的行::
[TIMESTAMP] ... Cluster health status changed from [GREEN] to [RED]

Reasons for node failure can vary, ranging from hardware or hypervisor failures, to out-of-memory errors. Check any of the monitoring tools outlined here for unusual changes in performance metrics that may have occurred around the same time the node failed, such as a sudden spike in the current rate of search or indexing requests. Once you have an idea of what may have happened, if it is a temporary failure, you can try to get the disconnected node(s) to recover and rejoin the cluster. If it is a permanent failure, and you are not able to recover the node, you can add new nodes and let Elasticsearch take care of recovering from any available replica shards; replica shards can be promoted to primary shards and redistributed on the new nodes you just added.

节点失败的原因可能不同,从硬件失败,管理程序失败到内存不足的错误。检查监视工具,这些工具可能是在节点失败的同时出现的性能指标的异常变化,比如当前搜索或索引请求的速度突然激增。一旦您知道可能发生了什么,如果是临时故障,您可以尝试让断开连接的节点恢复并重新加入集群。如果是永久性故障,您无法恢复节点,您可以添加新节点,并让Elasticsearch负责从任何可用的副本分片中恢复,副本分片可以提升到主分片,并在刚刚添加的新节点上重新分布。

However, if you lost both the primary and replica copy of a shard, you can try to recover as much of the missing data as possible by using Elasticsearch’s snapshot and restore module. If you’re not already familiar with this module, it can be used to store snapshots of indices over time in a remote repository for backup purposes.

但是,如果您同时丢失了分片的主分片和副本,那么您可以使用ES的快照和恢复模块尽可能多地恢复丢失的数据。如果您还不熟悉这个模块,那么可以使用它在远程存储库中存储索引的快照,以便进行备份。

Problem #2: Help! Data nodes are running out of disk space
问题#2:数据节点空间将要耗尽


25.png


If all of your data nodes are running low on disk space, you will need to add more data nodes to your cluster. You will also need to make sure that your indices have enough primary shards to be able to balance their data across all those nodes.

如果所有数据节点的磁盘空间都很低,那么将需要向集群添加更多的数据节点。你还需要确保您的索引拥有足够的主分片,以便能够跨所有这些节点能够平衡它的数据。

However, if only certain nodes are running out of disk space, this is usually a sign that you initialized an index with too few shards. If an index is composed of a few very large shards, it’s hard for Elasticsearch to distribute these shards across nodes in a balanced manner.

但是,如果只有特定的节点耗尽了磁盘空间,这通常是你用了太多的分片在初始化索引的时候。如果一个索引是由一些非常大的分片组成的,那么用ES很难以一种平衡的方式在节点之间分布这些分片。

Elasticsearch takes available disk space into account when allocating shards to nodes. By default, it will not assign shards to nodes that have over 85 percent disk in use. In Datadog, you can set up a threshold alert to notify you when any individual data node’s disk space usage approaches 80 percent, which should give you enough time to take action.

当master将分片分配给节点时,ES会考虑到节点可用的磁盘空间。默认情况下,它不会将分片分配给使用超过85%磁盘的节点。在Datadog中,您可以设置一个阈值警报,当任何单个数据节点的磁盘空间使用量接近80%时通知您,这应该会给您足够的时间采取行动。

There are two remedies for low disk space. One is to remove outdated data and store it off the cluster. This may not be a viable option for all users, but, if you’re storing time-based data, you can store a snapshot of older indices’ data off-cluster for backup, and update the index settings to turn off replication for those indices.

对于低磁盘空间有两种补救方法。一种是删除过时的数据并将其存储在集群之外。对于所有用户来说,这可能不是一个可行的选择,但是,如果您正在存储基于时间的数据,您可以将旧索引的数据快照存储到集群之外进行备份,并更新索引设置,以关闭对这些索引的复制。

The second approach is the only option for you if you need to continue storing all of your data on the cluster: scaling vertically or horizontally. If you choose to scale vertically, that means upgrading your hardware. However, to avoid having to upgrade again down the line, you should take advantage of the fact that Elasticsearch was designed to scale horizontally. To better accommodate future growth, you may be better off reindexing the data and specifying more primary shards in the newly created index (making sure that you have enough nodes to evenly distribute the shards).

如果需要继续将所有数据存储在集群上,那么第二种方法是惟一的选择:垂直或横向地伸缩集群。如果选择垂直伸缩,就意味着升级硬件。然而,为了避免再次升级,最好使用ES的横向伸缩。为了更好地适应未来的增长,你最好对数据进行索引重建,并在新创建的索引中指定更多的主碎片(确保您有足够的节点来均匀分布碎片)。

Another way to scale horizontally is to roll over the index by creating a new index, and using an alias to join the two indices together under one namespace. Though there is technically no limit to how much data you can store on a single shard, Elasticsearch recommends a soft upper limit of 50 GB per shard, which you can use as a general guideline that signals when it’s time to start a new index.

横向扩展的另一种方法是创建一个新索引,并使用别名滚动改变索引。虽然从技术上讲,您可以在一个分片上存储多少数据没有限制,但Elasticsearch建议在每个碎片上设置一个50 GB的软上限,您可以将其作为一个通用指南,在开始创建新索引时发出信号。

Problem #3: My searches are taking too long to execute
问题#3:我的搜索执行时间太长了


Search performance varies widely according to what type of data is being searched and how each query is structured. Depending on the way your data is organized, you may need to experiment with a few different methods before finding one that will help speed up search performance. We’ll cover two of them here: custom routing and force merging.

根据搜索的数据类型以及每个查询的结构,搜索性能会有很大的不同。根据您的数据的组织方式,您可能需要在找到一个有助于提高搜索性能的方法之前尝试一些不同的方法。我们将介绍其中的两个:自定义路由和强制合并。

Typically, when a node receives a search request, it needs to communicate that request to a copy (either primary or replica) of every shard in the index. Custom routing allows you to store related data on the same shard, so that you only have to search a single shard to satisfy a query.

通常,当一个节点收到一个搜索请求时,它需要将该请求传递给索引中的每个分片的副本(主分片和副本分片)。自定义路由允许你将相关数据存储在同一个shard上,这样您只需要搜索一个分片来满足查询。

For example, you can store all of blogger1’s data on the same shard by specifying a _routing value in the mapping for the blogger type within your index, blog_index.

例如,你可以在索引blog_index中为blogger类型指定一个_routing值,从而将blogger1的所有数据存储在相同的分片上。

First, make sure _routing is required so that you don’t forget to specify a custom routing value whenever you index information of the blogger type.

首先,确保需要_routing,以便在索引blogger类型的信息时不会忘记指定一个定制的路由值。
curl -XPUT "localhost:9200/blog_index" -d '
{
"mappings": {
"blogger": {
"_routing": {
"required": true
}
}
}
}'

当您准备索引与blogger1相关的文档时,请指定路由值:
curl -XPUT "localhost:9200/blog_index/blogger/1?routing=blogger1" -d '
{
"comment": "blogger1 made this cool comment"
}'

Now, in order to search through blogger1’s comments, you will need to remember to specify the routing value in the query like this:

现在,为了搜索blogger1的评论,您需要记住在查询中指定如下的路由值:
curl -XGET "localhost:9200/blog_index/_search?routing=blogger1" -d '
{
"query": {
"match": {
"comment": {
"query": "cool comment"
}
}
}
}'

In Elasticsearch, every search request has to check every segment of each shard it hits. So once you have reduced the number of shards you’ll have to search, you can also reduce the number of segments per shard by triggering the Force Merge API on one or more of your indices. The Force Merge API (or Optimize API in versions prior to 2.1.0) prompts the segments in the index to continue merging until each shard’s segment count is reduced to max_num_segments (1, by default). It’s worth experimenting with this feature, as long as you account for the computational cost of triggering a high number of merges.

在ES中,每个搜索请求都必须检查它所命中的每个分片的每一段。一旦你可以减少了搜索的分片数量,你也可以通过在一个或多个索引上触发Force Merge API来减少每个分片的段数量。强制合并API(或在2.1.0之前的版本中优化API)提示索引中的段合并,直到每个分片的段计数减少到max_num_segment(默认为1)。考虑一下这个成本和查询的时间成本,值得对该特性进行试验。

When it comes to shards with a large number of segments, the force merge process becomes much more computationally expensive. For instance, force merging an index of 10,000 segments down to 5,000 segments doesn’t take much time, but merging 10,000 segments all the way down to one segment can take hours. The more merging that must occur, the more resources you take away from fulfilling search requests, which may defeat the purpose of calling a force merge in the first place. In any case, it’s usually a good idea to schedule a force merge during non-peak hours, such as overnight, when you don’t expect many search or indexing requests.

当涉及到索引具有大量的段,段合并过程的计算开销就会大得多。例如,强制合并10000个段的索引到5000个段并不需要花费太多时间,但是将10000个段一直合并到一个段需要花费数小时。合并越多,搜索请求越快,这是调用force merge的目的。在任何情况下,通常最好在非高峰时间(比如在一夜之间)安排一个force merge,这样就不会有太多的搜索或索引请求。

Problem #4: How can I speed up my index-heavy workload?
问题#4:怎样才能加快我的索引沉重的工作量?


Elasticsearch comes pre-configured with many settings that try to ensure that you retain enough resources for searching and indexing data. However, if your usage of Elasticsearch is heavily skewed towards writes, you may find that it makes sense to tweak certain settings to boost indexing performance, even if it means losing some search performance or data replication. Below, we will explore a number of methods to optimize your use case for indexing, rather than searching, data.

ES具有许多预先配置的设置,这些设置试图确保您保留足够的资源用于搜索和索引数据。但是,如果您对ES的使用严重偏向于写操作,可能会发现调整某些设置以提高索引性能是有意义的,即使这意味着丢失一些搜索性能或数据副本。下面,我们将探索一些方法来优化索引而不是优化搜索性能。

Shard allocation: As a high-level strategy, if you are creating an index that you plan to update frequently, make sure you designate enough primary shards so that you can spread the indexing load evenly across all of your nodes. The general recommendation is to allocate one primary shard per node in your cluster, and possibly two or more primary shards per node, but only if you have a lot of CPU and disk bandwidth on those nodes. However, keep in mind that shard overallocation adds overhead and may negatively impact search, since search requests need to hit every shard in the index. On the other hand, if you assign fewer primary shards than the number of nodes, you may create hotspots, as the nodes that contain those shards will need to handle more indexing requests than nodes that don’t contain any of the index’s shards.

分片分配:作为一种高级策略,如果你正在创建频繁更新索引的集群,请确保指定了足够的主分片,这样你就可以将索引负载均匀地分布到所有节点上。一般的建议是为集群中的每个节点分配一个主分片,可能为每个节点分配两个或多个主分片,但前提是这些节点上有大量的CPU和磁盘带宽。但是,请记住,分片过度分配会增加开销,并可能对搜索产生负面影响,因为搜索请求需要命中索引中的每个分片。另一方面,如果你分配的主碎片数量少于节点数量,那么您可能会创建热点(热节点),因为包含这些分片的节点将需要处理更多的索引请求,而不包含索引分片的节点将不做什么操作。

Disable merge throttling: Merge throttling is Elasticsearch’s automatic tendency to throttle indexing requests when it detects that merging is falling behind indexing. It makes sense to update your cluster settings to disable merge throttling (by setting indices.store.throttle.type to “none”) if you want to optimize indexing performance, not search. You can make this change persistent (meaning it will persist after a cluster restart) or transient (resets back to default upon restart), based on your use case.

禁用合并节流:合并节流是ES在检测到合并落后于索引时自动抑制索引请求的趋势。更新集群设置以禁用合并节流是有意义的(设置index .store.throttle.type为none)。这样做可以优化索引性能,而不是搜索。根据你的用例,你可以使这个设置为persist(意味着在集群重新启动之后它将持续)或transient(在重新启动时重新设置为默认)。

Increase the size of the indexing buffer: This setting (indices.memory.index_buffer_size) determines how full the buffer can get before its documents are written to a segment on disk. The default setting limits this value to 10 percent of the total heap in order to reserve more of the heap for serving search requests, which doesn’t help you if you’re using Elasticsearch primarily for indexing.

增加索引缓冲区的大小:此设置(indices.memory.index_buffer_size)确定将文档写到磁盘上的段之前缓冲区的容量。默认设置限制为总堆的10%,以便为服务搜索请求保留更多的堆,如果您主要是在使用Elasticsearch进行索引,这对你是没有帮助。

Index first, replicate later: When you initialize an index, specify zero replica shards in the index settings, and add replicas after you’re done indexing. This will boost indexing performance, but it can be a bit risky if the node holding the only copy of the data crashes before you have a chance to replicate it.

*先索引,后复制:初始化索引时,在索引设置中指定0个复制碎片,索引完成后添加副本。这将提高索引性能,但如果拥有数据惟一副本的节点在您有机会复制数据之前崩溃,则可能存在一些风险。

Refresh less frequently: Increase the refresh interval in the Index Settings API. By default, the index refresh process occurs every second, but during heavy indexing periods, reducing the refresh frequency can help alleviate some of the workload.

不经常刷新:增加索引设置API中的刷新间隔。默认情况下,索引refresh过程每秒钟发生一次,但是在索引不断更新的时期,减少刷新频率可以帮助减轻一些工作负载。

Tweak your translog settings: As of version 2.0, Elasticsearch will flush translog data to disk after every request, reducing the risk of data loss in the event of hardware failure. If you want to prioritize indexing performance over potential data loss, you can change index.translog.durability to async in the index settings. With this in place, the index will only commit writes to disk upon every sync_interval, rather than after each request, leaving more of its resources free to serve indexing requests.

调整您的translog设置:在2.0版本中,弹性搜索将在每次请求之后将translog数据刷新到磁盘,从而在硬件故障时降低数据丢失的风险。如果希望将索引性能优先于潜在的数据丢失,可以更改index.translog.durability为async。有了这一点,索引将在sync_interval上提交对磁盘的写操作,而不是在每个请求之后,从而使更多的资源可以用于索引请求。

For more suggestions on boosting indexing performance, check out this guide from Elastic.

有关提高索引性能的更多建议,请参阅《ES》。

Problem #5: What should I do about all these bulk thread pool rejections?
问题#5:对于所有这些大容量线程池拒绝,我应该怎么做?


26.png


Thread pool rejections are typically a sign that you are sending too many requests to your nodes, too quickly. If this is a temporary situation (for instance, you have to index an unusually large amount of data this week, and you anticipate that it will return to normal soon), you can try to slow down the rate of your requests. However, if you want your cluster to be able to sustain the current rate of requests, you will probably need to scale out your cluster by adding more data nodes. In order to utilize the processing power of the increased number of nodes, you should also make sure that your indices contain enough shards to be able to spread the load evenly across all of your nodes.

线程池的拒绝通常表明向节点发送了过多的请求或者请求速度太快。如果这是一个临时的情况(例如,本周必须索引超大量的数据,并且预期它将很快恢复正常),可以尝试降低请求的速度。但是,如果您希望集群能够维持当前的请求速率,您可能需要通过添加更多的数据节点来扩展集群。为了利用增加的节点数量的处理能力,还应该确保索引包含足够的分片,以便能够在所有节点上均匀地分配负载。

Go forth and optimize!
优化

Even more performance tips are available in Elasticsearch’s learning resources and documentation. Since results will vary depending on your particular use case and setup, you can test out different settings and indexing/querying strategies to determine which approaches work best for your clusters.

在ES的学习资源和文档中可以找到更多的性能技巧。由于结果将根据您的特定用例和设置而变化,您可以测试不同的设置和索引/查询策略,以确定哪种方法最适合您的集群。

As you experiment with these and other optimizations, make sure to watch your Elasticsearch dashboards closely to monitor the resulting impact on your clusters’ key Elasticsearch performance metrics.

当您尝试这些优化和其他优化时,请确保密切关注您的ES仪表盘,以监视由此对集群的关键ES性能指标的影响。

With a built-in Elasticsearch dashboard that highlights key cluster metrics, Datadog enables you to effectively monitor Elasticsearch in real-time. If you already have a Datadog account, you can set up the Elasticsearch integrationin minutes. If you don’t yet have a Datadog account, sign up for a free trialtoday.

有了一个内置的ES仪表盘,它突出关键的集群指标,Datadog使您能够实时监控弹性搜索。如果您已经有了一个Datadog帐户,那么您可以在几分钟内设置Elasticsearch集成。如果你还没有一个Datadog帐户,那么今天就注册一个免费试用。

Source Markdown for this post is available on GitHub. Questions, corrections, additions, etc.? Please let us know.
继续阅读 »
Part4:如何解决ES的性能问题
本文是对一篇外文博客的翻译

This post is the final part of a 4-part series on monitoring Elasticsearch performance. Part 1 provides an overview of Elasticsearch and its key performance metrics, Part 2 explains how to collect these metrics, and Part 3describes how to monitor Elasticsearch with Datadog.

这篇文章是监控ES性能系列文章的最后一部分。第1部分概述了ES及其关键性能指标,第2部分解释了如何收集这些指标,第3部分描述了如何使用Datadog监视ES。

Like a car, Elasticsearch was designed to allow its users to get up and running quickly, without having to understand all of its inner workings. However, it’s only a matter of time before you run into engine trouble here or there. This article will walk through five common Elasticsearch challenges, and how to deal with them.

就像汽车一样,用户可以在无需了解其所有内部工作原理的情况下,快速地站起来并运行。然而,在这里或那里遇到引擎故障只是时间问题。本文将介绍五种常见的ES的挑战,以及如何处理它们。

Problem #1: My cluster status is red or yellow. What should I do?
问题#1:我的集群状态是红色或黄色。我应该做什么?


21.png


If you recall from Part 1, cluster status is reported as red if one or more primary shards (and its replicas) is missing, and yellow if one or more replica shards is missing. Normally, this happens when a node drops off the cluster for whatever reason (hardware failure, long garbage collection time, etc.). Once the node recovers, its shards will remain in an initializing state before they transition back to active status.

回顾第1部分,如果丢失一个或多个主分片(及其副本),集群状态将报告为红色;如果丢失一个或多个副本分片,则报告为黄色。通常,这种情况发生在节点出于某些原因(硬件故障、长时间的垃圾收集时间等)退出集群时。一旦节点恢复,它的分片在转换会活跃状态之前将保持初始化状态。

The number of initializing shards typically peaks when a node rejoins the cluster, and then drops back down as the shards transition into an active state, as shown in the graph below.

初始化碎片的数量通常在节点重新加入集群时达到峰值,然后随着分片转换为活跃状态而下降,如下图所示。

22.png


During this initialization period, your cluster state may transition from green to yellow or red until the shards on the recovering node regain active status. In many cases, a brief status change to yellow or red may not require any action on your part.

在此初始化期间,集群状态可能从绿色转变为黄色或红色,直到恢复节点上的分片重新恢复到活跃状态。在很多情况下,一个简短的状态变化为黄色或红色可能不需要你的任何行动。

23.png


However, if you notice that your cluster status is lingering in red or yellow state for an extended period of time, verify that the cluster is recognizing the correct number of Elasticsearch nodes, either by consulting Datadog’s dashboard or by querying the Cluster Health API detailed in Part 2.

但是,如果您注意到您的集群状态在红色或黄色状态中徘徊了很长一段时间,请通过查阅Datadog的仪表板或查询第2部分中详细介绍的集群健康API来验证集群是否识别了正确的ES节点数量。

24.png


If the number of active nodes is lower than expected, it means that at least one of your nodes lost its connection and hasn’t been able to rejoin the cluster. To find out which node(s) left the cluster, check the logs (located by default in the logs folder of your Elasticsearch home directory) for a line similar to the following:

如果活动节点的数量低于预期,则意味着至少有一个节点失去了连接,无法重新加入集群。要找出离开集群的节点,请检查日志(默认位于您的Elasticsearch home目录的logs文件夹中),查找与以下内容类似的行::
[TIMESTAMP] ... Cluster health status changed from [GREEN] to [RED]

Reasons for node failure can vary, ranging from hardware or hypervisor failures, to out-of-memory errors. Check any of the monitoring tools outlined here for unusual changes in performance metrics that may have occurred around the same time the node failed, such as a sudden spike in the current rate of search or indexing requests. Once you have an idea of what may have happened, if it is a temporary failure, you can try to get the disconnected node(s) to recover and rejoin the cluster. If it is a permanent failure, and you are not able to recover the node, you can add new nodes and let Elasticsearch take care of recovering from any available replica shards; replica shards can be promoted to primary shards and redistributed on the new nodes you just added.

节点失败的原因可能不同,从硬件失败,管理程序失败到内存不足的错误。检查监视工具,这些工具可能是在节点失败的同时出现的性能指标的异常变化,比如当前搜索或索引请求的速度突然激增。一旦您知道可能发生了什么,如果是临时故障,您可以尝试让断开连接的节点恢复并重新加入集群。如果是永久性故障,您无法恢复节点,您可以添加新节点,并让Elasticsearch负责从任何可用的副本分片中恢复,副本分片可以提升到主分片,并在刚刚添加的新节点上重新分布。

However, if you lost both the primary and replica copy of a shard, you can try to recover as much of the missing data as possible by using Elasticsearch’s snapshot and restore module. If you’re not already familiar with this module, it can be used to store snapshots of indices over time in a remote repository for backup purposes.

但是,如果您同时丢失了分片的主分片和副本,那么您可以使用ES的快照和恢复模块尽可能多地恢复丢失的数据。如果您还不熟悉这个模块,那么可以使用它在远程存储库中存储索引的快照,以便进行备份。

Problem #2: Help! Data nodes are running out of disk space
问题#2:数据节点空间将要耗尽


25.png


If all of your data nodes are running low on disk space, you will need to add more data nodes to your cluster. You will also need to make sure that your indices have enough primary shards to be able to balance their data across all those nodes.

如果所有数据节点的磁盘空间都很低,那么将需要向集群添加更多的数据节点。你还需要确保您的索引拥有足够的主分片,以便能够跨所有这些节点能够平衡它的数据。

However, if only certain nodes are running out of disk space, this is usually a sign that you initialized an index with too few shards. If an index is composed of a few very large shards, it’s hard for Elasticsearch to distribute these shards across nodes in a balanced manner.

但是,如果只有特定的节点耗尽了磁盘空间,这通常是你用了太多的分片在初始化索引的时候。如果一个索引是由一些非常大的分片组成的,那么用ES很难以一种平衡的方式在节点之间分布这些分片。

Elasticsearch takes available disk space into account when allocating shards to nodes. By default, it will not assign shards to nodes that have over 85 percent disk in use. In Datadog, you can set up a threshold alert to notify you when any individual data node’s disk space usage approaches 80 percent, which should give you enough time to take action.

当master将分片分配给节点时,ES会考虑到节点可用的磁盘空间。默认情况下,它不会将分片分配给使用超过85%磁盘的节点。在Datadog中,您可以设置一个阈值警报,当任何单个数据节点的磁盘空间使用量接近80%时通知您,这应该会给您足够的时间采取行动。

There are two remedies for low disk space. One is to remove outdated data and store it off the cluster. This may not be a viable option for all users, but, if you’re storing time-based data, you can store a snapshot of older indices’ data off-cluster for backup, and update the index settings to turn off replication for those indices.

对于低磁盘空间有两种补救方法。一种是删除过时的数据并将其存储在集群之外。对于所有用户来说,这可能不是一个可行的选择,但是,如果您正在存储基于时间的数据,您可以将旧索引的数据快照存储到集群之外进行备份,并更新索引设置,以关闭对这些索引的复制。

The second approach is the only option for you if you need to continue storing all of your data on the cluster: scaling vertically or horizontally. If you choose to scale vertically, that means upgrading your hardware. However, to avoid having to upgrade again down the line, you should take advantage of the fact that Elasticsearch was designed to scale horizontally. To better accommodate future growth, you may be better off reindexing the data and specifying more primary shards in the newly created index (making sure that you have enough nodes to evenly distribute the shards).

如果需要继续将所有数据存储在集群上,那么第二种方法是惟一的选择:垂直或横向地伸缩集群。如果选择垂直伸缩,就意味着升级硬件。然而,为了避免再次升级,最好使用ES的横向伸缩。为了更好地适应未来的增长,你最好对数据进行索引重建,并在新创建的索引中指定更多的主碎片(确保您有足够的节点来均匀分布碎片)。

Another way to scale horizontally is to roll over the index by creating a new index, and using an alias to join the two indices together under one namespace. Though there is technically no limit to how much data you can store on a single shard, Elasticsearch recommends a soft upper limit of 50 GB per shard, which you can use as a general guideline that signals when it’s time to start a new index.

横向扩展的另一种方法是创建一个新索引,并使用别名滚动改变索引。虽然从技术上讲,您可以在一个分片上存储多少数据没有限制,但Elasticsearch建议在每个碎片上设置一个50 GB的软上限,您可以将其作为一个通用指南,在开始创建新索引时发出信号。

Problem #3: My searches are taking too long to execute
问题#3:我的搜索执行时间太长了


Search performance varies widely according to what type of data is being searched and how each query is structured. Depending on the way your data is organized, you may need to experiment with a few different methods before finding one that will help speed up search performance. We’ll cover two of them here: custom routing and force merging.

根据搜索的数据类型以及每个查询的结构,搜索性能会有很大的不同。根据您的数据的组织方式,您可能需要在找到一个有助于提高搜索性能的方法之前尝试一些不同的方法。我们将介绍其中的两个:自定义路由和强制合并。

Typically, when a node receives a search request, it needs to communicate that request to a copy (either primary or replica) of every shard in the index. Custom routing allows you to store related data on the same shard, so that you only have to search a single shard to satisfy a query.

通常,当一个节点收到一个搜索请求时,它需要将该请求传递给索引中的每个分片的副本(主分片和副本分片)。自定义路由允许你将相关数据存储在同一个shard上,这样您只需要搜索一个分片来满足查询。

For example, you can store all of blogger1’s data on the same shard by specifying a _routing value in the mapping for the blogger type within your index, blog_index.

例如,你可以在索引blog_index中为blogger类型指定一个_routing值,从而将blogger1的所有数据存储在相同的分片上。

First, make sure _routing is required so that you don’t forget to specify a custom routing value whenever you index information of the blogger type.

首先,确保需要_routing,以便在索引blogger类型的信息时不会忘记指定一个定制的路由值。
curl -XPUT "localhost:9200/blog_index" -d '
{
"mappings": {
"blogger": {
"_routing": {
"required": true
}
}
}
}'

当您准备索引与blogger1相关的文档时,请指定路由值:
curl -XPUT "localhost:9200/blog_index/blogger/1?routing=blogger1" -d '
{
"comment": "blogger1 made this cool comment"
}'

Now, in order to search through blogger1’s comments, you will need to remember to specify the routing value in the query like this:

现在,为了搜索blogger1的评论,您需要记住在查询中指定如下的路由值:
curl -XGET "localhost:9200/blog_index/_search?routing=blogger1" -d '
{
"query": {
"match": {
"comment": {
"query": "cool comment"
}
}
}
}'

In Elasticsearch, every search request has to check every segment of each shard it hits. So once you have reduced the number of shards you’ll have to search, you can also reduce the number of segments per shard by triggering the Force Merge API on one or more of your indices. The Force Merge API (or Optimize API in versions prior to 2.1.0) prompts the segments in the index to continue merging until each shard’s segment count is reduced to max_num_segments (1, by default). It’s worth experimenting with this feature, as long as you account for the computational cost of triggering a high number of merges.

在ES中,每个搜索请求都必须检查它所命中的每个分片的每一段。一旦你可以减少了搜索的分片数量,你也可以通过在一个或多个索引上触发Force Merge API来减少每个分片的段数量。强制合并API(或在2.1.0之前的版本中优化API)提示索引中的段合并,直到每个分片的段计数减少到max_num_segment(默认为1)。考虑一下这个成本和查询的时间成本,值得对该特性进行试验。

When it comes to shards with a large number of segments, the force merge process becomes much more computationally expensive. For instance, force merging an index of 10,000 segments down to 5,000 segments doesn’t take much time, but merging 10,000 segments all the way down to one segment can take hours. The more merging that must occur, the more resources you take away from fulfilling search requests, which may defeat the purpose of calling a force merge in the first place. In any case, it’s usually a good idea to schedule a force merge during non-peak hours, such as overnight, when you don’t expect many search or indexing requests.

当涉及到索引具有大量的段,段合并过程的计算开销就会大得多。例如,强制合并10000个段的索引到5000个段并不需要花费太多时间,但是将10000个段一直合并到一个段需要花费数小时。合并越多,搜索请求越快,这是调用force merge的目的。在任何情况下,通常最好在非高峰时间(比如在一夜之间)安排一个force merge,这样就不会有太多的搜索或索引请求。

Problem #4: How can I speed up my index-heavy workload?
问题#4:怎样才能加快我的索引沉重的工作量?


Elasticsearch comes pre-configured with many settings that try to ensure that you retain enough resources for searching and indexing data. However, if your usage of Elasticsearch is heavily skewed towards writes, you may find that it makes sense to tweak certain settings to boost indexing performance, even if it means losing some search performance or data replication. Below, we will explore a number of methods to optimize your use case for indexing, rather than searching, data.

ES具有许多预先配置的设置,这些设置试图确保您保留足够的资源用于搜索和索引数据。但是,如果您对ES的使用严重偏向于写操作,可能会发现调整某些设置以提高索引性能是有意义的,即使这意味着丢失一些搜索性能或数据副本。下面,我们将探索一些方法来优化索引而不是优化搜索性能。

Shard allocation: As a high-level strategy, if you are creating an index that you plan to update frequently, make sure you designate enough primary shards so that you can spread the indexing load evenly across all of your nodes. The general recommendation is to allocate one primary shard per node in your cluster, and possibly two or more primary shards per node, but only if you have a lot of CPU and disk bandwidth on those nodes. However, keep in mind that shard overallocation adds overhead and may negatively impact search, since search requests need to hit every shard in the index. On the other hand, if you assign fewer primary shards than the number of nodes, you may create hotspots, as the nodes that contain those shards will need to handle more indexing requests than nodes that don’t contain any of the index’s shards.

分片分配:作为一种高级策略,如果你正在创建频繁更新索引的集群,请确保指定了足够的主分片,这样你就可以将索引负载均匀地分布到所有节点上。一般的建议是为集群中的每个节点分配一个主分片,可能为每个节点分配两个或多个主分片,但前提是这些节点上有大量的CPU和磁盘带宽。但是,请记住,分片过度分配会增加开销,并可能对搜索产生负面影响,因为搜索请求需要命中索引中的每个分片。另一方面,如果你分配的主碎片数量少于节点数量,那么您可能会创建热点(热节点),因为包含这些分片的节点将需要处理更多的索引请求,而不包含索引分片的节点将不做什么操作。

Disable merge throttling: Merge throttling is Elasticsearch’s automatic tendency to throttle indexing requests when it detects that merging is falling behind indexing. It makes sense to update your cluster settings to disable merge throttling (by setting indices.store.throttle.type to “none”) if you want to optimize indexing performance, not search. You can make this change persistent (meaning it will persist after a cluster restart) or transient (resets back to default upon restart), based on your use case.

禁用合并节流:合并节流是ES在检测到合并落后于索引时自动抑制索引请求的趋势。更新集群设置以禁用合并节流是有意义的(设置index .store.throttle.type为none)。这样做可以优化索引性能,而不是搜索。根据你的用例,你可以使这个设置为persist(意味着在集群重新启动之后它将持续)或transient(在重新启动时重新设置为默认)。

Increase the size of the indexing buffer: This setting (indices.memory.index_buffer_size) determines how full the buffer can get before its documents are written to a segment on disk. The default setting limits this value to 10 percent of the total heap in order to reserve more of the heap for serving search requests, which doesn’t help you if you’re using Elasticsearch primarily for indexing.

增加索引缓冲区的大小:此设置(indices.memory.index_buffer_size)确定将文档写到磁盘上的段之前缓冲区的容量。默认设置限制为总堆的10%,以便为服务搜索请求保留更多的堆,如果您主要是在使用Elasticsearch进行索引,这对你是没有帮助。

Index first, replicate later: When you initialize an index, specify zero replica shards in the index settings, and add replicas after you’re done indexing. This will boost indexing performance, but it can be a bit risky if the node holding the only copy of the data crashes before you have a chance to replicate it.

*先索引,后复制:初始化索引时,在索引设置中指定0个复制碎片,索引完成后添加副本。这将提高索引性能,但如果拥有数据惟一副本的节点在您有机会复制数据之前崩溃,则可能存在一些风险。

Refresh less frequently: Increase the refresh interval in the Index Settings API. By default, the index refresh process occurs every second, but during heavy indexing periods, reducing the refresh frequency can help alleviate some of the workload.

不经常刷新:增加索引设置API中的刷新间隔。默认情况下,索引refresh过程每秒钟发生一次,但是在索引不断更新的时期,减少刷新频率可以帮助减轻一些工作负载。

Tweak your translog settings: As of version 2.0, Elasticsearch will flush translog data to disk after every request, reducing the risk of data loss in the event of hardware failure. If you want to prioritize indexing performance over potential data loss, you can change index.translog.durability to async in the index settings. With this in place, the index will only commit writes to disk upon every sync_interval, rather than after each request, leaving more of its resources free to serve indexing requests.

调整您的translog设置:在2.0版本中,弹性搜索将在每次请求之后将translog数据刷新到磁盘,从而在硬件故障时降低数据丢失的风险。如果希望将索引性能优先于潜在的数据丢失,可以更改index.translog.durability为async。有了这一点,索引将在sync_interval上提交对磁盘的写操作,而不是在每个请求之后,从而使更多的资源可以用于索引请求。

For more suggestions on boosting indexing performance, check out this guide from Elastic.

有关提高索引性能的更多建议,请参阅《ES》。

Problem #5: What should I do about all these bulk thread pool rejections?
问题#5:对于所有这些大容量线程池拒绝,我应该怎么做?


26.png


Thread pool rejections are typically a sign that you are sending too many requests to your nodes, too quickly. If this is a temporary situation (for instance, you have to index an unusually large amount of data this week, and you anticipate that it will return to normal soon), you can try to slow down the rate of your requests. However, if you want your cluster to be able to sustain the current rate of requests, you will probably need to scale out your cluster by adding more data nodes. In order to utilize the processing power of the increased number of nodes, you should also make sure that your indices contain enough shards to be able to spread the load evenly across all of your nodes.

线程池的拒绝通常表明向节点发送了过多的请求或者请求速度太快。如果这是一个临时的情况(例如,本周必须索引超大量的数据,并且预期它将很快恢复正常),可以尝试降低请求的速度。但是,如果您希望集群能够维持当前的请求速率,您可能需要通过添加更多的数据节点来扩展集群。为了利用增加的节点数量的处理能力,还应该确保索引包含足够的分片,以便能够在所有节点上均匀地分配负载。

Go forth and optimize!
优化

Even more performance tips are available in Elasticsearch’s learning resources and documentation. Since results will vary depending on your particular use case and setup, you can test out different settings and indexing/querying strategies to determine which approaches work best for your clusters.

在ES的学习资源和文档中可以找到更多的性能技巧。由于结果将根据您的特定用例和设置而变化,您可以测试不同的设置和索引/查询策略,以确定哪种方法最适合您的集群。

As you experiment with these and other optimizations, make sure to watch your Elasticsearch dashboards closely to monitor the resulting impact on your clusters’ key Elasticsearch performance metrics.

当您尝试这些优化和其他优化时,请确保密切关注您的ES仪表盘,以监视由此对集群的关键ES性能指标的影响。

With a built-in Elasticsearch dashboard that highlights key cluster metrics, Datadog enables you to effectively monitor Elasticsearch in real-time. If you already have a Datadog account, you can set up the Elasticsearch integrationin minutes. If you don’t yet have a Datadog account, sign up for a free trialtoday.

有了一个内置的ES仪表盘,它突出关键的集群指标,Datadog使您能够实时监控弹性搜索。如果您已经有了一个Datadog帐户,那么您可以在几分钟内设置Elasticsearch集成。如果你还没有一个Datadog帐户,那么今天就注册一个免费试用。

Source Markdown for this post is available on GitHub. Questions, corrections, additions, etc.? Please let us know. 收起阅读 »

ES内存分配规划

阅读本文前,请先阅读ES内存分析。 ES默认配置下,heap是存在超卖情况的。

类目 默认占比 是否常驻 淘汰策略(在控制大小情况下) 控制参数
query cache 10% LRU indices.queries.cache.size
request cache 1% LRU indices.requests.cache.size
fielddata cache 无限制 LRU indices.fielddata.cache.size
segment memory 无限制 不能通过参数控制
common space 70% GC 通过熔断器 indices.breaker.total.limit 限制

common space(可GC)

子类目 默认占比 控制参数
indexing buffer 10% indices.memory.index_buffer_size
request agg data 60% indices.breaker.request.limit
in-flight data 100% network.breaker.inflight_requests.limit

通过上表可知,segment memory是非常重要,而且是不可通过参数干预的内存空间,而cache部分则可以提升性能,可以被清除。common space 是运行时的动态空间,可以被GC。

综上所述,需要保证segment memory+cache+common space不超过100%。由于熔断器是按整个heap大小来计算的,所以如果segment memory 过大,仍然可能会导致OOM。为了减少这种情况的发生,需要预留足够空间给segment。 优化

  1. 限制fielddata大小,fielddata是针对text类型进行排序、聚合才用到。正常应该避免这种情况发生。
  2. 限制request agg data大小,这个参数会影响聚合使用的内存,如果触发熔断,业务需要进行优化。

内存分配

                                                                                                                                                                                                                                                                                         
         
segment memory

       

         
预留10%
       
         
       
         
fielddata cache
       
         
限制在20%
       
         
       
         
query cache
       
         
限制10%
       
         
       
         
request cache
       
         
限制1%
       
         
       
         
indexing buffer
       
         
限制10%
       
         
       
         
request agg data
       
         
限制1%
       
         
父熔断器配置30%,扣除fielddata,agg剩余的就是in-flight
       
         
in-flight data
       
         
限制9%
       

参数设置

indices.fielddata.cache.size:1%--需要重启节点

PUT _cluster/settings
{
  "persistent": {
    "indices.breaker.fielddata.limit":"20%",
    "indices.breaker.request.limit":"1%",
    "indices.breaker.total.limit":"70%"

  }
}
继续阅读 »

阅读本文前,请先阅读ES内存分析。 ES默认配置下,heap是存在超卖情况的。

类目 默认占比 是否常驻 淘汰策略(在控制大小情况下) 控制参数
query cache 10% LRU indices.queries.cache.size
request cache 1% LRU indices.requests.cache.size
fielddata cache 无限制 LRU indices.fielddata.cache.size
segment memory 无限制 不能通过参数控制
common space 70% GC 通过熔断器 indices.breaker.total.limit 限制

common space(可GC)

子类目 默认占比 控制参数
indexing buffer 10% indices.memory.index_buffer_size
request agg data 60% indices.breaker.request.limit
in-flight data 100% network.breaker.inflight_requests.limit

通过上表可知,segment memory是非常重要,而且是不可通过参数干预的内存空间,而cache部分则可以提升性能,可以被清除。common space 是运行时的动态空间,可以被GC。

综上所述,需要保证segment memory+cache+common space不超过100%。由于熔断器是按整个heap大小来计算的,所以如果segment memory 过大,仍然可能会导致OOM。为了减少这种情况的发生,需要预留足够空间给segment。 优化

  1. 限制fielddata大小,fielddata是针对text类型进行排序、聚合才用到。正常应该避免这种情况发生。
  2. 限制request agg data大小,这个参数会影响聚合使用的内存,如果触发熔断,业务需要进行优化。

内存分配

                                                                                                                                                                                                                                                                                         
         
segment memory

       

         
预留10%
       
         
       
         
fielddata cache
       
         
限制在20%
       
         
       
         
query cache
       
         
限制10%
       
         
       
         
request cache
       
         
限制1%
       
         
       
         
indexing buffer
       
         
限制10%
       
         
       
         
request agg data
       
         
限制1%
       
         
父熔断器配置30%,扣除fielddata,agg剩余的就是in-flight
       
         
in-flight data
       
         
限制9%
       

参数设置

indices.fielddata.cache.size:1%--需要重启节点

PUT _cluster/settings
{
  "persistent": {
    "indices.breaker.fielddata.limit":"20%",
    "indices.breaker.request.limit":"1%",
    "indices.breaker.total.limit":"70%"

  }
}
收起阅读 »

ES内存使用分析及熔断器设置

内存占用

ES的JVM heap按使用场景分为可GC部分和常驻部分。 可GC部分内存会随着GC操作而被回收; 常驻部分不会被GC,通常使用LRU策略来进行淘汰; 内存占用情况如下图:

jvm.png

common space包括了indexing buffer和其他ES运行需要的class。indexing buffer由indices.memory.index_buffer_size参数控制, 默认最大占用10%,当full up后,该部分数据被刷入磁盘对应的Segments中。这部分空间是可以被回收反复利用的。

queryCache 是node级别的filter过滤器结果缓存,大小由indices.queries.cache.size 参数控制,默认10%。使用LRU淘汰策略。

requestCache是shard级别的query result缓存,通常 only requests of size 0 such as aggregations, counts and suggestions will be cached。使用LRU淘汰策略。通过indices.requests.cache.size参数控制,默认1%。设置后整个NODE都生效。

fieldDataCache,针对text字段,没有docValues属性(相当于列存储),当对text类型字段进行sort,agg时,需要将对应的字段内容全部加载到内存,这部分数据就放在fieldDataCache。通过indices.fielddata.cache.size 参数限制大小,默认不限制。这种情况下,占用内存会逐渐增多,直到触发熔断;新数据无法加载。

segmentsMemory ,缓存段信息,包括FST,Dimensional points for numeric range filters,Deleted documents bitset ,Doc values and stored fields codec formats等数据。这部分缓存是必须的,不能进行大小设置,通常跟index息息相关,close index、force merge均会释放部分空间。 可以通过命令

GET _cat/nodes?v&h=id,ip,port,r,ramPercent,ramCurrent,heapMax,heapCurrent,fielddataMemory,queryCacheMemory,requestCacheMemory,segmentsMemory

查看当前各块的使用情况。

熔断器

Elasticsearch 有一系列的断路器,它们都能保证内存不会超出限制:

  • indices.breaker.fielddata.limit fielddata 断路器默认设置堆的 60% 作为 fielddata 大小的上限。
  • indices.breaker.request.limit request 断路器估算需要完成其他请求部分的结构大小,例如创建一个聚合桶,默认限制是堆内存的 60%。它实际上是node level的一个统计值,统计的是这个结点上,各类查询聚合操作,需要申请的Bigarray的空间大小总和。 所以如果有一个聚合需要很大的空间,同时在执行的聚合可能也会被break掉。
  • indices.breaker.total.limit 父熔断,inflight、request(agg)和fielddata不会使用超过堆内存的 70%。
  • network.breaker.inflight requests.limit 限制当前通过HTTP等进来的请求使用内存不能超过Node内存的指定值。这个内存主要是限制请求内容的长度。 默认100%。
  • script.max_compilations_per_minute
  • 限制script并发执行数,默认值为15。

参考文档 https://www.elastic.co/guide/en/elasticsearch/reference/5.3/circuit-breaker.html#fielddata-circuit-breaker https://www.elastic.co/guide/cn/elasticsearch/guide/cn/_limiting_memory_usage.html http://zhengjianglong.leanote.com/post/ES%E8%AE%BE%E7%BD%AE

继续阅读 »

内存占用

ES的JVM heap按使用场景分为可GC部分和常驻部分。 可GC部分内存会随着GC操作而被回收; 常驻部分不会被GC,通常使用LRU策略来进行淘汰; 内存占用情况如下图:

jvm.png

common space包括了indexing buffer和其他ES运行需要的class。indexing buffer由indices.memory.index_buffer_size参数控制, 默认最大占用10%,当full up后,该部分数据被刷入磁盘对应的Segments中。这部分空间是可以被回收反复利用的。

queryCache 是node级别的filter过滤器结果缓存,大小由indices.queries.cache.size 参数控制,默认10%。使用LRU淘汰策略。

requestCache是shard级别的query result缓存,通常 only requests of size 0 such as aggregations, counts and suggestions will be cached。使用LRU淘汰策略。通过indices.requests.cache.size参数控制,默认1%。设置后整个NODE都生效。

fieldDataCache,针对text字段,没有docValues属性(相当于列存储),当对text类型字段进行sort,agg时,需要将对应的字段内容全部加载到内存,这部分数据就放在fieldDataCache。通过indices.fielddata.cache.size 参数限制大小,默认不限制。这种情况下,占用内存会逐渐增多,直到触发熔断;新数据无法加载。

segmentsMemory ,缓存段信息,包括FST,Dimensional points for numeric range filters,Deleted documents bitset ,Doc values and stored fields codec formats等数据。这部分缓存是必须的,不能进行大小设置,通常跟index息息相关,close index、force merge均会释放部分空间。 可以通过命令

GET _cat/nodes?v&h=id,ip,port,r,ramPercent,ramCurrent,heapMax,heapCurrent,fielddataMemory,queryCacheMemory,requestCacheMemory,segmentsMemory

查看当前各块的使用情况。

熔断器

Elasticsearch 有一系列的断路器,它们都能保证内存不会超出限制:

  • indices.breaker.fielddata.limit fielddata 断路器默认设置堆的 60% 作为 fielddata 大小的上限。
  • indices.breaker.request.limit request 断路器估算需要完成其他请求部分的结构大小,例如创建一个聚合桶,默认限制是堆内存的 60%。它实际上是node level的一个统计值,统计的是这个结点上,各类查询聚合操作,需要申请的Bigarray的空间大小总和。 所以如果有一个聚合需要很大的空间,同时在执行的聚合可能也会被break掉。
  • indices.breaker.total.limit 父熔断,inflight、request(agg)和fielddata不会使用超过堆内存的 70%。
  • network.breaker.inflight requests.limit 限制当前通过HTTP等进来的请求使用内存不能超过Node内存的指定值。这个内存主要是限制请求内容的长度。 默认100%。
  • script.max_compilations_per_minute
  • 限制script并发执行数,默认值为15。

参考文档 https://www.elastic.co/guide/en/elasticsearch/reference/5.3/circuit-breaker.html#fielddata-circuit-breaker https://www.elastic.co/guide/cn/elasticsearch/guide/cn/_limiting_memory_usage.html http://zhengjianglong.leanote.com/post/ES%E8%AE%BE%E7%BD%AE

收起阅读 »

玩转 Elasticsearch 的 SQL 功能

最近发布的 Elasticsearch 6.3 包含了大家期待已久的 SQL 特性,今天给大家介绍一下具体的使用方法。

首先看看接口的支持情况

目前支持的 SQL 只能进行数据的查询只读操作,不能进行数据的修改,所以我们的数据插入还是要走之前的常规索引接口。

目前 Elasticsearch 的支持 SQL 命令只有以下几个:

命令 说明
DESC table 用来描述索引的字段属性
SHOW COLUMNS 功能同上,只是别名
SHOW FUNCTIONS 列出支持的函数列表,支持通配符过滤
SHOW TABLES 返回索引列表
SELECT .. FROM table_name WHERE .. GROUP BY .. HAVING .. ORDER BY .. LIMIT .. 用来执行查询的命令

我们分别来看一下各自怎么用,以及有什么效果吧,自己也可以动手试一下,看看。

首先,我们创建一条数据:

POST twitter/doc/
{
  "name":"medcl",
  "twitter":"sql is awesome",
  "date":"2018-07-27",
  "id":123
}

RESTful下调用SQL

在 ES 里面执行 SQL 语句,有三种方式,第一种是 RESTful 方式,第二种是 SQL-CLI 命令行工具,第三种是通过 JDBC 来连接 ES,执行的 SQL 语句其实都一样,我们先以 RESTful 方式来说明用法。

RESTful 的语法如下:

POST /_xpack/sql?format=txt
{
    "query": "SELECT * FROM twitter"
}

因为 SQL 特性是 xpack 的免费功能,所以是在 _xpack 这个路径下面,我们只需要把 SQL 语句传给 query 字段就行了,注意最后面不要加上 ; 结尾,注意是不要!

我们执行上面的语句,查询返回的结果如下:

          date          |      id       |     name      |    twitter    
------------------------+---------------+---------------+---------------
2018-07-27T00:00:00.000Z|123            |medcl          |sql is awesome 

ES 俨然已经变成 SQL 数据库了,我们再看看如何获取所有的索引列表:

POST /_xpack/sql?format=txt
{
    "query": "SHOW tables"
}

返回如下:

              name               |     type      
---------------------------------+---------------
.kibana                          |BASE TABLE     
.monitoring-alerts-6             |BASE TABLE     
.monitoring-es-6-2018.06.21      |BASE TABLE     
.monitoring-es-6-2018.06.26      |BASE TABLE     
.monitoring-es-6-2018.06.27      |BASE TABLE     
.monitoring-kibana-6-2018.06.21  |BASE TABLE     
.monitoring-kibana-6-2018.06.26  |BASE TABLE     
.monitoring-kibana-6-2018.06.27  |BASE TABLE     
.monitoring-logstash-6-2018.06.20|BASE TABLE     
.reporting-2018.06.24            |BASE TABLE     
.triggered_watches               |BASE TABLE     
.watcher-history-7-2018.06.20    |BASE TABLE     
.watcher-history-7-2018.06.21    |BASE TABLE     
.watcher-history-7-2018.06.26    |BASE TABLE     
.watcher-history-7-2018.06.27    |BASE TABLE     
.watches                         |BASE TABLE     
apache_elastic_example           |BASE TABLE     
forum-mysql                      |BASE TABLE     
twitter      

有点多,我们可以按名称过滤,如 twitt 开头的索引,注意通配符只支持 %_,分别表示多个和单个字符(什么,不记得了,回去翻数据库的书去!):

POST /_xpack/sql?format=txt
{
    "query": "SHOW TABLES 'twit%'"
}

POST /_xpack/sql?format=txt
{
    "query": "SHOW TABLES 'twitte_'"
}

上面返回的结果都是:

     name      |     type      
---------------+---------------
twitter        |BASE TABLE     

如果要查看该索引的字段和元数据,如下:

POST /_xpack/sql?format=txt
{
    "query": "DESC twitter"
}

返回:

    column     |     type      
---------------+---------------
date           |TIMESTAMP      
id             |BIGINT         
name           |VARCHAR        
name.keyword   |VARCHAR        
twitter        |VARCHAR        
twitter.keyword|VARCHAR        

都是动态生成的字段,包含了 .keyword 字段。 还能使用下面的命令来查看,主要是兼容 SQL 语法。

POST /_xpack/sql?format=txt
{
    "query": "SHOW COLUMNS IN twitter"
}

另外,如果不记得 ES 支持哪些函数,只需要执行下面的命令,即可得到完整列表:

SHOW FUNCTIONS

返回结果如下,也就是当前6.3版本支持的所有函数,如下:

      name      |     type      
----------------+---------------
AVG             |AGGREGATE      
COUNT           |AGGREGATE      
MAX             |AGGREGATE      
MIN             |AGGREGATE      
SUM             |AGGREGATE      
STDDEV_POP      |AGGREGATE      
VAR_POP         |AGGREGATE      
PERCENTILE      |AGGREGATE      
PERCENTILE_RANK |AGGREGATE      
SUM_OF_SQUARES  |AGGREGATE      
SKEWNESS        |AGGREGATE      
KURTOSIS        |AGGREGATE      
DAY_OF_MONTH    |SCALAR         
DAY             |SCALAR         
DOM             |SCALAR         
DAY_OF_WEEK     |SCALAR         
DOW             |SCALAR         
DAY_OF_YEAR     |SCALAR         
DOY             |SCALAR         
HOUR_OF_DAY     |SCALAR         
HOUR            |SCALAR         
MINUTE_OF_DAY   |SCALAR         
MINUTE_OF_HOUR  |SCALAR         
MINUTE          |SCALAR         
SECOND_OF_MINUTE|SCALAR         
SECOND          |SCALAR         
MONTH_OF_YEAR   |SCALAR         
MONTH           |SCALAR         
YEAR            |SCALAR         
WEEK_OF_YEAR    |SCALAR         
WEEK            |SCALAR         
ABS             |SCALAR         
ACOS            |SCALAR         
ASIN            |SCALAR         
ATAN            |SCALAR         
ATAN2           |SCALAR         
CBRT            |SCALAR         
CEIL            |SCALAR         
CEILING         |SCALAR         
COS             |SCALAR         
COSH            |SCALAR         
COT             |SCALAR         
DEGREES         |SCALAR         
E               |SCALAR         
EXP             |SCALAR         
EXPM1           |SCALAR         
FLOOR           |SCALAR         
LOG             |SCALAR         
LOG10           |SCALAR         
MOD             |SCALAR         
PI              |SCALAR         
POWER           |SCALAR         
RADIANS         |SCALAR         
RANDOM          |SCALAR         
RAND            |SCALAR         
ROUND           |SCALAR         
SIGN            |SCALAR         
SIGNUM          |SCALAR         
SIN             |SCALAR         
SINH            |SCALAR         
SQRT            |SCALAR         
TAN             |SCALAR         
SCORE           |SCORE          

同样支持通配符进行过滤:

POST /_xpack/sql?format=txt
{
    "query": "SHOW FUNCTIONS 'S__'"
}

结果:

     name      |     type      
---------------+---------------
SUM            |AGGREGATE      
SIN            |SCALAR         

那如果要进行模糊搜索呢,Elasticsearch 的搜索能力大家都知道,强!在 SQL 里面,可以用 match 关键字来写,如下:

POST /_xpack/sql?format=txt
{
    "query": "SELECT SCORE(), * FROM twitter WHERE match(twitter, 'sql is') ORDER BY id DESC"
}

最后,还能试试 SELECT 里面的一些其他操作,如过滤,别名,如下:

POST /_xpack/sql?format=txt
{
    "query": "SELECT SCORE() as score,name as myname FROM twitter as mytable where name = 'medcl' OR name ='elastic' limit 5"
}

结果如下:

     score     |    myname     
---------------+---------------
0.2876821      |medcl          

或是分组和函数计算:

POST /_xpack/sql?format=txt
{
    "query": "SELECT name,max(id) as max_id FROM twitter as mytable group by name limit 5"
}

结果如下:

     name      |    max_id     
---------------+---------------
medcl          |123.0          

SQL-CLI下的使用

上面的例子基本上把 SQL 的基本命令都介绍了一遍,很多情况下,用 RESTful 可能不是很方便,那么可以试试用 CLI 命令行工具来执行 SQL 语句,妥妥的 SQL 操作体验。

切换到命令行下,启动 cli 程序即可进入命令行交互提示界面,如下:

➜  elasticsearch-6.3.0 ./bin/elasticsearch-sql-cli

     .sssssss.`                     .sssssss.
  .:sXXXXXXXXXXo`                `ohXXXXXXXXXho.
 .yXXXXXXXXXXXXXXo`            `oXXXXXXXXXXXXXXX-
.XXXXXXXXXXXXXXXXXXo`        `oXXXXXXXXXXXXXXXXXX.
.XXXXXXXXXXXXXXXXXXXXo.    .oXXXXXXXXXXXXXXXXXXXXh
.XXXXXXXXXXXXXXXXXXXXXXo``oXXXXXXXXXXXXXXXXXXXXXXy
`yXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.
 `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
   `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
     `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
       `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
         `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
           .XXXXXXXXXXXXXXXXXXXXXXXXXo`
         .oXXXXXXXXXXXXXXXXXXXXXXXXo`
       `oXXXXXXXXXXXXXXXXXXXXXXXXo`   `odo`
     `oXXXXXXXXXXXXXXXXXXXXXXXXo`   `oXXXXXo`
   `oXXXXXXXXXXXXXXXXXXXXXXXXo`   `oXXXXXXXXXo`
 `oXXXXXXXXXXXXXXXXXXXXXXXXo`   `oXXXXXXXXXXXXXo`
`yXXXXXXXXXXXXXXXXXXXXXXXo`    oXXXXXXXXXXXXXXXXX.
.XXXXXXXXXXXXXXXXXXXXXXo`   `oXXXXXXXXXXXXXXXXXXXy
.XXXXXXXXXXXXXXXXXXXXo`     /XXXXXXXXXXXXXXXXXXXXX
.XXXXXXXXXXXXXXXXXXo`        `oXXXXXXXXXXXXXXXXXX-
 -XXXXXXXXXXXXXXXo`            `oXXXXXXXXXXXXXXXo`
  .oXXXXXXXXXXXo`                `oXXXXXXXXXXXo.
    `.sshXXyso`        SQL         `.sshXhss.`

sql> 

当你看到一个硕大的创口贴,表示 SQL 命令行已经准备就绪了,查看一下索引列表,不,数据表的列表:

15301043943573.jpg

各种操作妥妥的,上面已经测试过的命令就不在这里重复了,只是体验不一样罢了。

如果要连接远程的 ES 服务器,只需要启动命令行工具的时候,指定服务器地址,如果有加密,指定 keystone 文件,完整的帮助如下:

➜  elasticsearch-6.3.0 ./bin/elasticsearch-sql-cli --help
Elasticsearch SQL CLI

Non-option arguments:
uri                  

Option                   Description                                           
------                   -----------                                           
-c, --check <Boolean>    Enable initial connection check on startup (default:  
                           true)                                               
-d, --debug              Enable debug logging                                  
-h, --help               show help                                             
-k, --keystore_location  Location of a keystore to use when setting up SSL. If 
                           specified then the CLI will prompt for a keystore   
                           password. If specified when the uri isn't https then
                           an error is thrown.                                 
-s, --silent             show minimal output                                   
-v, --verbose            show verbose output  

JDBC 对接

JDBC 对接的能力,让我们可以与各个 SQL 生态系统打通,利用众多现成的基于 SQL 之上的工具来使用 Elasticsearch,我们以一个工具来举例。

和其他数据库一样,要使用 JDBC,要下载该数据库的 JDBC 的驱动,我们打开: https://www.elastic.co/downloads/jdbc-client

15301048139518.jpg

只有一个 zip 包下载链接,下载即可。

然后,我们这里使用 DbVisualizer 来连接 ES 进行操作,这是一个数据库的操作和分析工具,DbVisualizer 下载地址是:https://www.dbvis.com/

下载安装启动之后的程序主界面如下图:

15301049453527.jpg

我们如果要使用 ES 作为数据源,我们第一件事需要把 ES 的 JDBC 驱动添加到 DbVisualizer 的已知驱动里面。我们打开 DbVisualizer 的菜单【Tools】-> 【Driver Manager】,打开如下设置窗口:

15301054144234.jpg

点击绿色的加号按钮,新增一个名为 Elasticsearch-SQL 的驱动,url format 设置成 jdbc:es:,如下图:

15301054340439.jpg

然后点击上图黄色的文件夹按钮,添加我们刚刚下载好且解压之后的所有 jar 文件,如下:

15301055143574.jpg

添加完成之后,如下图:

15301055446598.jpg

就可以关闭这个 JDBC 驱动的管理窗口了。下面我们来连接到 ES 数据库。

选择主程序左侧的新建连接图标,打开向导,如下:

15301057385898.jpg

选择刚刚加入的 Elasticsearch-SQL 驱动:

15301057824336.jpg

设置连接字符串,此处没有登录信息,如果有可以对应的填上:

15301064989466.jpg

点击 Connect,即可连接到 ES,左侧导航可以展开看到对应的 ES 索引信息:

15301065711818.jpg

同样可以查看相应的库表结果和具体的数据:

15301066251658.jpg

用他自带的工具执行 SQL 也是不在话下:

15301068015599.jpg

同理,各种 ETL 工具和基于 SQL 的 BI 和可视化分析工具都能把 Elasticsearch 当做 SQL 数据库来连接获取数据了。

最后一个小贴士,如果你的索引名称包含横线,如 logstash-201811,只需要做一个用双引号包含,对双引号进行转义即可,如下:

POST /_xpack/sql?format=txt
{
"query":"SELECT COUNT(*) FROM \"logstash-*\""
}

关于 SQL 操作的文档在这里:

https://www.elastic.co/guide/en/elasticsearch/reference/current/sql-jdbc.html

Enjoy!

继续阅读 »

最近发布的 Elasticsearch 6.3 包含了大家期待已久的 SQL 特性,今天给大家介绍一下具体的使用方法。

首先看看接口的支持情况

目前支持的 SQL 只能进行数据的查询只读操作,不能进行数据的修改,所以我们的数据插入还是要走之前的常规索引接口。

目前 Elasticsearch 的支持 SQL 命令只有以下几个:

命令 说明
DESC table 用来描述索引的字段属性
SHOW COLUMNS 功能同上,只是别名
SHOW FUNCTIONS 列出支持的函数列表,支持通配符过滤
SHOW TABLES 返回索引列表
SELECT .. FROM table_name WHERE .. GROUP BY .. HAVING .. ORDER BY .. LIMIT .. 用来执行查询的命令

我们分别来看一下各自怎么用,以及有什么效果吧,自己也可以动手试一下,看看。

首先,我们创建一条数据:

POST twitter/doc/
{
  "name":"medcl",
  "twitter":"sql is awesome",
  "date":"2018-07-27",
  "id":123
}

RESTful下调用SQL

在 ES 里面执行 SQL 语句,有三种方式,第一种是 RESTful 方式,第二种是 SQL-CLI 命令行工具,第三种是通过 JDBC 来连接 ES,执行的 SQL 语句其实都一样,我们先以 RESTful 方式来说明用法。

RESTful 的语法如下:

POST /_xpack/sql?format=txt
{
    "query": "SELECT * FROM twitter"
}

因为 SQL 特性是 xpack 的免费功能,所以是在 _xpack 这个路径下面,我们只需要把 SQL 语句传给 query 字段就行了,注意最后面不要加上 ; 结尾,注意是不要!

我们执行上面的语句,查询返回的结果如下:

          date          |      id       |     name      |    twitter    
------------------------+---------------+---------------+---------------
2018-07-27T00:00:00.000Z|123            |medcl          |sql is awesome 

ES 俨然已经变成 SQL 数据库了,我们再看看如何获取所有的索引列表:

POST /_xpack/sql?format=txt
{
    "query": "SHOW tables"
}

返回如下:

              name               |     type      
---------------------------------+---------------
.kibana                          |BASE TABLE     
.monitoring-alerts-6             |BASE TABLE     
.monitoring-es-6-2018.06.21      |BASE TABLE     
.monitoring-es-6-2018.06.26      |BASE TABLE     
.monitoring-es-6-2018.06.27      |BASE TABLE     
.monitoring-kibana-6-2018.06.21  |BASE TABLE     
.monitoring-kibana-6-2018.06.26  |BASE TABLE     
.monitoring-kibana-6-2018.06.27  |BASE TABLE     
.monitoring-logstash-6-2018.06.20|BASE TABLE     
.reporting-2018.06.24            |BASE TABLE     
.triggered_watches               |BASE TABLE     
.watcher-history-7-2018.06.20    |BASE TABLE     
.watcher-history-7-2018.06.21    |BASE TABLE     
.watcher-history-7-2018.06.26    |BASE TABLE     
.watcher-history-7-2018.06.27    |BASE TABLE     
.watches                         |BASE TABLE     
apache_elastic_example           |BASE TABLE     
forum-mysql                      |BASE TABLE     
twitter      

有点多,我们可以按名称过滤,如 twitt 开头的索引,注意通配符只支持 %_,分别表示多个和单个字符(什么,不记得了,回去翻数据库的书去!):

POST /_xpack/sql?format=txt
{
    "query": "SHOW TABLES 'twit%'"
}

POST /_xpack/sql?format=txt
{
    "query": "SHOW TABLES 'twitte_'"
}

上面返回的结果都是:

     name      |     type      
---------------+---------------
twitter        |BASE TABLE     

如果要查看该索引的字段和元数据,如下:

POST /_xpack/sql?format=txt
{
    "query": "DESC twitter"
}

返回:

    column     |     type      
---------------+---------------
date           |TIMESTAMP      
id             |BIGINT         
name           |VARCHAR        
name.keyword   |VARCHAR        
twitter        |VARCHAR        
twitter.keyword|VARCHAR        

都是动态生成的字段,包含了 .keyword 字段。 还能使用下面的命令来查看,主要是兼容 SQL 语法。

POST /_xpack/sql?format=txt
{
    "query": "SHOW COLUMNS IN twitter"
}

另外,如果不记得 ES 支持哪些函数,只需要执行下面的命令,即可得到完整列表:

SHOW FUNCTIONS

返回结果如下,也就是当前6.3版本支持的所有函数,如下:

      name      |     type      
----------------+---------------
AVG             |AGGREGATE      
COUNT           |AGGREGATE      
MAX             |AGGREGATE      
MIN             |AGGREGATE      
SUM             |AGGREGATE      
STDDEV_POP      |AGGREGATE      
VAR_POP         |AGGREGATE      
PERCENTILE      |AGGREGATE      
PERCENTILE_RANK |AGGREGATE      
SUM_OF_SQUARES  |AGGREGATE      
SKEWNESS        |AGGREGATE      
KURTOSIS        |AGGREGATE      
DAY_OF_MONTH    |SCALAR         
DAY             |SCALAR         
DOM             |SCALAR         
DAY_OF_WEEK     |SCALAR         
DOW             |SCALAR         
DAY_OF_YEAR     |SCALAR         
DOY             |SCALAR         
HOUR_OF_DAY     |SCALAR         
HOUR            |SCALAR         
MINUTE_OF_DAY   |SCALAR         
MINUTE_OF_HOUR  |SCALAR         
MINUTE          |SCALAR         
SECOND_OF_MINUTE|SCALAR         
SECOND          |SCALAR         
MONTH_OF_YEAR   |SCALAR         
MONTH           |SCALAR         
YEAR            |SCALAR         
WEEK_OF_YEAR    |SCALAR         
WEEK            |SCALAR         
ABS             |SCALAR         
ACOS            |SCALAR         
ASIN            |SCALAR         
ATAN            |SCALAR         
ATAN2           |SCALAR         
CBRT            |SCALAR         
CEIL            |SCALAR         
CEILING         |SCALAR         
COS             |SCALAR         
COSH            |SCALAR         
COT             |SCALAR         
DEGREES         |SCALAR         
E               |SCALAR         
EXP             |SCALAR         
EXPM1           |SCALAR         
FLOOR           |SCALAR         
LOG             |SCALAR         
LOG10           |SCALAR         
MOD             |SCALAR         
PI              |SCALAR         
POWER           |SCALAR         
RADIANS         |SCALAR         
RANDOM          |SCALAR         
RAND            |SCALAR         
ROUND           |SCALAR         
SIGN            |SCALAR         
SIGNUM          |SCALAR         
SIN             |SCALAR         
SINH            |SCALAR         
SQRT            |SCALAR         
TAN             |SCALAR         
SCORE           |SCORE          

同样支持通配符进行过滤:

POST /_xpack/sql?format=txt
{
    "query": "SHOW FUNCTIONS 'S__'"
}

结果:

     name      |     type      
---------------+---------------
SUM            |AGGREGATE      
SIN            |SCALAR         

那如果要进行模糊搜索呢,Elasticsearch 的搜索能力大家都知道,强!在 SQL 里面,可以用 match 关键字来写,如下:

POST /_xpack/sql?format=txt
{
    "query": "SELECT SCORE(), * FROM twitter WHERE match(twitter, 'sql is') ORDER BY id DESC"
}

最后,还能试试 SELECT 里面的一些其他操作,如过滤,别名,如下:

POST /_xpack/sql?format=txt
{
    "query": "SELECT SCORE() as score,name as myname FROM twitter as mytable where name = 'medcl' OR name ='elastic' limit 5"
}

结果如下:

     score     |    myname     
---------------+---------------
0.2876821      |medcl          

或是分组和函数计算:

POST /_xpack/sql?format=txt
{
    "query": "SELECT name,max(id) as max_id FROM twitter as mytable group by name limit 5"
}

结果如下:

     name      |    max_id     
---------------+---------------
medcl          |123.0          

SQL-CLI下的使用

上面的例子基本上把 SQL 的基本命令都介绍了一遍,很多情况下,用 RESTful 可能不是很方便,那么可以试试用 CLI 命令行工具来执行 SQL 语句,妥妥的 SQL 操作体验。

切换到命令行下,启动 cli 程序即可进入命令行交互提示界面,如下:

➜  elasticsearch-6.3.0 ./bin/elasticsearch-sql-cli

     .sssssss.`                     .sssssss.
  .:sXXXXXXXXXXo`                `ohXXXXXXXXXho.
 .yXXXXXXXXXXXXXXo`            `oXXXXXXXXXXXXXXX-
.XXXXXXXXXXXXXXXXXXo`        `oXXXXXXXXXXXXXXXXXX.
.XXXXXXXXXXXXXXXXXXXXo.    .oXXXXXXXXXXXXXXXXXXXXh
.XXXXXXXXXXXXXXXXXXXXXXo``oXXXXXXXXXXXXXXXXXXXXXXy
`yXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.
 `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
   `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
     `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
       `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
         `oXXXXXXXXXXXXXXXXXXXXXXXXXXXXo`
           .XXXXXXXXXXXXXXXXXXXXXXXXXo`
         .oXXXXXXXXXXXXXXXXXXXXXXXXo`
       `oXXXXXXXXXXXXXXXXXXXXXXXXo`   `odo`
     `oXXXXXXXXXXXXXXXXXXXXXXXXo`   `oXXXXXo`
   `oXXXXXXXXXXXXXXXXXXXXXXXXo`   `oXXXXXXXXXo`
 `oXXXXXXXXXXXXXXXXXXXXXXXXo`   `oXXXXXXXXXXXXXo`
`yXXXXXXXXXXXXXXXXXXXXXXXo`    oXXXXXXXXXXXXXXXXX.
.XXXXXXXXXXXXXXXXXXXXXXo`   `oXXXXXXXXXXXXXXXXXXXy
.XXXXXXXXXXXXXXXXXXXXo`     /XXXXXXXXXXXXXXXXXXXXX
.XXXXXXXXXXXXXXXXXXo`        `oXXXXXXXXXXXXXXXXXX-
 -XXXXXXXXXXXXXXXo`            `oXXXXXXXXXXXXXXXo`
  .oXXXXXXXXXXXo`                `oXXXXXXXXXXXo.
    `.sshXXyso`        SQL         `.sshXhss.`

sql> 

当你看到一个硕大的创口贴,表示 SQL 命令行已经准备就绪了,查看一下索引列表,不,数据表的列表:

15301043943573.jpg

各种操作妥妥的,上面已经测试过的命令就不在这里重复了,只是体验不一样罢了。

如果要连接远程的 ES 服务器,只需要启动命令行工具的时候,指定服务器地址,如果有加密,指定 keystone 文件,完整的帮助如下:

➜  elasticsearch-6.3.0 ./bin/elasticsearch-sql-cli --help
Elasticsearch SQL CLI

Non-option arguments:
uri                  

Option                   Description                                           
------                   -----------                                           
-c, --check <Boolean>    Enable initial connection check on startup (default:  
                           true)                                               
-d, --debug              Enable debug logging                                  
-h, --help               show help                                             
-k, --keystore_location  Location of a keystore to use when setting up SSL. If 
                           specified then the CLI will prompt for a keystore   
                           password. If specified when the uri isn't https then
                           an error is thrown.                                 
-s, --silent             show minimal output                                   
-v, --verbose            show verbose output  

JDBC 对接

JDBC 对接的能力,让我们可以与各个 SQL 生态系统打通,利用众多现成的基于 SQL 之上的工具来使用 Elasticsearch,我们以一个工具来举例。

和其他数据库一样,要使用 JDBC,要下载该数据库的 JDBC 的驱动,我们打开: https://www.elastic.co/downloads/jdbc-client

15301048139518.jpg

只有一个 zip 包下载链接,下载即可。

然后,我们这里使用 DbVisualizer 来连接 ES 进行操作,这是一个数据库的操作和分析工具,DbVisualizer 下载地址是:https://www.dbvis.com/

下载安装启动之后的程序主界面如下图:

15301049453527.jpg

我们如果要使用 ES 作为数据源,我们第一件事需要把 ES 的 JDBC 驱动添加到 DbVisualizer 的已知驱动里面。我们打开 DbVisualizer 的菜单【Tools】-> 【Driver Manager】,打开如下设置窗口:

15301054144234.jpg

点击绿色的加号按钮,新增一个名为 Elasticsearch-SQL 的驱动,url format 设置成 jdbc:es:,如下图:

15301054340439.jpg

然后点击上图黄色的文件夹按钮,添加我们刚刚下载好且解压之后的所有 jar 文件,如下:

15301055143574.jpg

添加完成之后,如下图:

15301055446598.jpg

就可以关闭这个 JDBC 驱动的管理窗口了。下面我们来连接到 ES 数据库。

选择主程序左侧的新建连接图标,打开向导,如下:

15301057385898.jpg

选择刚刚加入的 Elasticsearch-SQL 驱动:

15301057824336.jpg

设置连接字符串,此处没有登录信息,如果有可以对应的填上:

15301064989466.jpg

点击 Connect,即可连接到 ES,左侧导航可以展开看到对应的 ES 索引信息:

15301065711818.jpg

同样可以查看相应的库表结果和具体的数据:

15301066251658.jpg

用他自带的工具执行 SQL 也是不在话下:

15301068015599.jpg

同理,各种 ETL 工具和基于 SQL 的 BI 和可视化分析工具都能把 Elasticsearch 当做 SQL 数据库来连接获取数据了。

最后一个小贴士,如果你的索引名称包含横线,如 logstash-201811,只需要做一个用双引号包含,对双引号进行转义即可,如下:

POST /_xpack/sql?format=txt
{
"query":"SELECT COUNT(*) FROM \"logstash-*\""
}

关于 SQL 操作的文档在这里:

https://www.elastic.co/guide/en/elasticsearch/reference/current/sql-jdbc.html

Enjoy!

收起阅读 »

有老铁测试了es6.3.0的sql功能吗?

sql
我单机装了个6.3.0
执行的时候老出现如下错误,有老铁遇到过吗?
 
我是这么来启动的
./elasticsearch-sql-cli http://127.0.0.1:9200
sql> show tables;
name | type
----------------+---------------
hello |BASE TABLE

sql> select * from hello;
Server error [Server encountered an error [Cannot extract value [deliveraddress.address] from source]. [SqlIllegalArgumentException[Cannot extract value [deliveraddress.address] from source]
at org.elasticsearch.xpack.sql.execution.search.extractor.FieldHitExtractor.extractFromSource(FieldHitExtractor.java:139)
at org.elasticsearch.xpack.sql.execution.search.extractor.FieldHitExtractor.extract(FieldHitExtractor.java:95)
at org.elasticsearch.xpack.sql.execution.search.SearchHitRowSet.getColumn(SearchHitRowSet.java:114)
at org.elasticsearch.xpack.sql.session.AbstractRowSet.column(AbstractRowSet.java:18)
 
 
 
这是测试数据的mapping
{
"test2": {
"properties": {
"deliveraddress": {
"properties": {
"phone_no": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"default": {
"type": "boolean"
},
"address": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"province": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"city": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"mapping_id": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"full_address": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"zip_code": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
}
}
},
"alipaywealth": {
"properties": {
"balance": {
"type": "long"
},
"total_quotient": {
"type": "long"
},
"huabei_creditamount": {
"type": "long"
},
"mapping_id": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"huabei_totalcreditamount": {
"type": "long"
},
"total_profit": {
"type": "long"
}
}
},
"id": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
}
}
}
}

这是测试数据
{
"_id": "5b1cbc7935eb6e0007a154bb",
"deliveraddress": [
{
"phone_no": "13*******98",
"default": true,
"address": "江苏省无asdads市徐***镇",
"province": "江苏",
"city": "无锡",
"mapping_id": "3561511087asdasd341",
"name": "b***",
"full_address": "湖asd***上7号",
"zip_code": "214400"
},
{
"phone_no": "15*******70",
"default": false,
"address": "江苏省苏州asdasdasd张家港经济技术开发区",
"province": "江苏",
"city": "苏州",
"mapping_id": "3561511asdasd505341",
"name": "a**",
"full_address": "新asd路***德***",
"zip_code": "215600"
}
],
"alipaywealth": {
"balance": 0,
"total_quotient": 0,
"huabei_creditamount": 500,
"mapping_id": "3561511asdsa63505341",
"huabei_totalcreditamount": 500,
"total_profit": 0
}
}
 
 
---
 
初步怀疑是不是不支持嵌套,数组啥的呀
 
然后我就翻了翻源码,发现了这个
 
我的错误就是在最后一个else里出现的
 
仔细一看,发现这个地方循环只要走了两次,或者前面的条件不成立就肯定会抛这个异常,这怎么看上去像是有点问题呢
 
    @SuppressWarnings("unchecked")
Object extractFromSource(Map<String, Object> map) {
Object value = map;
boolean first = true;
// each node is a key inside the map
for (String node : path) {
if (value == null) {
return null;
} else if (first || value instanceof Map) {
first = false;
value = ((Map<String, Object>) value).get(node);
} else {
throw new SqlIllegalArgumentException("Cannot extract value [{}] from source", fieldName);
}
}
return unwrapMultiValue(value);
}
继续阅读 »
我单机装了个6.3.0
执行的时候老出现如下错误,有老铁遇到过吗?
 
我是这么来启动的
./elasticsearch-sql-cli http://127.0.0.1:9200
sql> show tables;
name | type
----------------+---------------
hello |BASE TABLE

sql> select * from hello;
Server error [Server encountered an error [Cannot extract value [deliveraddress.address] from source]. [SqlIllegalArgumentException[Cannot extract value [deliveraddress.address] from source]
at org.elasticsearch.xpack.sql.execution.search.extractor.FieldHitExtractor.extractFromSource(FieldHitExtractor.java:139)
at org.elasticsearch.xpack.sql.execution.search.extractor.FieldHitExtractor.extract(FieldHitExtractor.java:95)
at org.elasticsearch.xpack.sql.execution.search.SearchHitRowSet.getColumn(SearchHitRowSet.java:114)
at org.elasticsearch.xpack.sql.session.AbstractRowSet.column(AbstractRowSet.java:18)
 
 
 
这是测试数据的mapping
{
"test2": {
"properties": {
"deliveraddress": {
"properties": {
"phone_no": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"default": {
"type": "boolean"
},
"address": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"province": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"city": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"mapping_id": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"full_address": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"zip_code": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
}
}
},
"alipaywealth": {
"properties": {
"balance": {
"type": "long"
},
"total_quotient": {
"type": "long"
},
"huabei_creditamount": {
"type": "long"
},
"mapping_id": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"huabei_totalcreditamount": {
"type": "long"
},
"total_profit": {
"type": "long"
}
}
},
"id": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
}
}
}
}

这是测试数据
{
"_id": "5b1cbc7935eb6e0007a154bb",
"deliveraddress": [
{
"phone_no": "13*******98",
"default": true,
"address": "江苏省无asdads市徐***镇",
"province": "江苏",
"city": "无锡",
"mapping_id": "3561511087asdasd341",
"name": "b***",
"full_address": "湖asd***上7号",
"zip_code": "214400"
},
{
"phone_no": "15*******70",
"default": false,
"address": "江苏省苏州asdasdasd张家港经济技术开发区",
"province": "江苏",
"city": "苏州",
"mapping_id": "3561511asdasd505341",
"name": "a**",
"full_address": "新asd路***德***",
"zip_code": "215600"
}
],
"alipaywealth": {
"balance": 0,
"total_quotient": 0,
"huabei_creditamount": 500,
"mapping_id": "3561511asdsa63505341",
"huabei_totalcreditamount": 500,
"total_profit": 0
}
}
 
 
---
 
初步怀疑是不是不支持嵌套,数组啥的呀
 
然后我就翻了翻源码,发现了这个
 
我的错误就是在最后一个else里出现的
 
仔细一看,发现这个地方循环只要走了两次,或者前面的条件不成立就肯定会抛这个异常,这怎么看上去像是有点问题呢
 
    @SuppressWarnings("unchecked")
Object extractFromSource(Map<String, Object> map) {
Object value = map;
boolean first = true;
// each node is a key inside the map
for (String node : path) {
if (value == null) {
return null;
} else if (first || value instanceof Map) {
first = false;
value = ((Map<String, Object>) value).get(node);
} else {
throw new SqlIllegalArgumentException("Cannot extract value [{}] from source", fieldName);
}
}
return unwrapMultiValue(value);
}
收起阅读 »

关于同义词检索方案的一点实践经验

最近一直在搞同义词搜索的问题,踩了一些坑,总结了一些经验,尤其是刚刚接触搜索和 ES,所以如果有不对的,或者不完备的地方也希望大家能提出改进意见。。。

下面是自己留下的文档记录:


需求

同义词检索也是搜索引擎必备的功能之一,例如,我们希望用户在搜索广东话的同时,也能找出和粤语有关的信息;用户在搜索苹果手机的同时,包含iPhone的内容也能被检索并呈现。

在现实生活中,相同语义的表述词汇往往有很多,而用户在检索的时候很难在一条 query 中将它们全部体现,所以识别和提供同义词检索显然可以获得更高的召回率。

需求剖析

在思考解决方案之前,我们不妨再来看看刚才提到的两个例子:

  1. 苹果手机iPhone
  2. 广东话粤语

我们先来看第一个,苹果手机iPhone

显然,这两个词是等价的,因为苹果公司发布的所有手机产品都叫 iPhone,而 iPhone 这个名字也没有被其他公司使用过。

于是,当用户搜索“苹果手机购买”的时候,我们也就有理由将它拆分成“苹果手机购买”和“iPhone购买”,分别进行检索,再将结果合并返回。


语言学中对这样的词组,称为是同义词中的相对同义词,或是等义词等义同义词。它们表达意思完全一致,在绝大多数语境中都可以相互替换,同时对上下文也不会产生影响。

这样的词组还有很多,例如:猫熊熊猫柚子文旦等等,这些等义词大抵来说有这样几种来源:

  1. 音节减缩形成:机枪机关枪坦克坦克车电扇电风扇
  2. 音译和音译形成:出租车的士维生素维他命
  3. 地域叫法不同,或新旧叫法:单车自行车脚踏车西红柿番茄马铃薯洋芋土豆黄瓜胡瓜
  4. 昵称代称:周杰伦周董
  5. 描述角度不同,学名方言差异:电脑计算机曲别针回形针

这些词组多以名词呈现,数量比较少,词组规模也较小,同时变化也很小。


接下来我们再来看第二组词:广东话粤语

广东话粤语这两个词代表的意思是相同的吗?它们也是可以相互替换的吗?

答案显然是否定的。

广东话从语义本身来说是一个比较粗糙的概念,它不仅指广东省内的粤语,还涵盖了潮汕话,客家话,雷州话等其他方言。而粤语却是一个非常严肃的概念,对语音语调都有非常详细的规定,不仅通用于广东省大部分地区,还包括广西、香港、澳门等地,甚至东南亚和北美。它们联系在于,大部分广东地区的人说的是粤语。

如此说来,给广东话粤语这样非常相似却又并不完全一致的词直接划上等号是有失偏颇的。当然,其实仔细考虑也不难发现,和广东话有相似之处却又不完全相同的词还有很多,例如:客家话广州话广府话等等。


语言学中把这样的词汇,称作是相对同义词,或是近义词。它们在意义上有一些相似之处,只能在特定的语境中进行替换。

它们的差别可能包括:

  1. 语义上:毁坏损坏(前者更严重),介绍说明(前者可以对人施加作用)
  2. 色彩上:团结勾结

对于这类相似却又不完全相同的近义词,在搜索的时候提供关联搜索是一个不错的方案。例如用户搜索“毁坏公物如何处罚”的时候,查询结果可以由90%的“损坏公物如何处罚”和10%的“毁坏公物如何处罚”查询结果合并后返回,从而获得更多的召回。

这些近义词以动词为主,不仅数量多,词组的规模也大,例如靠近的近义词可以是:靠拢逼近接近迫近等等。

解决思路

可替换的等义词问题中,我们可以直接使用 Elasticsearch 原生的 synonyms 功能来完成。虽说原生 synonyms 功能不支持热更新,而且需要将词典事先放进制定目录,不过好在这类等义词数量并不多,变化也并不大,尚且属于一劳永逸的任务。

对于不可直接替换的近义词问题,如果直接套用原生的 Synonyms 虽然可能会带来更多召回,但是查准率却骤降。

对于这类问题,我们期待的场景是,一旦发现用户 query 中的某个词有近义词,我们就将它拆分替换,成为多个 query 进行联合搜索。就像前面的例子:用户搜索“损坏公物如何处罚”的时候,查询结果可以由90%的“损坏公物如何处罚”和10%的“毁坏公物如何处罚”查询结果合并后返回。如此说来,使用 Elasticsearch 提供的 boosting_query 就成为了一个自然而然的想法。

不过稍加思考也不难发现,boosting_query 中 weight 的获得并不容易,也就是前面例子中的90%10%这组数字应该怎么设定,这也是近义词联合搜索中的重点。

先回到我们刚才的例子:当用户搜索“损坏公物如何处罚”的时候,我们本能地觉得用90%损坏10%毁坏合并在一起是“合理的”,这样的本能其实是来自于:我们主观地认为在检索“搞坏公物”这个事实的时候,90%的用户会使用毁坏来描述,10%的用户会使用损坏来描述。

简而言之,这组数字可以理解为用户群体描述同一个问题时,对词组选择的组成比例。再换个说法,也就是在当前这条 query 中,原词和近义词之间的可替换程度

再举一例,在“广东话入门”这条 query 中,从“表达学习语言”的语义上来看,广东话粤语差别并不大,这条 query 自然可以拆分成“广东话入门”和“粤语入门”,进行联合搜索,而且它们的 weight 甚至可以设置为 1:1 来获得更多合理的召回。

反过来,在“粤语歌曲推荐”这条 query 中,广东话的 weight 就需要慎重考虑,一方面是因为本身就没有“广东话歌曲”这种说法,另一方面也因为在广泛的语料中,歌曲广东话这两个词极少同时被提及。所以几遍是“粤语歌曲推荐”的拆分成分中有“广东话歌曲推荐”,weight 也需要被设置地非常低(倘若真的没有“粤语歌曲”相关的内容,推荐“广东话”的内容作为替补)。

说到这里,其实已经很明白了,语言模型是可以在这里被使用的,而语言模型的困惑度也与前面提到的 weight 一脉相承。

所以大致计算流程可以是:获得用户的query之后进行分词,在词组中寻找所有可能的同义词替换,将所有替换后的 query 分别放进语言模型中获得困惑度(或其他 metrics),依据它们来作为 boosting query 中的 weight。

graph TD;
广东话入门-->'广东话','入门';
'广东话','入门'-->_'广东话','入门'_;
'广东话','入门'-->_'粤语','入门'_;
_'广东话','入门'_-->语言模型;
_'粤语','入门'_-->语言模型;
语言模型-->PPL:0.65;
语言模型-->PPL:0.35;

对于这里语言模型的选择,可以使用传统的ngram,也可以使用双向的LSTM这样一些成熟的方案从语料中训练,也可以使用一些现成的方案:http://ai.baidu.com/tech/nlp/dnnlm_cn

继续阅读 »

最近一直在搞同义词搜索的问题,踩了一些坑,总结了一些经验,尤其是刚刚接触搜索和 ES,所以如果有不对的,或者不完备的地方也希望大家能提出改进意见。。。

下面是自己留下的文档记录:


需求

同义词检索也是搜索引擎必备的功能之一,例如,我们希望用户在搜索广东话的同时,也能找出和粤语有关的信息;用户在搜索苹果手机的同时,包含iPhone的内容也能被检索并呈现。

在现实生活中,相同语义的表述词汇往往有很多,而用户在检索的时候很难在一条 query 中将它们全部体现,所以识别和提供同义词检索显然可以获得更高的召回率。

需求剖析

在思考解决方案之前,我们不妨再来看看刚才提到的两个例子:

  1. 苹果手机iPhone
  2. 广东话粤语

我们先来看第一个,苹果手机iPhone

显然,这两个词是等价的,因为苹果公司发布的所有手机产品都叫 iPhone,而 iPhone 这个名字也没有被其他公司使用过。

于是,当用户搜索“苹果手机购买”的时候,我们也就有理由将它拆分成“苹果手机购买”和“iPhone购买”,分别进行检索,再将结果合并返回。


语言学中对这样的词组,称为是同义词中的相对同义词,或是等义词等义同义词。它们表达意思完全一致,在绝大多数语境中都可以相互替换,同时对上下文也不会产生影响。

这样的词组还有很多,例如:猫熊熊猫柚子文旦等等,这些等义词大抵来说有这样几种来源:

  1. 音节减缩形成:机枪机关枪坦克坦克车电扇电风扇
  2. 音译和音译形成:出租车的士维生素维他命
  3. 地域叫法不同,或新旧叫法:单车自行车脚踏车西红柿番茄马铃薯洋芋土豆黄瓜胡瓜
  4. 昵称代称:周杰伦周董
  5. 描述角度不同,学名方言差异:电脑计算机曲别针回形针

这些词组多以名词呈现,数量比较少,词组规模也较小,同时变化也很小。


接下来我们再来看第二组词:广东话粤语

广东话粤语这两个词代表的意思是相同的吗?它们也是可以相互替换的吗?

答案显然是否定的。

广东话从语义本身来说是一个比较粗糙的概念,它不仅指广东省内的粤语,还涵盖了潮汕话,客家话,雷州话等其他方言。而粤语却是一个非常严肃的概念,对语音语调都有非常详细的规定,不仅通用于广东省大部分地区,还包括广西、香港、澳门等地,甚至东南亚和北美。它们联系在于,大部分广东地区的人说的是粤语。

如此说来,给广东话粤语这样非常相似却又并不完全一致的词直接划上等号是有失偏颇的。当然,其实仔细考虑也不难发现,和广东话有相似之处却又不完全相同的词还有很多,例如:客家话广州话广府话等等。


语言学中把这样的词汇,称作是相对同义词,或是近义词。它们在意义上有一些相似之处,只能在特定的语境中进行替换。

它们的差别可能包括:

  1. 语义上:毁坏损坏(前者更严重),介绍说明(前者可以对人施加作用)
  2. 色彩上:团结勾结

对于这类相似却又不完全相同的近义词,在搜索的时候提供关联搜索是一个不错的方案。例如用户搜索“毁坏公物如何处罚”的时候,查询结果可以由90%的“损坏公物如何处罚”和10%的“毁坏公物如何处罚”查询结果合并后返回,从而获得更多的召回。

这些近义词以动词为主,不仅数量多,词组的规模也大,例如靠近的近义词可以是:靠拢逼近接近迫近等等。

解决思路

可替换的等义词问题中,我们可以直接使用 Elasticsearch 原生的 synonyms 功能来完成。虽说原生 synonyms 功能不支持热更新,而且需要将词典事先放进制定目录,不过好在这类等义词数量并不多,变化也并不大,尚且属于一劳永逸的任务。

对于不可直接替换的近义词问题,如果直接套用原生的 Synonyms 虽然可能会带来更多召回,但是查准率却骤降。

对于这类问题,我们期待的场景是,一旦发现用户 query 中的某个词有近义词,我们就将它拆分替换,成为多个 query 进行联合搜索。就像前面的例子:用户搜索“损坏公物如何处罚”的时候,查询结果可以由90%的“损坏公物如何处罚”和10%的“毁坏公物如何处罚”查询结果合并后返回。如此说来,使用 Elasticsearch 提供的 boosting_query 就成为了一个自然而然的想法。

不过稍加思考也不难发现,boosting_query 中 weight 的获得并不容易,也就是前面例子中的90%10%这组数字应该怎么设定,这也是近义词联合搜索中的重点。

先回到我们刚才的例子:当用户搜索“损坏公物如何处罚”的时候,我们本能地觉得用90%损坏10%毁坏合并在一起是“合理的”,这样的本能其实是来自于:我们主观地认为在检索“搞坏公物”这个事实的时候,90%的用户会使用毁坏来描述,10%的用户会使用损坏来描述。

简而言之,这组数字可以理解为用户群体描述同一个问题时,对词组选择的组成比例。再换个说法,也就是在当前这条 query 中,原词和近义词之间的可替换程度

再举一例,在“广东话入门”这条 query 中,从“表达学习语言”的语义上来看,广东话粤语差别并不大,这条 query 自然可以拆分成“广东话入门”和“粤语入门”,进行联合搜索,而且它们的 weight 甚至可以设置为 1:1 来获得更多合理的召回。

反过来,在“粤语歌曲推荐”这条 query 中,广东话的 weight 就需要慎重考虑,一方面是因为本身就没有“广东话歌曲”这种说法,另一方面也因为在广泛的语料中,歌曲广东话这两个词极少同时被提及。所以几遍是“粤语歌曲推荐”的拆分成分中有“广东话歌曲推荐”,weight 也需要被设置地非常低(倘若真的没有“粤语歌曲”相关的内容,推荐“广东话”的内容作为替补)。

说到这里,其实已经很明白了,语言模型是可以在这里被使用的,而语言模型的困惑度也与前面提到的 weight 一脉相承。

所以大致计算流程可以是:获得用户的query之后进行分词,在词组中寻找所有可能的同义词替换,将所有替换后的 query 分别放进语言模型中获得困惑度(或其他 metrics),依据它们来作为 boosting query 中的 weight。

graph TD;
广东话入门-->'广东话','入门';
'广东话','入门'-->_'广东话','入门'_;
'广东话','入门'-->_'粤语','入门'_;
_'广东话','入门'_-->语言模型;
_'粤语','入门'_-->语言模型;
语言模型-->PPL:0.65;
语言模型-->PPL:0.35;

对于这里语言模型的选择,可以使用传统的ngram,也可以使用双向的LSTM这样一些成熟的方案从语料中训练,也可以使用一些现成的方案:http://ai.baidu.com/tech/nlp/dnnlm_cn

收起阅读 »

ES5.3聚合内存溢出bug

有以下DSL

{
  "size" : 0,
  "query" : { },
  "_source" : false,
  "aggregations" : {
    "aggData" : {
      "terms" : {
        "field" : "url",
        "size" : 200,
        "min_doc_count" : 1,
        "shard_min_doc_count" : 0,
        "show_term_doc_count_error" : false,
        "order" : [
          {
            "PV" : "desc"
          }
        ]
      },
      "aggregations" : {
        "PV" : {
          "cardinality" : {
            "field" : "userssid"
          }
        }
      }
    }
  }
}

目的是对用户访问的URL进行分组统计,按独立用户数来排序。 执行后,data节点频繁FGC,内存无法回收,随即OOM,然后data节点脱离,集群变为red。 最初以为是cardinality精度问题导致内存使用过多,随即将precision_threshold设置为100,再次执行,内存使用量确实少了很多,但是还是用到GB级别。为了确认是否是cardinality问题,去掉外层聚合,直接执行

"aggregations" : {
        "PV" : {
          "cardinality" : {
            "field" : "userssid"
          }
        }
      }

发现响应非常快,而且内存占用只有KB级别。 再次单独执行外部聚合,发现也非常快,于是猜测是order导致,将order去掉,果然,如丝般顺滑,再也没有OOM。 为了解决这种OOM,首先想到的是熔断器。默认indices.breaker.request.limit配置是60%。改成10%后,触发熔断,集群正常,但是多点几次之后,data还是出现OOM了。 于是逐步调试,发现每执行1次,内存就增加一点,熔断返回后并没有被回收,直到OOM。基本确定是这里的order导致内存泄露了。 就在此时,同事反馈在5.6不会有这个问题,于是去查release note,果然在5.5的版本发现fix了这个问题。问题描述。 这个bug的根本原因是:

terms aggregations at the root level use the global_ordinals execution hint by default.
When all sub-aggregators can be run in breadth_first mode the collected buckets for these sub-aggs are dense (remapped after the initial pruning).
But if a sub-aggregator is not deferrable and needs to collect all buckets before pruning we don't remap global ords and the aggregator needs to deal with sparse buckets.
Most (if not all) aggregators expect dense buckets and uses this information to allocate memories.
This change forces the remap of the global ordinals but only when there is at least one sub-aggregator that cannot be deferred.

解决方案: 1,升级到5.5以上版本;

2,DSL增加"execution_hint":"map",属性。

{
  "size" : 0,
  "query" : { },
  "_source" : false,
  "aggregations" : {
    "aggData" : {
      "terms" : {
        "field" : "url",
        "size" : 200,
"execution_hint":"map",
        "min_doc_count" : 1,
        "shard_min_doc_count" : 0,
        "show_term_doc_count_error" : false,
        "order" : [
          {
            "PV" : "desc"
          }
        ]
      },
      "aggregations" : {
        "PV" : {
          "cardinality" : {
            "field" : "userssid"
          }
        }
      }
    }
  }
}
继续阅读 »

有以下DSL

{
  "size" : 0,
  "query" : { },
  "_source" : false,
  "aggregations" : {
    "aggData" : {
      "terms" : {
        "field" : "url",
        "size" : 200,
        "min_doc_count" : 1,
        "shard_min_doc_count" : 0,
        "show_term_doc_count_error" : false,
        "order" : [
          {
            "PV" : "desc"
          }
        ]
      },
      "aggregations" : {
        "PV" : {
          "cardinality" : {
            "field" : "userssid"
          }
        }
      }
    }
  }
}

目的是对用户访问的URL进行分组统计,按独立用户数来排序。 执行后,data节点频繁FGC,内存无法回收,随即OOM,然后data节点脱离,集群变为red。 最初以为是cardinality精度问题导致内存使用过多,随即将precision_threshold设置为100,再次执行,内存使用量确实少了很多,但是还是用到GB级别。为了确认是否是cardinality问题,去掉外层聚合,直接执行

"aggregations" : {
        "PV" : {
          "cardinality" : {
            "field" : "userssid"
          }
        }
      }

发现响应非常快,而且内存占用只有KB级别。 再次单独执行外部聚合,发现也非常快,于是猜测是order导致,将order去掉,果然,如丝般顺滑,再也没有OOM。 为了解决这种OOM,首先想到的是熔断器。默认indices.breaker.request.limit配置是60%。改成10%后,触发熔断,集群正常,但是多点几次之后,data还是出现OOM了。 于是逐步调试,发现每执行1次,内存就增加一点,熔断返回后并没有被回收,直到OOM。基本确定是这里的order导致内存泄露了。 就在此时,同事反馈在5.6不会有这个问题,于是去查release note,果然在5.5的版本发现fix了这个问题。问题描述。 这个bug的根本原因是:

terms aggregations at the root level use the global_ordinals execution hint by default.
When all sub-aggregators can be run in breadth_first mode the collected buckets for these sub-aggs are dense (remapped after the initial pruning).
But if a sub-aggregator is not deferrable and needs to collect all buckets before pruning we don't remap global ords and the aggregator needs to deal with sparse buckets.
Most (if not all) aggregators expect dense buckets and uses this information to allocate memories.
This change forces the remap of the global ordinals but only when there is at least one sub-aggregator that cannot be deferred.

解决方案: 1,升级到5.5以上版本;

2,DSL增加"execution_hint":"map",属性。

{
  "size" : 0,
  "query" : { },
  "_source" : false,
  "aggregations" : {
    "aggData" : {
      "terms" : {
        "field" : "url",
        "size" : 200,
"execution_hint":"map",
        "min_doc_count" : 1,
        "shard_min_doc_count" : 0,
        "show_term_doc_count_error" : false,
        "order" : [
          {
            "PV" : "desc"
          }
        ]
      },
      "aggregations" : {
        "PV" : {
          "cardinality" : {
            "field" : "userssid"
          }
        }
      }
    }
  }
}
收起阅读 »

highlight 返回来的title 不全 看截图


360截图20180609164613963.jpg

$params = [
'index' => 'soso_*',
'type' => 'links_1',
'body' => [
'query' => [
'match' => [
'title' => $kw
]
],
'highlight' => [
'pre_tags' => '<em>',
'post_tags' => '</em>',
'fields' => [
'title' => new \stdClass()
]
],
]
];
继续阅读 »

360截图20180609164613963.jpg

$params = [
'index' => 'soso_*',
'type' => 'links_1',
'body' => [
'query' => [
'match' => [
'title' => $kw
]
],
'highlight' => [
'pre_tags' => '<em>',
'post_tags' => '</em>',
'fields' => [
'title' => new \stdClass()
]
],
]
];
收起阅读 »

Elastic日报 第289期 (2018-06-01)

1、Elasticsearch snapshot 备份的使用方法
https://elasticsearch.cn/article/648
2、ElasticSearch + Canal 开发千万级的实时搜索系统
http://t.cn/R8vjBwD
3、【线下活动】2018-06-30 南京Elastic Meetup日程安排
https://elasticsearch.cn/article/647

编辑:铭毅天下
归档:https://elasticsearch.cn/article/649
订阅:https://tinyletter.com/elastic-daily
 
继续阅读 »
1、Elasticsearch snapshot 备份的使用方法
https://elasticsearch.cn/article/648
2、ElasticSearch + Canal 开发千万级的实时搜索系统
http://t.cn/R8vjBwD
3、【线下活动】2018-06-30 南京Elastic Meetup日程安排
https://elasticsearch.cn/article/647

编辑:铭毅天下
归档:https://elasticsearch.cn/article/649
订阅:https://tinyletter.com/elastic-daily
  收起阅读 »

Elasticsearch snapshot 备份的使用方法

常见的数据库都会提供备份的机制,以解决在数据库无法使用的情况下,可以开启新的实例,然后通过备份来恢复数据减少损失。虽然 Elasticsearch 有良好的容灾性,但由于以下原因,其依然需要备份机制。

  1. 数据灾备。在整个集群无法正常工作时,可以及时从备份中恢复数据。
  2. 归档数据。随着数据的积累,比如日志类的数据,集群的存储压力会越来越大,不管是内存还是磁盘都要承担数据增多带来的压力,此时我们往往会选择只保留最近一段时间的数据,比如1个月,而将1个月之前的数据删除。如果你不想删除这些数据,以备后续有查看的需求,那么你就可以将这些数据以备份的形式归档。
  3. 迁移数据。当你需要将数据从一个集群迁移到另一个集群时,也可以用备份的方式来实现。

Elasticsearch 做备份有两种方式,一是将数据导出成文本文件,比如通过 elasticdumpesm 等工具将存储在 Elasticsearch 中的数据导出到文件中。二是以备份 elasticsearch data 目录中文件的形式来做快照,也就是 Elasticsearch 中 snapshot 接口实现的功能。第一种方式相对简单,在数据量小的时候比较实用,当应对大数据量场景效率就大打折扣。我们今天就着重讲解下第二种备份的方式,即 snapshot api 的使用。

备份要解决备份到哪里、如何备份、何时备份和如何恢复的问题,那么我们接下来一个个解决。

1. 备份到哪里

在 Elasticsearch 中通过 repository 定义备份存储类型和位置,存储类型有共享文件系统、AWS 的 S3存储、HDFS、微软 Azure的存储、Google Cloud 的存储等,当然你也可以自己写代码实现国内阿里云的存储。我们这里以最简单的共享文件系统为例,你也可以在本地做实验。

首先,你要在 elasticsearch.yml 的配置文件中注明可以用作备份路径 path.repo ,如下所示:

path.repo: ["/mount/backups", "/mount/longterm_backups"]

配置好后,就可以使用 snapshot api 来创建一个 repository 了,如下我们创建一个名为 my_backup 的 repository。

PUT /_snapshot/my_backup
{
  "type": "fs",
  "settings": {
    "location": "/mount/backups/my_backup"
  }
}

之后我们就可以在这个 repository 中来备份数据了。

2. 如何备份

有了 repostiroy 后,我们就可以做备份了,也叫快照,也就是记录当下数据的状态。如下所示我们创建一个名为 snapshot_1 的快照。

PUT /_snapshot/my_backup/snapshot_1?wait_for_completion=true

wait_for_completion 为 true 是指该 api 在备份执行完毕后再返回结果,否则默认是异步执行的,我们这里为了立刻看到效果,所以设置了该参数,线上执行时不用设置该参数,让其在后台异步执行即可。

执行成功后会返回如下结果,用于说明备份的情况:

{
  "snapshots": [
    {
      "snapshot": "snapshot_1",
      "uuid": "52Lr4aFuQYGjMEv5ZFeFEg",
      "version_id": 6030099,
      "version": "6.3.0",
      "indices": [
        ".monitoring-kibana-6-2018.05.30",
        ".monitoring-es-6-2018.05.28",
        ".watcher-history-7-2018.05.30",
        ".monitoring-beats-6-2018.05.29",
        "metricbeat-6.2.4-2018.05.28",
        ".monitoring-alerts-6",
        "metricbeat-6.2.4-2018.05.30"
      ],
      "include_global_state": true,
      "state": "SUCCESS",
      "start_time": "2018-05-31T12:45:57.492Z",
      "start_time_in_millis": 1527770757492,
      "end_time": "2018-05-31T12:46:15.214Z",
      "end_time_in_millis": 1527770775214,
      "duration_in_millis": 17722,
      "failures": [],
      "shards": {
        "total": 28,
        "failed": 0,
        "successful": 28
      }
    }
  ]
}

返回结果的参数意义都是比较直观的,比如 indices 指明此次备份涉及到的索引名称,由于我们没有指定需要备份的索引,这里备份了所有索引;state 指明状态;duration_in_millis 指明备份任务执行时长等。

我们可以通过 GET _snapshot/my_backup/snapshot_1获取 snapshot_1 的执行状态。

此时如果去 /mount/backups/my_backup 查看,会发现里面多了很多文件,这些文件其实都是基于 elasticsearch data 目录中的文件生成的压缩存储的备份文件。大家可以通过 du -sh . 命令看一下该目录的大小,方便后续做对比。

3. 何时备份

通过上面的步骤我们成功创建了一个备份,但随着数据的新增,我们需要对新增的数据也做备份,那么我们如何做呢?方法很简单,只要再创建一个快照 snapshot_2 就可以了。

PUT /_snapshot/my_backup/snapshot_2?wait_for_completion=true

当执行完毕后,你会发现 /mount/backups/my_backup 体积变大了。这说明新数据备份进来了。要说明的一点是,当你在同一个 repository 中做多次 snapshot 时,elasticsearch 会检查要备份的数据 segment 文件是否有变化,如果没有变化则不处理,否则只会把发生变化的 segment file 备份下来。这其实就实现了增量备份。

elasticsearch 的资深用户应该了解 force merge 功能,即可以强行将一个索引的 segment file 合并成指定数目,这里要注意的是如果你主动调用 force merge api,那么 snapshot 功能的增量备份功能就失效了,因为 api 调用完毕后,数据目录中的所有 segment file 都发生变化了。

另一个就是备份时机的问题,虽然 snapshot 不会占用太多的 cpu、磁盘和网络资源,但还是建议大家尽量在闲时做备份。

4. 如何恢复

所谓“养兵千日,用兵一时”,我们该演练下备份的成果,将其恢复出来。通过调用如下 api 即可快速实现恢复功能。

POST /_snapshot/my_backup/snapshot_1/_restore?wait_for_completion=true
{
  "indices": "index_1",
  "rename_replacement": "restored_index_1"
}

通过上面的 api,我们可以将 index_1 索引恢复到 restored_index_1 中。这个恢复过程完全是基于文件的,因此效率会比较高。

虽然我们这里演示的是在同一个集群做备份与恢复,你也可以在另一个集群上连接该 repository 做恢复。我们这里就不做说明了。

5. 其他

由于 Elasticsearch 版本更新比较快,因此大家在做备份与恢复的时候,要注意版本问题,同一个大版本之间的备份与恢复是没有问题的,比如都是 5.1 和 5.6 之间可以互相备份恢复。但你不能把一个高版本的备份在低版本恢复,比如将 6.x 的备份在 5.x 中恢复。而低版本备份在高版本恢复有一定要求:

1) 5.x 可以在 6.x 恢复

2) 2.x 可以在 5.x 恢复

3) 1.x 可以在 2.x 恢复

其他跨大版本的升级都是不可用的,比如1.x 的无法在 5.x 恢复。这里主要原因还是 Lucene 版本问题导致的,每一次 ES 的大版本升级都会伴随 Lucene 的大版本,而 Lucene 的版本是尽量保证向前兼容,即新版可以读旧版的文件,但版本跨越太多,无法实现兼容的情况也在所难免了。

6. 继续学习

本文只是简单对 snapshot 功能做了一个演示,希望这足够引起你的兴趣。如果你想进一步深入的了解该功能,比如备份的时候如何指定部分索引、如何查询备份和还原的进度、如何跨集群恢复数据、如何备份到 HDFS 等,可以详细阅读官方手册https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html,如果在使用的过程中遇到了问题,欢迎留言讨论。

继续阅读 »

常见的数据库都会提供备份的机制,以解决在数据库无法使用的情况下,可以开启新的实例,然后通过备份来恢复数据减少损失。虽然 Elasticsearch 有良好的容灾性,但由于以下原因,其依然需要备份机制。

  1. 数据灾备。在整个集群无法正常工作时,可以及时从备份中恢复数据。
  2. 归档数据。随着数据的积累,比如日志类的数据,集群的存储压力会越来越大,不管是内存还是磁盘都要承担数据增多带来的压力,此时我们往往会选择只保留最近一段时间的数据,比如1个月,而将1个月之前的数据删除。如果你不想删除这些数据,以备后续有查看的需求,那么你就可以将这些数据以备份的形式归档。
  3. 迁移数据。当你需要将数据从一个集群迁移到另一个集群时,也可以用备份的方式来实现。

Elasticsearch 做备份有两种方式,一是将数据导出成文本文件,比如通过 elasticdumpesm 等工具将存储在 Elasticsearch 中的数据导出到文件中。二是以备份 elasticsearch data 目录中文件的形式来做快照,也就是 Elasticsearch 中 snapshot 接口实现的功能。第一种方式相对简单,在数据量小的时候比较实用,当应对大数据量场景效率就大打折扣。我们今天就着重讲解下第二种备份的方式,即 snapshot api 的使用。

备份要解决备份到哪里、如何备份、何时备份和如何恢复的问题,那么我们接下来一个个解决。

1. 备份到哪里

在 Elasticsearch 中通过 repository 定义备份存储类型和位置,存储类型有共享文件系统、AWS 的 S3存储、HDFS、微软 Azure的存储、Google Cloud 的存储等,当然你也可以自己写代码实现国内阿里云的存储。我们这里以最简单的共享文件系统为例,你也可以在本地做实验。

首先,你要在 elasticsearch.yml 的配置文件中注明可以用作备份路径 path.repo ,如下所示:

path.repo: ["/mount/backups", "/mount/longterm_backups"]

配置好后,就可以使用 snapshot api 来创建一个 repository 了,如下我们创建一个名为 my_backup 的 repository。

PUT /_snapshot/my_backup
{
  "type": "fs",
  "settings": {
    "location": "/mount/backups/my_backup"
  }
}

之后我们就可以在这个 repository 中来备份数据了。

2. 如何备份

有了 repostiroy 后,我们就可以做备份了,也叫快照,也就是记录当下数据的状态。如下所示我们创建一个名为 snapshot_1 的快照。

PUT /_snapshot/my_backup/snapshot_1?wait_for_completion=true

wait_for_completion 为 true 是指该 api 在备份执行完毕后再返回结果,否则默认是异步执行的,我们这里为了立刻看到效果,所以设置了该参数,线上执行时不用设置该参数,让其在后台异步执行即可。

执行成功后会返回如下结果,用于说明备份的情况:

{
  "snapshots": [
    {
      "snapshot": "snapshot_1",
      "uuid": "52Lr4aFuQYGjMEv5ZFeFEg",
      "version_id": 6030099,
      "version": "6.3.0",
      "indices": [
        ".monitoring-kibana-6-2018.05.30",
        ".monitoring-es-6-2018.05.28",
        ".watcher-history-7-2018.05.30",
        ".monitoring-beats-6-2018.05.29",
        "metricbeat-6.2.4-2018.05.28",
        ".monitoring-alerts-6",
        "metricbeat-6.2.4-2018.05.30"
      ],
      "include_global_state": true,
      "state": "SUCCESS",
      "start_time": "2018-05-31T12:45:57.492Z",
      "start_time_in_millis": 1527770757492,
      "end_time": "2018-05-31T12:46:15.214Z",
      "end_time_in_millis": 1527770775214,
      "duration_in_millis": 17722,
      "failures": [],
      "shards": {
        "total": 28,
        "failed": 0,
        "successful": 28
      }
    }
  ]
}

返回结果的参数意义都是比较直观的,比如 indices 指明此次备份涉及到的索引名称,由于我们没有指定需要备份的索引,这里备份了所有索引;state 指明状态;duration_in_millis 指明备份任务执行时长等。

我们可以通过 GET _snapshot/my_backup/snapshot_1获取 snapshot_1 的执行状态。

此时如果去 /mount/backups/my_backup 查看,会发现里面多了很多文件,这些文件其实都是基于 elasticsearch data 目录中的文件生成的压缩存储的备份文件。大家可以通过 du -sh . 命令看一下该目录的大小,方便后续做对比。

3. 何时备份

通过上面的步骤我们成功创建了一个备份,但随着数据的新增,我们需要对新增的数据也做备份,那么我们如何做呢?方法很简单,只要再创建一个快照 snapshot_2 就可以了。

PUT /_snapshot/my_backup/snapshot_2?wait_for_completion=true

当执行完毕后,你会发现 /mount/backups/my_backup 体积变大了。这说明新数据备份进来了。要说明的一点是,当你在同一个 repository 中做多次 snapshot 时,elasticsearch 会检查要备份的数据 segment 文件是否有变化,如果没有变化则不处理,否则只会把发生变化的 segment file 备份下来。这其实就实现了增量备份。

elasticsearch 的资深用户应该了解 force merge 功能,即可以强行将一个索引的 segment file 合并成指定数目,这里要注意的是如果你主动调用 force merge api,那么 snapshot 功能的增量备份功能就失效了,因为 api 调用完毕后,数据目录中的所有 segment file 都发生变化了。

另一个就是备份时机的问题,虽然 snapshot 不会占用太多的 cpu、磁盘和网络资源,但还是建议大家尽量在闲时做备份。

4. 如何恢复

所谓“养兵千日,用兵一时”,我们该演练下备份的成果,将其恢复出来。通过调用如下 api 即可快速实现恢复功能。

POST /_snapshot/my_backup/snapshot_1/_restore?wait_for_completion=true
{
  "indices": "index_1",
  "rename_replacement": "restored_index_1"
}

通过上面的 api,我们可以将 index_1 索引恢复到 restored_index_1 中。这个恢复过程完全是基于文件的,因此效率会比较高。

虽然我们这里演示的是在同一个集群做备份与恢复,你也可以在另一个集群上连接该 repository 做恢复。我们这里就不做说明了。

5. 其他

由于 Elasticsearch 版本更新比较快,因此大家在做备份与恢复的时候,要注意版本问题,同一个大版本之间的备份与恢复是没有问题的,比如都是 5.1 和 5.6 之间可以互相备份恢复。但你不能把一个高版本的备份在低版本恢复,比如将 6.x 的备份在 5.x 中恢复。而低版本备份在高版本恢复有一定要求:

1) 5.x 可以在 6.x 恢复

2) 2.x 可以在 5.x 恢复

3) 1.x 可以在 2.x 恢复

其他跨大版本的升级都是不可用的,比如1.x 的无法在 5.x 恢复。这里主要原因还是 Lucene 版本问题导致的,每一次 ES 的大版本升级都会伴随 Lucene 的大版本,而 Lucene 的版本是尽量保证向前兼容,即新版可以读旧版的文件,但版本跨越太多,无法实现兼容的情况也在所难免了。

6. 继续学习

本文只是简单对 snapshot 功能做了一个演示,希望这足够引起你的兴趣。如果你想进一步深入的了解该功能,比如备份的时候如何指定部分索引、如何查询备份和还原的进度、如何跨集群恢复数据、如何备份到 HDFS 等,可以详细阅读官方手册https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html,如果在使用的过程中遇到了问题,欢迎留言讨论。

收起阅读 »