专栏首页架构说数据库如何做到平滑扩容

数据库如何做到平滑扩容

摘要: 数据库一般有2种,传统的关系数据 例如 mysql 和内存数据例如redis。

分库分表的扩容是一件头疼的问题,如果采用对db层做一致性hash,

或是中间价的支持,它的成本过于高昂了,

如果不如此,只能停机维护来处理,对高可用性会产生影响

那是否有方案,既可以快速扩展,又不降低可用性?

这一篇,我们聊聊分库分表的扩展方案,供大家一起探讨。

一、水平分库扩展问题

为了增加db的并发能力,常见的方案就是对数据进行sharding,也就是常说的分库分表,这个需要在初期对数据规划有一个预期,从而预先分配出足够的库来处理。

比如目前规划了3个数据库,基于uid进行取余分片,那么每个库上的划分规则如下:

如上我们可以看到,数据可以均衡的分配到3个数据库里面。

但是,如果后续业务发展的速度很快,用户量数据大量上升,当前容量不足以支撑,应该怎么办?

需要对数据库进行水平扩容,再增加新库来分解。 新库加入之后,原先sharding到3个库的数据,就可以sharding到四个库里面了

不过此时由于分片规则进行了变化(uid%3 变为uid%4),

大部分的数据,无法命中在原有的数据库上了,需要重新分配,大量数据需要迁移。

比如之前uid1通过uid1%3 分配在A库上,新加入库D之后, 算法改为uid1%4 了,此时有可能就分配在B库上面了。

如果你有看到之前《一致性哈希的原理与实践》,就会发现新增一个节点,大概会有90%的数据需要迁移,这个对DB同学的压力还是蛮大的,那么如何应对?

一般有以下几种方式。

二、停服迁移

停服迁移是最常见的一种方案了,一般如下流程:

  1. 预估停服时间,发布停服公告
  2. 停服,通过事先做好的数据迁移工具,按照新的分片规则,进行迁移
  3. 修改分片规则
  4. 启动服务

我们看到这种方式比较安全,停服之后没有数据写入,能够保证迁移工作的正常进行,没有一致性的问题。唯一的问题,就是停服了和时间压力了。

  1. 停服,伤害用户体验,同时也降低了服务器的可用性
  2. 必须在制定时间内完成迁移,如果失败,需要择日再次进行。同时增加了开发人员的压力,容易发生大的事故
  3. 数据量的巨大的时候,迁移需要大量时间

那有没有其他方式来改进一下,我们看下以下两种方案。

三、升级从库

线上数据库,我们为了保持其高可用,一般都会每台主库配一台从库,读写在主库,然后主从同步到从库。如下,A,B是主库,A0和B0是从库。

此时,当需要扩容的时候,我们把A0和B0升级为新的主库节点,如此由2个分库变为4个分库。同时在上层的分片配置,做好映射,规则如下:

uid%4=0和uid%4=2的分别指向A和A0,也就是之前指向uid%2=0的数据,分裂为uid%4=0和uid%4=2 uid%4=1和uid%4=3的指向B和B0,也就是之前指向uid%2=1的数据,分裂为uid%4=1和uid%4=3

因为A和A0库的数据相同,B和B0数据相同,所以此时无需做数据迁移即可(扩容期间不允许业务发生,业务阻塞)

只需要变更一下分片配置即可,通过配置中心更新,无需重启。

由于之前uid%2的数据分配在2个库里面,此时分散到4个库中, 由于老数据还存在(uid%4=0,还有一半uid%4=2的数据), 所以需要对冗余数据做一次清理。

而这个清理,不会影响线上数据的一致性,可是随时随地进行。

处理完成以后,为保证高可用,以及下一步扩容需求。可以为现有的主库再次分配一个从库。

总结一下此方案步骤如下:

  1. 修改分片配置,做好新库和老库的映射。(手工处理)
  2. 同步配置,从库升级为主库(手工处理)
  3. 解除主从关系 (手工处理)
  4. 冗余数据清理(手工处理)
  5. 为新的数据节点搭建新的从库(手工处理)

四、双写迁移

双写的方案,更多的是针对线上数据库迁移来用的,当然了,对于分库的扩展来说也是要迁移数据的,因此,也可以来协助分库扩容的问题。

原理和上述相同,做分裂扩容,只是数据的同步方式不同了。

1.增加新库写链接

双写的核心原理,就是对需要扩容的数据库上,增加新库,并对现有的分片上增加写链接,同时写两份数据。

因为新库的数据为空,所以数据的CRUD对其没有影响,在上层的逻辑层,还是以老库的数据为主。

2.新老库数据迁移

通过工具,把老库的数据迁移到新库里面,此时可以选择同步分裂后的数据(1/2)来同步,也可以全同步,一般建议全同步,最终做数据校检的时候好处理。

3.数据校检

按照理想环境情况下,数据迁移之后,因为是双写操作,所以两边的数据是一致的,特别是insert和update,一致性情况很高。但真实环境中会有网络延迟等情况,对于delete情况并不是很理想,比如:

A库删除数据a的时候,数据a正在迁移,还没有写入到C库中,此时C库的删除操作已经执行了,C库会多出一条数据。

此时就需要做好数据校检了,数据校检可以多做几遍,直到数据几乎一致,尽量以旧库的数据为准。(手工处理)

4.分片配置修改

数据同步完毕,就可以把新库的分片映射重新处理了,还是按照老库分裂的方式来进行,

u之前uid%2=0,变为uid%4=0和uid%4=2的 uid%2=1,变为uid%4=1和uid%4=3的。

上面数据特点 数据集中在一个表中

四、reids 3.0 以前

redis3.0 key/value,

3.0以下版本采用Key的一致性hash算法来区分key存储在哪个Redis实例上

采用这种方式也存在两个问题

  • 扩容问题:

因为使用了一致性哈稀进行分片,那么不同的key分布到不同的Redis-Server上,当我们需要扩容时,需要增加机器到分片列表中,这时候会使得同样的key算出来落到跟原来不同的机器上,这样如果要取某一个值,会出现取不到的情况,之前的缓存相当于全部失效。对于扩容问题,Redis的作者提出了一种名为Pre-Sharding的方式。即事先部署足够多的Redis服务。

  • 单点故障问题:

当集群中的某一台服务挂掉之后,客户端在根据一致性hash无法从这台服务器取数据。对于单点故障问题,我们可以使用Redis的HA高可用来实现。利用Redis-Sentinal来通知主从服务的切换。

Redis的作者提出了一种叫做presharding的方案来解决动态扩容和数据分区的问题,实际就是在同一台机器上部署多个Redis实例的方式,当容量不够时将多个实例拆分到不同的机器上, 这样实际就达到了扩容的效果

拆分过程如下:

  1. 在新机器上启动好对应端口的Redis实例。
  2. 配置新端口为待迁移端口的从库。
  3. 待复制完成,与主库完成同步后,切换所有客户端配置到新的从库的端口。
  4. 配置从库为新的主库。
  5. 移除老的端口实例。
  6. 重复上述过程迁移好所有的端口到指定服务器上。

依然有缺点,手工处理和迁移过程失败的风险

五、reids 3.0

分片: Redis Cluster在设计中没有使用一致性哈希(Consistency Hashing),

而是使用数据分片引入哈希槽(hash slot)来

旁白:

Redis Cluster是自己做的crc16的简单hash算法,没有用一致性hash。Redis的作者认为它的crc16(key) mod 16384的效果已经不错了,虽然没有一致性hash灵活,但实现很简单 使用单节点时的redis时只有一个表,所有的key都放在这个表里; 改用Redis Cluster以后会自动为你生成16384个分区表,

当前集群有3个节点,槽默认是平均分的: 节点 A (6381)包含 0 到 5499号哈希槽. 节点 B (6382)包含5500 到 10999 号哈希槽. 节点 C (6383)包含11000 到 16383号哈希槽.

数据迁移

数据迁移可以理解为slot(槽)和key的迁移,这个功能很重要,极大地方便了集群做线性扩展,以及实现平滑的扩容或缩容。 使用哈希槽的好处就在于可以方便的添加或移除节点。 当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了; 当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;

我们以后新增或移除节点的时候不用先停掉所有的 redis 服务

需要维护迁移过程中的状态,依赖程序的健壮性。

Q1 你感觉方案四和方案5差不都,都需要迁移数据,区别在哪里?

2P架构

P2P模式,无中央结点,结点之间通过一个称之为Gossip的协议进行通信。

  • 为什么是16384?

很显然,我们需要维护节点和槽之间的映射关系,每个节点需要知道自己有哪些槽,并且需要在结点之间传递这个消息。

为了节省存储空间,每个节点用一个Bitmap来存放其对应的槽:

2k = 2*1024*8 = 16384,也就是说,每个结点用2k的内存空间,总共16384个比特位,就可以存储该结点对应了哪些槽。然后这2k的信息,通过Gossip协议,在结点之间传递。

  • 客户端存储路由信息

对于客户端来说,维护了一个路由表:每个槽在哪台机器上。这样存储(key, value)时,根据key计算出槽,再根据槽找到机器。

  • 无损扩容

虽然Hash环可以减少扩容时失效的key的数量,但毕竟有丢失。

而在redis-cluster中,当新增机器之后,槽会在机器之间重新分配,

同时被影响的数据会自动迁移,从而做到无损扩容。

旁白:槽会在机器之间重新分配,依然有不命中的现象,需要二次查询

  • 主从复制

redis-cluster也引入了master-slave机制,从而提供了fail-over机制,

这很大程度上解决了“缓存雪崩“的问题。

旁白:升级从库 这个方案有这个缺点

总结如下:

1、节点自动发现

2、slave->master 选举,集群容错

3、Hot resharding:在线分片

4、基于配置(nodes-port.conf)的集群管理

5、客户端与redis节点直连、不需要中间proxy层.

6、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.

引用:

1 数据库秒级平滑扩容架构方案

2 http://antirez.com/news/110

本文分享自微信公众号 - 架构说(JiaGouS),作者:程序员小王

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 高性能MySql学习笔记1——锁、事务、隔离级别

    提问: 事物的概念什么是脏读?不可重复读 为什么需要锁? 因为数据库要解决并发控制问题。在同一时刻,可能会有多个客户端对Table1.rown进行操作,比如有的...

    程序员小王
  • [LeetCode] Symmetric Tree

    1、题目名称 Symmetric Tree https://leetcode.com/problems/symmetric-tree/ 2、题目内容 Give...

    程序员小王
  • 使用虚拟节点改进的一致性哈希算法

    1 作者:@lionets 分析缺点 连接:http://my.oschina.net/lionets/blog/288066 2 作者:@糖拌咸鱼 ...

    程序员小王
  • 【干货-73页PPT下载】大数据服务民生--让数据多跑腿  让群众少跑路

    作者:巩怀志 华傲数据 资源中心总监 从事IT和数据领域8年,专注于政府、电信在大数据领域的创新 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?...

    小莹莹
  • 做数据挖掘工作需要具备哪些思维原理?

    大数据时代,计算模式也发生了转变,从“流程”核心转变为“数据”核心。Hadoop体系的分布式计算框架已经是“数据”为核心的范式。非结构化数据及分析需求,将改变I...

    华章科技
  • 做数据挖掘工作需要具备的五大思维原理

    一、数据核心原理 从“流程”核心转变为“数据”核心 大数据时代,计算模式也发生了转变,从“流程”核心转变为“数据”核心。Hadoop体系的分布式计算框架已经是“...

    钱塘数据
  • 中国电信要帮北京疏堵;Uber陷数据隐私争议 | DT数读

    6月15日,北京交通发展研究院与中国电信达成战略合作,共建“城市与交通大数据联合实验室”,通过整合城市交通与电信大数据资源,推动大数据在交通行业的深化应用。

    DT数据侠
  • 大数据“近因偏差”的烦恼

    全世界90%的数据都是最近几年生成的,人们对这个结论可能已经耳熟能详。尽管我能找到的这个说法的最早出处是在2013年5月,但是,这种趋势却始终未曾发生变化。事实...

    企鹅号小编
  • 数据库分库分表如何避免“过度设计”和“过早优化”

    关系型数据库本身比较容易成为系统瓶颈,单机存储容量、连接数、处理能力都有限。当单表的数据量达到1000W或100G以后,由于查询维度较多,即使添加从库、优化索引...

    黄泽杰
  • 美团面试官:说说你对数据库分库分表的理解?

    关系型数据库本身比较容易成为系统瓶颈,单机存储容量、连接数、处理能力都有限。当单表的数据量达到1000W或100G以后,由于查询维度较多,即使添加从库、优化索引...

    黄泽杰

扫码关注云+社区

领取腾讯云代金券