高性能架构之数据库集群

第70篇

极客时间《从0开始学架构》课程笔记。

关系数据库目前还是各种业务系统中关键和核心的存储系统,在很多场景下高性能的设计最核心的部分就是关系数据库的设计。因此高性能架构模式的重点依然是高性能数据库集群的设计。

高性能数据库集群有两种方式:
1、读写分离,其本质是将访问压力分散到集群中的多个节点,但是没有分散存储压力。
2、分库分表,既可以分散访问压力,又可以分散存储压力。

一、读写分离

读写分离的基本原理是将数据库读写操作分散到不同的节点上。

11857-3228639ed429614d.png
读写分离基本架构

读写分离的基本实现:

  • 数据库服务器搭建主从集群,一主一从、一主多从都可以。
  • 数据库主机负责读写操作,从机只负责读操作。
  • 数据库主机通过复制将数据同步到从机,每台数据库服务器都存储了所有的业务数据。
  • 业务服务器将写操作发给数据库主机,将读操作发给数据库从机。

从以上4点可以看出,第3、4两步操作是实现重点,因此也带来两个问题:

问题1:主从复制延迟

一般主从复制延迟可能达到 1 秒或更长,如果业务服务器将数据写入到数据库主服务器后立刻(1 秒内)进行读取,此时读操作访问的是从机,主机还没有将数据复制过来,到从机读取数据是读不到最新数据的,业务上就可能出现问题。

常见解决方案

  1. 写操作后的读操作指定发给数据库主服务器
  2. 读从机失败后再读一次主机
  3. 关键业务读写操作全部指向主机,非关键业务采用读写分离

问题2:分配机制

将读写操作区分开,然后访问不同的数据库服务器,一般有两种方式:程序代码封装和中间件封装。

1、 程序代码封装

程序代码封装指在代码中抽象一个数据访问层(所以有的文章也称这种方式为“中间层封装”),实现读写操作分离和数据库服务器连接的管理。

特点:

  • 实现简单,而且可以根据业务做较多定制化的功能。
  • 每个编程语言都需要自己实现一次,无法通用,如果一个业务包含多个编程语言写的多个子系统,则重复开发的工作量比较大。
  • 故障情况下,如果主从发生切换,则可能需要所有系统都修改配置并重启。

2、中间件封装

中间件封装指的是独立一套系统出来,实现读写操作分离和数据库服务器连接的管理。中间件对业务服务器提供 SQL 兼容的协议,业务服务器无须自己进行读写分离。对于业务服务器来说,访问中间件和访问数据库没有区别,事实上在业务服务器看来,中间件就是一个数据库服务器。

特点:

  • 能够支持多种编程语言,因为数据库中间件对业务服务器提供的是标准 SQL 接口。
  • 要支持完整的 SQL 语法和数据库服务器的协议,实现比较复杂,细节特别多,很容易出现 bug。
  • 数据库中间件自己不执行真正的读写操作,但所有的数据库操作请求都要经过中间件,中间件的性能要求也很高。
  • 数据库主从切换对业务服务器无感知,数据库中间件可以探测数据库服务器的主从状态。

建议:一般情况下采用程序语言封装的方式,或者使用成熟的开源数据库中间件。如MySQL Router、Atlas。

二、分库分表

业务分库

业务分库指的是按照业务模块将数据分散到不同的数据库服务器。

问题

业务分库能够分散存储和访问压力,但同时也带来了新的问题。

  1. join 操作问题
    业务分库后,原本在同一个数据库中的表分散到不同数据库中,导致无法使用 SQL 的 join 查询。
  2. 事务问题
    原本在同一个数据库中不同的表可以在同一个事务中修改,业务分库后,表分散到不同的数据库中,无法通过事务统一修改。虽然数据库厂商提供了一些分布式事务的解决方案(例如,MySQL 的 XA),但性能实在太低,与高性能存储的目标是相违背的。
  3. 成本问题
    业务分库同时也带来了成本的代价,本来 1 台服务器搞定的事情,现在要 3 台,如果考虑备份,那就是 2 台变成了 6 台。

方案

因为用户规模不同,对于小公司初创业务,不建议一开始就这样拆分。对于业界成熟的大公司来说,最好在业务开始设计时就考虑业务分库。

分表

将不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。
因此需要分表,单表数据拆分有两种方式:垂直分表和水平分表

11857-5a70c9e22e5aac3a.png
单表数据拆分

分表说明:

  1. 实际架构设计过程中不局限切分的次数,可以切一次、两次,也可以切很多次。
  2. 单表进行切分后,是否要将切分后的多个表分散在不同的数据库服务器中,可以根据实际的切分效果来确定,并不强制要求单表切分为多表后一定要分散到不同数据库中。

分表能够有效地分散存储压力和带来性能提升,但和分库一样,也会引入各种复杂性。

垂直分表

垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。
垂直分表引入的复杂性主要体现在表操作的数量要增加。

水平分表

水平分表适合表行数特别大的表,有的公司要求单表行数超过 5000 万就必须进行分表,这个数字可以作为参考,但并不是绝对标准,关键还是要看表的访问性能。

水平分表会引入更多的复杂性,主要表现在下面4方面:路由、join操作、count()操作、order by 操作。

  • 1、路由
    水平分表后,某条数据具体属于哪个切分后的子表,需要增加路由算法进行计算,这个算法会引入一定的复杂性。

常见的路由算法有3种:范围路由、Hash路由、配置路由,优缺点对比表如下:

路由算法 定义 设计复杂点 优点 缺点
范围路由 选取有序的数据列(例如,整形、时间戳等)作为路由的条件,不同分段分散到不同的数据库表中 分段大小的选取。分段太小会导致切分后子表数量过多,增加维护复杂度;分段太大可能会导致单表依然存在性能问题 可以随着数据的增加平滑地扩充新的表 分布不均匀
Hash路由 选取某个列(或者某几个列组合也可以)的值进行 Hash 运算,然后根据 Hash 结果分散到不同的数据库表中 初始表数量的选取。表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题 表分布比较均匀 扩充新的表很麻烦,所有数据都要重新分布
配置路由 增加路由表,用一张独立的表来记录路由信息 设计简单,容易扩充。在扩充表的时候,只需要迁移指定的数据,然后修改路由表就可以了 必须多查询一次,会影响整体性能;而且路由表本身如果太大(例如,几亿条数据),性能同样可能成为瓶颈
  • 2、join操作
    水平分表后,数据分散在多个表中,如果需要与其他表进行 join 查询,需要在业务代码或者数据库中间件中进行多次 join 查询,然后将结果合并。

  • 3、count()操作
    水平分表后,虽然物理上数据分散到多个表中,但某些业务逻辑上还是会将这些表当作一个表来处理。
    常见处理方式对比如下:

处理方式 操作方法 优点 缺点
count()相加 在业务代码或者数据库中间件中对每个表进行 count() 操作,然后将结果相加 实现简单 性能比较低
记录数表 新建一张表,表名为“记录数表”,包含 table_name、row_count 两个字段,每次插入或者删除子表数据成功后,都更新“记录数表” 性能要大大优于 count() 相加的方式 复杂度增加不少,对子表的操作要同步操作“记录数表”,增加了数据库的写压力,并且会出现数据不一致问题
  • 4、order by 操作
    水平分表后,数据分散到多个子表中,排序操作无法在数据库中完成,只能由业务代码或者数据库中间件分别查询每个子表中的数据,然后汇总进行排序。

分库分表的实现方法

分库分表具体的实现方式也是“程序代码封装”和“中间件封装”,但实现会更复杂。读写分离实现时只要识别 SQL 操作是读操作还是写操作,通过简单的判断 SELECT、UPDATE、INSERT、DELETE 几个关键字就可以做到,而分库分表的实现除了要判断操作类型外,还要判断 SQL 中具体需要操作的表、操作函数(例如 count 函数)、order by、group by 操作等,然后再根据不同的操作进行不同的处理。

猜你喜欢

转载自blog.csdn.net/weixin_34250434/article/details/86780006