# 23 | MySQL经常遇到的高可用、分片问题,NewSQL是如何解决的? 你好,我是李玥。 在这个系列课程中,我们讲的都是如何解决生产系统中面临的一些存储系统相关的问题。在最后两节课里面,我们来说点儿新东西,看一下存储这个技术领域,可能会有哪些值得关注的新技术。当然,技术圈每天都有很多新的技术出现,也会经常发很多论文,出现很多的开源项目,这些大多数都不太靠谱儿。 今天我给你要说的这个New SQL,它是我个人认为非常靠谱,甚至在未来可能会取代MySQL这样的关系型数据库的一个技术。MySQL是几乎每一个后端开发人员必须要精通的数据库,既然New SQL非常有可能在将来替代MySQL,那我们就非常有必要提前去了解一下了。 ## 什么是New SQL? 什么是New SQL?这个说来话长了,还要从存储技术发展的历史来解读。我们知道,早期只有像MySQL这样的关系数据库,这种关系型数据库因为支持SQL语言,后来被叫做SQL或者Old SQL。 然后,出现了Redis和很多KV存储系统,性能上各种吊打MySQL,而且因为存储结构简单,所以比较容易组成分布式集群,并且能够做到水平扩展、高可靠、高可用。因为这些KV存储不支持SQL,为了以示区分,被统称为No SQL。 No SQL本来希望能凭借高性能和集群的优势,替代掉Old SQL。但用户是用脚投票的,这么多年实践证明,你牺牲了SQL这种强大的查询能力和ACID事务支持,用户根本不买账,直到今天,Old SQL还是生产系统中最主流的数据库。 这个时候,大家都开始明白了,无论你其他方面做的比Old SQL好再多,SQL和ACID是刚需,这个命你革不掉的。你不支持SQL,就不会有多少人用。所以你看,近几年很多之前不支持SQL的数据库,都开始支持SQL了,甚至于像Spark、Flink这样的流计算平台,也都开始支持SQL。当然,虽然说支持SQL,但这里面各个产品的支持程度是参差不齐的,多多少少都有一些缩水。对于ACID的支持,基本上等同于就没有。 这个时候,New SQL它来了!简单地说,New SQL就是兼顾了Old SQL和No SQL的优点: * 完整地支持SQL和ACID,提供和Old SQL隔离级别相当的事务能力; * 高性能、高可靠、高可用,支持水平扩容。 像Google的Cloud Spanner、国产的OceanBase以及开源的[CockroachDB](https://github.com/cockroachdb/cockroach)都属于New SQL数据库。Cockroach这个英文单词是蟑螂的意思,所以一般我们都把CockroachDB俗称为小强数据库。 这些New SQL凭什么就能做到Old SQL和No SQL做不到的这些特性呢?那我们就以开源的CockroachDB为例子,来看一下New SQL是不是真的这么厉害。 ## CockroachDB是如何实现数据分片和弹性扩容的? 首先,我们一起先来简单看一下CockroachDB的架构,从架构层面分析一下,它是不是真的像宣传的那么厉害。我们先来看一下它的架构图(图片来自于[官方文档](https://github.com/cockroachdb/cockroach/blob/master/docs/design.md)): ![](https://static001.geekbang.org/resource/image/8c/82/8c78db973e66bb62b23c8e85afe78082.jpg) 这是一个非常典型的分层架构,我们从上往下看。最上层是SQL层,SQL层支持和关系型数据库类似的逻辑数据结构,比如说库、表、行和列这些逻辑概念。SQL层向下调用的是一个抽象的接口层Structured Data API,实际实现这个API的是下面一层:Distributed, Monolithic KV Store,这就是一个分布式的KV存储系统。 我们先不深入进去看细节,从宏观层面上分析一下这个架构。你可以看到,这个架构仍然是我们之间讲过的,大部分数据库都采用的二层架构:执行器和存储引擎。它的SQL层就是执行器,下面的分布式KV存储集群就是它的存储引擎。 那我们知道,MySQL的存储引擎InnoDB,实际上是基于文件系统的B+树,像Hive和HBase,它们的存储引擎都是基于HDFS构建的。那CockroachDB这种,使用分布式KV存储来作为存储引擎的设计,理论上也是可行的,并没有什么特别难以逾越的技术壁垒。 而且,使用分布式KV存储作为存储引擎,实现高性能、高可靠、高可用,以及支持水平扩容这些特性,就不是什么难事儿了,其中很多分布式KV存储系统已经做到了,这里面使用的一些技术和方法,大多我们在之前的课程中也都讲到过。CockroachDB在实现它的存储引擎这一层,就是大量地借鉴,甚至是直接使用了已有的一些成熟技术。 它的分片算法采用的是范围分片,我们之前也讲到过,范围分片对查询是最友好的,可以很好地支持范围扫描这一类的操作,这样有利于它支撑上层的SQL查询。 它采用[Raft](https://raft.github.io/)一致性协议来实现每个分片的高可靠、高可用和强一致。这个Raft协议,它的一个理论基础,就是我们之前讲的复制状态机,并且在复制状态机的基础上,Raft实现了集群自我监控和自我选举来解决高可用的问题。Raft也是一个被广泛采用的、非常成熟的一致性协议,比如etcd也是基于Raft来实现的。 CockroachDB的元数据直接分布在所有的存储节点上,依靠流言协议来传播,这个流言协议,我们在《[16 | 用Redis构建缓存集群的最佳实践有哪些?](https://time.geekbang.org/column/article/217590)》这节课中也讲到过,在Redis Cluster中也是用流言协议来传播元数据变化的。 CockroachDB用上面这些成熟的技术解决了集群问题,在单机的存储引擎上,更是直接使用了RocksDB作为它的KV存储引擎。RocksDB也是值得大家关注的一个新的存储系统,下节课我们会专门讲RocksDB。 你可以看到,CockroachDB的存储引擎,也就是它的分布式KV存储集群,基本上没有什么大的创新,就是重用了已有的一些成熟的技术,这些技术在我们之前讲过的其他存储系统中,全部都见到过。我讲这些并没有贬低CockroachDB的意思,相反,站在巨人的肩膀上,才能看得更远,飞得更高,这是一种非常务实的做法。 ## CockroachDB能提供金融级的事务隔离性么? 接下来我们说一下CockroachDB是怎么实现ACID的,它的ACID是不是类似于分布式事务的残血版?这是一个非常关键的问题,直接影响到它有没有可能在未来取代MySQL。 在说ACID之前,我们还是要简单说一下CockroachDB是怎么解析和执行SQL的。我们在《[10 | 走进黑盒:SQL是如何在数据库中执行的?](https://time.geekbang.org/column/article/213176)》这节课中讲过SQL是如何在MySQL中执行的,在CockroachDB中,这个执行的流程也是差不多的。同样是先解析SQL生成语法树,转换成逻辑执行计划,再转换为物理执行计划,优化后,执行物理执行计划返回查询结果,这样一个流程。 只是在CockroachDB中,物理执行计划就更加复杂了,因为它的物理执行计划面对的是一个分布式KV存储系统,在涉及到查找、聚合这类操作的时候,有可能需要涉及到多个分片(Range)。大致过程就是类似于Map-Reduce的逻辑,先查找元数据确定可能涉及到的分片,然后把物理执行计划转换成每个分片上的物理执行计划,在每个分片上去并行执行,最后,再对这些执行结果做汇总。 然后我们再来说CockroachDB的ACID。我们在《[04 | 事务:账户余额总是对不上账,怎么办?](https://time.geekbang.org/column/article/206544)》这节课中讲到过四种事务隔离级别,分别是RU、RC、RR和SERIALIZABLE,那CockroachDB能提供哪种隔离级别呢?答案是,以上四种都不是。 CockroachDB提供了另外两种隔离级别,分别是:**Snapshot Isolation (SI)** 和 **Serializable Snapshot Isolation (SSI)**,其中SSI是CockroachDB默认的隔离级别。 这两种隔离级别和之前提到的四种隔离级别是什么关系呢?我们通过下面这张表,和MySQL默认的隔离级别RR做一个对比。 ![](https://static001.geekbang.org/resource/image/20/67/20e20d983ad7519e6eae11821a3f1567.jpg) 首先我们看SI这一行。我们之前讲到过,RR这种隔离级别,可以很好地解决脏读和不可重复读的问题,虽然可能会产生幻读,但实际上对绝大多数事务影响不大。SI不会发生脏读、不可重复读,也不会发生幻读的情况,这个隔离级别似乎比RR还要好。 但你要注意一下,我们这个表格比之前那个表格多了一列:写倾斜。可以看到,RR是不会出现写倾斜问题的,但是SI会有写倾斜问题。 什么是写倾斜?我们还是拿账户余额的例子来说明。比如说,我们的账户需要支持主副卡,主卡和副卡都分别有自己的余额,并且这个余额是可以透支的,只要满足主副卡的余额之和大于0就行了。如果我们需要给主卡支出100元,那SQL只要这样写就可以了: ``` update account set balance = balance - 100 -- 在主卡中扣减100元 where id = ? and (select balance from account where id = ?) -- 主卡余额 + (select balance from account where id = ?) -- 附卡余额 >= 100; -- 主副卡余额之和必须大于100元 ``` 在传统的RR隔离级别下,由于更新数据时会对记录加锁,即使更新主副卡的两个SQL分别在两个事务中并发执行,也不会出现把主副卡的余额之和扣减成负数的情况。 但是在SI级别下,由于它没有加锁,而是采用快照的方式来实现事务的隔离,这个时候,如果并发地去更新主副卡余额,是有可能出现把主副卡余额之和扣减为负数的情况的。这种情况称为**写倾斜**。这里顺便提一句,写倾斜是普遍的译法,我个人觉得“倾斜”这个词翻译得并不准确,实际上它表达的,就是因为没有检测读写冲突,也没有加锁,导致数据写错了。 SSI隔离级别在SI的基础上,加入了冲突检测的机制,通过检测读写冲突,然后回滚事务的方式来解决写倾斜的问题,当然这种方式付出的代价是降低性能,并且冲突严重的情况下,会频繁地出现事务回滚。 从理论上来说,CockroachDB支持的SI和SSI这两种事务隔离级别,能提供的事务隔离性,已经与传统的RC和RR隔离级别不相上下了,可以满足大多数在线交易类系统对ACID的要求。 ## 小结 New SQL是新一代的分布式数据库,它具备原生分布式存储系统高性能、高可靠、高可用和弹性扩容的能力,同时还兼顾了传统关系型数据库的SQL支持。更厉害的是,它还提供了和传统关系型数据库不相上下的、真正的事务支持,具备了支撑在线交易类业务的能力。 CockroachDB是开源的New SQL数据库。它的存储引擎是一个分布式KV存储集群,执行器则大量借鉴了PostgreSQL的一些设计和实现,是一个集很多现有数据库和分布式存储系统技术于一身,这样的一个数据库产品。 从设计上来看,CockroachDB这类New SQL数据库,有非常大的潜质可以真正地取代MySQL这类传统的关系型数据库。但是我们也应该看到,目前这些New SQL数据库都还处于高速发展阶段,并没有被大规模地应用到生产系统中去。我也不建议你做小白鼠,在重要的系统上去使用它。 ## 思考题 课后请你去看一下[Raft](https://raft.github.io/)一致性协议,然后简单总结一下,CockroachDB是如何利用Raft协议实现Range高可用、高可靠和强一致的。欢迎你在留言区与我讨论。 感谢你的阅读,如果你觉得今天的内容对你有帮助,也欢迎把它分享给你的朋友。