再来说说NoSql应用,通常搜索引擎的取数据的过程是:
首先通过搜索词匹配倒排表得到一个只有id的结果集,然后通过id匹配正排索引拿到对应的文档字段,最后返回结果,这样的好处是:
- 可以让倒排索引尽量小,保证IO性能
- id是由搜索引擎自行分配维护的,并不依赖外部映射关系,做到将文档id和文档内容分离,使得文档内容可以像NoSql一样横向扩展字段
- 可以在返回搜索结果的同时把文档原始内容带上,通过一次查询就返回前端展示所必须的信息(排序和内容),从而免去了从db取数据的IO开销
这样来说,搜索引擎确实可以一定程度上接替一部分db的工作,有做第二db(NoSql)的能力。
这次简单聊聊搜索引擎在NoSql上的典型应用场景:
1. 业务宽表
业务宽表应该是最常遇见的一类NoSql应用,作用是关联在db中相互独立存储的几张业务表为一张大中间表,从而可以将复杂的取数逻辑简化为一次查询,看上去很有诱惑力。那为什么不直接把这些业务字段在db中就存储为一张表呢,大致的原因是:
- 某个产品在由小到大的发展过程中必然随着业务线的拆分,对应的业务db库表也必然随之拆分,方便开发维护(解耦)
- 如果表存储的数据量很大,要进行一次ddl操作的代价会很高(锁表),新的业务需求(新增字段)就不得不通过新建一张附表来避免锁表带来的业务中断
事物都具有两面性,拆表解决了上述的问题,同时也带来了新的麻烦,如果某个业务同时依赖了多张业务表,那么进行一次数据交互就必然伴随着多次db操作(复杂的取数逻辑),如果还需要对某个字段进行排序,就必须得借助join操作(增大db压力)。
这时为了简化逻辑或者是减轻db压力,就可以在业务表之外新建一张业务宽表存储到ES,即使数据量很大(上十亿),依然可以快速的添加字段而不会引起锁表操作,而且NoSql的特性也天然适合业务快速发展的场景。
Tips:搜索引擎一般响应时间在0-100ms左右,ES因为gc的原因偶尔会有秒级的rt,所以应用需要评估对引擎响应时间(rt)的敏感程度。
2. 大数据交换/存储
离线计算有时得到的结果很大(比如根据各种消费规则算出一批潜在客户),而又需要结果进行各种在线查询计算,如果是千万级别的数据,如果直接导入db,可能会严重影响在线业务,而传统的大数据存储,比如HBase,在二级索引方面又没有那么给力,而ES可以支撑千万级别离线数据的快速导入,也能在导入完成后提供在线查询业务,相对会比较适合这个场景。
还有一个典型的大数据存储场景就是日志存储系统(ELK)了,一般情况下在线业务输出的日志量都是很惊人的,而且是一个典型的写多读少应用,同时需要强大的写入性能和比较强的搜索匹配能力,ES也是比较合适的载体。
Tips:在这个场景下,应用需要注意控制写入速率,避免引擎因为merge或者垃圾回收而导致长时间无响应,另外尽量保证所在集群与在线业务集群物理隔离。
3. 增强关键字匹配
db(mysql)尽管也有全文索引能力,但是对于昂贵的db资源来说,用在全文搜索的场景上并不太合适,如果需要提供几百万数据的全文检索能力,几台vm就足够搜索引擎以足够的性能跑了,这样的场景,搜索引擎就可以作为一个具有全文检索能力的廉价存储资源使用。
Tips:作为存储资源使用的情况下,需要注意的是搜索引擎提供的是“近实时”的查询服务,经常性的是在数据写入之后几秒或者几分钟后才可见,应用需要评估对数据实时性的敏感程度,过于敏感的业务不建议应用在这个场景。
4. 外部索引
以HBase为例,其拥有廉价且强大的大数据存储能力,可以自动分裂数据文件,保证读写性能稳定。但是要提供稳定的在线查询能力,HBase的rowkey设计非常微妙,而且大数据量情况下重建rowkey是个高成本的操作,原生又不支持二级索引,这时要保证HBase查询的灵活稳定,最好的办法就是在外部建立一个二级索引,既拥有搜索引擎强大的检索能力,又有自身稳定的存储性能,而且即使外部索引需要重建,也只需要在新索引创建完成之后切换查询流量就可以了。
Tips:保证两侧数据的一致性是这种场景下经常遇到的难题,如果还没有有完善的双写机制,比较合适的是用合理的补偿机制来保证。
5. 在线统计
ES在聚合查询上确实下了不少功夫,从1.x版本到5.x版本,聚合查询的功能一直在不断完善,聚合查询提供的是一定程度上的统计查询能力,而且比较侧重于ELK之类的日志分析,主要是:
- 只能提供top n功能,不支持翻页
- 如果数据量比较大(亿),而且数据变更比较频繁,查询的耗时经常是秒级的。
因此如果是对rt不很敏感的业务,又不能通过db在线查询解决,在明确上述缺陷的前提下,也是可以用ES来做“在线”统计查询工作的,当然建议还是:
- 尽量降低数据更新频率,频繁的更新会导致ES频繁reopen index searcher,造成io压力上升,导致查询超时
- 尽量保证索引数据量不要太大(索引拆分)
(未完待续)