一、【what】什么是数据库表拆分
把原本存储于一个DB/Table的数据,通过一系列的切分规则,分布到不同的DB/Table中,再通过相应的DB/Table路由规则找到需要查询的具体的DB/Table,以进行Query操作。
打个比方,就好比原来把许多水果放在一个篮子里。现在可以把水果按种类放在多个篮子里,并且想吃某种水果时,知道从哪个篮子里面取。
举个分库分表的简单例子:假设有一张表是记录用户购买信息的订单表order,由于order表记录数太多,我们将拆分成256张表。 拆分的记录根据user_id%256取得对应的表进行存储,前台应用则根据对应的user_id%256,找到对应订单存储的表进行访问。那么分表以后,假设我们想要查询user_id为257的用户对应的订单信息,则对应的SQL语句如下: select * from order_1 where user_id=257; 其中,order_1是根据257%256=1计算得出。
二、【why】为什么要做数据库表拆分
对于大型的互联网应用来说,数据库单表的记录行数可能达到千万级甚至是亿级,并且数据库面临着极高的并发访问。采用Master-Slave主从模式的MySQL架构,只能够对数据库的读进行扩展,而对数据库的写操作还是集中在Master上,并且单个Master挂载的Slave也不可能无限制多。因此,还需要通过其他手段对数据库的吞吐能力进行进一步的扩展,以满足高并发访问与海量数据存储的需要!
数据库拆分的好处:
分表减少了单表的记录条数,从而减少了数据查询所需要的时间,提高了数据库的吞吐;
分库减少了单库的访问压力(单库存在连接数限制),从而提高了整个系统的并发处理能力;
三、【how】怎样做数据库表拆分
1.两种拆分方式
(1)垂直拆分
拆表:即拆字段,将表中的一些字段拆出去单独成表,例如我们开发中的帖子主表与帖子扩展表就是利用了表的垂直拆分;
拆库:把关系紧密(比如同一业务/模块)的表拆分出来单独放在一个数据库中,例如把用户相关的表放在一个数据库;把订单相关的表放在一个数据库,把支付相关的表放在一个数据库;
说明:垂直拆分适合业务之间耦合度非常低的系统,拆分后业务清晰,拆分规则明确,对系统影响较小。
(2)水平拆分
概念:将某张数据量很大、访问频繁的表,按照某个特定的规则,将数据分散到多个表,甚至是多个数据库中,这样每张表或者每个库都含有一部分数据。
策略:1.根据数值范围,比如用户Id为1-10000的记录分到第一个库,10001-20000的分到第二个库,以此类推。(容易扩容,存在热点问题) 2.根据数值取模,比如用户Id mod n,余数为0的记录放到第一个库,余数为1的放到第二个库,以此类推。(不存在热点问题,不易扩容)
(3)联合使用
多数系统会将垂直切分和水平切分联合使用,先对系统做垂直切分,再针对每一小搓表的情况选择性地做水平切分,从而将整个数据库切分成一个分布式矩阵。
垂直切分一般是分析表间的聚合关系,把关系紧密的表放在一起。在垂直切分出的表聚集内,一般可以找出一个“根元素”,再按“根元素”进行水平切分。这样把所有和“根元素”关联的数据放入一个shard里,出现跨shard关联的可能性就非常的小。比如:对于社交网站,几乎所有数据最终都会关联到某个用户上,基于用户进行切分就是最好的选择。
2.数据拆分的常见问题及解决方案
(1)跨节点Join问题
根本做法还是尽量避免使用join或者使join发生在单机,比如可以分两次查询,第一次查询找出关联数据的id,第二次查询根据这些id得到关联数据。当然针对跨节点Join,有一些巧妙的做法来规避,如下: <1>小表广播:所谓全局表,就是有可能系统中所有模块都可能会依赖到的一些表。比较类似我们理解的“数据字典”。为了避免跨库join查询,我们可以将这类表在其他每个数据库中均保存一份。同时,这类数据通常也很少发生修改(甚至几乎不会),所以也不用太担心“一致性”问题。 <2>字段冗余:“订单表”中保存“卖家Id”的同时,将卖家的“Name”字段也冗余,这样查询订单详情的时候就不需要再去查询“卖家用户表”。
(2)跨节点count,order by,group by及聚合函数
分别在各个节点上得到结果然后再进行合并,因为在每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。
例如,在水平分表的情况下,我们求平均分数(聚合函数AVG的实现),可以第一步先并行地从各节点获取总分数以及个数,然后合并各节点的结果,即可得出最终的平均分数了。
针对类似求Top100的问题,可以单独准备一张表来记录查询;
(3)数据扩容
<1>根据数值范围拆分的情况,容易扩容;
<2>根据数值取模拆分的情况会稍微复杂一点,利用对2的倍数取余具有向前兼容的特性(如对4取余得1的数对2取余也是1)来分配数据,可以避免行级别数据的迁移,但是依然需要进行表级别数据的迁移。
(4)分布式事务
<1>两阶段提交,最大限度地保证了跨数据库操作的“原子性”,是分布式系统下最严格的事务实现方式(优化:三阶段提交更为严格)。
<2>一阶段提交(Best Efforts 1PC),不需要”协调者”角色,各节点之间不存在协调操作。 <3>事务补偿,保证最终一致性,对于那些对性能要求很高,但对一致性要求并不高的系统,往往并不苛求系统的实时一致性,只要在一个允许的时间周期内达到最终一致性即可,这使得事务补偿机制成为一种可行的方案。笼统地讲,与事务在执行中发生错误后立即回滚的方式不同,事务补偿是一种事后检查并补救的措施,它只期望在一个容许时间周期内得到最终一致的结果就可以了。一些常见的实现方式有:对数据进行对帐检查;基于日志进行比对;定期同标准数据来源进行同步,等等。
(5)分布式全局唯一ID
<1>UUID,UUID生成的是length=32的16进制格式的字符串,算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成UUID,使用UUID作主键是最简单的方案,但是缺点也是非常明显的,由于UUID非常的长,除占用大量存储空间外,最主要的问题是在索引上。
<2>在数据库中建立一个Sequence表,该表包含table_name和nextid两个字段,每当需要为某个表的新纪录生成ID时就从Sequence表中取出对应表的nextid,并将nextid的值加1后更新到数据库中以备下次使用。此方案也较简单,但缺点同样明显:该表很容易成为系统性能瓶颈,同时它也存在单点问题。
<3>Twitter的分布式自增ID算法Snowflake(推荐),包含 1. 41位的时间序列(精确到毫秒,41位的长度可以使用69年) 2. 10位的机器标识(10位的长度最多支持部署1024个节点,支持多机房的分布式,需要使用zookeeper) 3. 12位的计数顺序号(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号) 最高位是符号位,始终为0。 加起来刚好64位,为一个Long型。这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
(6)跨分片的排序分页
需要在不同的分片节点中将数据进行排序并返回,并将不同分片返回的结果集进行汇总和再次排序,最后再返回给用户。
举个例子,如图,如果想取出第10页数据,因为各分片节点中的数据可能是随机的,为了排序的准确性,必须把所有分片节点的前N页数据都排序好后做合并,最后再进行整体的排序。很显然,这样的操作是比较消耗资源的,用户越往后翻页,系统性能将会越差。
在实际应用中,我们可以考虑走备库、缓存或者大数据平台等。
3.不同规模采用不同的实现方案
(1)客户端实现数据路由
该方案的优点是不需要引入中间件,直接在客户端配置,程序把控力强,简单场景方便使用。缺点是对代码侵入性强,因为代码端要去管理路由;配置管理复杂,如果配置错误,数据可能完全乱掉,修复也会比较复杂。
(2)数据库中间件
使用中间件可以实现自动的分库分表,对应用透明,使用类似于单实例;使用门槛低,应用只需要考虑分布式事务,跨库join,而不用考虑数据的路由;方便水平扩容。使用了中间件之后,应用看到的还是单实例数据库,不需要考虑分布式的情况,对开发来说是比较有优势的。
四、【extend】分库分表中间件
1.mysql-proxy
mysql官方提供的mysql中间件服务,分库分表性能较差;
2.Cobar
属于阿里B2B事业群,始于2008年,已停止维护,在应用程序和MySQL之间搭建一层Proxy,阿里后来的TDDL、DRDS都借鉴复用了Cobar-Proxy;
3.TDDL
淘宝根据自己的业务特点开发了(Tabao Distributed Data Layer, 外号:头都大了),解决了很多布式数据库问题,已不再使用。TDDL并非独立的中间件,只能算作中间层,处于业务层和JDBC层中间,是以Jar包方式提供给应用调用,属于JDBC Shard的思想。TDDL复杂度相对较高。当前公布的文档较少,只开源动态数据源,分表分库部分还未开源,不推荐使用。
4.DRDS
阿里的分布式数据库服务,不开源。,吸收了Cobar核心的Cobar-Proxy(一套独立的类似MySQL-Proxy协议的解析端),同时借鉴了TDDL丰富的分布式数据库实践经验(分布式Join支持,聚合函数支持,排序函数支持等),形成了一套完整的分布式数据库方案。
5.MyCAT
社区爱好者在阿里Cobar基础上进行二次开发,社区活跃度较高,相比Cobar有两个显著提高:并发量有大幅提高;增加了对Order By,Group By,Limit等聚合功能;
6.Atlas
360团队开发,基于MySQL协议的数据中间层项目,它是在mysql-proxy 0.8.2版本上对其进行的优化,开源,据说在高并发下经常会挂掉;
7.DBProxy
美团点评DBA团队开发,是对360公司开源的Atlas做的进一步改进工作,形成了新的高可靠、高可用企业级数据库中间件,开源;
8.sharding-JDBC
来自当当的数据库水平分片框架,Sharding-JDBC直接封装JDBC API,可以理解为增强版的JDBC驱动,以jar包形式提供服务;
9.Oceanus
来自58同城,开源,也实现了类似于MySQL-Proxy的中间解析层,支持聚合函数,排序函数等
总结:各自厂商提供的分库分表都有各自的特点,大家根据自己的业务场景现在合适的分表分库策略。
cialis forms generic cialis price cialis generic purchase