知识卡片

读写分离的分配机制:程序代码封装与中间件封装的权衡

结构图卡

内容

读写分离的第二个设计复杂度是”分配机制”——把读写操作区分开来分别发往不同数据库服务器这件事,具体由谁来做、放在哪一层做,有两种主流路径。程序代码封装是在业务代码里抽象出一个数据访问层,由这一层负责读写分离逻辑和数据库连接管理(如基于Hibernate做简单封装,淘宝的TDDL就是这条路线上比较有名的通用开源实现)。这种方式实现相对简单、可以按业务做较多定制,但缺点也很直接:每种编程语言都要各自实现一遍,无法跨语言复用,如果一个业务系统里同时有多种编程语言开发的子系统,重复开发的工作量会明显增加;而且一旦发生主从故障切换,可能所有接入的系统都要跟着改配置、重启,牵连面比较大。中间件封装则是把读写分离和数据库连接管理独立成一套单独的系统,这个中间件对业务服务器暴露标准SQL兼容协议——在业务服务器眼里,访问中间件和直接访问数据库没有区别,中间件本身在业务侧看起来就是一台数据库服务器。这种方式的优势是天然支持多种编程语言(因为暴露的是标准SQL接口),而且数据库主从切换对业务服务器完全无感知,由中间件自己去探测主从状态(比如向测试表写一条数据,写成功的就是当前的主机);但代价是中间件本身要完整实现SQL语法和数据库通信协议,细节极多、极易出bug,需要相当长时间才能稳定下来,而且所有数据库请求都要经过中间件这一层,对中间件自身的性能要求也很高。正因为中间件封装的复杂度比程序代码封装高出一个数量级,一般情况下推荐用程序代码封装或采用已经成熟的开源数据库中间件(如MySQL官方推出的MySQL Router、奇虎360开源的Atlas),只有具备足够人力投入、且预期未来会有大量业务系统接入的大公司,才值得自己投入研发一套数据库中间件——因为这类系统一旦做成,接入的业务系统越多,节省的重复开发成本就越可观。

结构图

flowchart TB
  A["读写分离的分配机制"]
  A --> B["程序代码封装<br/>业务代码内抽象数据访问层<br/>如TDDL"]
  B --> B1["优点:实现简单,可按业务定制"]
  B --> B2["缺点:每种语言要各自实现一遍<br/>主从切换可能需所有接入系统改配置重启"]
  A --> C["中间件封装<br/>独立系统,暴露标准SQL协议<br/>业务侧感知不到读写分离的存在"]
  C --> C1["优点:天然支持多语言<br/>主从切换对业务服务器无感知"]
  C --> C2["缺点:需完整实现SQL语法与通信协议<br/>细节多易出bug,且自身性能要求高"]
  B2 --> D["一般建议:用程序代码封装<br/>或采用成熟开源中间件(MySQL Router/Atlas)<br/>只有大公司值得自研中间件"]
  C2 --> D

参考来源

- 位置:《从零开始学架构》第14讲《高性能数据库集群:读写分离》"分配机制"(源文件:_epub-src/OEBPS/text00001.html) - 结论依据:原文分别说明程序代码封装"实现简单,而且可以根据业务做较多定制化的功能……每个编程语言都需要自己实现一次,无法通用……故障情况下,如果主从发生切换,则可能需要所有系统都修改配置并重启",中间件封装"能够支持多种编程语言……数据库中间件要支持完整的 SQL 语法和数据库服务器的协议……实现比较复杂,细节特别多,很容易出现 bug……数据库主从切换对业务服务器无感知",并总结"由于数据库中间件的复杂度要比程序代码封装高出一个数量级,一般情况下建议采用程序语言封装的方式,或者使用成熟的开源数据库中间件",直接支撑本卡片结论与结构图。 - 原始内容:程序代码封装指在代码中抽象一个数据访问层……中间件封装指的是独立一套系统出来,实现读写操作分离和数据库服务器连接的管理……由于数据库中间件的复杂度要比程序代码封装高出一个数量级,一般情况下建议采用程序语言封装的方式,或者使用成熟的开源数据库中间件。