最近数据库圈的一个比较大的事件是 NoSQL 先驱之一的 RethinkDB 的关张大吉,RethinkDB这个事情本身我就不多做评论了,现在这个时机去分析不免有马后炮的嫌疑,今天我想借着这个引子谈谈新型数据库的未来。 36大数据 纵观过去十年数据库的发展,其实是相当迅速,随着互联网的发展以及业务数据量的不断膨胀,大家渐渐发现传统的单机RDBMS 开始出现一些瓶颈,很多业务的模型也和当初数据库设计的场景不太一样,一个最典型的思潮就是反范式设计,通过适当的数据冗余来减小 JOIN 带来的开销,当初 *-NF 的设计目的是尽可能的减小数据冗余,但现在的硬件和存储容量远非当年可比,而且存储介质也在慢慢发生变化,从磁带到磁盘到闪存,甚至最近慢慢开始变成另一个异构的分布式存储系统 (如 Google F1, TiDB),RDBMS 本身也需要进化。
在 NoSQL 蓬勃发展的这几年也是 Web 和移动端崛起的几年,但是在 NoSQL 中反范式的设计是需要以付出一致性为代价,不过这个世界的业务多种多样,大家渐渐发现将一致性交给业务去管理会极大的增加业务的复杂度,但是在数据库层做又没有太好的可以 Scale 的方案,SPOF 问题和单机性能及带宽瓶颈会成为悬在业务头上的达摩克利斯之剑。
NewSQL 的出现就是为了解决扩展性和一致性之间的矛盾,我所理解的 NewSQL 主要还是面向 OLTP 业务和轻量级的OLAP业务,大型复杂的OLAP 数据库暂时不在本文讨论的范围。对于NewSQL 来说,我觉得应该需要明确一下必备的性质,什么样的数据库才能称之为NewSQL,在我看来,应该有以下几点必备的特性:
1.SQL
2.ACID Transaction, 支持跨行事务
3.Auto-scale
4.Auto-failover
SQL 一切为了兼容性!兼容性!兼容性! SQL 的支持是和之前 NoSQL 从接口上最大的不同点, 但是在一个分布式系统上支持 SQL 和在单机 RDBMS 上也是完全不一样的,更关键的是如何更好的利用多个节点的计算能力,生成更好的执行计划,将计算逻辑尽可能的均摊到多个存储节点上,设计这样一个 SQL engine 的更像是在做一个分布式计算框架,考虑的侧重点和单机上的查询引擎是不一样的,毕竟网络的开销和延迟是单机数据库之前完全不需要考虑的。
不过 从最近几年社区的实现来看,尤其是一些 OLAP 的系统,已经有不少优秀的分布式SQL 实现 ,比如: Hive, Impala,Presto, SparkSQL 等优秀的开源项目。虽然 OLTP 的 SQL engine 和 OLAP 的侧重点有所不同,但是很多技术是相通的,但是一些单机 RDBMS 的 SQL 是很难直接应用在分布式环境下的,比如存储过程和视图等。正如刚才提到的,越来越多的业务开始接受反范式的设计,甚至很多新的业务都禁止使用存储过程和外键等,这是一个好的现象。
ACID Transaction 对于 OLTP 类型的 NewSQL 来说,最重要的特点我认为是需要支持 ACID 的跨行事务,其实隔离级别和跨行事务是个好东西,能用更少的代码写出正确的程序,这个交给业务程序员写基本费时费力还很难写对,但在分布式场景下支持分布式事务需要牺牲点什么。
本身分布式 MVCC 并不是太难做,在 Google BigTable / Spanner 这样的系统中显式的多版本已经是标配,那么其实牺牲掉的是一定的延迟,因为在分布式场景中实现分布式事务在生产环境中基本就只有两阶段提交的一种办法,不过呢,考虑到 Multi-Paxos 或者 Raft 这样的复制协议总是要 有延迟的,两阶段提交所带来的 contention cost 相比复制其实也不算太多,对于一个分布式数据库来说我们优化的目标永远是吞吐。延迟问题也可以通过缓存和灵活降低隔离级别等方法 搞定。
很多用户在进行架构设计的时候,通常会避免分布式事务,但是这其实将很多复杂度转移给了业务层,而且实现正确的事务语义是非常复杂的事情,对于开发者的要求还蛮高的,之前其实没有太多的办法,因为几乎没有一个方案能在数据库层面上支持事务,但是有些对一致性的要求很高的业务对此仍然是绕不开的。
就如同 Google 的 Senior Fellow Engineer,Jeff Dean 在 去年的一个会议上提到,作为工程师最后悔的事情就是没有在 BigTable 上支持跨行事务,以 至于在随后的 10 多年,Google 内部的团队前赴后继的在 BigTable 上造了多套事务的轮子以支撑业务,不过这个遗憾在 Spanner 中已经弥补了,也算是 happy ending。
其实 Spanner 的例子是非常好的,而且我认为 Google Spanner 及 F1 是第一个在线上大规模使用的 NewSQL 系统,很多经验和设计我们这些后来者是可以借鉴的。我认为,从使用者的角度来 说,滥用事务带来的性能问题不能作为在数据库层面上不支持的理由,很多场景如果使用得当 ,能极大的降低业务开发的复杂度。
Scale 作为一个现代的数据库,可扩展性我觉得是排在第一位的,而且这里的可扩展性值得好好说一 下,目前关系型数据库的扩展方案上,基本只有分库分表和 PROXY 中间件两种方案,但是这两种方案并没有办法做到透明的弹性扩展,而且到达一定规模后的运维成本几乎是指数级别上升,这也是大多数公司在微服务实践的过程中遇到的最大障碍,服务可以很好的做到无状态和解耦合,但是数据天然是有状态的,比较理想的情况是有一个统一的,通用的存储层,让服务层能够放开手脚。
弹性扩展的技术在很多 NoSQL 里已经有过很好的实践,目前比较成熟的 方案还是类 BigTable 式的 Region based 的方案,在 Key-Value 层面上实现水平扩展。另外值得一提的是,对于 NewSQL 这类需要支持 SQL 的数据库而言,region based 的数据切分是目前唯一的方案, 因为 SQL 的查询需要支持顺序 Scan 的数据访问方式,基于一致性哈希的方案很难支持高效的顺序访问。
这里隐含了一个假设就是真正能够支持 Scale 的 NewSQL 架构很难是基于中间件的方案,SQL above NoSQL 是目前来说唯一的可行方案,从 Google 的选型也能看出来。
基于中间件的方案的问题是中间件很难生成足够高效的执行计划,而且底 层的多个数据库实例之间并没有办法提供一个统一的事务语义,在执行跨节点的 JOIN 或者事务的时候没有办法保证一致性,另外底层的数据库实例(一般来说是 PostgreSQL 或者 MySQL) 本身并没有原生的扩展方案,需要中间件层面上额外做大量的工作,这也是为什么中间件的方案很多,但是做完美非常困难。
|