分类什么是分库分表?为什么要分库分表?数据库分表数据库:一个数据库被分成多个数据库,部署到不同的机器上。分表:将一个数据库表分成多个表。2、为什么要分库分表?2.1为什么需要分库?如果业务量激增,数据库可能会遇到性能瓶颈。这时候就需要考虑拆分数据库了。从这些方面来看:磁盘存储业务量会急剧增加,一个MySQL单机的磁盘容量会爆炸,分裂成多个数据库,磁盘使用率会大大降低。并发连接支持我们知道数据库连接是有限的。在高并发场景下,大量的访问数据库的请求是单台MySQL服务器无法处理的!当前非常流行的微服务架构似乎是为了处理高并发的。将订单、用户、商品等不同模块拆分为多个应用,将单个数据库拆分为多个不同功能模块(订单库、用户库、商品库)的数据库,分担读写压力。2.2为什么要分表?如果数据量太大,SQL查询会变慢。如果一条查询SQL漏掉了索引,千万级数据量的表可能会拖垮整个数据库。即使SQL命中了索引,如果表的数据量超过1000万,查询速度也会明显变慢。这是因为索引一般是B+树结构。如果数据有千万级别,B+树的高度会增加,查询会变慢。大家还记得MySQL的B+树的高度是怎么计算的吗?顺便回顾一下InnoDB存储引擎的最小存储单元是页,一个页的大小是16k。B+树的叶子存储数据,内部节点存储键值+指针。索引组织表通过非叶子节点和指针的二分查找的方式来判断数据在哪个页,然后在数据页中查找需要的数据。B+树结构图如下:假设B+树的高度为2,有一个根节点和若干个叶子节点。这棵B+树存储的记录总数=根节点指针数*单个叶节点记录的行数。如果一行记录的数据大小为1k,那么单个叶子节点可以存储的记录数=16k/1k=16。非叶子节点存储了多少个指针?我们假设主键ID是bigint类型,长度为8字节(面试官问你int类型,一个int是32位,4字节),InnoDB源码中指针大小设置为6字节码,所以是8+6=14个字节,16k/14B=16*1024B/14B=1170所以一棵高度为2的B+树可以存储117016=18720条这样的数据记录。同样,一棵高度为3的B+树可以存储11701170*16=21902400条,大约可以存储2000万条记录。B+树的高度一般为1-3层。如果B+到了4层,查询的时候会多查磁盘,SQL就会变慢。所以单表数据量太大,SQL查询会变慢,需要考虑分表。3、如何分库分表3.1垂直拆分3.1.1垂直分库在业务发展初期,业务功能模块比较少。为了快速上线和迭代,往往使用单一数据库来存储数据。数据库结构如下:但随着业务的蓬勃发展,系统功能也在逐步完善。这时可以根据系统中不同的业务进行拆分,比如拆分成用户库、订单库、积分库、商品库,分别部署在不同的数据库服务器上。这就是垂直数据库划分。垂直分库可以将原来单一数据库的压力分担到不同的数据库,可以很好的应对高并发场景。垂直拆分后的数据库结构如下:3.1.2垂直表拆分如果单个表包含几十个甚至上百个列,管理起来很混乱。如果每次都选择*,也会占用IO资源。这时候我们可以把一些不常用的数据量大或者长度长的列表拆分到另外一张表中。例如,用户表包含user_id、user_name、mobile_no、age、email、nickname、address、user_desc。如果email、address、user_desc等字段不常用,我们可以拆分成另外一张表,命名为userDetails表。这就是垂直表拆分3.2水平拆分3.2.1数据库水平分区数据库水平分区是指将一个表的数据量划分到不同的数据库服务器中。每个服务器都有相同的数据库和表,只是表Collections中的数据不同。可以有效缓解单机单库的性能瓶颈和压力。用户数据库的水平拆分架构如下:3.2.2水平分表如果一个表的数据量太大,可以将数据按照一定的规则(比如哈希取模,范围)拆分到多个表中.订单表按时间范围拆分如下:3.3.横向分库分表策略一般有几种分库分表策略,适用于不同的场景:rangerangehashmodulorange+hashmodulohybrid3.3.1rangerange范围,即范围政策司表。比如我们可以把表的主键从0到1000万分到一张表,从1000万到2000万分到另一张表。如下图所示:当然有时候我们也可以按时间范围来划分,比如不同年月的订单放在不同的表中,这也是一种范围划分策略。该方案的优点是方便扩容,不需要数据迁移。假设数据量增加到5000万,我们只需要横向增加一张表即可。之前0到4000万的数据不需要迁移。缺点:该方案会存在热点问题,因为订单id一直在增加,也就是说最近一段时间聚集在一张表中。比如上个月的订单量在1000万到2000万之间。通常用户查看最近一个月的订单比较多,请求都发到order_1表,导致数据热点。3.3.2Hash取模hash取模策略:指定路由key(一般为user_id,orderid为key)对分表总数进行取模,将数据分散到各个表中。比如原来的order表信息,我们把它分成4个子表:比如id=1,模4,就会得到1,放到t_order_1中;id=3,对4取模,得到3,放入t_order_3;这种方案的优点:hash取模的方式,不会有明显的热点问题。缺点:如果一开始按照hash取模分成4张表,以后某个时候,表的数据量会再次达到瓶颈,需要扩容,会比较吃力.比如从4张表扩展到8张表,id=5的数据本来在(5%4=1,t_order_1),现在应该放在(5%8=5,t_order_5),也就是也就是说,需要迁移历史数据。3.3.3Range+hash取模混合由于range存在热数据问题,因此很难通过hash取模扩展实现数据迁移。我们可以将这两种解决方案结合在一起。长则长,短则弃。更简单的方法是,在拆分数据库时,我们可以先使用range范围方案。比如订单id在0到4000万之间,分到订单库1;id在4000万到8000万之间的数据分到订单库2,以后扩容的时候,id在8000万到1.2亿的数据分到订单库3。然后在订单库中,使用哈希取模策略,将不同的订单分到不同的表中。4、什么时候考虑分库分表?4.1什么时候分表?如果你的系统处于高速发展期,如果每天新增几十万个订单,订单表的查询效率明显变慢,需要分库分表进行规划。B+树索引的高度一般为2~3层。如果数据量在千万级,高度可能会变成4层,数据量会明显变慢。不过业内传言,500万条数据一般都考虑分表。4.2当分库业务快速发展,或者多个服务共享一个数据库,数据库成为性能瓶颈时,需要考虑分库。比如可以提取订单、用户等,可以创建新的应用(其实是微服务的思想),可以拆分数据库(订单库、用户库)。5、分库和表分库会导致什么问题?数据库,则本地事务无效,需要使用分布式事务。5.2跨库关联和跨节点Join问题:解决这个问题,可以分成两个查询来实现5.3排序问题跨节点计数,orderby,groupby和聚合函数等问题:可以在每个节点上获取结果并应用它们终端执行合并。5.4分页问题解决方案一:在每个节点都找到对应的结果后,在代码端进行聚合,然后进行分页。方案二:将分页交给前端,前端传递pageSize和pageNo,对每个数据库节点进行分页,然后汇总前端总数。这样做的缺点是会造成空支票。如果分页需要排序,那不好做。5.5分布式ID数据库拆分后,不能再依赖数据库本身的主键生成机制。最简单的方法是考虑UUID,或者使用雪花算法生成分布式ID。6、分库分表中间件目前流行的分库分表中间件有很多:cobarMycatSharding-JDBCAtlasTDDL(淘宝)vitess
