作者简介:李国强,百度商业平台研发部资深研发工程师。工作方向包括分布式数据库 BaikalDB、分布式SQL 中间件等,对构建高性能、高可用分布式文件系统有较多实践和较深入的理解。
欢迎关注 Star github.com/baidu/BaikalDB 国内加速镜像库gitee
商业平台研发部目前面临三大业务特点:
商业存储原有架构不统一:采用了MySQL满足事务性需求;采用各种OLAP类系统满足在线分析需求;采用自研的内存存储(字面服务,加速镜像服务等)满足各种加速查询场景;采用redis、表格系统、基于SSD的KV系统等满足各种KV场景;采用建库、倒排基础查询、推荐模块、ElasticSearch满足各种检索需求;采用搜索词PV仓库满足大容量PV查询需求。
以上这些专用系统,架构混杂,数量庞多,运维扩容成本极高,并且内存占用过多造成资源浪费与混布困难。 过去10年,为了解决各种业务问题,我们存储团队一直在做加法,开发运维了大量的存储模块。为了解决这个问题,我们在2017年开始研发BaikalDB,旨在解决上述问题,把存储架构统一,希望能够支持更多新业务的存储需求。
BaikalDB是一个兼容 MySQL 协议的分布式可扩展存储系统,支持PB级结构化数据的随机实时读写,整体系统架构如下:
BaikalDB基于 RocksDB 实现单机存储,基于Multi Raft 协议(braft库)保障副本数据一致性,基于brpc实现节点通讯交互,其中
BaikalDB 的核心特性有:
在系统研发过程中,BaikalDB以业务需求为导向规划快速迭代,在业务使用中深度打磨优化,随业务成长而成长,关键功能的时间节点如下
从2018年上线以来, BaikalDB 已部署1.5K+数据表,数据规模达到600+TB,存储节点达到1.7K+。
BaikalDB项目我们已经做了近4年,是逐步克服困难的4年。和其他数据库团队不同的是,BaikalDB一直是研发人员自运维,因此很多时候遇到问题不只是功能上,还有高效运维方面的。 索引是数据库的灵魂,不同的业务需求本质上是需要采用不同的索引来实现的。 而稳定性是数据库的基础,服务不稳定意味着业务方无法安心使用。 性能是数据库的关键,高性能意味着使用较少的资源支持大量的业务。 本次主要分享BaikalDB在构建索引、稳定性与安全性、性能方面的三大挑战与一些解决思路。
select xx from xx where id = 3 and name in (1,3,4)
=>
select xx from xx where id =? and name in (?)
BaikalDB线上有几十亿pv,可以聚合成几千类SQL,每类SQL我们会统计扫描行数、过滤行数、平响、pv、错误数等信息。
基于这些统计信息进行分析,我们发现有5%的低效SQL占了42%的扫描资源。因此,如果优化这个5%的SQL,对线上的性能和稳定非常重要。我们重点分析了这些低效SQL,发现主要有两个原因导致: - 系统早期只做了基于规则的索引选择,线上部分表索引非常多,导致部分SQL选了低效索引。 - 业务RD对SQL优化并不熟悉,并且能参考的优化信息不够全,导致部分表缺少合适的索引。
业务系统对检索功能需求较多:物料检索、账户检索、图片检索、商品检索、文档检索等。需求量增长4倍。业务系统原有检索能力无法复用,需要搭建redis、建库、倒排基础查询、高级检索等检索专用模块,而且对不同业务需要适配不同的代码。线上光各种专用检索模块就达到十几个。因此急需一套开箱即用的检索功能,快速满足大部分基础检索需求。
业务需求多变,每个月都有十多个由于业务需求,优化需求的各种加索引需求。老流程加索引需要建表、导数据、禁写等,单次变更耗时1-3pd。
以上这些问题,我们都归类为索引相关问题。数据库的核心之一,就是支持各种索引来满足业务需求。与此相对的各种专用存储,实际上就是把某项索引够能做好,来满足某一类业务需求,不够通用。
我们采用了如下方案来解决这些问题:
为了应对索引选错的情况,BaikalDB增加了基于代价的索引选择。对于能匹配到多个索引的SQL,需要选择一个最优的代价最小的去执行。何为最优索引,检索量是个很重要的指标,一条SQL获取的数据量固定,检索量越少说明该索引越高效,为了选出最优的索引,需要预估出使用某个索引的检索量,那么我们就需要一些统计信息。BaikalDB维护的统计信息包括列直方图、Count-Min Sketch、distinct count等等。
查询条件(范围,等值,IN)在采样数据中的占比我们称之为选择率,再乘以表的总行数可以预估该条件需要检索的行数。对于单列索引可以直接计算出索引的检索行数,对于联合索引涉及多列,假设每列是相互独立的,那么将每一列的选择率相乘即可得到联合索引的选择率,进而可以预估联合索引的检索行数。
有部分表缺少合适的索引,导致SQL低效。为了应对这个问题,BaikalDB实现了一套索引推荐方案。通过对每类SQL的多种信息进行统计,包括:扫描行数,过滤行数,平响,pv。计算出过滤率=过滤行数/扫描行数,过滤率越大,扫描行数越大则越低效。然后再结合过滤率,平响,和统计哪个条件过滤最多,通过这些信息来综合推荐索引。
为了应对业务不断增加的检索需求,BaikalDB实现了FULLTEXT索引,内置了7种切词类型,依然通过RocksDB存储倒排拉链。倒排拉链分为3层,realtime层,buffer层,base层。
目前拉链采用了arrow格式:arrow.apache.org 。这个格式最大的特点是不需要解析,因此速度比protobuf更快,但是构造单行性能比pb差很多。因此我们realtime使用的是pb,buffer和base层使用的是arrow格式。 BaikalDB内置布尔引擎,基于like或match against语法,可以实现不同的算子组合归并,方便业务定制功能。
Online Schema Change的核心思想是通过状态跳变,实现异步变更,整个过程无需锁表。整个状态分为NONE、DELETE_ONLY、WRITE_ONLY、WRITE_LOCAL、PULBIC这几个状态。
为了解决稳定性不足的问题,我们对线上影响稳定性的问题总了分类总结:
这些影响稳定性的问题,会严重损害业务,造成业务pvlost,客户投诉、赔款等。因此我们根据问题发生的频率与严重程度,制定不同优先级逐步解决
之前的备份系统直接基于SQL读写,性能很差,导入1亿行(147G)数据,需要耗费8小时以上。而BaikalDB本身是个分布式数据库,数据量很大,因此这种恢复速度显然不能满足业务需求。
实时输出能力的缺失,导致业务无法像使用mysql那样,把数据实时同步到各个系统(例如redis,udw,做备份表,etl后流给检索系统),无法满足业务多样化的需求。
为了能让业务更安心的使用,我们采用了下述方案来进行解决:
物理隔离:即拆分集群,基于meta的调度与raft的addpeer能力,BaikalDB支持不同表直接一键拆分存储集群,整个过程中业务无感知。 但是物理隔离无法解决全部问题,集群拆分过多的话,资源消耗会增多,并且运维压力也会变大。况且单个表多个SQL相互影响这个也无法通过拆分集群解决。 因此我们增加了基于令牌桶流控实现的逻辑隔离。
令牌桶的分配也是通过SQL聚类进行,线上几千类,每类SQL都会统计最近1小时的qps,扫描量等信息,以此来分配令牌。除此之外为了突发流量,BaikalDB采取了单速双桶策略,在承诺令牌之外预留部分超额令牌,保障额外的突增流量可以获取到令牌。
BaikalDB选取了第一条DML指令的某一个region为primary region(sync point),在执行COMMIT/ROLLBACK的时候首先向primary region发送请求,保证primary region执行成功,再向其他region发送COMMIT/ROLLBACK,让primary region来充当事务协调者的角色。 BaikalDB分布式事务采用两阶段提交,prepare后节点故障,节点恢复后反查Primary Region。 Store为了防止行锁卡raft状态机,采用Leader持锁成功后raft同步的方式进行单语句复制。
rocksdb达到stall状态时,hold addpeer/index操作,达到根据rocksdb压力动态调控的目的。 对于大region,进行拆分写sst操作,做到每个sst大约是128M。 增加预估大小分裂机制,减少大region。 简单来说,就是根据rocksdb压力来动态调控:压力小,全速迁移,压力大,不迁移。
BaikalDB直接操作sst数据,彻底解决计划开销的问题。并且恢复的时候直接ingest sst到rocksdb中,解决memtable写入开销问题,恢复速度提升百倍以上。 采用了COW机制,只有数据变更才更新备份,减少备份压力(一个百亿行的表,每天更新数据占比很小)。
类似MySQL,BaikalDB实现了Binlog来实时输出数据。这里面有几个设计点可供参考:
随着C端业务的不断增多,对系统的KV性能要求也越来越高
业务方 | 性能需求 |
---|---|
基木鱼C端H5渲染页 | 几十万 qps 查询 |
百度统计url历史 | 几十万 qps写入(sata盘) |
商阿C端物料查询 | 几十万 qps查询 |
搜索词PV仓库,关键词推荐 | 百万级 qps查询 |
然后由于BaikalDB是一个通用存储,因此性能对比基于SSD的KV系统存在明显的差距。在相同资源下,对比了基于SSD的KV系统发现只有其1/4左右的性能,急需优化。
商业这边有许多千万物料的大户,还有各种对客户画像,人群画像的分析系统,需要处理较多数据的查询。目前基于表格系统、ElasticSearch、Spark+HDFS等不同系统的查询无法满足业务需求,现状如下:
业务方 | 性能需求 | 性能现状 |
---|---|---|
Optlog(表格存储) | 毫秒级 | 若干秒 |
物料列表(ES)(大户千万物料) | 十秒级查询 | 经常超时 |
星云业务(宽表500列) | 一天数据秒级分析 | 新业务 |
观星盘人群画像离线(HDFS) | 分钟级 | 小时级 |
为解决以上问题,我们采取的方案是:
我们从火焰图分析,结合KV场景取单行数据,显然查询计划在整个查询过程中消耗很高。 我们对于性能优化的核心点是抓大放小。 小:火焰图发现查询计划开销好高,开始优化查询计划,内存池,对象池等等 大:每天几十亿pv,可以聚合成几千条SQL;cache查询计划,几十亿次缩减成约几百万次(几千sql*实例线程数)
BaikalDB设计特点:在满足OLTP的基础上尽可能支持OLAP。我们与业务方进行深度和做,尽量满足业务的查询需求。具体如下:
本文先简单介绍了商业系统的业务背景与存储需求。通过持续迭代BaikalDB过程中遇到的一些问题挑战进行分析与思考,并给出我们的一些解决方案。
主要包括针对索引方面的问题,我们开发了代价索引选择,开箱即用的检索功能,在线索引变更;针对稳定性与安全性方面,我们主要采用了物理+逻辑隔离的多租户方案,基于sst的全量备份恢复方案,以及binlog方案来保证;针对性能方面,分享了一些我们的性能优化的方案。
这些问题都是过去我们遇到的典型问题,希望对大家有所启发。随着系统的发展迭代,我们还在不断遇到新问题新思路,欢迎大家持续关注BaikalDB的开源项目github.com/baidu/BaikalDB 。
原文:https://www.cnblogs.com/c3584411/p/15306041.html