《从 0 开始学架构》学习归整(四)高性能数据库集群

内容纲要

读写分离

原理

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

读写分离

基本实现

  • 搭建主从集群(一主 N 从;N >= 1)
  • 数据库主机负责读写操作;从机只负责读操作
  • 数据库主机通过复制将数据同步到从机上;每台数据库服务器都存储了所有的业务数据
  • 业务服务器将写操作发给主机,读操作发给从机

主从、主备有本质区别,请注意区分

复杂度

主从复制延迟

问题:存在时间差,即如果业务服务器将数据写入主服务器,在主从服务器数据还没有同步完成时就从从服务器读取数据,会导致数据不存在或读出旧数据。

解决办法

  1. 写操作后的读操作指定发给主服务器
    • 对业务侵入性大;如果程序员不熟悉这种方式,可能会出现 BUG
  2. 二次读取:读从机失败后再从主机读取数据
    • 如果存在大量二次读写操作,会给主机带来很大压力
  3. 关键业务读写操作全部指向主机;非关键业务完全读写分离
    • 业务分离得更清晰
分配机制

分配方式

  1. 程序代码封装:
    • 本质:在代码中抽象出一个专门用来分离读写操作和管理数据库服务器的数据访问层
    • 特点
      • 实现简单,可以根据业务做较多定制化操作
      • 每种语言都需要自己单独写一次(可能增加开发量)
      • 故障情况下,如果主从切换,可能需要所有业务服务器修改配置
  2. 中间件封装
    • 本质:把数据库访问分离出来成为一套独立的系统(在业务服务器看来,中间件服务就是数据库)
    • 特点
      • 支持多语言(中间件提供标准 SQL 接口)
      • 中间件支持完整的 SQL 语法和协议(实现复杂)
      • 中间件不自己执行读写操作,但所有操作都经过中间件(对性能要求高、可用性影响大)
      • 对业务服务器来说,数据库主从切换无感知

数据库中间件开发成本高,通常可以选择程序代码封装或者使用成熟的中间件。

读写分离作为一种优化方式,并不是所有业务都需要直接上读写分离;在考虑读写分离之前,可以先看看一些其它策略,比如缓存等。

分表分库

读写分离分散了访问压力,分表分库则可以分散数据库的存储压力。

单台数据库的存储压力:

  • 数据库太大,读写性能下降(索引也会变大,依然会下降)
  • 数据文件太大,数据恢复和备份需要的时间会变得很长
  • 数据文件随着变大,丢失数据的影响范围更广

业务分库

业务分库是指按照不同的业务模块,将数据分表存储到不同的数据库。

分库带来的问题
  • 无法使用 join 操作
  • 无法通过事务统一修改
  • 成本变高
建议(初创公司)

不建议过早分库。

原因:

  1. 业务存在很大不确定性(能活下来的十不存一),业务发展带来的性能压力不一定能到瓶颈,分库带来的价值不大,成本却不低
  2. 业务分库后,join、事务等很难发挥能力
  3. 分库后,不用的数据需要读取不同的数据库,增加业务代码的工作量

大公司业务体量和资源接入级别不一样,应尽早考虑分表分库

分表

分表是指将业务数据分散到不同的数据库(单台性能满足的情况下,不一定要分散到不同数据库)。

单表数据拆分通常有:

  • 垂直分表
    • 分字段
  • 水平分表
    • 分行(数据)

分表

为什么要分表
  • 海量业务数据(大公司用户数据、订单数据等)容易达到单台服务器的瓶颈

垂直分表

垂直分表适合将表中不常用、占用空间大的字段拆分出去。

水平分表

水平分表适合表行数特别大的表。复杂的表和简单的表的切分标准不一样,需要仔细研究。

水平分表的复杂性
  • 路由:水平切分后,确定某条数据属于那个库哪个表,需要增加路由算法进行计算
    • 范围路由:选取有序数据列(整形、时间戳等)作为路由条件,不同分段分配到不同的库、表中
      • 难点:分段大小,太大可能性能依然不够,太小导致分表过多,增加复杂度;一般在 100 ~ 2000 万之间,具体需要看表的复杂度
      • 优点:可以随着数据增加,平滑扩充新表
      • 缺点:可能会分布不均(有得表多,有的表少;比如用时间戳分表)
    • Hash 路由:选取一个或多个列的值进行 Hash 运算,根据得到的结果通过一定策略分散到不同的表
      • 难点:初始表数量的选取,初始表太多维护起来麻烦,太少容易达到性能瓶颈;增加子表数量困难,需要所有数据重新分布
      • 优点:分布均匀
      • 缺点:表数量扩充困难,增加子表需要重新分布
    • 配置路由:即路由表,用一张表来记录路由信息
      • 难点:相对低,操作简单
      • 优点:扩充简单,迁移方便(只需要迁移数据,再更改路由表信息)
      • 缺点:需要多次查询,增加性能消耗;路由表如果过大,同样存在分表、分库面临的问题(死循环)
  • join 操作:由于数据分散在不同的表、库当中,在 join 查询的时候,需要编写复杂的代码或者使用中间件进行多次 join,再返回合并结果
  • count 操作:数据分散在不同表,在 count 的时候不能简单一次达成
    • 常见处理方式
      • count 相加:分别对子表进行 count 操作,然后再把结果相加
        • 优点:实现简单
        • 缺点:性能比较低
      • 记录表:记录每张子表的数据量,每次更新子表数据,更新记录表的数据
        • 优点:对性能影响较小
        • 缺点:增加复杂度;需要确保记录数跟实际子表的数据量准确性;记录表本身会增加数据库写入量
  • order by 操作:数据分散在不同的数据库、表中,排序无法由数据库完成,需要编写业务代码来排序

分表实现方式

  • 业务代码封装
  • 中间件封装

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注