一、数据库的拆分
当数据库的数据量非常大时,水平拆分和垂直拆分是两种常见的降低数据库大小,提升性能的方法。其实在大多数分布式场景中,水平拆分和垂直拆分也通常是两种降低耦合,提升性能的架构设计或者业务拆分方法。
假设我们在数据库中有用户表1
2
3
4
5
6
7
8
9
10
11create table user(
id bigint,
name varchar(50),
password varchar(32),
age int,
sex tinyint,
email varchar(32),
sign varchar(64),
intro varchar(256)
...
)engine=innodb charset=utf8;
水平拆分是指,以某个字段(如ID)为依据,按照一定规则(例如hash、取模),将一个库(表)上的数据拆分到多个库(表)上,以降低单库(表)的大小,水平切分后,各个库(表)的特点是:
(1)每个库(表)的结构都一样
(2)每个库(表)的数据不一样,没有交集
(3)所有库(表)的并集是全量数据
垂直拆分是将一个属性较多,一行数据较大的表,将不同的属性拆分到不同的表中,以降低单库(表)的大小,达到提升性能的目的的方法。垂直拆分后,各个库(表)的特点是:
(1)每个库(表)的结构都不一样
(2)一般来说每个库(表)的属性至少有一列交集,一般是主键
(3)所有库(表)的数据并集是全量数据
以上文的用户表为例,如果要垂直拆分,可能拆分的结果会是这样的:
1 | create table user_base( |
从结果上来看,水平拆分实际上是将数据进行了拆分存储,垂直拆分是将元数据或者字段以及数据进行拆分存储。
二、垂直拆分的依据是什么
那垂直拆分的依据又是什么呢?当一个表属性很多时,如何来进行垂直拆分呢。通常情况下,我们会按照以下几点进行数据的拆分:
(1)将长度较短、访问频率高的属性尽量放在一个表里,这个表暂且称为主表
(2)将字段较长、访问频率较低的属性尽量放在一个表里,这个表暂且称为扩展表
(3)如何1和2都满足,还可以考虑第三点,将经常一起访问的属性,也放在一个表里
优先考虑1、2,第3点不是必须的,如果实在属性过多,主表和扩展表都可以有多个。
一般来说,数据量并发量较大时,数据库的上层都会有一个服务层,需要注意的是,当应用需要同时访问主表和扩展表中的数据时,服务层不要使用join来连表查询,而是应该分两次进行查询。
原因是,在大数据、高并发的互联网场景下,一般来说,吞吐量和拓展性是主要矛盾。
(1)join更消耗数据库性能
(2)join或让base表和ext表耦合在一起(必须在一个数据库实例上),不利于数据量大时拆分到不同的数据库实例上,毕竟减少数据量,提升性能才是垂直拆分的初衷。
三、为什么要这样拆分
为什么将字段段、访问频率高的属性放到一个表里?为什么垂直拆分可以提升性能?因为:
(1)数据库有自己的内存buffer,会将磁盘上的数据load到内存buffer里
(2)内存buffer缓存数据是以row为单位的
(3)在内存有限的情况下,在数据库的buffer里缓存短row,就能缓存更多数据
(4)在数据库内存buffer里缓存访问频率高的row,就能提升缓存命中率,减少磁盘IO
还是以上面的用户表为例,假如数据库的缓存buffer有1G,未拆分的user表一行数据的大小为1k,那么只能缓存100w行数据,如果拆分成user_base和user_ext之后:
(1)user_base访问频率高,一行大小只有0.1k,那内存buffer就可以近乎缓存1000w行user_base数据
(2)user_ext访问频率低,一行大小0.9k
拆分后缓存就能更多命中记录,磁盘访问概率大大降低,数据库访问的时延会大大降低,吞吐量也就会相应增加。
四、总结
1、水平拆分和垂直拆分都是降低数据量大小,提升数据库性能的常见手段
2、流量大、数据量大时,不要通过join来获取主表和扩展表的属性
3、数据库的拆分依据,尽量把长度较短、访问频率较高的属性放在主表中