【日记】高并发下的DB分库分表分区策略

设计数据库的时候,顺便想了一下,作为一个购物应用,高并发的访问和大量数据存储对DB是个很大的压力。

于是查了一下分库分表分区的解决方案。

0.为什么要分库分表?
数据库中的数据量不一定是可控的,在未进行分库分表的情况下,随着时间和业务的发展,
库中的表会越来越多,表中的数据量也会越来越大,相应地,数据操作,增删改查的开销也会越来越大;
另外,一台服务器的资源(CPU、磁盘、内存、IO等)是有限的,最终数据库所能承载的数据量、数据处理能力都将遭遇瓶颈。


1.分库:
把原本存储于一个库的数据分块存储到多个库上,在物理实现上分成多个服务器,不同的分库在不同服务器上。
orderid,userid,ordertime,.....
userid%4=0,用分库1
userid%4=1,用分库2
userid%4=2, 用分库3
userid%4=3,用分库4
上面这个就是一个简单的分库路由,根据userid选择分库,即不同的服务器

用途:
用分库可以解决单台服务器性能不够,或者成本过高问题。

2.分表:
把原本存储于一个表的数据分块存储到多个表上。
减少单表的记录条数,以便减少数据查询所需要的时间,提高数据库的吞吐,
分表后,大访问量的情况下单表的并发能力提高了,总表可以根据不同的查询,将并发压力分到不同的小表里面。

[root@BlackGhost test]# ls |grep user
alluser.MRG
alluser.frm
user1.MYD
user1.MYI
user1.frm
user2.MYD
user2.MYI
user2.frm

[root@BlackGhost test]# cat alluser.MRG |more
user1
user2
#INSERT_METHOD=LAST

用途:
应对高并发访问。


3.分区:
把存放数据的文件分成了多个存储块,一张大表进行分区后,还是一张表,但是他存放数据的区块数变多了。
如何突破磁盘的读写能力,从而达到提高mysql性能。
这个是数据库分的,应用程序透明,无需修改。

[root@BlackGhost test]# ls |grep aa
aa#P#p1.MYD
aa#P#p1.MYI
aa#P#p2.MYD
aa#P#p2.MYI
aa.frm
aa.par

用途:
访问量不大,但是表数据很多的表,适合采取分区的方式。
比如按照时间分区:
大部分只查询最近的订单数据,那么大部分只访问一个分区,
比整个表小多了,数据库可以更加好的缓存,性能也提高了。

4.分库分表的实施方式。
分库分表有垂直切分和水平切分两种:
(1)垂直切分:将表按照功能模块、关系密切程度划分出来,部署到不同的库上。
     例如,分库:我们会建立定义数据库workDB、商品数据库payDB、用户数据库userDB、日志数据库logDB等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等。
           分表:如userid,name,addr一个表,为了防止表过大,分成userid,name和userid,addr两个表。

(2)水平切分,当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,例如userID散列、按性别、按省,进行划分,然后存储到多个结构相同的表,和不同的库上。例如,我们的userDB中的用户数据表中,每一个表的数据量都很大,就可以把userDB切分为结构相同的多个userDB:part0DB、part1DB等,再将userDB上的用户数据表userTable,切分为很多userTable:userTable0、userTable1等,然后将这些表按照一定的规则存储到多个userDB上。

(3) 使用哪一种方式?要看数据库中数据量的瓶颈所在,并综合项目的业务类型进行考虑。
如果数据库是因为表太多而造成海量数据,并且项目的各项业务逻辑划分清晰、低耦合,那么规则简单明了、容易实施的垂直切分必是首选。
而如果数据库中的表并不多,但单表的数据量很大、或数据热度很高,这种情况之下就应该选择水平切分,水平切分比垂直切分要复杂一些,它将原本逻辑上属于一体的数据进行了物理分割,除了在分割时要对分割的粒度做好评估,考虑数据平均和负载平均,后期也将对项目人员及应用程序产生额外的数据管理负担。
在现实项目中,往往是这两种情况兼而有之,这就需要做出权衡,甚至既需要垂直切分,又需要水平切分。我们的游戏项目便综合使用了垂直与水平切分,我们首先对数据库进行垂直切分,然后,再针对一部分表,通常是用户数据表,进行水平切分。

5.分库分表策略:
  要选择适当的分表策略,使得数据能够较为均衡地分布到多张表中,且不能影响正常的查询。
  假设有一个用来记录用户购买信息的订单表(ORDER),由于ORDER表中的记录条数太多,需要被拆分为256张表(拆分表的数量一般是2的N次方),拆分的规则是根据USER_ID%256取得对应的表存储记录。而前台应用则能根据USER_ID%256的规则去找到对应订单存储的表,再去存储该记录的表中取出数据(余数为0,则查0号表,余数为233,则查233号表)。这样以来,USER_ID便变成了一个必须的查询条件,否则将会因为无法定位数据存储的表而无法对数据进行访问。
  分库策略和分表的类似。
 
  分库分表结合策略:
  大多数时候,数据库会同时面临高并发访问的压力和海量数据的存储问题。这时就要同时采用分库分表策略,
  分库分表的策略比起仅分库或仅分表的策略要更为复杂,一种分库分表的路由策略如下:
    1.中间变量=USER_ID%(分库数量*每个库的表数量)
    2.库=取整数(中间变量/每个库的表数量)
    3.表=中间变量%每个库的表数量

    同样是订单表,同样采用用户ID作为路由字段,首先使用USER_ID对库数量*每个库表的数量取模,得到一个中间变量;然后使用中间变量除以每个库表的数量,取整,便得到对应的库;而中间变量对每个库表的数量取模,即得到对应的表。
    假设将原来的单库单表ORDER表拆分为256个库,每个库包含1024个表,那么按照前面所提到的路由策略,对于USER_ID=262145的访问,路由的计算过程如下:
    1.中间变量=262145%(256*1024)=1
    2.库=(1/1024)取整=0
    3.表=1%1024=1

因此得出,对于USER_ID=262145的订单记录的查询和修改,将会被路由到第0个库的第1个ORDER_1表中执行。

 

6.分库分表存在的问题
(1) 事务问题。
在执行分库分表之后,由于数据存储到了不同的库上,数据库事务管理出现了困难。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。

(2)跨库跨表的join问题。
在执行了分库分表之后,难以避免会将原本逻辑关联性很强的数据划分到不同的表、不同的库上,这时,表的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表,结果原本一次查询能够完成的业务,可能需要多次查询才能完成。

(3)额外的数据管理负担和数据运算压力。
额外的数据管理负担,最显而易见的就是数据的定位问题和数据的增删改查的重复执行问题,这些都可以通过应用程序解决,但必然引起额外的逻辑运算,例如,对于一个记录用户成绩的用户数据表userTable,业务要求查出成绩最好的100位,在进行分表之前,只需一个order by语句就可以搞定,但是在进行分表之后,将需要n个order by语句,分别查出每一个分表的前100名用户数据,然后再对这些数据进行合并计算,才能得出结果。

 

参考文献:
Mysql分表和分区的区别、分库和分表区别
https://www.cnblogs.com/sanduzxcvbnm/p/11640063.html

数据库分表和分库的概念与策略(这个作者的博客风格也很可爱)
https://www.cnblogs.com/yanggb/p/10816502.html

★本文中摘抄内容的版权属于参考文献原作者,感谢他们的无私分享。★

Guess you like

Origin blog.csdn.net/ttyt1217/article/details/107411350