select xx from xx where id = 3 and name in (1,3,4)
=>
select xx from xx where id =? and name in (?)
相同类别的SQL有一些共同点:查询计划相同,索引大概率是同一个,一般具有相同的业务逻辑
2.2 挑战一:统一存储如何构建高效多功能的索引系统来满足业务高速迭代 a. 低效SQL消耗大,优化困难
BaikalDB线上有几十亿pv,可以聚合成几千类SQL,每类SQL我们会统计扫描行数、过滤行数、平响、pv、错误数等信息。
基于这些统计信息进行分析,我们发现有5%的低效SQL占了42%的扫描资源。因此,如果优化这个5%的SQL,对线上的性能和稳定非常重要。我们重点分析了这些低效SQL,发现主要有两个原因导致: - 系统早期只做了基于规则的索引选择,线上部分表索引非常多,导致部分SQL选了低效索引。 - 业务RD对SQL优化并不熟悉,并且能参考的优化信息不够全,导致部分表缺少合适的索引。 b. 业务检索需求激增
业务系统对检索功能需求较多:物料检索、账户检索、图片检索、商品检索、文档检索等。需求量增长4倍。业务系统原有检索能力无法复用,需要搭建redis、建库、倒排基础查询、高级检索等检索专用模块,而且对不同业务需要适配不同的代码。线上光各种专用检索模块就达到十几个。因此急需一套开箱即用的检索功能,快速满足大部分基础检索需求。 c. BaikalDB索引变更能力不足
业务需求多变,每个月都有十多个由于业务需求,优化需求的各种加索引需求。老流程加索引需要建表、导数据、禁写等,单次变更耗时1-3pd。
以上这些问题,我们都归类为索引相关问题。数据库的核心之一,就是支持各种索引来满足业务需求。与此相对的各种专用存储,实际上就是把某项索引够能做好,来满足某一类业务需求,不够通用。
我们采用了如下方案来解决这些问题: a. 实现基于代价的索引选择与索引推荐,不需要业务参与来优化低效SQL
查询条件(范围,等值,IN)在采样数据中的占比我们称之为选择率,再乘以表的总行数可以预估该条件需要检索的行数。对于单列索引可以直接计算出索引的检索行数,对于联合索引涉及多列,假设每列是相互独立的,那么将每一列的选择率相乘即可得到联合索引的选择率,进而可以预估联合索引的检索行数。
有部分表缺少合适的索引,导致SQL低效。为了应对这个问题,BaikalDB实现了一套索引推荐方案。通过对每类SQL的多种信息进行统计,包括:扫描行数,过滤行数,平响,pv。计算出过滤率=过滤行数/扫描行数,过滤率越大,扫描行数越大则越低效。然后再结合过滤率,平响,和统计哪个条件过滤最多,通过这些信息来综合推荐索引。 b. 实现定制化切词的倒排索引和多功能布尔引擎,支持一键添加检索功能
2.3 挑战二:稳定性和安全性是存储的基础,如何让业务安心使用 a. 存储的稳定性问题会严重损害业务
为了解决稳定性不足的问题,我们对线上影响稳定性的问题总了分类总结:
流量突增问题占比41%,包括业务慢SQL,索引操作出错,部分业务流量变高,压力过高等情况
事务问题占比25%,包括死锁问题,事务延迟问题,两阶段过程中遇到的雪崩问题
禁写问题占比17%,包括数据迁移太快,sata盘io性能差,rocksdb发生禁写
其他问题,包括偶发core,网络超时等
这些影响稳定性的问题,会严重损害业务,造成业务pvlost,客户投诉、赔款等。因此我们根据问题发生的频率与严重程度,制定不同优先级逐步解决 b. 全量备份能力不足导致故障恢复慢
之前的备份系统直接基于SQL读写,性能很差,导入1亿行(147G)数据,需要耗费8小时以上。而BaikalDB本身是个分布式数据库,数据量很大,因此这种恢复速度显然不能满足业务需求。 c. 缺少实时输出能力,业务无法实时输出
实时输出能力的缺失,导致业务无法像使用mysql那样,把数据实时同步到各个系统(例如redis,udw,做备份表,etl后流给检索系统),无法满足业务多样化的需求。
为了能让业务更安心的使用,我们采用了下述方案来进行解决: a. 物理、逻辑双隔离的多租户能力,解决突发流量带来的稳定性问题
物理隔离:即拆分集群,基于meta的调度与raft的addpeer能力,BaikalDB支持不同表直接一键拆分存储集群,整个过程中业务无感知。 但是物理隔离无法解决全部问题,集群拆分过多的话,资源消耗会增多,并且运维压力也会变大。况且单个表多个SQL相互影响这个也无法通过拆分集群解决。 因此我们增加了基于令牌桶流控实现的逻辑隔离。
c. 基于RocksDB状态的迁移限速方案
rocksdb达到stall状态时,hold addpeer/index操作,达到根据rocksdb压力动态调控的目的。 对于大region,进行拆分写sst操作,做到每个sst大约是128M。 增加预估大小分裂机制,减少大region。 简单来说,就是根据rocksdb压力来动态调控:压力小,全速迁移,压力大,不迁移。 d. 基于SST的备份恢复,实现亿行数据分钟级恢复
BaikalDB直接操作sst数据,彻底解决计划开销的问题。并且恢复的时候直接ingest sst到rocksdb中,解决memtable写入开销问题,恢复速度提升百倍以上。 采用了COW机制,只有数据变更才更新备份,减少备份压力(一个百亿行的表,每天更新数据占比很小)。
e. 实现Binlog实时备份,业务可以实时输出数据去其他系统
类似MySQL,BaikalDB实现了Binlog来实时输出数据。这里面有几个设计点可供参考: