谈一谈数据分片
前言
一般情况下,项目都是从一个小项目不断迭代成一个庞大的、复杂的大项目,比如我们做一个电商系统的时候,首先会把所有的业务表(比如订单表、用户表、商品表等等)放在同一个数据库中。

但后来项目越做越大,这样把所有数据存储到单一数据节点的解决方案,在性能、可用性和维护成本这三个方面无法满足互联网海量数据的应用场景。
性能
以MySQL的innerDB存储引擎来讲,由于大多数关系型的数据库采用的是B+树的索引,在数据量超过超过阈值的情况下,索引深度的增加也将使得磁盘访问的IO次数增加,进一步会导致查询性能的下降(一般B+树的控制在1-3之间,也就是说,一次主键索引查询,可能会出现1-3次IO,一张表存2000万条数据就要掂量掂量了);与此同时,高并发访问请求也使得单一数据节点成为系统瓶颈。
可用性
服务的无状态化,能够达到很小成本的随意扩容,这将会导致系统的最终压力都落在数据库。然而单一数据节点,很难承担。数据节点的可用性,是整个系统的关键。
运维成本
在一个数据库实例中的数据达到阈值以上,对于DBA的运维压力就会增大。数据备份和数据恢复时间成本都将随着数据量的大小而愈发不可控。一般来讲,单一数据库实例的数据的阈值在1TB之内。
在传统的关系型数据库无法满足互联网场景需要的情况下,将数据存储在天然支持分布式的 NoSQL 数据库(比如我公司的订单表就存储到MongoDB上)。但 NoSQL 对 SQL 的不友好以及事务等问题,使得它们始终无法完全的替代关系型数据库。
数据分片
数据分片是指按照某个维度将存放在单一数据节点中的数据分散地存放在多个数据节点或表中,来达到提升性能瓶颈以及可用性的目的。
数据分片的核心手段就是对关系型数据库进行分库和分表。分库和分表均可以有效的避免由数据量超过可承受阈值而产生的查询瓶颈。除此之外,分库还能够用于有效的分散对数据节点单点的访问量(你想想看,查订单的去订单节点查,查用户的去用户节点去查);
分表虽然无法缓解数据节点的压力,但却能够提供尽量将分布式事务变为本地事务来处理,一旦涉及到跨库的更新操作,分布式事务往往会使问题变得复杂(比如用户下订单时,扣积分、减库存就够你喝一壶了)。
使用主从的分片方式,可以有效的避免数据单点,从而提升数据架构的可用性(更新操作去主节点,查询操作去从节点)。
通过分库和分表进行数据的拆分来使得各个表的数据量保持在阈值以下,以及对流量进行疏导应对高访问量,是应对高并发和海量数据系统的有效手段。
数据分片的拆分方式又分为垂直分片和水平分片。
垂直分片
按照业务拆分的方式叫做垂直分片,又叫做纵向拆分,就是专库专用的意思(有点像公司各个部门的意思,各司其职)。拆分之前,一个数据节点由多个业务表构成,每个表存储着不同的业务数据。而拆分之后,我们把表按照业务进行归类,分布到不同的数据节点中,从而将压力分散至不同的数据节点。比如与用户相关的放到用户库,订单相关的放到订单库。

垂直分片需要对架构和设计进行调整。通常来讲,是来不及应对互联网业务需求快速变化的;而且,它也无法真正地解决单点瓶颈。垂直拆分可以缓解数据量和访问量带来的问题,但无法根治。如果垂直拆分之后,表中的数据量依然超过单节点所能承载的阈值(比如我司在前段时间的618大促的时候,其他业务节点都没有问题,只有营销节点CPU打满,以至于拖累整个支付链不可用),则需要水平分片来进一步处理。
水平分片
水平分片又叫做横向拆分(就像一个公司会在不同的城市设置不同的分公司去处理日常工作)。相对于垂直分片,它不再将数据根据业务逻辑归类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个节点或表中,每个分片仅包含数据的一部分。例如:根据主键分片,偶数主键的记录放入 0 库(或表),奇数主键的记录放入 1 库(或表)。

水平分片从理论上突破了单机数据量处理的瓶颈,并且扩展相对自由,是分库分表的标准解决方案。
问题
虽然数据分片解决了性能、可用性以及单点备份恢复等问题,但分布式的架构也引入了新的问题。
复杂的数据库操作
面对散乱的分库分表之后的数据,开发工程师和数据库管理员对数据库的操作变得复杂就是其中的重要挑战之一。他们需要知道数据需要从哪个具体的数据库的哪个表中获取。
你想想看,原来我们都在同一个地方办公,你想叫张三、李四都很容易,但现在大家都分派在天南海北,交流起来就不方便。
数据聚合
能够正确地运行在单节点数据库中的 SQL,在分片之后的数据库中并不一定能够正确运行。例如,分表导致表名称的修改,或者分页、排序、聚合分组等操作的不正确处理。
比如,我们想查询订单表的前10条数据,以前只要一条SQL语句搞定,现在分到不同的数据节点,这时再想分页查询会让你崩溃。

分布式事务(跨库事务)
采用分表策略,在降低一个表数据量的情况下,尽量使用本地事务,因为都在一个数据节点下就可以避免跨库事务的问题。
在不能避免跨库事务的场景,有些业务还是要保证事务的一致性,基于 XA 分布式事务在高并发的场景中性能无法满足需要,大多采用最终一致性的柔性事务代替强一致事务。

写在最后
好兄弟可以点赞并关注我的公众号“javaAnswer”,全部都是干货。
