探秘分布式解决方案: 多机DB带来的挑战——谈数据库的读写分离、非对称复制和数据分发
当网站业务快速发展,数据量不断地上升,那么不可避免的底层DB压力会越来越大。
一般最开始都可以靠更换硬件来解决,加钱上更好的机子是最简单的方案,但这只限于你的增加的硬件费用算起来相对经济以及确实单机能顶得住业务量来说的。
但是在你业务激增的情况下,数据和访问量的增长势必会突破单机的极限,即单一节点的计算能力无法管理所有的应用状态。你就只能用其他的方式来解决这个问题。
那么不靠升级硬件的话,你就得给当前的数据库减压
减压的方法主要有这么几个:
- 应用和SQL 层面的优化, 看是不是有着来自应用层完全没必要的压力(一般来说都挺多的,所以得好好优化)
- 引入其他中间件,降低实际落到 DB 的查询。 比如可以引入缓存、搜索引擎
- 把原来一台数据库的数据和访问分到多台数据库上边,也就是给DB做分布式/集群了
这篇博客探讨的最主要就是第三条里多机数据库的一种实现, 读写分离、主从架构。
读写分离
其实对于大型网站来说,有着不少业务都是读多写少的,这个状况也会直接反映到数据库上边。这种场景就完全可以采用读写分离的形式。
- 什么是读写分离
读写分离这个东西理解起来很简单,见名思意。即将读数据和写数据分开了,我画了个图,这大概就是一个读写分离的基本架构
可以看到其实就是在原来一个单机数据库的结构上增加了一个读库,这个库不承担写数据的工作,只提供读取数据的服务。
而就这么一个结构变化,他会带来两个问题
- 读库、写库的数据复制问题
- 服务对于数据源的选择问题
读写分离带来的挑战
- 数据复制
你如果希望通过增加读库的形式来分担主库的读压力,那么首先就需要解决数据如何从主库复制到读库的问题。
而非常好的一点就是,市面上主流关系型数据库都是自带这个功能的。可以直接使用其本身的机制。比如 MySQL 就支持 Master/Slave 结构,并且提供了基于SQL的和基于行的几种复制模式。这里就不展开说具体数据库实现了。
可功能虽说可以实现,但有一点是毋庸置疑的那就是数据复制会存在延时的可能,一致性是保证不了的。
读写分离用处很大,可以非常有效的分担主库读数据的压力。不过只能在对数据延时相对不太敏感的业务中使用。
- 数据源的选择
你增加一个读库,对于你的服务来说影响还是比较大的。
起码你的应用从现在开始, 必须根据不同的情况来选择不同的数据源了。
比如你的增删改操作 都得走主库,事务中的读操作也得走主库。 而你也得考虑到从库的数据复制延迟,所以根据业务的不同,应用普通的 ‘读’ 操作的选择也会有所差异。
那么如何应对这个数据源的选择呢? 你总不能在代码里手写不同的方法去请求的是不同的数据源吧 ,这其实也是一种方法,不过真这么用的估计是没有的。
其实数据源的选择这也是一个比较大的模块, 涉及到SQL路由、数据访问层、如何扩容等,具体的看有机会留到之后讲多机DB的博客说吧。
这里简单说下,其实应对方式呢主要就是需要使用一些工具/中间件了,各大语言的社区应该都有相关的轮子。比如 JAVA ,就有像是 Mycat 这样的中间件,也有像是 Sharding-JDBC 这样的嵌入程序的工具,至于用哪种根据业务来调研就行了。
非对称的数据复制
其实数据复制这个机制,数据库的自带实现都是所谓的镜像型复制, 也就是说你从库和主库的数据是一样的。
但有的时候, 你的业务需求可能会需要你这两个地方的数据不一样。
比如你的业务在复杂一点,你可能做了 分表
那么完全有可能有的表你在主库里和从库里, 他分表的依据还不一样。 就比如订单表里有买家和商家的ID, 那么你有可能在主库里边是通过买家ID来分的表, 但是你读库里又要用商家ID来分, 这种需求用数据库本身的机制就无法实现了。
不过方法总比困难多,面对这种场景 我们就可以选择引入一个 数据变更平台
引入了数据变更平台后,结构可能就会变成这样:
这里可以看到,这个所谓的 数据变更平台 啊,其实就是 实现数据库本身的接口,将数据复制到本机后,再将数据序列化成其他形式投递给读库
那么引入了这么一个数据变更平台之后,其实就可以做到所谓 非对称的数据复制了
读库的实现可以和写库不同
来,回到上边最开始说的问题。 做读写分离是为了什么?
是为了给当前的数据库减压,从而引入一个只读的数据库,从而有效的分担主库读数据的压力
那么这个减压的方式,所谓的引入只读数据库,其实定义还可以更加灵活一点。
- 数据库产品选型不一样
其实这种情况,也是会出现的。
比如你主库其实是个 Oracle 数据库,然后你服务有点顶不住了,想搞个一主多从。
问题是Oracle这东西又血马贵, 你说我用几个 MySQL 来当从库,好不好鸭?
- 用其他的中间件来当读库
再想一想, 缓存这个东西 是不是也可以理解成一个读库, 只不过是只有部分热数据的读库。
而对于缓存的数据更新一般都是通过在代码中进行数据库的增删改操作时同步更新, 所以缓存这个地方略过,可以把目光放到这几个上: 搜索引擎、大数据存储
其实像搜索引擎这种东西,也是可以理解为 “读库” 的, 因为作用其实是一样的, 都是为了给数据库减压,也是只读。
你一个应用,数据都存在关系型数据库里。然后将一些需要搜索的、或者说数据量巨大的数据, 给他压进搜索引擎、大数据储存。 像是什么ES、HBASE 里边。
然后一些特定的查询就可以直接路由到这些中间件里,既提高了查询效率,也减少了DB的压力,这不是很美好么?
这些需求如何应对?
之前在非对称的数据复制里说到了数据变更平台。这里来将这个抽象的概念核心给提取出来。 所谓数据变更平台他做的事情,其实还是一个所谓的数据分发
只是说之前说的数据变更平台 单纯的做了一个数据转化的操作,其实他可以继续拓展下去。
比如将复制过来的数据,序列化后投入到消息队列中。
然后监听消息队列的不同消费客户端就可以将其解析后存入不同的中间件了, 这也是经典实现了。
比如同步 MySQL 的数据后进行数据分发,一般都是用 binlog 监听的模式, 一个流程处理大概是这样子的:
监听 binlog 日志 > 消息队列 > 消费客户端
业界当然也早就有这种轮子了, 这是几个 binlog 监听的轮子:
mysql-binlog-connector-java
go-mysql-elasticsearch
这是大名鼎鼎的 canal : https://github.com/alibaba/canal
去读下 canal 的文档就知道,其实就是伪装成 MySQL 的 slave,来拉取MySQL数据进行数据的分发。
结语
说完了读写分离的挑战和应对,也解释了非对称复制和数据分发的原理
但是在数据库的分布式应用上,我说的也只是冰山一角。
其他的还有 分库分表、SQL路由、数据扩容、平滑迁移等等等等…… 太多了
而读写分离这个架构呢,虽然好处多,但是终归因为数据复制的延迟性 导致了其无法用在需要强一致的场景。
在多个节点上维护同一份状态并且保证彼此一致的成本又太高 (多主/多写)
所以最终还是需要设计一组策略,把应用数据分成若干份,让每一份数据路由到不同的节点进行处理。 就是说要分库分表了, 这也是数据膨胀后必定会面临的一个场景。
那么分库分表这个点呢,就留到下一篇 探秘分布式解决方案 系列的博客里再说吧。
看啥时候挤时间写写 (应该不会鸽的)
1 COMMENT