Elasticsearch 数据库优化实战:让你的 ES 飞起来

2,504 阅读8分钟

​​​​​​​​​​​​​​​摘要: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 可以有多种不同的方式选择。

1. Canal=>Elasticsearch

使用 Canal 直接消费 Mysql binlog 日志写入 ES,这种方式如果 Mysql 写入量大,会面临 Canal 写入阻塞问题。

2. 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 查询不满足要求的时候我们需要对查询进行调优。

常用的方法有:

点击关注,第一时间了解华为云新鲜技术~