Elasticsearch学习笔记

作者: siddontang | 来源:发表于2015-01-18 18:03 被阅读89391次

    Why Elasticsearch?

    由于需要提升项目的搜索质量,最近研究了一下Elasticsearch,一款非常优秀的分布式搜索程序。最开始的一些笔记放到github,这里只是归纳总结一下。

    首先,为什么要使用Elasticsearch?最开始的时候,我们的项目仅仅使用MySQL进行简单的搜索,然后一个不能索引的like语句,直接拉低MySQL的性能。后来,我们曾考虑过sphinx,并且sphinx也在之前的项目中成功实施过,但想想现在的数据量级,多台MySQL,以及搜索服务本身HA,还有后续扩容的问题,我们觉得sphinx并不是一个最优的选择。于是自然将目光放到了Elasticsearch上面。

    根据官网自己的介绍,Elasticsearch是一个分布式搜索服务,提供Restful API,底层基于Lucene,采用多shard的方式保证数据安全,并且提供自动resharding的功能,加之github等大型的站点也采用Elasticsearch作为其搜索服务,我们决定在项目中使用Elasticsearch。

    对于Elasticsearch,如果要在项目中使用,需要解决如下问题:

    1. 索引,对于需要搜索的数据,如何建立合适的索引,还需要根据特定的语言使用不同的analyzer等。
    2. 搜索,Elasticsearch提供了非常强大的搜索功能,如何写出高效的搜索语句?
    3. 数据源,我们所有的数据是存放到MySQL的,MySQL是唯一数据源,如何将MySQL的数据导入到Elasticsearch?

    对于1和2,因为我们的数据都是从MySQL生成,index的field是固定的,主要做的工作就是根据业务场景设计好对应的mapping以及search语句就可以了,当然实际不可能这么简单,需要我们不断的调优。

    而对于3,则是需要一个工具将MySQL的数据导入Elasticsearch,因为我们对搜索实时性要求很高,所以需要将MySQL的增量数据实时导入,笔者唯一能想到的就是通过row based binlog来完成。而近段时间的工作,也就是实现一个MySQL增量同步到Elasticsearch的服务。

    Lucene

    Elasticsearch底层是基于Lucene的,Lucene是一款优秀的搜索lib,当然,笔者以前仍然没有接触使用过。:-)

    Lucene关键概念:

    • Document:用来索引和搜索的主要数据源,包含一个或者多个Field,而这些Field则包含我们跟Lucene交互的数据。
    • Field:Document的一个组成部分,有两个部分组成,name和value。
    • Term:不可分割的单词,搜索最小单元。
    • Token:一个Term呈现方式,包含这个Term的内容,在文档中的起始位置,以及类型。

    Lucene使用Inverted index来存储term在document中位置的映射关系。
    譬如如下文档:

    • Elasticsearch Server 1.0 (document 1)
    • Mastring Elasticsearch (document 2)
    • Apache Solr 4 Cookbook (document 3)

    使用inverted index存储,一个简单地映射关系:

    Term Count Docuemnt
    1.0 1 <1>
    4 1 <3>
    Apache 1 <3>
    Cookbook 1 <3>
    Elasticsearch 2 <1>.<2>
    Mastering 1 <2>
    Server 1 <1>
    Solr 1 <3>

    对于上面例子,我们首先通过分词算法将一个文档切分成一个一个的token,再得到该token与document的映射关系,并记录token出现的总次数。这样就得到了一个简单的inverted index。

    Elasticsearch关键概念

    要使用Elasticsearch,笔者认为,只需要理解几个基本概念就可以了。

    在数据层面,主要有:

    • Index:Elasticsearch用来存储数据的逻辑区域,它类似于关系型数据库中的db概念。一个index可以在一个或者多个shard上面,同时一个shard也可能会有多个replicas。
    • Document:Elasticsearch里面存储的实体数据,类似于关系数据中一个table里面的一行数据。
      document由多个field组成,不同的document里面同名的field一定具有相同的类型。document里面field可以重复出现,也就是一个field会有多个值,即multivalued。
    • Document type:为了查询需要,一个index可能会有多种document,也就是document type,但需要注意,不同document里面同名的field一定要是相同类型的。
    • Mapping:存储field的相关映射信息,不同document type会有不同的mapping。

    对于熟悉MySQL的童鞋,我们只需要大概认为Index就是一个db,document就是一行数据,field就是table的column,mapping就是table的定义,而document type就是一个table就可以了。

    Document type这个概念其实最开始也把笔者给弄糊涂了,其实它就是为了更好的查询,举个简单的例子,一个index,可能一部分数据我们想使用一种查询方式,而另一部分数据我们想使用另一种查询方式,于是就有了两种type了。不过这种情况应该在我们的项目中不会出现,所以通常一个index下面仅会有一个type。

    在服务层面,主要有:

    • Node: 一个server实例。
    • Cluster:多个node组成cluster。
    • Shard:数据分片,一个index可能会存在于多个shards,不同shards可能在不同nodes。
    • Replica:shard的备份,有一个primary shard,其余的叫做replica shards。

    Elasticsearch之所以能动态resharding,主要在于它最开始就预先分配了多个shards(貌似是1024),然后以shard为单位进行数据迁移。这个做法其实在分布式领域非常的普遍,codis就是使用了1024个slot来进行数据迁移。

    因为任意一个index都可配置多个replica,通过冗余备份的方式保证了数据的安全性,同时replica也能分担读压力,类似于MySQL中的slave。

    Restful API

    Elasticsearch提供了Restful API,使用json格式,这使得它非常利于与外部交互,虽然Elasticsearch的客户端很多,但笔者仍然很容易的就写出了一个简易客户端用于项目中,再次印证了Elasticsearch的使用真心很容易。

    Restful的接口很简单,一个url表示一个特定的资源,譬如/blog/article/1,就表示一个index为blog,type为aritcle,id为1的document。

    而我们使用http标准method来操作这些资源,POST新增,PUT更新,GET获取,DELETE删除,HEAD判断是否存在。

    这里,友情推荐httpie,一个非常强大的http工具,个人感觉比curl还用,几乎是命令行调试Elasticsearch的绝配。

    一些使用httpie的例子:

    # create
    http POST :9200/blog/article/1 title="hello elasticsearch" tags:='["elasticsearch"]'
    
    # get
    http GET :9200/blog/article/1
    
    # update
    http PUT :9200/blog/article/1 title="hello elasticsearch" tags:='["elasticsearch", "hello"]'
    
    # delete
    http DELETE :9200/blog/article/1
    
    # exists
    http HEAD :9200/blog/article/1
    
    

    索引和搜索

    虽然Elasticsearch能自动判断field类型并建立合适的索引,但笔者仍然推荐自己设置相关索引规则,这样才能更好为后续的搜索服务。

    我们通过定制mapping的方式来设置不同field的索引规则。

    而对于搜索,Elasticsearch提供了太多的搜索选项,就不一一概述了。

    索引和搜索是Elasticsearch非常重要的两个方面,直接关系到产品的搜索体验,但笔者现阶段也仅仅是大概了解了一点,后续在详细介绍。

    同步MySQL数据

    Elasticsearch是很强大,但要建立在有足量数据情况下面。我们的数据都在MySQL上面,所以如何将MySQL的数据导入Elasticsearch就是笔者最近研究的东西了。

    虽然现在有一些实现,譬如elasticsearch-river-jdbc,或者elasticsearch-river-mysql,但笔者并不打算使用。

    elasticsearch-river-jdbc的功能是很强大,但并没有很好的支持增量数据更新的问题,它需要对应的表只增不减,而这个几乎在项目中是不可能办到的。

    elasticsearch-river-mysql倒是做的很不错,采用了python-mysql-replication来通过binlog获取变更的数据,进行增量更新,但它貌似处理MySQL dump数据导入的问题,不过这个笔者真的好好确认一下?话说,python-mysql-replication笔者还提交过pull解决了minimal row image的问题,所以对elasticsearch-river-mysql这个项目很有好感。只是笔者决定自己写一个出来。

    为什么笔者决定自己写一个,不是因为笔者喜欢造轮子,主要原因在于对于这种MySQL syncer服务(增量获取MySQL数据更新到相关系统),我们不光可以用到Elasticsearch上面,而且还能用到其他服务,譬如cache上面。所以笔者其实想实现的是一个通用MySQL syncer组件,只是现在主要关注Elasticsearch罢了。

    项目代码在这里go-mysql-elasticsearch,现已完成第一阶段开发,内部对接测试中。

    go-mysql-elasticsearch的原理很简单,首先使用mysqldump获取当前MySQL的数据,然后在通过此时binlog的name和position获取增量数据。

    一些限制:

    • binlog一定要变成row-based format格式,其实我们并不需要担心这种格式的binlog占用太多的硬盘空间,MySQL 5.6之后GTID模式都推荐使用row-based format了,而且通常我们都会把控SQL语句质量,不允许一次性更改过多行数据的。
    • 需要同步的table最好是innodb引擎,这样mysqldump的时候才不会阻碍写操作。
    • 需要同步的table一定要有主键,好吧,如果一个table没有主键,笔者真心会怀疑设计这个table的同学编程水平了。多列主键也是不推荐的,笔者现阶段不打算支持。
    • 一定别动态更改需要同步的table结构,Elasticsearch只能支持动态增加field,并不支持动态删除和更改field。通常来说,如果涉及到alter table,很多时候已经证明前期设计的不合理以及对于未来扩展的预估不足了。

    更详细的说明,等到笔者完成了go-mysql-elasticsearch的开发,并通过生产环境中测试了,再进行补充。

    总结

    最近一周,笔者花了不少时间在Elasticsearch上面,现在算是基本入门了。其实笔者觉得,对于一门不懂的技术,找一份靠谱的资料(官方文档或者入门书籍),蛋疼的对着资料敲一遍代码,不懂的再问google,最后在将其用到实际项目,这门技术就算是初步掌握了,当然精通还得在下点功夫。

    现在笔者只是觉得Elasticsearch很美好,上线之后铁定会有坑的,那时候只能慢慢填了。话说,笔者是不是要学习下java了,省的到时候看不懂代码就惨了。:-)

    相关文章

      网友评论

      • 飞奔在java路上的菜鸟:博主,您好:想请教两个问题:1、go-mysql-elasticsearch怎么在Windows环境下使用,我再Windows环境下使用,可以同步insert、update、delete就是已经在mysql中的数据没有同步进去。2)linux测试环境中安装了4个mysql实例,需要指定mysql.sock的路径。
      • FredGan:纠正一个错误:POST是更新,PUT是新增。
      • 哦小白:hi,我在使用go-mysql-elasticsearch,现在遇到一个问题,初始化的时候,dump的sql文件执行了,但是一会儿后,es里面的数据没有了,想再重新初始化dump数据,怎么操作啊?现在只是binlog同步,skip了dump文件。
      • 86c917423624:不错不错,收藏了。

        推荐下,分库分表中间件 Sharding-JDBC 源码解析 17 篇:http://www.yunai.me/categories/Sharding-JDBC/?jianshu&401
        aec055493426:写的不错,谢谢博主;已收藏~
        12a033ef755a:写的不错,谢谢博主;已收藏~
      • 翱翔云端:实时同步,何不试试Storm或者Spark?
      • 谭建纳:可以考虑canal来监控mysql的binlog做增量
      • c2e6e7333036:学习一下,谢谢楼主分享。
      • 盖小同学:学习了~
      • 夜行侠_5d95:夜行侠老师视频教学:es问题汇总
        http://www.itjoin.org/
        1、如何防止索引被删除(权限控制)
        2、搜索的精准性控制
        3、解决搜索中英文数字混合
        4、搜索中in查询
        5、安装问题
        6、es源码内部是如何做到插件化扩展
        7、统计时展示不全
        8、搜索条件过多,内容过长报错
        9、索引某一字段内容太大
        10、自动补全设计
        11、es的分布式如何实现RPC底层
        12、es的分片机制
        13、head插件的使用
        14、es分片和副本级的是原理
        15、如何做到只更新部分数据
        16、数组在index中如何制定
        17、es搜索结果窗口太大
        18、group by里如何排序
        19、es底层新增和删除索引的全过程
        20、es底层检索的全过程
      • cb62717b0bc6:我想请问一下 做MySQL增量同步是根据什么条件判断的?谢谢。
        cb62717b0bc6:2017/05/02 16:14:16 binlogsyncer.go:79: [info] create BinlogSyncer with config &{1001 mysql 127.0.0.1 3306 root 123 false false <nil> false}
        github.com/siddontang/go-mysql/canal/canal.go:245: binlog must ROW format, but STATEMENT now
        github.com/siddontang/go-mysql/canal/canal.go:70:
        /root/workspace/src/github.com/siddontang/go-mysql-elasticsearch/river/river.go:90:
        /root/workspace/src/github.com/siddontang/go-mysql-elasticsearch/river/river.go:53:
        这是什么问题 各位大神帮忙看看
        cb62717b0bc6:@siddontang binlog pos 是在哪里维护的?是监控数据库的什么条件同步?谢谢
        siddontang:@子非鱼_98e7 自己维护同步的 binlog pos
      • 刘若安丶:请问一下,我用你的go-mysql-elasticsearch、同步数据的时候 速度是每秒200多条、是不是我哪里设置不对或者其他原因导致有点太慢了、
        siddontang:@刘若安丶 我们实测从 mysql 同步 binlog event 速度很快,但可能写入到 ES 比较慢,因为是单条连接顺序写入。
      • 老码农不上班:谢谢分享。
      • JimmyGan:好分享,谢谢
      • 叶小然:博主,很想请教一下,你学习这个es的周期大概是多久,因为我也是最近工作上要用到es,无意查找到这篇文章,很受用。想知道你这样的大牛,学习一个es这样的新技术大概是多久,好有个参考
      • sunwk:牛逼爆了,请接收我的膝盖
      • 68df098a965f:最近在用go-mysql-elasticsearch,非常好用,谢谢作者,同时想请教几个问题,能回答万分感激:
        1、server_id指的是什么,我配成一样的可以同步,不一样的也可以同步;
        2、启动会报listen stat addr 192.168.1.120:12805 err listen tcp 192.168.1.120:12805: bind: address already in use 但是一样能正常工作,是跟什么配置有关么?
      • ef68bf587519:请问如果是把一对多关系的两张表,在ES中建了宽表,这时候导入工具还有用么?还是说需要特殊开发?
      • ab4bb0305e15:我做iOS前端,想调用elastic search的接口并传递参数是怎么传的呢?是否有封装的框架?
      • d1ac2c1b38e2:没想到这几天弄这个同步问题,先搜到你的github项目,又搜到你的文章。不错,原来你是个中国人!哈哈!我还在github上写一丢丢蹩脚的英文。之前是感觉你应该能看懂中文,不然怎么知道我贴的错误是什么。幸会幸会!
      • tomsen:不错我收藏了
      • 46ea08af5dac:挺详细,很好,我也搞不清type,不知道有啥用,和table对应好像不贴切
      • 乾坤笔:关于es这块,你后续没有更新了,期待啊!
      • 2f87cd1982e6:go-mysql-elasticsearch 现在运用到生产环境了么? 楼主 在 go-mysql-elasticsearch 没有运用到生产环境前 用的啥
      • 无毒砒霜:讲的很好,赞楼主
      • f723dd39c6dc:go-mysql-elasticsearch,生产环境用到go-my-sql了吗? :smile:
      • 4720302f7d18:看完Elastic Reference,楼主讲的很好
      • siddontang:@张志鹏yi Open Source, Distributed, RESTful Search Engine 参考https://github.com/elastic/elasticsearch
      • 2d67763b3e22:楼主大神,我就问一下,elasticsearch 到底是个啥东东啊?? 搜索引擎?数据库? 还是。。?
        醉看风拂袖_:@张志鹏yi 你来吃屎涩牙齿
      • siddontang:@bean2017 对,多谢提醒,只是我忘记改了,不好意思。
      • bean2017:楼主的文章很赞 :+1: ,不过有关Elasticsearch关键概念有些地方是错误的: 在elasticsearch和关系型数据库的一些概念的对应关系: Elasticsearch的 index --> DB , Elasticsearch的type --> table, Elasticsearch的Document -->对应table的一行记录row, Document的Field --> table里面的column
      • siddontang:@SMILEOFYOURS 没用过elasticsearch-river-mysql,google或者直接github上面提issue吧。
      • SMILEOFYOURS:请教下elasticsearch-river-mysql,如何使用。
      • c2b5c974f8f7:对es的概念讲解得很好

      本文标题:Elasticsearch学习笔记

      本文链接:https://www.haomeiwen.com/subject/lsthxttx.html