0%

分库分表

分库分表

分库分表是为了解决单库单表数据量太大,导致的数据库压力过大

分库分表有两种方式,分为垂直拆分和水平拆分

垂直拆分

垂直分表和垂直分库

  • 垂直分表 把一张大表中的字段拆分为两张表
  • 垂直分库 由于垂直分表之后,两个表还是存储在同一个数据库中,而如果数据库中表过多,数据库的压力过大,可以按照业务来对数据库进行拆分

带来的问题

  • 单机的ACID被打破,要么放弃原本的单机事务,修改实现,要么引入分布式事务
  • join操作很不方便,不能很方便地利用数据库自身的join,需要应用或者其他方式来解决
  • 靠外键去进行约束的场景会受到影响

垂直拆分的规则简单,尤其适合各业务之间的耦合度较低且业务清晰的系统

水平拆分

水平分表和水平分库

  • 水平分表
  • 水平分库 如果对于同一业务中的表,依然是数据量特别大,可以对数据库进行水平拆分,其中的结构完全相同

带来的问题

  • 单机的ACID同样可能被打破
  • Join操作同样可能被影响
  • 靠外键去进行约束的场景会被影响
  • 依赖单库的自增序列生成唯一ID会受影响
  • 针对单个逻辑意义上的表的查询要跨库

几种典型的分片规则为:

  • 根据日期来分片
  • 根据某个特定的字段,如用户id来进行取模分片

问题解决

单机ACID问题

单机事务问题可以使用分布式事务 https://zhhll.icu/2022/分布式/分布式问题/2.分布式事务/,或者最终一致性 https://zhhll.icu/2022/分布式/分布式问题/3.数据一致性/ 来解决

自增序列问题

之前在单机时我们经常使用mysql的autoIncrement来生成id,但是在进行分库分表后,这种方式不能满足我们的要求了,根据id的特性:唯一性和连续性,我们来寻找解决方案

  • 唯一性,如果只考虑唯一性的话,那么可以参考UUID来生成,但是连续性不好
  • 连续性,考虑到整体全局的连续性的话,我们只能采用独立的系统来解决这个问题

将所有的id生成交给一个单独的系统去处理,所有的机器都是用id生成服务来生成,这个方式有几点不足

  • 性能问题,每次都远程取id会有性能损耗,改进方案是每次取一段,剩下的缓存在本地,这样就不需要每次都去远程获取了,但是这样如果机器宕机了,可能会有部分id的浪费
  • id生成服务的稳定性,这里使用了一个新的服务,无疑又增加了一个宕机的风险点
  • 存储问题

可以使用分布式ID生成器,如Snowflake算法

join问题

由于数据已经分布到多个数据库中了,join操作可能会出现跨库的情况,这个如何操作呢?这里说几个思路

  • 将需要进行数据库join的操作分为多次数据库操作
  • 数据冗余,对一些常用的信息进行冗余,将原本需要join的操作变成单表查询
  • 借助elasticsearch来做宽表解决跨库

分布式事务问题

因为是在不同的数据库,可能会出现分布式事务问题。要找到合适的分库键,根据不同的业务逻辑选择适合该业务逻辑的分库键

用户维度

  • 适用场景:在许多互联网应用中,如社交、电商、金融等领域,如果业务以用户为核心,用户的操作和数据是系统的主要负载,可选择用户 ID 作为分库键。例如,在一个社交平台中,用户的好友列表、发布的内容、消息等数据都与用户紧密相关。
  • 优点:
    • 数据隔离性好,不同用户的数据会被存储在不同的数据库或表中,方便进行用户级别的数据管理和操作。
    • 数据访问的热点分散,不同用户的请求会被路由到不同的数据库,避免了单个数据库的热点问题,提高了系统的并发处理能力。

订单维度

  • 适用场景:对于电商系统中的订单数据,可选择订单 ID 作为分库键。因为订单相关的操作,如订单的创建、查询、修改和删除等,都可以基于订单 ID 进行。
  • 优点:
    • 保证订单数据的存储和操作在不同的数据库节点上分布均匀,提高订单处理的性能。
    • 方便对订单数据进行分布式存储和处理,减少单个数据库的存储和访问压力。

当然分库键只能减少分布式事务,而不能避免。可以使用消息中间件来实现柔性事务

欢迎关注我的其它发布渠道