1.引言
随着互联网应用的普及,海量数据的存储和访问成为了系统设计的瓶颈问题。对于一个大型的互联网应用,每天几十亿pv无疑对数据库造成了相当高的负载。对于系统的稳定性和扩展性造成了极大的问题。
通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式:
(1)水平切分数据库:可以降低单台机器的负载,同时最大限度的降低了宕机造成的损失;
(2)通过负载均衡策略:有效的降低了单台机器的访问负载,降低了宕机的可能性;
(3)通过集群方案:解决了数据库宕机带来的单点数据库不能访问的问题;
(4)通过读写分离策略:最大限度提高了应用张读取数据的速度和并发量
2.基本原理与概念
(1)什么是数据切分
如果在单一数据库上处理应用数据捉襟见肘而需要进行分区化之类的处理,是如何办到的呢?答案就是:Sharding。
Sharding不是某个特定数据库附属的功能,而是在具体实现技术的抽象,是水平扩展的解决方案,主要目的是为了实现单点服务器的i/o能力限制,解决数据库扩展性问题。也就是: 通过一系列的切分规则将数据水平分布到不同的DB或table中,再通过相应的DB路由或者table路由规则找到需要查询的具体的DB或者table,进行query操作。
举一个例子:我们针对一个Blog应用中的日志来说明,比如日志文章article表有如下字段:article_id(int),tile(varchar(128)),contnet(varchar(1024)),user_id(int)
这样一张表我们怎样切分呢?怎样将这样的数据分不到不同的数据库中的表中呢?其实分析blog应用,我们可以得出如下结论:可以使用user_id字段作为我们分库的规则基础
将user_id为1-10000的所有文章放入DB1的article表中,将user_id为10001-20000的所有文章放入DB2的article表中,以此类推,一直到DBn。
这样一来,文章数据就被分到了各个数据库中,达到了数据切分的目的。接下来要解决的就是怎么找到具体的数据库呢?解决方法也很明显:既然分库的时候我们使用了分区字段user_id,那么数据库路由的时候当然少不了user_id。我们知道了user_id,利用分库时的规则,反过来定位具体数据库,比如user_id=234,利用刚才的规则,就会定位到DB1,降入user_id是12343,利用该规则,就会定位到DB2.以此类推,利用分库的规则,反向路由到具体的DB,这个过程我们称为DB路由。
(2)如何数据切分
1)分库
也就是物理上的数据切分。将数据通过一系列的切分规则将数据分布到不同的DB服务器上,通过路由规则访问特定的数据库。这样一来,每次访问的就不是单台服务器了,而是N台,这样就可以降低单台机器的负载压力。分库方式:
1>按号段分
如用user_id来区分,1-1000的对应DB1,1001-2000的对应DB2,以此类推。
优点:可部分迁移
缺点:数据分布不均
2>hash取模分
对user_id进行hsah(如果user_id是数字的话,直接用user_id也行),然后用一个特定的数字,比如需要将一个数据库切分成4个数据库的话,我们就用4对user_id进行取模,也就是user_id%4,这样的话就有4种结果:1的时候对应DB1,2的时候对应DB2,3的时候对应DB3,0的时候对应DB4,这样一来数据就会非常均匀的将数据分配到4个DB中。
优点:数据分布均匀
缺点:不能按照近期性能分摊数据
2)分表
也就是数据库内的数据切分。对数据通过一系列的切分规则,将数据分不到一个数据库的不同表中,如将article表分为article_001,article_002等子表,若干个表水平拼接会在逻辑上组成一个完整的article表。
这么做作用是十分明显的。举个例子:比如article表中有5000w条数据,此时我们在表中insert一条新的数据,insert完成之后,数据库会针对这张表重新建立索引,而5000w条数据建立索引的系统开销是不容忽视的。但是如果我们把表分为100个子表呢?从article_001一直到article_100,5000w数据平均下来,每个子表中就只有50w条数据,这时候我们向一张只有50w条数据的表中insert之后建立索引的时间就会成数量级的下降,极大的提高了DB运行时的效率,提高了DB的并发量。
综上:分库降低了单点机器的负载;分表提高了了数据操作的效率,尤其是写操作的效率