高并发写场景:数据分片之数据库分库分表
分库和分表:数据库的分库和分表其实是两个概念:分库指的是将数据库拆分为多个小数据库,原来存储在单个数据库中的数据被分开存储到各个小数据库中.分表指的是将单个数据表拆分为多个结构完全一致的表.原来存储在单个数据表中的数据被分开存储到各个表中.由于数据库的分库操作和分表操作一般会同时进行.所以通常将它们合并在一起称为数据库分库分表.数据量大:当业务发展到一定阶段时.数据库中已存储了海量数据.每个数据库表中都存储量千万行甚至上亿行数据,业务对数据表执行SQL语句时扫描的数据行增多.性能开销被严重放大.以MySql数据库为例.如果单个数据表中的数据量超过2000万行.则会导致表结构B树的层级增多.数据读/写的磁盘I/O操作次数增加.此外.在涉及数据表结构修改的场景下.DDL语句执行完成消耗的时间令人难以接受.为了解决单表的读/写效率问题.一般会进行分表操作.并发量大:海量用户访问单个数据库.很快会达到数据库处理的上限.无论是数据库的最大连接请求数 CPU资源 内存资源还是网络带宽均有可能成为性能瓶颈.为了解决数据库性能问题.一般会进行分库操作.分表的目的是提高一台服务器的单数据库处理能力.而分库的目的是充分利用多台服务器资源.在拆分维度上.分库分表可以分为垂直拆分和水平拆分.其中垂直拆分侧重于业务拆分.而水平拆分侧重基于数据拆分.垂直拆分:垂直拆分包括垂直分库与垂直分表.垂直分库指的是按照业务归属将单个数据库中的数据表进行分类.与不同业务相关的数据表拆分到不同的数据库中.其核心是专库专用.以电商产品为例.在业务起步时期.为了快速上线.可能使用单个数据库存储商品表 物流表 商家表 订单表.当业务达到一定量级时.在按照电商业务维度垂直拆分出商品库 物流库 商家库 订单库.如图所示.垂直分库可以实现不同业务归属的数据解耦.将不同业务数据交给各业务研发团队独立维护.有效保证了各团队的职责单一.在高并发场景下.由于垂直分库使用不同服务器维护不同业务的数据库.数据库并发量得到一定程度提升.垂直分表指的是将一个数据表按照字段分成多个表.每个表存储其中一部分字段.分表的依据可以是字段被频繁访问的频率 字段值大小等.还是以电商产品为例.最初的商品表可能有名称 商品图片 价格 限购数 商品描述 售后说明等字段.但是这些字段的曝光率有较大的差距.在用户搜索商品 商品推荐或商家全部商品列表等高频访问场景下.仅需要名称 商品图片和价格字段.而限购书 商品描述和售后说明字段仅在用户点击进入商品详情页后才会被读取.而且商品描述和售后说明字段的值比较大.于是商品表可以被垂直拆分为商品基本信息表和商品详情表.如图所示.垂直分表可以很好的隔离核心数据和非核心数据,数据库是以行为为单位将数据加载到内存中的.通过垂直分表拆分以后核心数据表的字段大多访问频率较高.且字段值也都较小.因此可以将更多的数据加载到内存中.来提高查询命中率.减少磁盘I/O.以此来提升数据库性能.不过.垂直分表仅适合数据量不大但字段较多的数据存储场景.由于拆分后各表的数据行没有变化.因此垂直分表并没有消除单表数据数据量过大的问题.水平拆分:水平拆分同样包括水平分库与水平分表.水平分库是指将同一个数据库中的数据按照某种规则拆分到多个数据库中.这些数据库可以被部署在不同的服务器上.并且在每个数据库拥有哪些表以及每个表的结构都与拆分前的数据完全一致.如图所示.以存储已注册用户的用户库为例.将用户库水平拆分为3个数据库.用户库1 用户库2 用户库3.这三个数据库完全相同的用户表.对于一条用户数据.按照uid(用户ID)与3取模的结果来决定将其拆分到哪个数据库中.水平分库通过利用多服务器资源充分提高了数据库并发处理能力.且拆分后每个数据库内的单表数据量也得到了有效控制.水平分表是指在同一个数据库内.将一个数据表中的数据按照某种规则拆分到多个表中.每个表格的结构都与拆分前的表完全一致.如图所示.还是以用户库为例.在用户库中将用户表拆分为3个结构一样的表.用户表-1 用户表-2 用户表-3.对于一条用户数据.同样按照uid(用户ID)与3取模的结果来决定将其拆分到那个表中.水平分表解决了单表数据量过大的问题.但是由于拆分后的表还是在同一个数据库中.所以依然在竞争同一台服务器的请求连接数 CPU 内存网络带宽等资源.为了进一步提升数据库性能.水平分表还可以结合水平分库.即水平分库分表.将拆分后的表分散到不同的数据库中.达到分布式的效果.如图所示.水平分库使数据库拥有分布式能力.水平分表使数据量过大的单表SQL语句的执行效率得到提升.可以根据业务需要来选择是水平分库 水平分表还是水平分库分表.如果在业务场景中用户并发量很大.但是数据量较小.则可以选择水平分库.不选择水平分表.如果在业务场景中用户并发量很小.但是数据量较大.则可以不选择水平分库.只选择水平分表.如果在业务场景中用户并发量很大.数据量也大.则可以选择水平分库分表.水平拆分规则:水平拆分规则是指数据路由算法.用于决定一条数据应该被拆分到哪个库和那个表中.更广泛的说法是.一条数据应该被拆分到那个数据分区.最理想的情况下.数据路由算法应该保证每个分区有均等的数据量和数据读/写请求量.如果某个数据分区存储的数据量远大于其他数据分区.就称此情况为数据偏斜.如果某个数据分区的读/写请求量远大于其他数据分区.就将这个数据分区称为数据热点.一种适合的数据路由算法应该避免出现数据倾斜与数据热点.1.范围分区法:将数据以可排序字段值的区间为依据进行数据分区.比如数据唯一ID 数据创建时间等.如图所示.按照数据创建时间将每半年作为一个区间进行数据分区.范围分区法可以很方便的支持分区查询.比如上面的例子.可以轻松地得到某个月所有的数据.另外.范围分区法对分区扩容也友好.比如增加数据分区时.只需要设置更多地数据范围.而基本上不会变更已有的分区数据范围.但是.范围分区法是否可以保持各数据分区的数据量均匀分布非常依赖分区字段的属性.如果分区字段有自增属性.比如用户表使用用户Id作为分区依据.那么用户由于用户id自增.每个分区保持范围等长即可保证数据量的均匀分布.而如果用户表使用用户昵称这种具有随机性质的字段作为分区依据.那么由于用户昵称值的随机值.每个分区的范围长度可能不同.即数据分区之间难以保证数据量的均匀分布.容易造成数据偏斜.如果使用带有时间属性的字段作为分区依据.数据范围是近半年的数据分区的读/写频率更高.则容易出现数据热点.2.哈希分区法:为了防止出现数据偏斜与数据热点的问题.很多分布式存储系统都会采用哈希函数来确定数据分区.最简单的哈希分区法是取模法.先计算出所选数据字段的哈希值.再与数据分区数目N取模.即hash()%N.取模结果对应数据分区0~N-1.此方法最大的优势是实现简单.但是对于数据分区扩容缺少灵活性.一旦数据分区数目N有变化.所有的数据就都需要重新分区.更好的做法是与对数据分区扩容友好的范围分区法相结合.即对哈希值进行范围分区.每个数据分区接收哈希值在指定范围内的数据.如图所示.哈希分区法的优势是无视数据字段属性.无论是自增属性还是随机字符串.均可以通过哈希函数转化为数字.而且.使用优秀的哈希函数可以使数据量均匀分布,在很大程度上避免了数据偏斜与数据热点的出现.3.一致性哈希算法:一致性哈希分区法最重要的结构是哈希环.如图所示.数值0~2的32次方-1作为2的32次方个节点依次排列在哈希环上并首尾相连.哈希环维护了每个哈希值与其数据分区的路由关系.每个数据分区都通过哈希计算.所得到的哈希值与2的32次方取模后被映射到哈希环的某个节点.每条数据都以某个字段进行哈希计算.所得到的哈希值也与2的32次方取模后被映射到哈希环的某个节点.然后从这个节点出发.在哈希环上顺时针查找到第一个数据分区节点负责存储此数据.一致性哈希分区法的主要优点是.当增加或移除一个数据分区时.只有其在哈希环上逆时针相邻的数据需要重新分区.其他数据不会受到影响.由于一致性哈希性算法并不指定每个数据分区的哈希值范围.所以数据分区在哈希环上分布越均匀.各个数据分区的数据流就越均衡.但是当数据分区较少时.有很大的可能是它们在哈希环上的分布较为集中.进而造成数据偏斜.为了避免这种情况出现.一致性哈希算法引入了虚拟节点机制.对于每个数据分区.计算出多个哈希值.每个计算结果都被放置到哈希环的对应节点上.这些节点被称为虚拟节点.一个实际的数据分区可以对应多个虚拟节点.通过虚拟节点机制可以将数据分区数目放大.数据分区对应的虚拟节点越多.哈希环上的节点就越多.它们也容易在哈希环上均匀分布.数据偏斜的影响就会越来越小.扩容方案:当某个分库承载的数据量或请求量远高于其他分库.或者现有的分库分表架构的数据量已经趋于饱和时.都需要进行扩容操作.这里推荐的是平滑扩容方案是从库升级法.如图所示.假设某数据库被拆分为三个库和六个表.各个分库存储的数据范围分别为r0~r1 r1~r2 r2~r3.此时分库的DB0的数据量和资源压力过大.使用从库升级法对DB0分库扩容的步骤如下.1.位DB0增加Slave节点(即从库).开始主从复制操作,将DB0的数据同步到从库.这一步不一定需要专门来做.因为在常见的数据库分库分表方案中.分库都会使用主从复制架构来保证每个分库的高可用.从库本来就是存在的.2.主从复制完成后.DB0主库临时封禁写请求操作.保证不在有增量数据.3.检查主从库数据.如果数据完全一致.则表示数据已经完全被同步到从库.此时断开主从关系.4.修改DB0的数据范围为r0~(r0r1)/2.即DB0负责原来数据范围的前一半.5.将DB0从库提升为主库并命名为DB3.同时设置数据范围为(r0r1)/2~r1.即DB3负责原DB0数据范围的后一半.6.确认上游业务均以感知到分库数据范围的变更后.解封DB0的写请求操作.此时分库扩容已经完成.业务已恢复正常写数据库.7.启动离线任务.将DB0和DB3的数据范围外的另一半冗余数据删除.最终DB0被扩容为如图所示的分库.对整个数据库基于从库升级法扩容时.分库数量会翻倍.所以这种扩容方式也被称为翻倍扩容法.其扩容步骤与单个分库的扩容步骤类似.只不过是对每个分库都执行从库升级.1.对于每个分库增加从库.开始主从复制操作.同样.这一步不一定需要做.2.各分库主从复制完成后.主库临时封禁写请求操作.3.检查各分库的主动库数据.如果数据完全一致.则断开全部主从关系.4.修改原分库的数据范围为原数据范围的前一半.5.将各分库的从库提升为主库.同时设置其数据范围为原数据范围的后一半.6.确认上游业务均以感知到分库数据范围的变更后.解封全部分库写请求操作.数据库恢复对外业务.7.启动离线任务.将原分库和新分库的数据范围外的另一半冗余数据全部删除.最终DB0和DB1如图所示.语雀地址https://www.yuque.com/itbosunmianyi/xg8vfe?《Go.》 密码xbkk 欢迎大家访问.提意见.