一、缘起
当数据库的数据量非常大时,
水平切分
和
垂直拆分
是两种常见的降低数据库大小,提升性能的方法。假设有用户表:
user(
uid bigint,
name varchar(16),
pass varchar(16),
age int,
sex tinyint,
flag tinyint,
sign varchar(64),
intro varchar(256)
…);
水平切分
是指,以某个字段为依据(例如
uid
),按照一定规则(例如取模),将一个库(表)上的数据拆分到多个库(表)上,以降低单库(表)大小,达到提升性能的目的的方法,水平切分后,各个库(表)的特点是:
(
1
)每个库(表)的
结构
都一样
(
2
)每个库(表)的
数据
都不一样,没有交集
(
3
)所有库(表)的并集是全量数据
二、什么是垂直拆分
垂直拆分
是指,将一个属性较多,一行数据较大的表,将不同的属性拆分到不同的表中,以降低单库(表)大小,达到提升性能的目的的方法,垂直切分后,各个库(表)的特点是:
(
1
)每个库(表)的
结构
都不一样
(
2
)一般来说,每个库(表)的
属性
至少有一列交集,一般是主键
(
3
)所有库(表)的并集是全量数据
还是以上文提到的用户表为例,如果要垂直拆分,可能拆分结果会是这样的:
user_base
(
uid bigint,
name varchar(16),
pass varchar(16),
age int,
sex tinyint,
flag tinyint,
…);
user_ext
(
uid bigint,
sign varchar(64),
intro varchar(256)
…);
三、垂直切分的依据是什么
当一个表属性很多时,如何来进行垂直拆分呢?如果没有特殊情况,拆分依据主要有几点:
(
1
)将长度较短,访问频率较高的属性尽量放在一个表里,这个表暂且称为
主表
(
2
)将字段较长,访问频率较低的属性尽量放在一个表里,这个表暂且称为
扩展表
如果
1
和
2
都满足,还可以考虑第三点:
(
3
)经常一起访问的属性,也可以放在一个表里
优先考虑
1
和
2
,第
3
点不是必须。另,如果实在属性过多,主表和扩展表都可以有多个。
一般来说,
数据量并发量比较大
时,数据库的上层都会有一个
服务层
。需要注意的是,当应用方需要同时访问主表和扩展表中的属性时,服务层
不要使用
join
来连表访问,而应该分两次进行查询:
原因是,大数据高并发互联网场景下,一般来说,
吞吐量和扩展性
是主要矛盾:
(
1
)
join
更消损耗数据库性能
(
2
)
join
会让
base
表和
ext
表
耦合在一起
(必须在一个数据库实例上),不利于数据量大时拆分到不同的数据库实例上(机器上)。毕竟减少数据量,提升性能才是垂直拆分的初衷。
四、为什么要这么这么拆分
为何要将字段短,访问频率高的属性放到一个表内?为何这么垂直拆分可以提升性能?因为:
(
1
)数据库有自己的内存
buffer
,会将磁盘上的数据
load
到内存
buffer
里(暂且理解为进程内缓存吧)
(
2
)内存
buffer
缓存数据是
以
row
为单位
的
(
3
)在内存有限的情况下,在数据库内存
buffer
里缓存短
row
,就能缓存更多的数据
(
4
)在数据库内存
buffer
里缓存访问频率高的
row
,就能提升缓存命中率,减少磁盘的访问
举个例子就很好理解了:
假设数据库内存
buffer
为
1G
,未拆分的
user
表
1
行数据大小为
1k
,那么只能缓存
100w
行数据。
如果垂直拆分成
user_base
和
user_ext
,其中:
(
1
)
user_base
访问频率高(例如
uid
,
name
,
passwd
, 以及一些
flag
等),一行大小为
0.1k
(
2
)
user_ext
访问频率低(例如签名, 个人介绍等),一行大小为
0.9k
那边内存
buffer
就就能缓存近乎
1000w
行
user_base
的记录,访问磁盘的概率会大大降低,数据库访问的时延会大大降低,吞吐量会大大增加。
五、总结
(
1
)
水平拆分
和
垂直拆分
都是降低数据量大小,提升数据库性能的常见手段
(
2
)流量大,数据量大时,
数据访问要有
service
层
,并且
service
层不要通过
join
来获取主表和扩展表的属性
(
3
)
垂直拆分的依据
,尽量把长度较短,访问频率较高的属性放在主表里
希望没有浪费你这一分钟,帮转哈。
==【完】==
相关阅读: