摘要:ES已经成为了全能型的数据产品,在很多领域越来越受欢迎,本文旨在从数据库领域分析ES的使用。
本文分享自华为云社区《Elasticsearch数据库加速实践》,原文作者:css_blog 。
一、方案说明
Elasticsearch主要功能是什么,不同的场景有不同的定位,在日志场景我们可以用ELK生态搭建日志分析系统,在搜索领域ES是当前最热门的搜索引擎。在大数据领域,ES可以对标Hbase提供海量日志的数据仓库;在数据库领域ES可以作为查询分析型的分析型数据库使用。ES已经成为了全能型的数据产品,在很多领域越来越受欢迎,本文旨在从数据库领域分析ES的使用。
ES不是关系型数据库,数据更新采用乐观锁,通过版本号控制,不支持事务处理,这也是ES区别于传统数据库(Mysql)的地方;但是ES支持精确查询加速,多条件任意组合查询,多种聚合查询,查询速度很快,可以替代数据库复杂条件查询的场景需求,甚至可以代替数据库做二级索引。
在数据库加速场景通常的做法是客户产生的商品订单数据会写入Mysql类关系型数据库,数据库写入保证事务性,但是随着商品订单的数据越来越多,同时客户查询的条件多变,无法所有字段都建立索引,数据库的查询能力远远不能满足查询诉求。我们考虑用ES全量同步数据库数据,在ES中做多条件聚合查询,查询的结果可以在Mysql中做关联搜索,在查询商品订单详情展示, Mysql数据和ES数据可以不要求实时一致,可以通canal消费Mysql binlog日志信息, 同步到ES,实现一次写入,保证数据一致性。以下数据库都以Mysql为例进行说明。
二、索引原理分析
ES为什么查询能力远远超过Mysql关系型数据库,主要是他们的实现原理和底层存储的数据结构差异决定的,以下比较两种产品的实现原理。
Elasticsearch会对所有输入的文本进行处理,建立索引放入内存中,从而提高搜索效率。在这一点上ES要优于MySQL的B+树的结构,MySQL需要将索引放入磁盘,每次读取需要先从磁盘读取索引然后寻找对应的数据节点,但是ES能够直接在内存中就找到目标文档对应的大致位置,最大化提高效率。并且在进行组合查询的时候MySQL的劣势更加明显,它不支持复杂的组合查询比如聚合操作,即使要组合查询也要事先建好索引,但是ES就可以完成这种复杂的操作,默认每个字段都是有索引的,在查询的时候可以各种互相组合。
(1)数据库索引B+树
数据库中索引都是以树来组织的,常用的有B tree,B-tree,B+tree,以下介绍B+tree的组织结构。
首先我们先想象下为什么需要建立索引,假设我们有一张表book,存储了我们保持的书籍信息,名称,作者,发布时间等,我们有10000条记录,如果我们需要找一本为《database》的书,那我们的SQL为:
select name,author form book where name = ‘database’;
我们需要扫描整个表,全量比较才可以,如果我们对name建立索引,书名已经按照顺序排序,查询时只需要找到对应位置就可以快速获取结果。
索引的本质是通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
数据库采用B+tree建立索引:
B+tree 数据只存储在叶子节点中。这样在B树的基础上每个节点存储的关键字数更多,树的层级更少所以查询数据更快,所有指关键字指针都存在叶子节点,所以每次查找的次数都相同所以查询速度更稳定。
(2)Elasticsearch索引原理
ES建立索引采用倒排索引的方式存储。
对输入的所有数据都建立索引,并且把所有和文档对应起来,在我们查找数据的时候我们直接查找词典(Term),在找到Term对应的文档ID,进而找到数据。这和Mysql使用B+tree树建立索引的方式类似,但是如果词典Term很大,对Term的搜索就会很慢,ES进一步建议了词典索引(FST),提升词典的搜索能力。
Term Index 以树的形式保存在内存中,运用了FST+压缩公共前缀方法极大的节省了内存,通过Term Index查询到Term Dictionary所在的block再去磁盘上找term减少了IO次数。
Term Dictionary 排序后通过二分法将检索的时间复杂度从原来N降低为logN。
三、查询对比分析
以下对于数据库搜索常用的场景对比ES和数据库:
- 全文检索
ES支持全文检索,可以对数据分词,每个词通过FSP建立词典索引,而Mysql关系数据库则不支持,想象下如果搜索的不是整个字段而是字段中的几个关键词,使用Mysql搜索必须全表扫描。
- 精确搜索
如果Mysql对该字段建立过索引,使用ES搜索和Mysql搜索性能差异不大,可能Mysql更快点,但是ES是分布式系统,可以支持PB级别的数据搜索,对大表搜索ES优势更明显。
- 多条件查询
我们知道Mysql需要对字段建立索引才能加速搜索过程,而ES默认是全索引的,对于多条件查询,触发Mysql建立联合索引,否则多个字段搜索,Mysql 先选择一个字段搜索,结果在使用第二个字段过滤得到最终结果。
ES则采用多个字段结果集交并操作,使用bitmap或者skiplist加快搜索速度,相比Mysql优势明显。
- 聚合搜索
Mysql聚合搜索如果没有建立索引需要全表扫描排序,如果建立索引在B+tree上进行范围查询。
ES为了加快聚合搜索速度,通过Doc value来解决聚合搜索问题。DocValue就是列式存储。
存储结果如下:
Docvalue数据按照文档ID排序,DocValue将随机读取变成了顺序读取,
在es中,因为分片的存在,数据被拆分成多份,放在不同机器上。但是给用户体验却好像只有一个库一样。对于聚合查询,其处理是分两阶段完成的:
- Shard 本地的 Lucene Index 并行计算出局部的聚合结果。
- 收到所有的 Shard 的局部聚合结果,聚合出最终的聚合结果。
这种两阶段聚合的架构使得每个 shard 不用把原数据返回,而只用返回数据量小得多的聚合结果。这样极大的减少了网络带宽的消耗。
- 多副本加速
我们知道ES有shard和replica的概念,副本一方面可以保证数据的可靠性,另一方面多副本可以加快搜索速度提高搜索并发能力。
四、数据库到Elasticsearch同步方案
结合用户实际的使用方式和数据量的大小,Mysql数据到ES可以有多种不同的方式选择。
- Canal=>Elasticsearch
使用Canal直接消费Mysql binlog日志写入ES,这种方式如果Mysql写入量大,会面临Canal写入阻塞问题。
- Canal =>Kafka=>Elasticsearch
Canal数据写入到Kafka,使用另外的app消费Kafka数据同步到ES
五、问题汇总
1.索引shard问题
在Mysql数据同步到ES中面临索引的建立的问题,在数据写入ES之前我们需要提前规划数据的shards和replicas的个数,replicas 可以动态修改,但是shards数创建完成后不能修改。
随着Mysql数据量的增加,如果shard太少,就会导致每个shard的数据量太大的问题。
如果一个索引600G,只有3 个shard,每个shard就200G,会极大的损耗查询能力,也不利于数据迁移。
我们可以按照月来滚动创建索引,通过索引别名把所有索引关联起来使用。
test_data-202101 test_data-202102
2.查询加速问题
在使用ES对数据库进行加速的场景,我们希望的是ES查询能力尽可能快。在ES查询不满足要求的时候我们需要对查询进行调优。
常用的方法有: