分库分表的知识

简述

分库分表是数据量大的场景下的一种技术优化方案,当数据量逐渐增大,单库单表已经无法满足业务需求时,分库分表成为了一个必要的选项。
分库分表可以有效地缓解数据库的性能瓶颈,提高系统的稳定性和可扩展性。但是,也不是一旦数据量增大就直接分库分表一把梭的,还是得根据实际情况实际执行。一般我们认为,单表行数超过 500 万行或者单表容量超过 2GB之后,才需要考虑做分库分表了,小于这个数据量,遇到性能问题先建议大家通过其他优化来解决。

分库

分库主要解决的是并发量大的问题。因为并发量一旦上来了,那么数据库就可能会成为瓶颈,因为数据库的连接数是有限的,虽然可以调整,但是也不是无限调整的。
所以,当你的数据库的读或者写的QPS过高,导致你的数据库连接数不足了的时候,就需要考虑分库了。
那既然是分库,本质也是数据分片,根据业务选择垂直分库或者水平分库。
垂直分库是将数据库按照功能或业务模块进行分割,例如将订单相关的表存储在一个数据库中,将用户信息相关的表存储在另一个数据库中。垂直分库的优点是可以减少单个数据库的数据量和复杂度,降低单点故障的风险,并且更容易进行扩展和维护。但是,垂直分库可能会导致跨库查询和事务处理的复杂性增加,需要进行更多的数据同步和维护。
水平分库是将数据库中的数据按照某种规则(例如按照用户ID或时间范围)进行分割,并将每个分片存储在不同的数据库中。水平分库的优点是可以处理海量数据,并且可以更容易地进行扩展和负载均衡。

但是,水平分库需要更多的数据库实例和更复杂的数据同步和维护机制,而且在跨分片查询和事务处理方面也存在一些限制,所以一般情况下都会只采用垂直分库,而用分表来解决数据量大的问题。

分表

假如你的单表数据量非常大,因为并发不高,数据量连接可能还够,但是存储和查询的性能遇到了瓶颈了,你做了很多优化之后还是无法提升效率的时候,就需要考虑做分表了。
分表本质也是数据分片,根据业务选择垂直分表或者水平分表。
垂直分表就是把这张表中某一条记录的多个字段,拆分到多张表中,表中的数据的字段数会变少,使得每一个单表中的数据的存储有所下降。
水平分表就是把一张表中的不同的记录分别放到不同的表中,使得每一个单表中的数据的存储有所下降。那这种分表模式的话就得规划合适的分片键了,根据规划的分片键来决定数据落到哪张表里。

分片

分片算法

直接取模

在分库分表时,我们是事先可以知道要分成多少个库和多少张表的,所以,比较简单的就是取模的方式。
比如我们要分成128张表的话,就用一个整数来对128取模就行了,得到的结果如果是002,那么就把数据放到order_002这张表中。

Hash取模

那如果分表字段不是数字类型,而是字符串类型怎么办呢?有一个办法就是哈希取模,就是先对这个分表字段取Hash,然后在再取模。
但是需要注意的是,Java中的hash方法得到的结果有可能是负数,需要考虑这种负数的情况。

一致性Hash

前面两种取模方式都比较不错,可以使我们的数据比较均匀的分布到多张分表中。但是还是存在一个缺点。
那就是如果需要扩容二次分表,表的总数量发生变化时,就需要重新计算hash值,就需要涉及到数据迁移了。
为了解决扩容的问题,我们可以采用一致性哈希的方式来做分表。一致性哈希可以按照常用的hash算法来将对应的key哈希到一个具有2^32次方个节点的空间中,形成成一个顺时针首尾相接的闭合的环形。所以当添加新表时,只有增加表的位置和逆时针方向第一张表之间的键会受影响。
一致性Hash算法可参考另一篇博文,这里不做多解释。

全局ID

涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题,因为在单表中我们可以用数据库主键来做唯一ID,但是如果做了分库分表,多张单表中的自增主键就一定会发生冲突。那就不具备全局唯一性了。
那么,如何生成一个全局唯一的ID呢?有以下几种方式:

UUID

很多人对UUID都不陌生,它是可以做到全局唯一的,而且生成方式也简单,但是我们通常不推荐使用他做唯一ID,首先UUID太长了,其次字符串的查询效率也比较慢,而且没有业务含义,根本看不懂。

雪花算法

雪花算法也是比较常用的一种分布式ID的生成方式,它具有全局唯一、递增、高可用的特点。
雪花算法生成的主键主要由 4 部分组成,1bit符号位、41bit时间戳位、10bit工作进程位以及 12bit 序列号位。
时间戳占用41bit,精确到毫秒,总共可以容纳约69年的时间。
工作进程位占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,做多可以容纳1024个节点。
序列号占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID。
所以,一个雪花算法可以在同一毫秒内最多可以生成1024 X 4096 = 4194304个唯一的ID

公共基因算法

这个算法来源于对于基因的分类的计算,可以把一个自定义的Id 加上 期望根据 某个字段来分片查询的hash值来组成新的Id,这样这个作为分片id的话就很方便的处理额外的查询字段了。

分库分表的工具

在选定了分表字段和分表算法之后,那么,如何把这些功能给实现出来,需要怎么做呢?
我们如何可以做到像处理单表一样处理分库分表的数据呢?这就需要用到一个分库分表的工具了。
目前市面上比较不错的分库分表的开源框架主要有三个,分别是sharding-jdbc、TDDL和Mycat

Sharding-JDBC

现在叫ShardingSphere(Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar这3款相互独立的产品组成)。它定位为轻量级Java框架,在Java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
开原地址:https://shardingsphere.apache.org

TDDL

TDDL 是淘宝开源的一个用于访问数据库的中间件, 它集成了分库分表, 读写分离,权重调配,动态数据源配置等功能。封装 jdbc 的 DataSource给用户提供统一的基于客户端的使用。
开源地址:https://github.com/alibaba/tb_tddl

Mycat

Mycat是一款分布式关系型数据库中间件。它支持分布式SQL查询,兼容MySQL通信协议,以Java生态支持多种后端数据库,通过数据分片提高数据查询处理能力。
开源地址:https://github.com/MyCATApache/Mycat2

存在问题

分库分表之后,会带来很多问题。
首先,做了分库分表之后,所有的读和写操作,都需要带着分表字段,这样才能知道具体去哪个库、哪张表中去查询数据。如果不带的话,就得支持全表扫描。
但是,单表的时候全表扫描比较容易,但是做了分库分表之后,就没办法做扫表的操作了,如果要扫表的话就要把所有的物理表都要扫一遍。
还有,一旦我们要从多个数据库中查询或者写入数据,就有很多事情都不能做了,比如跨库事务就是不支持的。
所以,分库分表之后就会带来因为不支持事务而导致的数据一致性的问题。
其次,做了分库分表之后,以前单表中很方便的分页查询、排序等等操作可能就都失效了,因为我们不能跨多表进行分页、排序。
总之,分库分表虽然能解决一些大数据量、高并发的问题,但是同时也会带来一些新的问题。所以,在做数据库优化的时候,还是建议大家优先选择其他的优化方式,最后再考虑分库分表。

你可能感兴趣的:(数据库)