互联网系统需要处理大量用户的请求。比如微信日活用户破10亿,海量的用户每天产生海量的数量;美团外卖,每天都是几千万的订单,那这些系统的用户表、订单表、交易流水表等是如何处理呢?

数据量只增不减,历史数据又必须要留存,非常容易成为性能的瓶颈,而要解决这样的数据库瓶颈问题,“读写分离”和缓存往往都不合适,目前比较普遍的方案就是使用NoSQL/NewSQL或者采用分库分表。

使用分库分表时,主要有垂直拆分和水平拆分两种拆分模式,都属于物理空间的拆分。

分库分表方案:只分库、只分表、分库又分表。

拆分方式

  • 垂直拆分

垂直拆分又称为纵向拆分,垂直拆分是将表按库进行分离,或者修改表结构按照访问的差异将某些列拆分出去。应用时有垂直分库和垂直分表两种方式,一般谈到的垂直拆分主要指的是垂直分库。
如下图所示,采用垂直分库,将用户表和订单表拆分到不同的数据库中




mysql随机写速率太低 mysql 随机数范围_mysql随机写速率太低


垂直分表就是将一张表中不常用的字段拆分到另一张表中,从而保证第一张表中的字段较少,避免出现数据库跨页存储的问题,从而提升查询效率
解决方案:一个表中字段过多,还有有些字段经常使用,有些字段不经常使用,或者还有text等字段信息。可以考虑使用垂直分表方案。


mysql随机写速率太低 mysql 随机数范围_数据_02


垂直拆分优点:
1、拆分后业务清晰,拆分规则明确;
2、易于数据的维护和扩展;
3、可以使得行数据变小,一个数据块 (Block) 就能存放更多的数据,在查询时就会减少 I/O 次数;
4、可以达到最大化利用 Cache 的目的,具体在垂直拆分的时候可以将不常变的字段放一起,将经常改变的放一起;
5、便于实现冷热分离的数据表设计模式。 垂直拆分缺点:
1、主键出现冗余,需要管理冗余列;
2、会引起表连接 JOIN 操作,可以通过在业务服务器上进行 join 来减少数据库压力,提高了系统的复杂度;
3、依然存在单表数据量过大的问题;
4、事务处理复杂;

  • 水平拆分

水平拆分又称为横向拆分。 相对于垂直拆分,它不再将数据根据业务逻辑分类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个表仅包含数据的一部分,如下图所示。


mysql随机写速率太低 mysql 随机数范围_mysql 列 随机数_03


水平拆分优点:
1、拆分规则设计好,join 操作基本可以数据库做;
2、不存在单库大数据,高并发的性能瓶颈;
3、切分的表的结构相同,应用层改造较少,只需要增加路由规则即可;
4、提高了系统的稳定性和负载能力。 水平拆分缺点
1、拆分规则难以抽象;
2、跨库Join性能较差;
3、分片事务的一致性难以解决;
4、数据扩容的难度和维护量极大。

日常工作中,我们通常会同时使用两种拆分方式,垂直拆分更偏向于产品/业务/功能拆分的过程,在技 术上我们更关注水平拆分的方案。

水平拆分:解决表中记录过多问题。

垂直拆分:解决表过多或者是表字段过多问题。

主键策略

在很多中小项目中,我们往往直接使用数据库自增特性来生成主键ID,这样确实比较简单。而在分库分表的环境中,数据分布在不同的数据表中,不能再借助数据库自增长特性直接生成,否则会造成不同数据表主键重复。下面介绍几种ID生成算法。

  • UUID

UUID是通用唯一识别码(Universally Unique Identififier)的缩写,长度是16个字节,被表示为32个十六进制数字,以“ - ”分隔的五组来显示,格式为8-4-4-4-12,共36个字符,例如: 550e8400-e29b-41d4-a716-446655440000。UUID在生成时使用到了以太网卡地址、纳秒级时间、芯片ID码和随机数等信息,目的是让分布式系统中的所有元素都能有唯一的识别信息。使用UUID做主键,可以在本地生成,没有网络消耗,所以生成性能高。但是UUID比较长,没有规律性,耗费存储空间。
如果UUID作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,影响性能,而且每个非聚簇索引上都维护了主键,uuid作为主键又占内存。

  • COMB(UUID变种)

COMB(combine)型是数据库特有的一种设计思想,可以理解为一种改进的GUID,它通过组合GUID和系统时间,以使其在索引和检索事有更优的性能。数据库中没有COMB类型,它是Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中设计出来的。COMB设计思路是这样的:既然UniqueIdentififier数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么我们能不能通过组合的方式,保留UniqueIdentififier的前10个字节,用后6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与UniqueIdentififier组合起来,在保 留UniqueIdentififier的唯一性的同时增加了有序性,以此来提高索引效率。解决UUID无序的问题,性能优于UUID。

  • SNOWFLAKE

有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成,SnowFlake解决了这种需求。SnowFlake是Twitter开源的分布式ID生成算法,结果是一个long型的ID,long型是8个字节,64-bit。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中 心,5个bit的机器ID),12bit作为毫秒内的流水号,最后还有一个符号位,永远是0。如下图所 示:


mysql随机写速率太低 mysql 随机数范围_mysql随机写速率太低_04


SnowFlake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID重复,并且效率较高。经测试SnowFlake每秒能够产生26万个ID。缺点是强依赖机器时钟,如果多台机器环境时钟没同步,或时钟回拨,会导致发号重复或者服务会处于不可用状态。因此一些互联网公司也基于上述的方案做了封装,例如百度的uidgenerator(基于SnowFlake)和美团的leaf(基于数据库和SnowFlake)等。

  • 数据库ID表

比如A表分表为A1表和A2表,我们可以单独的创建一个MySQL数据库,在这个数据库中创建一张表,这张表的ID设置为自动递增,其他地方需要全局唯一ID的时候,就先向这个这张表中模拟插 入一条记录,此时ID就会自动递增,然后我们获取刚生成的ID后再进行A1和A2表的插入。例如,下面DISTRIBUTE_ID就是我们创建要负责ID生成的表,结构如下:


mysql随机写速率太低 mysql 随机数范围_mysql随机写速率太低_05


当分布式集群环境中哪个应用需要获取一个全局唯一的分布式ID的时候,就可以使用代码连接这个数据库实例,执行如下SQL语句即可。
insert into DISTRIBUTE_ID(createtime) values(NOW());
select LAST_INSERT_ID();

  • redis/zk

分片策略

  • 分片概念

分片:表示分配过程,是一个逻辑上概念,表示如何实现 分库分表:表示分配结果,是一个物理上概念,表示最终实现的结果 数据库扩展方案
1、横向扩展:一个库变多个库,加机器数量
2、纵向扩展:一个库还是一个库,优化机器性能,加高配CPU或内存
在分布式存储系统中,数据需要分散存储在多台设备上,分片就是把数据库横向扩展到多个数据库服务器上的一种有效的方式,其主要目的就是为突破单节点数据库服务器的 I/O 能力限制,解决数据库扩展性问题

  • 分片策略

基于范围分片
根据特定字段的范围进行拆分,比如用户ID、订单时间、产品价格等。例如:
{[1 - 100] => Cluster A, [101 - 199] => Cluster B}
优点:新的数据可以落在新的存储节点上,如果集群扩容,数据无需迁移。
缺点:数据热点分布不均,数据冷热不均匀,导致节点负荷不均。 哈希取模分片
整型的Key可直接对设备数量取模,其他类型的字段可以先计算Key的哈希值,然后再对设备数量取模。假设有n台设备,编号为0 ~ n-1,通过Hash(Key) % n就可以确定数据所在的设备编号。该模式也称为离散分片。
优点:实现简单,数据分配比较均匀,不容易出现冷热不均,负荷不均的情况。
缺点:扩容时会产生大量的数据迁移,比如从n台设备扩容到n+1,绝大部分数据需要重新分配和 迁移。 一致性哈希分片
采用Hash取模的方式进行拆分,后期集群扩容需要迁移旧的数据。使用一致性Hash算法能够很大程度的避免这个问题,所以很多中间件的集群分片都会采用一致性Hash算法。
一致性Hash是将数据按照特征值映射到一个首尾相接的Hash环上,同时也将节点(按照IP地址或者机器名Hash)映射到这个环上。对于数据,从数据在环上的位置开始,顺时针找到的第一个节点即为数据的存储节点。Hash环示意图与数据的分布如下:


mysql随机写速率太低 mysql 随机数范围_字段_06


一致性Hash在增加或者删除节点的时候,受到影响的数据是比较有限的,只会影响到Hash环相邻的节点,不会发生大规模的数据迁移。

扩容方案

当系统用户进入了高速增长期时,即便是对数据进行分库分表,但数据库的容量,还有表的数据量也总会达到天花板。当现有数据库达到承受极限时,就需要增加新服务器节点数量进行横向扩容


mysql随机写速率太低 mysql 随机数范围_数据库_07


横向扩展会有什么技术难度?
1、数据迁移问题
2、分片规则改变
3、数据同步、时间点、数据一致性 解决方案
1、停机扩容
这是一种很多人初期都会使用的方案,尤其是初期只有几台数据库的时候。停机扩容的具体步骤如下:
站点发布一个公告,例如:“为了为广大用户提供更好的服务,本站点将在今晚00:00-2:00之间升级,给您带来不便抱歉";
时间到了,停止所有对外服务;
新增n个数据库,然后写一个数据迁移程序,将原有x个库的数据导入到最新的y个库中。比如分片规则由%x变为%y
数据迁移完成,修改数据库服务配置,原来x个库的配置升级为y个库的配置
重启服务,连接新库重新对外提供服务
回滚方案:万一数据迁移失败,需要将配置和数据回滚,改天再挂公告
优点:简单
缺点:
1、停止服务,缺乏高可用
2、如果有问题没有及时测试出来启动了服务,运行后发现问题,数据会丢失一部分,难以回滚。
适用场景:
1、小型网站
2、大部分游戏
3、对高可用要求不高的服务 平滑扩容
数据库扩容的过程中,如果想要持续对外提供服务,保证服务的可用性,平滑扩容方案是最好的选择。平滑扩容就是将数据库数量扩容成原来的2倍,比如:由2个数据库扩容到4个数据库,具体步骤如下:
新增2个数据库
配置双主进行数据同步(先测试、后上线)


mysql随机写速率太低 mysql 随机数范围_数据库_08


数据同步完成之后,配置双主双写(同步因为有延迟,如果时时刻刻都有写和更新操作,会存在不准确问题)


mysql随机写速率太低 mysql 随机数范围_mysql随机写速率太低_09