返回顶部

分库分表问题

数据库可以通过主从复制将数据复制多份实现读写分离,读走从库,写走主库,应对量并发读的能力,同时提高数据安全性。

但是对于单个表,还存在很多问题,比如:

  • 单表记录过多,字段加上索引,索引的占用空间也会越来越大,影响查询。
  • 不同的数据,用户,商品等都存放在⼀个库中,甚至一张表中,⼀旦崩溃,全部模块都受到影响。

因此可以做分库分表,将问题隔离在某一张表或者某一个库中,降低单个库的压力。基本思想是依照某⼀种策略将数据尽量平均地分配到多个数据库节点或者多个表中。分库分表后,不同的节点值保存部分数据,所有库表加到一块就是所有的数据。拆分分为垂直拆分和水平拆分。

一些电商系统中的实践就是:不同的商品放在不同的库,高流量下来比如双11,即使⼀部分商品⽆法浏览下单,也能保证大部分商品没问题。

一、垂直拆分

原则⼀般是专库专用,注重业务相关性,将数据库的表分离到不同的库中,比如用户关系放在用户库,内容放在内容库。
但是水平拆分无法解决单张表记录量增长的问题,⽐如视频或者直播业务中,用户会在视频播放过程中发送大量的弹幕,会有数据膨胀的问题。因此需要做单张表拆分。

二、水平拆分

水平拆分更注重数据的特点,将单表内具有一定相关性的记录拆分到不同的表或者库中。但是要考虑拆分规则,也就是如何根据单条记录定位到库。
可选方案有:

  • 根据字段进行hash值计算,比如根据用户ID做hash,相当于把ID打散,然后对数据库的个数取余,得到的值就是记录对应要存放的库。
  • 用常规字段做区间划分,比如根据记录的创建时间,⼀条记录是1⽉创建,插⼊和查找的时候⽤创建时间这个字段先去找到对应的1号库。但是这种方式有明显的热点现象。⽐如6月11月是购物的热点事件,对应的两个表或者库的QPS就会更多。

三、分库分表引入的问题

主要是水平分库

1、区分键的问题

分区键问题:具体的表现是,如果把记录存放在哪个库是依据主键ID进行hash取余计算得来的,也就是分表的时候是根据主键定位到具体的库。那么后续如果根据非主键,比如根据用户名去查找记录,现在只根据用户名去定位记录,就要先找到主键ID,否则就要把所有库扫描一遍。

解决办法之⼀就是:建立⼀个额外的映射表。这个表只有两个字段用户名和主键ID,查询记录时候先根据用户名去找ID,然后再根据ID去定位到库表,虽然这个映射表会有⼀定的存储消耗,但是毕竟只有两个字段,相对来说可以接受。

2、数据库特性难以实现

聚合操作:在未分库分表之前查询记录总数时只需要在SQL中执⾏count()聚合函数即可,现在数据被分散到多个库表中,要么对所有的表都计算一遍或者比方说将计数的数据单独在⼀张表中或者记录在Redis⾥⾯单独记录。

3、全局ID问题

水平分表之后引入的另外一个比较严重的问题就是如何选择全局唯一ID作为主键。

四、如何选择全局ID

1、业务字段作为主键

选择业务字段作为主键,⽐如⾝份证,邮箱或者⼿机号等,但是并不通⽤

  • 比如⾝份证,对于一些匿名登陆产生的记录,则⽆法使用身份证证作为主键
  • 比如邮箱手机号,一般邮箱和手机号还存在变更的可能

2、UUID作为全局

主键要保证全局唯⼀性,保证每个机器上生成的记录都不会冲突,UUID类似的随机生成可以保证唯一,但是无法保证一定递增。
为什么要保证递增?

  • 因为可能会根据ID排序,⽐如弹幕系统,会按照倒序看最新发送的弹幕。如果ID不能保证递增,只能额外用时间字段记录时间戳,对所有的记录再排序。
  • 另外B+树的数据页是叶内记录按照ID递增,如果ID非递增,则可能导致记录非尾部插入最终数据页分裂,降低插⼊的性能。

3、雪花算法计算全局ID

Snowflake的核心思想是将64bit的⼆进制数字分成若干部分,每⼀部分都存储有特定含义的数据,比如说时间戳、机器ID、序列号等等,最终⽣成全局唯⼀的有序ID。
比如当前机器上,第2台机器,时间戳第32毫秒,有12个序列号位,当前时间戳下可以生成2^12个序号ID,基本可以满足要求。
雪花算法缺点:

  • 比较依赖系统时间戳,重启服务器就会机器编号,时间不准可能导致生成重复ID,当⽣成重复ID,可以暂停发号,进行时钟校准。
  • ⽐如服务的QPS并不⾼,时间间隔是毫秒为单位,导致当前毫秒下可以生成2^12个序号ID,序列号只了1-10的前几个ID,可以序列号的起始号随机或者扩⼤时间间隔为s。

五、总结

1、什么时候进行分库分表

很多说法是:

  • 1、表中记录超过2000万行,性能会出现瓶颈,主要是因为一个三级的
  • 2、单标记录超过500万行或者表的实际存储超过2GB、

实际也跟MySQL的配置和机器的硬件性能有关,mysql为了提高查询性能会把表的索引非叶子节点(指针和key)都加载到内存中,如果设置的内存足够大,索引文件都能加载到内存中,不会有问题,一个3层的B+树索引可以满足千万级别的数据存储。但是当单表的记录量超过这个上限,内存无法完全存放索引,查询就会产生多余的磁盘IO,从而性能下降。

2、原则

分库分表会对使⽤数据库带来⼀些不便,但是相比它所带来的扩展性和性能⽅⾯的提升还是需要做的,因为经历过分库分表后的系统,才能够突破单机的容量和请求量的瓶颈。
对于分库分表的原则主要有以下几点:

  • 1、如果在性能上没有瓶颈点那么就尽量不做分库分表;
  • 2、如果要做,就尽量⼀次到位基本能够满足几年内很长一段时间的需求。

posted on 2022-09-02 23:51  weilanhanf  阅读(884)  评论(0编辑  收藏  举报

导航