分库分表介绍
在当前业务量迅猛增加的情况下,数据库经常面临性能的极致挑战。尤其是在处理大规模的数据集,例如超过千万条数据记录的情况下,SQL查询的性能将显著下降。随着数据量的增加,查询所需要扫描的数据范围变得更广,从而导致查询速度的减慢。
此外,数据规模的膨胀也会对数据库的整体性能产生影响。例如,一个数据库如果承担了过大的数据容量,可能会遭遇存储空间和I/O性能的难题。存储空间的问题表现为数据库需要更多的硬件资源来存储数据,而I/O性能的问题则是因为大量的数据需要读写,而读写操作的效率受到硬件性能的限制。
在表结构中,字段数量的增多同样会对效率产生负面影响。字段数量的增多意味着每个记录的大小增加,从而导致数据的读写速度下降。
为了解决上述问题,采纳分库分表策略被广泛认为是有效的解决方案之一。分库分表策略的核心思想是将一个大型的数据库分解为多个小型的数据库,从而将数据分散存储在不同的地方,减少单点压力,提高查询效率。
分库有垂直分库、水平分库;分表有垂直分表、水平分表。具体介绍可参考:数据库分库分表实践
数据热点介绍
数据热点也叫数据倾斜,是指在构建数据存储架构时,通常会依据某种特定标识,如ID或年份,将数据分配至各个不同的表中。然而,在实际运用中,近期数据常常在某个特定的表中高度集中。这就导致了在日常业务操作中,对这些近期数据的频繁访问,进而引发了对此特定表的高频次操作。与此同时,对其他分表的访问频率则相对较低。
尽管采用分表策略在一定程度上减少了整体的数据操作量,但这种策略无法完全解决单张表所承受的巨大访问压力。数据的总量被分散至多个表中,但在特定的时间点或特定的业务场景下,仍然会有大量的数据操作请求集中到某一张表上。这就导致了这张表的访问压力巨大,可能会引发性能瓶颈,从而影响整个系统的稳定运行。
水平分库分表的策略
这里主要先讲水平分表的策略,水平分库的策略可以在此基础上扩展。
一、按数据区间划分
以电商订单表为例,可将表的主键ID按一定数值进行分表,或按年份进行分表,具体原因如下。
电商订单表的主键ID做为分表策略的判断条件,可以提升数据处理和查询的效率。例如,可以将主键ID按照一定的数值范围进行分表,这样每个分表就可以独立进行数据查询和更新操作,从而减少大型数据库操作时的延迟。
或者,可以将订单表按照年份进行分表。这样做的好处是,当需要对某一特定年份的订单数据进行深入分析时,可以直接访问那一年份的分表,而不需要扫描整个订单表,可以显著提高数据查询的速度和效率。
主键ID按一定数值进行分表:
订单表A(每1000万分表) | ||
---|---|---|
ID | 标题 | 其它字段 |
1 | 某某某某某某某某某某 | 某某某某某某某某某某某某 |
......... | 某某某某某某某某某某 | 某某某某某某某某某某某某 |
10000000 | 某某某某某某某某某某 | 某某某某某某某某某某某某 |
订单表B(每1000万分表) | ||
---|---|---|
ID | 标题 | 其它字段 |
10000001 | 某某某某某某某某某某 | 某某某某某某某某某某某某 |
......... | 某某某某某某某某某某 | 某某某某某某某某某某某某 |
20000000 | 某某某某某某某某某某 | 某某某某某某某某某某某某 |
按年份进行分表:
订单表2023(2023年订单) | ||
---|---|---|
ID | 标题 | 年份字段 |
......... | 某某某某某某某某某某 | 2023年创建时间 |
......... | 某某某某某某某某某某 | 2023年创建时间 |
......... | 某某某某某某某某某某 | 2023年创建时间 |
订单表2024(2024年订单) | ||
---|---|---|
ID | 标题 | 年份字段 |
......... | 某某某某某某某某某某 | 2024年创建时间 |
......... | 某某某某某某某某某某 | 2024年创建时间 |
......... | 某某某某某某某某某某 | 2024年创建时间 |
数据区间划分优点
具有良好的扩展性,无需进行数据迁移。当数据量增长1千万或迎接新的年度时,仅需新增一张表即可,原有数据无需迁移。
数据区间划分缺点
会遇到所谓的“数据热点”问题。这是由于,不管是根据ID还是按照年份来划分数据,近期数据通常会集中存储在同一张表中。在日常业务操作中,对这些数据的频繁访问会导致对某张表进行高频率的操作,而对其他分表的访问则相对较少。尽管分表策略降低了整体的数据操作量,但单张表的访问压力依然很大。
在实施水平分库策略时,同样可能会出现某个数据库承担高频访问负担,而其他数据库访问量则相对较低的现象。
二、按哈希取模划分
哈希取模策略是一种数据处理方法,它通过对数据进行哈希运算后,再对其结果进行取模操作,从而将数据映射到特定的库或表中。这个取模运算通常与库的数量或表的数量相对应,因此涉及的数值是固定不变的。
以PHP代码为例说明哈希取模的逻辑
代码语言:php复制// 订单ID数据
$key = "2811285227";
// 映射的数量
$numberOfBuckets = 4;
// 使用crc32()函数获取字符串的哈希值
$hash = hash('crc32', $key);
// 使用取模运算符将哈希值映射到特定范围
$bucketIndex = intval(abs(crc32($key)) % $numberOfBuckets);
echo "Key '{$key}' 映射取模为 {$bucketIndex} .";
// 当前程序运行结果为 Key '2811285227' 映射取模为 2 .
以电商订单表为例,此策略涉及将单一的订单表拆分为4个子表。在此过程中,利用订单标识(OrderID)作为哈希函数的输入,通过取模运算决定数据存储的具体位置。
例如,若运算结果为2,相应的订单数据将被写入第二张子表。这种方法确保了数据在存储时的均匀分布,进而提升了数据检索和管理的效率。
具体逻辑如下图所示:
哈希取模划分的优点
在处理数据时,将有效避免数据集中的热点问题。
哈希取模划分的缺点
不利于扩容,具体来说,当数据库容量需要增加时,由于模数的改变,之前存储的数据需要重新进行取模运算,并重新分配至新的数据库表中,这便涉及到了数据迁移的问题。
例如,若数据库在初始设计时采用哈希取模方式分为4个子表,而在后续扩展时,需要从4张表增加到6张表,原有的数据分布将不再合理,必须重新进行计算和分表,同时,旧有数据也需要进行迁移处理。
采用此类方法进行水平分库,同样也会遇到扩展容量的挑战。
总结
两种方法均存在一定问题,它们的优劣正好相反。具体应用时需要结合实际情况来决定,理论上而言,采用哈希取模的方式更贴近分库分表的初衷,但同时也需要评估数据迁移所带来的潜在风险。