精华内容
下载资源
问答
  • 数据库分片如何设计
    千次阅读
    2019-09-16 18:40:11

    什么是分片,分片的作用,如何分片,分片的几种方法,分片的表示方法,分片完之后如何进行分配?
    1.分片
    在一个分布式数据库中,全局数据库是由各个局部数据库逻辑组合而成;反之,各个局部数据库是由全局数据库的某种逻辑分割而得,所以对全局数据的划分称为分片。
    2.分片的作用:
    (1)减少网络传输量:对数据进行复制存储,目的是可以就近访问所需数据副本,减少网络上的数据传输量。
    (2)增大事务处理的局部性;
    (3)提高数据的可用性和查询效率;
    (4)负载均衡。
    3.分片的过程:
    分片过程是将全局数据进行逻辑划分和实际物理分配过程。全局数据由分片成各个片段数据,各个片段分配到不同的场地(服务器)上。
    全局数据库(GBD)----->片段数据库(FDB)-------->物理数据库(PDB)。
    4.分片的方法:
    (1)水平分片:一个表T被分成若干片:T1,T2,…,Tn,其中每个片包含T的一部分行并且T的每一行都会出现在一个片中。
    (2)垂直分片:一个表T被分成若干片:T1,T2,…,Tn,每片包含T的一部分列。每一列必须至少被一个片包含,并且每个片必须包含候选码的列。
    (3)混合分片:是以上两种方法的混合。可以先水平分片再垂直分片,或先垂直分片再水平分片,但他们的结果是不相同的。
    5.分片的表示方法:
    图形表示法、分片树表示法。
    6.分配
       分配,就是将分片的数据放在多个站点,利用冗余来达到系统的最大可用性和可靠性。将分片的副本放在多个站点,当本地系统要访问数据时,首先判断该数据是本地的数据还是其他站点的数据,如果是本地的数据,就直接可以在本地进行读取。这样就缩短了系统的响应时间。当要访问的数据的站点发生故障不可用时,系统可以直接访问其副本所在的站点,以此来达到系统的最大可用性。

    更多相关内容
  • 分布式数据库系统之【分片设计

    千次阅读 2020-11-19 11:41:56
      两种设计策略 ▍两种设计策略 ...分布设计 + 分片 + 分配 物理设计 性能调优     分片简介 ▍分片的相关定义 分片(Fragmentation):对全局数据的划分 片段(Fragment):划分的结果称为片段

    content

    1. 两种设计策略
    2. 分片简介
    3. 水平分片设计
    4. 垂直分片设计
    5. 分片的表示方法
    6. 分配设计
    7. 数据复制技术

     
     
     

    两种设计策略

    两种设计策略

    • 自顶向下(top_down):用于设计一个新的数据库系统(本节着重点)
    • 自底向上(bottom_up):已经存在多个数据库系统,将它们集成为一个数据库系统

    top_down设计过程

    1. 需求分析
    2. 概念设计 + 视图集成 + E-R表示 + 转换到关系模式
    3. 分布设计 + 分片 + 分配
    4. 物理设计
    5. 性能调优

     
     

    分片简介

    分片的相关定义

    • 分片(Fragmentation):对全局数据的划分
    • 片段(Fragment):划分的结果称为片段
    • 分配(Allocation):将片段指定到场地
    • 分割(Partition):每个片段只存储在一个场地
    • 复制(Replication):每个片段存储在一个以上的场地

    分片的作用

    • 减少网络传输量 —— 分片后进行复制,从多个副本中就近访问;或者将需频繁访问的分片存储在本地场地上
    • 增大事务处理的局部性 —— 局部场地需要的分片分配在各自的场地上,增加局部事务效率
    • 提高数据的可用性 —— 当某一场地发生故障,非故障场地上的副本同样是可用的
    • 使负载均衡 —— 破除数据访问瓶颈,提高系统整体效率

    分片的过程

    分片过程是将全局数据进行逻辑划分和物理分配的过程。全局数据由分片模式划分成各个数据片段,各个数据片段由分配模式存储在各个场地上。

    在这里插入图片描述

    分片的原则

    • 完备性 —— 所有全局数据必须映射到某个片段上
    • 可重构性 —— 所有片段必须能够重新组合成全局数据
    • 不相交性 —— 水平划分时不能有交集;垂直划分时交集只能是主键

    分片的种类

    • 水平分片 —— 按元组划分
    • 垂直分片 —— 按属性划分
    • 混合分片 —— 水平分片 + 垂直分片

    分片的透明性

    • 分片透明性 —— 用户不必考虑「数据属于哪个片段」
    • 分配透明性 —— 用户不必考虑「片段属于哪个场地」
    • 局部映射透明性 —— 用户不必考虑「关于底层数据库的一切」

     
     

    水平分片设计

    典例

    在这里插入图片描述
    在这里插入图片描述

    谓词

    • 简单谓词(Simple Predicate) :只包含一个操作符号的查询谓词
    • 小项谓词(Minterm Predicate) :由多个简单谓词组合成的查询谓词

    基本水平分片(Primary horizontal fragmentation)

    1. 找到具有完备性最小性的简单谓词集合
    2. 派生小项谓词集合
    3. 消除无意义的小项谓词

    在这里插入图片描述
     
    导出水平分片(Derived horizontal fragmentation)

    一个关系的分片不是基于关系本身的属性,而是根据另一个与其有关联的关系的属性来划分。

    在这里插入图片描述

     
     
     
     

    垂直分片设计

    典例

    在这里插入图片描述

    在这里插入图片描述

    紧密度

    紧密度(affinity)是用来度量属性间的关系 —— 很显然,联系越紧密的属性,越不应该分开,越不应该被分到两个场地。

     
     
     

    分片的表示方法

    图形表示法
    在这里插入图片描述

    分片树表示法

    在这里插入图片描述

     
     
     

    分配设计

    三种分配方式

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    考虑因素

    应用角度出发(分片更多):增加事务处理的局部性;提高系统的可靠性和可用性;增加系统的并行性

    系统角度出发(分片更少):降低系统的运行和维护开销;使系统负载均衡;方便一致性维护

    二者是矛盾的。
     
    设计原则

    数据库因素应用因素场地因素网络通信因素
    片段的大小查询对片段的读频度场地上存储数据的单位代价网络带宽
    片段的选择度查询对片段的写频度场地上处理数据的单位代价网络延迟
    查询的启动场地场地间的通信代价
    传输的数据帧大小

    分配模型

    总代价 = 存储代价 + 处理代价

    总代价 = ∑ S k + ∑ Q i ∑Sk+∑Qi Sk+Qi

    (🌟注意一下)

     
     
     

    数据复制技术

    优势

    • 减少网络负载(因为就近访问,减少了网络传输代价)
    • 提高系统性能(因为就近访问+并行处理)
    • 更好的负载均衡(因为可以分布到多个节点)

    缺点

    • 数据一致性问题
    • 数据冲突问题

    分类

    • 同步复制 —— 实时的、严格的一致性;频繁的通信;较高的响应时间
    • 异步复制 —— 非实时的;较短的响应时间;潜在的数据冲突

    实现

    • 基于触发器
    • 基于日志
    • 基于时间戳
    • 基于API

     
     
     
     
     
     
     
     
     
     
     
     

    M o r e More More

    展开全文
  • 数据库架构设计——分布式数据库设计

    千次阅读 多人点赞 2022-01-16 07:55:16
    一定要认识到数据库技术正在经历一场较大的变革,及早掌握好分布式架构设计。 一、分布式数据库概念 Wiki 官方对分布式数据库的定义为: A distributed database is a database in which data is stored across ...

    摘要

    现在互联网应用已经普及,数据量不断增大。对淘宝、美团、百度等互联网业务来说,传统单实例数据库很难支撑其性能和存储的要求,所以分布式架构得到了很大发展。一定要认识到数据库技术正在经历一场较大的变革,及早掌握好分布式架构设计。

    一、分布式数据库概念

    Wiki 官方对分布式数据库的定义为:

    A distributed database is a database in which data is stored across different physical locations. It may be stored in multiple computers located in the same physical location (e.g. a data centre); or maybe dispersed over a network of interconnected computers.

    从定义来看,分布式数据库是一种把数据分散存储在不同物理位置的数据库。对比我们之前学习的数据库,数据都是存放在一个实例对应的物理存储上,而在分布式数据库中,数据将存放在不同的数据库实例上。

    分布式数据库的架构

    从图中我们可以看到,在分布式数据库下,分布式数据库本身分为计算层、元数据层和存储层:

    • 计算层就是之前单机数据库中的 SQL 层,用来对数据访问进行权限检查、路由访问,以及对计算结果等操作。
    • 元数据层记录了分布式数据库集群下有多少个存储节点,对应 IP、端口等元数据信息是多少。当分布式数据库的计算层启动时,会先访问元数据层,获取所有集群信息,才能正确进行 SQL 的解析和路由等工作。另外,因为元数据信息存放在元数据层,那么分布式数据库的计算层可以有多个,用于实现性能的扩展。
    • 存储层用来存放数据,但存储层要和计算层在同一台服务器上,甚至不求在同一个进程中。

    分布式数据库的优势是把数据打散到不同的服务器上,这种横向扩展的 Scale Out 能力,能解决单机数据库的性能与存储瓶颈。

    • 从理论上来看,分布式数据库的性能可以随着计算层和存储层的扩展,做到性能的线性提升。
    • 从可用性的角度看,如果存储层发生宕机,那么只会影响 1/N 的数据,N 取决于数据被打散到多少台服务器上。所以,分布式数据库的可用性对比单机会有很大提升,单机数据库要实现99.999% 的可用性或许很难,但是分布式数据库就容易多了。
    • 当然,分布式数据库也存在缺点:正因为数据被打散了,分布式数据库会引入很多新问题,比如自增实现、索引设计、分布式事务等

    二、分布式MySQL架构

    学习分布式 MySQL 架构前,我们先看一下原先单机 MySQL 架构是怎样的。

    可以看到,原先客户端是通过 MySQL 通信协议访问 MySQL 数据库,MySQL 数据库会通过高可用技术做多副本,当发生宕机进行切换。

    那么对于分布式 MySQL 数据库架构,其整体架构如下图所示:

    从上图可以看到,这时数据将打散存储在下方各个 MySQL 实例中,每份数据叫“分片(Shard)”。在分布式 MySQL 架构下,客户端不再是访问 MySQL 数据库本身,而是访问一个分布式中间件。这个分布式中间件的通信协议依然采用 MySQL 通信协议(因为原先客户端是如何访问的MySQL 的,现在就如何访问分布式中间件)。分布式中间件会根据元数据信息,自动将用户请求路由到下面的 MySQL 分片中,从而将存储存取到指定的节点。

    另外,分布式 MySQL 数据库架构的每一层都要由高可用,保证分布式数据库架构的高可用性。对于上层的分布式中间件,是可以平行扩展的:即用户可以访问多个分布式中间件,如果其中一个中间件发生宕机,那么直接剔除即可。因为分布式中间件是无状态的,数据保存在元数据服务中,它的高可用设计比较容易。对于元数据来说,虽然它的数据量不大,但数据非常关键,一旦宕机则可能导致中间件无法工作,所以,元数据要通过副本技术保障高可用。最后,每个分片存储本身都有副本,通过我们之前学习的高可用技术,保证分片的可用性。也就是说,如果分片 1 的 MySQL 发生宕机,分片 1 的从服务器会接替原先的 MySQL 主服务器,继续提供服务。

    但由于使用了分布式架构,那么即使分片 1 发生宕机,需要 60 秒的时间恢复,这段时间对于业务的访问来说,只影响了 1/N 的数据请求。可以看到,分布式 MySQL 数据库架构实现了计算层与存储层的分离,每一层都可以进行 Scale Out 平行扩展,每一层又通过高可用技术,保证了计算层与存储层的连续性,大大提升了MySQL 数据库的性能和可靠性,为海量互联网业务服务打下了坚实的基础。

    三、数据库的分库分表

    学习了分布式数据库的架构,知道各类分布式数据库都离不开计算层、存储层、元数据层这三层关系。另外,很重要的一点是,知道分布式数据库是把数据打散存储在一个个分片中。在基于MySQL 的分布式数据库架构中,分片就存在于 MySQL 实例中。学习分布式数据库中,一个非常重要的设计:正确地把数据分片,充分发挥分布式数据库架构的优势。

    3.1 选出分片键

    对于类似淘宝、京东、拼多多这样业务体量的应用来说,单实例 MySQL 数据库在性能和存储容量上肯定无法满足“双 11、618 ”大促的要求,所以要改造成分布式数据库架构。

    在对表中的数据进行分片时,首先要选出一个分片键(Shard Key),即用户可以通过这个字段进行数据的水平拆分。对于我们之前使用的电商业务的订单表orders,其表结构如下所示:

    CREATE TABLE `orders` (
    
      `O_ORDERKEY` int NOT NULL,
      `O_CUSTKEY` int NOT NULL,
      `O_ORDERSTATUS` char(1) NOT NULL,
      `O_TOTALPRICE` decimal(15,2) NOT NULL,
      `O_ORDERDATE` date NOT NULL,
      `O_ORDERPRIORITY` char(15) NOT NULL,
      `O_CLERK` char(15) NOT NULL,
      `O_SHIPPRIORITY` int NOT NULL,
      `O_COMMENT` varchar(79) NOT NULL,
      PRIMARY KEY (`O_ORDERKEY`),
      KEY `idx_custkey_orderdate` (`O_CUSTKEY`,`O_ORDERDATE`),
      KEY `ORDERS_FK1` (`O_CUSTKEY`),
      KEY `idx_custkey_orderdate_totalprice` (`O_CUSTKEY`,`O_ORDERDATE`,`O_TOTALPRICE`),
      KEY `idx_orderdate` (`O_ORDERDATE`),
      KEY `idx_orderstatus` (`O_ORDERSTATUS`),
      CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`O_CUSTKEY`) REFERENCES `customer` (`C_CUSTKEY`)
    ) ENGINE=InnoDB

    对于上面的表orders,可以选择的分片键有:o_orderkey、o_orderdate、也可以是o_custkey。在选出分片键后,就要选择分片的算法,比较常见的有 RANGE 和 HASH 算法。

    比如,表 orders,选择分片键 o_orderdate,根据函数 YEAR 求出订单年份,然后根据RANGE 算法进行分片,这样就能设计出基于 RANGE 分片算法的分布式数据库架构:

    从图中我们可以看到,采用 RANGE 算法进行分片后,表 orders 中,1992 年的订单数据存放在分片 1 中、1993 年的订单数据存放在分片 2 中、1994 年的订单数据存放在分片 3中,依次类推,如果要存放新年份的订单数据,追加新的分片即可。

    不过,RANGE 分片算法在分布式数据库架构中,是一种非常糟糕的算法,因为对于分布式架构,通常希望能解决传统单实例数据库两个痛点:

    • 性能可扩展,通过增加分片节点,性能可以线性提升;
    • 存储容量可扩展,通过增加分片节点,解决单点存储容量的数据瓶颈。

    那么对于订单表 orders 的 RANGE 分片算法来说,你会发现以上两点都无法实现,因为当年的数据依然存储在一个分片上(即热点还是存在于一个数据节点上)。如果继续拆细呢?比如根据每天进行 RANGE 分片?这样的确会好一些,但是对“双 11、618”这样的大促来说,依然是单分片在工作,热点依然异常集中。所以在分布式架构中,RANGE 分区算法是一种比较糟糕的算法。但它也有好处:可以方便数据在不同机器间进行迁移(migrate),比如要把分片 2 中 1992 年的数据迁移到分片 1,直接将表进行迁移就行。

    而对海量并发的 OLTP 业务来说,一般推荐用 HASH 的分区算法。这样分片的每个节点都可以有实时的访问,每个节点负载都能相对平衡,从而实现性能和存储层的线性可扩展。我们来看表 orders 根据 o_orderkey 进行 HASH 分片,分片算法如下:

    在上述分片算法中,分片键是 o_orderkey,总的分片数量是 4(即把原来 1 份数据打散到 4 张表中),具体来讲,分片算法是将 o_orderkey 除以 4 进行取模操作。最终,将表orders 根据 HASH 算法进行分布式设计后的结果如下图所示:

    可以看到,对于订单号除以 4,余数为 0 的数据存放在分片 1 中,余数为 1 的数据存放在分片 2 中,余数为 2 的数据存放在分片 3 中,以此类推。这种基于 HASH 算法的分片设计才能较好地应用于大型互联网业务,真正做到分布式数据库架构弹性可扩展的设计要求。

    但是,表 orders 分区键选择 o_orderkey 是最好地选择吗?并不是。我们看一下库中的其他表,如表 customer、lineitem,这三张表应该是经常一起使用的,比如查询用户最近的订单明细。如果用 o_orderkey 作分区键,那么 lineitem 可以用 l_orderkey 作为分区键,但这时会发现表customer 并没有订单的相关信息,即无法使用订单作为分片键。如果表 customer 选择另一个字段作为分片键,那么业务数据无法做到单元化,也就是对于表customer、orders、lineitem,分片数据在同一数据库实例上。所以,如果要实现分片数据的单元化,最好的选择是把用户字段作为分区键,在表 customer 中就是将 c_custkey 作为分片键,表orders 中将 o_custkey 作为分片键,表 lineitem 中将 l_custkey 作为分片键:

    这样做的好处是:根据用户维度进行查询时,可以在单个分片上完成所有的操作,不用涉及跨分片的访问,如下面的 SQL:

    SELECT * FROM orders
    
    INNER JOIN lineitem ON o_orderkey = l_orderkey
    
    INNER JOIN customer ON o_custkey = c_custkey
    
    WHERE o_custkey = 1
    
    ORDER BY o_orderdate DESC LIMIT 10

    所以,分布式数据库架构设计的原则是:选择一个适合的分片键和分片算法,把数据打散,并且业务的绝大部分查询都是根据分片键进行访问

    那为什么互联网业务这么适合进行分布式架构的设计呢?因为互联网业务大部分是 To C 业务,分片键就是用户的 ID,业务的大部分访问都是根据用户 ID 进行查询,比如:

    • 查看某个用户下的微博/短视频;
    • 查看某个用户的商品信息/购买记录;
    • 查看某个用户自己的余额信息。

    学完分片键的选择后,接着就是规划分片,也就我们经常提到的分库分表。

    3.2 分库分表

    分片到底是什么呢?其实,前面说的分片本质是一张张表,而不是数据库实例,只是每个分片是在 MySQL 数据库实例中,严格来说:

    分片 = 实例 + 库 + 表 = ip@port:db_name:table_name

    对于前面的表orders,假设根据 HASH 算法进行分片,那么可以进行如下的分库分表设计:

    1. 每个分片的表名库名都一样,如库 tpch,表名 orders;
    2. 每个分片的库名不一样,表名一样,如库名 tpch01、tpch02、tpch03、tpch04,表名orders;
    3. 每个分片的表名不一样,库名一样,如库名 tpch,表名分别为 orders01、orders02、orders03、orders04;
    4. 每个分片的库名不一样,表名也不一样,如分片 1 的表在库名 tpch01下,表名为oders01;分片 2 的表名在库名 tpch02,表名为 orders02;分片 3 的表名在库名tpch03,表名为 orders03;分片 3 的表名在库名 tpch04,表名为 orders04。

    在这 4 种分库分表规则中,最推荐的是第 4 种,也是我们通常意义说的分库分表,这样做的好处有以下几点:

    • 不同分片的数据可以在同一 MySQL 数据库实例上,便于做容量的规划和后期的扩展;
    • 同一分片键的表都在同一库下,方便做整体数据的迁移和扩容。

    如果根据第 4 种标准的分库分表规范,那么分布式 MySQL 数据库的架构可以是这样:

     有没有发现,按上面这样的分布式设计,数据分片完成后,所有的库表依然是在同一个 MySQL实例上。牢记,分布式数据库并不一定要求有很多个实例,最基本的要求是将数据进行打散分片。接着,用户可以根据自己的需要,进行扩缩容,以此实现数据库性能和容量的伸缩性。这才是分布式数据库真正的魅力所在

    对于上述的分布式数据库架构,一开始我们将 4 个分片数据存储在一个 MySQL 实例上,但是如果遇到一些大促活动,可以对其进行扩容,比如把 4 个分片扩容到 4 个MySQL实例上:

    如果完成了大促活动,又可以对资源进行回收,将分片又都放到一台 MySQL 实例上,这就是对资源进行缩容。总的来说,对分布式数据库进行扩缩容在互联网公司是一件常见的操作,比如对阿里来说,每年下半年 7 月开始,他们就要进行双 11 活动的容量评估,然后根据评估结果规划数据库的扩容。

    3.3 数据库的扩缩容

    在 HASH 分片的例子中,我们把数据分片到了 4 个节点,然而在生产环境中,为了方便之后的扩缩容操作,推荐一开始就把分片的数量设置为不少于 1000 个。不用担心分片数量太多,因为分片 1 个还是 1000 个,管理方式都是一样的,但是 1000 个,意味着可以扩容到 1000 个实例上,对于一般业务来说,1000 个实例足够满足业务的需求了(BTW,网传阿里某核心业务的分布式数据库分片数量为 10000个)。如果到了 1000 个分片依然无法满足业务的需求,这时能不能拆成 2000 个分片呢?从理论上来说是可以的,但是这意味着需要对一张表中的数据进行逻辑拆分,这个工作非常复杂,通常不推荐。所以,一开始一定要设计足够多的分片。在实际工作中,我遇到很多次业务将分片数量从 32、64 拆成 256、512。每次这样的工作,都是扒一层皮,太不值得。所以,做好分布式数据库设计的工作有多重要!

    那么扩容在 MySQL 数据库中如何操作呢?其实,本质是搭建一个复制架构,然后通过设置过滤复制,仅回放分片所在的数据库就行,这个数据库配置在从服务器上大致进行如下配置:

    # 分片1从服务器配置
    
    replicate_do_db ="tpch01"

    所以在进行扩容时,首先根据下图的方式对扩容的分片进行过滤复制的配置:

    然后再找一个业务低峰期,将业务的请求转向新的分片,完成最终的扩容操作:

    至于缩容操作,本质就是扩容操作的逆操作

    3.4 分库分表的总结

    • 分布式数据库数据分片要先选择一个或多个字段作为分片键;
    • 分片键的要求是业务经常访问的字段,且业务之间的表大多能根据这个分片键进行单元化;
    • 如果选不出分片键,业务就无法进行分布式数据库的改造;
    • 选择完分片键后,就要选择分片算法,通常是 RANGE 或 HASH 算法;
    • 海量 OLTP 业务推荐使用 HASH 算法,强烈不推荐使用 RANGE 算法;
    • 分片键和分片算法选择完后,就要进行分库分表设计,推荐不同库名表名的设计,这样能方便后续对分片数据进行扩缩容;
    • 实际进行扩容时,可以使用过滤复制,仅复制需要的分片数据。

    四、分布式数据库索引设计

    在分布式数据库架构下,索引的设计也需要做调整,否则无法充分发挥分布式架构线性可扩展的优势

    4.1 主键选择

    对主键来说,要保证在所有分片中都唯一,它本质上就是一个全局唯一的索引。如果用大部分同学喜欢的自增作为主键,就会发现存在很大的问题。因为自增并不能在插入前就获得值,而是要通过填 NULL 值,然后再通过函数 last_insert_id()获得自增的值。所以,如果在每个分片上通过自增去实现主键,可能会出现同样的自增值存在于不同的分片上。

    比如,对于电商的订单表 orders,其表结构如下(分片键是o_custkey,表的主键是o_orderkey):

    CREATE TABLE `orders` (
      `O_ORDERKEY` int NOT NULL auto_increment,
      `O_CUSTKEY` int NOT NULL,
      `O_ORDERSTATUS` char(1) NOT NULL,
      `O_TOTALPRICE` decimal(15,2) NOT NULL,
      `O_ORDERDATE` date NOT NULL,
      `O_ORDERPRIORITY` char(15) NOT NULL,
      `O_CLERK` char(15) NOT NULL,
      `O_SHIPPRIORITY` int NOT NULL,
      `O_COMMENT` varchar(79) NOT NULL,
      PRIMARY KEY (`O_ORDERKEY`),
      KEY (`O_CUSTKEY`)
      ......
    ) ENGINE=InnoDB

    如果把 o_orderkey 设计成上图所示的自增,那么很可能 o_orderkey 同为 1 的记录在不同的分片出现,如下图所示:

    所以,在分布式数据库架构下,尽量不要用自增作为表的主键,这也是我们在第一模块“表结构设计”中强调过的:自增性能很差、安全性不高、不适用于分布式架构。讲到这儿,我们已经说明白了“自增主键”的所有问题,那么该如何设计主键呢?依然还是用全局唯一的键作为主键,比如 MySQL 自动生成的有序 UUID;业务生成的全局唯一键(比如发号器);或者是开源的 UUID 生成算法,比如雪花算法(但是存在时间回溯的问题)。总之,用有序的全局唯一替代自增,是这个时代数据库主键的主流设计标准,如果你还停留在用自增做主键,或许代表你已经落后于时代发展了。

    4.2 索引设计

    通过分片键可以把 SQL 查询路由到指定的分片,但是在现实的生产环境中,业务还要通过其他的索引访问表。还是以前面的表 orders 为例,如果业务还要根据 o_orderkey 字段进行查询,比如查询订单 ID 为 1 的订单详情:

    SELECT * FROM orders WHERE o_orderkey = 1

    我们可以看到,由于分片规则不是分片键,所以需要查询 4 个分片才能得到最终的结果,如果下面有 1000 个分片,那么就需要执行 1000 次这样的 SQL,这时性能就比较差了。

    但是,我们知道 o_orderkey 是主键,应该只有一条返回记录,也就是说,o_orderkey 只存在于一个分片中。这时,可以有以下两种设计:

    • 同一份数据,表 orders 根据 o_orderkey 为分片键,再做一个分库分表的实现;
    • 在索引中额外添加分片键的信息。

    这两种设计的本质都是通过冗余实现空间换时间的效果,否则就需要扫描所有的分片,当分片数据非常多,效率就会变得极差。而第一种做法通过对表进行冗余,对于 o_orderkey 的查询,只需要在 o_orderkey = 1 的分片中直接查询就行,效率最高,但是设计的缺点又在于冗余数据量太大。所以,改进的做法之一是实现一个索引表,表中只包含 o_orderkey 和分片键 o_custkey,如:

    CREATE TABLE idx_orderkey_custkey (
      o_orderkey INT
      o_custkey INT,
      PRIMARY KEY (o_orderkey)
    )

    如果这张索引表很大,也可以将其分库分表,但是它的分片键是 o_orderkey,如果这时再根据字段 o_orderkey 进行查询,可以进行类似二级索引的回表实现:先通过查询索引表得到记录 o_orderkey = 1 对应的分片键 o_custkey 的值,接着再根据 o_custkey 进行查询,最终定位到想要的数据,如:

    SELECT * FROM orders WHERE o_orderkey = 1
    
    *************************************************************************
    
    # step 1
    
    SELECT o_custkey FROM idx_orderkey_custkey 
    
    WHERE o_orderkey = 1
    
    # step 2
    
    SELECT * FROM orders 
    
    WHERE o_custkey = ? AND o_orderkey = 1

    这个例子是将一条 SQL 语句拆分成 2 条 SQL 语句,但是拆分后的 2 条 SQL 都可以通过分片键进行查询,这样能保证只需要在单个分片中完成查询操作。不论有多少个分片,也只需要查询 2个分片的信息,这样 SQL 的查询性能可以得到极大的提升。

    通过索引表的方式,虽然存储上较冗余全表容量小了很多,但是要根据另一个分片键进行数据的存储,依然显得不够优雅。因此,最优的设计,不是创建一个索引表,而是将分片键的信息保存在想要查询的列中,这样通过查询的列就能直接知道所在的分片信息。

    如果我们将订单表 orders 的主键设计为一个字符串,这个字符串中最后一部分包含分片键的信息,如:

    o_orderkey = string(o_orderkey + o_custkey)

    那么这时如果根据 o_orderkey 进行查询:

    SELECT * FROM Orders
    
    WHERE o_orderkey = '1000-1';

    由于字段 o_orderkey 的设计中直接包含了分片键信息,所以我们可以直接知道这个订单在分片1 中,直接查询分片 1 就行。同样地,在插入时,由于可以知道插入时 o_custkey 对应的值,所以只要在业务层做一次字符的拼接,然后再插入数据库就行了。这样的实现方式较冗余表和索引表的设计来说,效率更高,查询可以提前知道数据对应的分片信息,只需 1 次查询就能获取想要的结果。

    这样实现的缺点是,主键值会变大一些,存储也会相应变大。只要主键值是有序的,插入的性能就不会变差。而通过在主键值中保存分片信息,却可以大大提升后续的查询效率,这样空间换时间的设计,总体上看是非常值得的。当然,这里我们谈的设计都是针对于唯一索引的设计,如果是非唯一的二级索引查询,那么非常可惜,依然需要扫描所有的分片才能得到最终的结果,如:

    SELECT * FROM Orders
    
    WHERE o_orderate >= ? o_orderdate < ?

    因此,再次提醒你,分布式数据库架构设计的要求是业务的绝大部分请求能够根据分片键定位到 1 个分片上。如果业务大部分请求都需要扫描所有分片信息才能获得最终结果,那么就不适合进行分布式架构的改造或设计。

    最后,我们再来回顾下淘宝用户订单表的设计:

    上图是我的淘宝订单信息,可以看到,订单号的最后 6 位都是 308113,所以可以大概率推测出:

    • 淘宝订单表的分片键是用户 ID;
    • 淘宝订单表,订单表的主键包含用户 ID,也就是分片信息。这样通过订单号进行查询,可以获得分片信息,从而查询 1 个分片就能得到最终的结果。

    4.3 全局表设计

    在分布式数据库中,有时会有一些无法提供分片键的表,但这些表又非常小,一般用于保存一些全局信息,平时更新也较少,绝大多数场景仅用于查询操作。例如 tpch 库中的表 nation,用于存储国家信息,但是在我们前面的 SQL 关联查询中,又经常会使用到这张表,对于这种全局表,可以在每个分片中存储,这样就不用跨分片地进行查询了。如下面的设计:

    4.4 唯一索引设计

    唯一索引的设计,与主键一样,如果只是通过数据库表本身唯一约束创建的索引,则无法保证在所有分片中都是唯一的。所以,在分布式数据库中,唯一索引一样要通过类似主键的 UUID 的机制实现,用全局唯一去替代局部唯一,但实际上,即便是单机的 MySQL 数据库架构,我们也推荐使用全局唯一的设计。因为你不知道,什么时候,你的业务就会升级到全局唯一的要求了。

    4.5 分布式数据库索引总结

    • 分布式数据库主键设计使用有序 UUID,全局唯一;
    • 分布式数据库唯一索引设计使用 UUID 的全局唯一设计,避免局部索引导致的唯一问题;
    • 分布式数据库唯一索引若不是分片键,则可以在设计时保存分片信息,这样查询直接路由到一个分片即可;
    • 对于分布式数据库中的全局表,可以采用冗余机制,在每个分片上进行保存。这样能避免查询时跨分片的查询。

    五、分布式数据库架构选型

    学习了分布式数据库的分片设计、表结构设计、索引设计等,相信你已经有能力构建一个分布式数据库系统了。但现在数据分好了,索引也设计好了,但是如果访问这些数据和索引呢?

    访问分布式数据库有两种模式:

    • 业务直接根据分库分表访问 MySQL 数据库节点;
    • 根据中间件访问。

    5.1 分库分表直接访问

    在设计分片时,我们已经明确了每张表的分片键信息,所以业务或服务可以直接根据分片键对应的数据库信息,直接访问底层的 MySQL 数据节点,比如在代码里可以做类似的处理:

    void InsertOrders(String orderKey, int userKey...) {
    
      int shard_id = userKey % 4;
    
      if (shard_id == 0) {
    
        conn = MySQLConncetion('shard1',...);
    
        conn.query(...);
    
      } else if (shard_id == 1) {
    
        conn = MySQLConncetion('shard2',...);
    
        conn.query(...);   
    
      } else if (shard_id == 2) {
    
        conn = MySQLConncetion('shard3',...);
    
        conn.query(...);   
    
      } else if (shard_id == 3) {
    
        conn = MySQLConncetion('shard4',...);
    
        conn.query(...);   
    
      }
    
    }

    从这段代码中我们可以看到,在业务代码中会嵌入分库分表的路由逻辑,在业务层计算出对应分片的信息,然后访问数据库:

    • 这种处理方式的好处是与单实例数据库没有太大的不同,只是多了一次计算分片的操作,没有额外的开销,性能非常好(我听说支付宝的分布式数据库为了最求极致的性能,用的就是直接访问分片的方式)。
    • 这种处理逻辑的缺点是业务需要知道分片信息,感知分片的变化。对于上面的例子,如果分片 shard1 发生变化,又或者进行了扩容,业务就需要跟着修改。

    为了解决这个缺点,比较好的处理方式是使用名字服务,而不要直接通过 IP 访问分片。这样当分片发生切换,又或者扩容缩容时,业务也不需要进行很大的改动。

    又因为业务比较多,需要访问分布式数据库分片逻辑的地方也比较多。所以,可以把分片信息存储在缓存中,当业务启动时,自动加载分片信息。比如,在 Memcached 或 Redis 中保存如下的分片信息,key 可以是分库分表的表名,value通过 JSON 或字典的方式存放分片信息:

    {
      'key': 'orders',
      'shard_info' : {
        'shard_key' : 'o_custkey',
        'shard_count' : 4,
        'shard_host' : ['shard1.xxx.com','shard2.xxx.com','...'],
        ‘shard_table' : ['tpch00/orders01','tpch01/orders02','...'],
      }
    }

    如果要进行跨分片的访问,则需要业务自己处理相关逻辑。不过我们前面已经说过,分布式数据库设计要求单元化,绝大部分操作需要在一个分片中完成。如果不能,那么可能都不推荐分布数据库的改造。总之,分库分表的直接访问方式,要求业务控制一切有关分布式数据库的操作,需要明确每个分片的具体信息,做好全流程的把控。

    5.2 使用中间件技术

    另一种比较流行的分布式数据库访问方式是通过分布式数据库中间件。数据库中间件本身模拟成一个 MySQL 数据库,通信协议也都遵循 MySQL 协议:业务之前怎么访问MySQL数据库的,就如何访问MySQL分布式数据库中间件。

    这样做的优点是:业务不用关注分布式数据库中的分片信息,把它默认为一个单机数据库使用就好了。这种模式也是大部分同学认为分布式数据库该有的样子,如下面的图:

    可以看到,通过分布式 MySQL 中间件,用户只需要访问中间件就行,下面的数据路由、分布式事务的实现等操作全部交由中间件完成。所以,分布式数据库中间件变成一个非常关键的核心组件。业界比较知名的 MySQL 分布式数据库中间件产品有:ShardingShpere、DBLE、TDSQL 等。

    • ShardingSphere于 2020 年 4 月 16 日成为 Apache 软件基金会的顶级项目、社区熟度、功能支持较多,特别是对于分布式事务的支持,有多种选择(ShardingSphere 官网地址)。
    • DBLE 是由知名 MySQL 服务商爱可生公司开源的 MySQL 中间件产品,已用于四大行核心业务,完美支撑传统银行去 IOE,转型分布式架构的探索。除了中间件技术外,爱可生公司还有很多关于 MySQL 数据库、分布式数据库设计等方面的综合经验。
    • TDSQL MySQL 版(TDSQL for MySQL)是腾讯打造的一款分布式数据库产品,具备强一致高可用、全球部署架构、分布式水平扩展、高性能、企业级安全等特性,同时提供智能 DBA、自动化运营、监控告警等配套设施,为客户提供完整的分布式数据库解决方案。

    目前 TDSQL 已经为超过500+的政企和金融机构提供数据库的公有云及私有云服务,客户覆盖银行、保险、证券、互联网金融、计费、第三方支付、物联网、互联网+、政务等领域。TDSQL MySQL 版亦凭借其高质量的产品及服务,获得了多项国际和国家认证,得到了客户及行业的一致认可。

    你要注意,使用数据库中间件虽好,但其存在一个明显的缺点,即多了一层中间层的访问,单个事务的访问耗时会有上升,对于性能敏感的业务来说,需要有这方面的意识和考虑。重要的一点是,虽然使用分布式数据库中间件后,单个事务的耗时会有所上升,但整体的吞吐率是不变的,通过增大并发数,可以有效提升分布式数据库的整体性能。

    5.3 分布式数据库架构选型

    那么,选择业务直连分布式数据库?还是通过数据库中间件访问?这是一个架构选型要考虑的问题。根据我的经验来说,对于较小业务(高峰期每秒事务不超过 1000 的业务),选择通过数据库中间件访问分布式数据库是比较优的方式。

    • 因为这样的业务通常处于爬升期,满足业务的各项功能或许是业务的主要目标。通过分布式中间件屏蔽下面的分片信息,可以让开发同学专注于业务的开发。
    • 另一方面,通过使用中间件提供的分布式事务就能满足简单的跨分片交易,解决分布式数据库中最难的问题。
    • 但如果你的业务是一个海量互联网业务,中间件的瓶颈就会显现,单个事务的耗时会上升,低并发下,性能会有一定下降。而且中间件提供的 2PC 分布式事务性能就更不能满足业务的需求了。所以类似支付宝、阿里这样的业务,并没有使用分布式数据库中间件的架构,而是采用了业务直连的模式。

    很多同学或许会问,如果不用数据库中间件,怎么解决 JOIN 这些问题呢?业务层去实现还是很麻烦的。的确,中间件可以完成这部分的功能。但如果真是数据量比较大,跨分片的场景,相信我,中间件也不能满足你的要求。所以,使用分布式数据库架构是一种折中,你要学会放弃很多,从而才能得到更多。

    六、全链路的条带化设计

    学习了分布式数据库架构的基本设计,完成了数据分片、表结构、索引的设计,相信学完这几讲之后,你已经基本了解分布式数据库了,也能够设计出一个分布式数据库的基础架构。但这些远远不够,因为当我们提到分布式架构时,除了数据库要完成分布式架构的改造,业务层也要完成分布式架构的改造,最终完成条带化的设计。

    6.1 什么是条带化

    条带化是存储的一种技术,将磁盘进行条带化后,可以把连续的数据分割成相同大小的数据块,简单的说,条带化就是把每段数据分别写入阵列中不同磁盘上的方法。可以看到,条带化的本质是通过将数据打散到多个磁盘,从而提升存储的整体性能,这与分布式数据库的分片理念是不是非常类似呢?下图显示了 RAID0 的条带化存储:

    从图中可以看到,进行 RAID 条带化后,数据存放在了三块磁盘上,分别是磁盘 1、磁盘 2、磁盘 3,存储的数据也进行了打散,分别存储在了条带 1、条带 2、条带 3 上。这样一来,当访问某一个数据的时候,可以并行地从 3 个磁盘上取出数据,写入也可以同时写入 3 个磁盘,提升了存储的性能。

    6.2 全链路的条带化设计

    分布式数据库的本质是:将数据根据某个或几个列(称之为“分片键”),然后依据预先设定的算法(分片算法)进行打散,形成一个个的分片。更重要的是,分布式数据库中的表,要能选出一个统一的分片键,即大部分表都能根据这个分片键打散数据,这样当后续业务进行访问数据时,可以在一个分片中完成单元化的闭环操作,不用涉及跨分片的访问。下图显示了对于 tpch 分布式架构改造后的分片效果:

    从图中我们可以看到,这与我们之前所提倡的条带化的思想比较类似,即数据打散,性能得到提升,对于分布式数据库来说,分片越多,性能上限也就越高。

    但是,这只是对数据库层做了条带化,没有站在全链路的角度上进行条带化设计。我们来看一个例子,假设是电商中比较重要的订单服务,并且对表 orders 进行了分布式的条带化设计:

    可以看到,订单服务可以根据字段 o_custkey 访问不同分片的数据,这也是大部分业务会进行的设计(由于服务层通常是无状态的,因此这里不考虑高可用的情况)。但是,这样的设计不符合全链路的条带化设计思想。全链路的设计思想,要将上层服务也作为条带的一部分进行处理,也就是说,订单服务也要跟着分片进行分布式架构的改造。所以,如果进行全链路的条带化设计,那么上面的订单服务应该设计成:

    可以看到,如果要进行分布式的条带化设计时,上层业务服务也需要进行相应的分布式改造,将1个“大”订单服务层也拆分成多个“小”订单服务,其中每个订单服务访问自己分片的数据。

    这样设计的好处在于:

    • 安全性更好,每个服务可以校验访问用户是否本分片数据;
    • 上层服务跟着数据分片进行条带化部署,业务性能更好;
    • 上层服务跟着数据分片进行条带化部署,可用性更好;

    第1点通常比较好理解,但是 2、3点 就不怎么好理解了。为什么性能也会更好呢?这里请你考虑一下业务的部署情况,也就是,经常听说的多活架构设计。

    6.3 多活架构

    对于高可用的架构设计要做到跨机房部署,实现的方式是无损半同复制,以及最新的 MySQL Group Rreplication 技术。数据库实例通过三园区进行部署。这样,当一个机房发生宕机,可以快速切换到另一个机房。我们再来回顾下三园区的架构设计:

    图中显示了通过无损半同步复制方式进行的三园区高可用架构设计,从而实现同城跨机房的切换能力。但这只是单实例 MySQL 数据库架构,如果到分布式架构呢?所有分片都是在一个机房吗?

    如果所有分片都在一个机房,你会发现,这时机房 2、机房3 中的数据库都只是从机,只能进行读取操作,而无法实现写入操作,这就是我们说的单活架构。与单活架构不同,多活架构是指不同地理位置上的系统,都能够提供业务读/写服务。这里的“活”是指实时提供读/写服务的意思,而不仅仅只是读服务。多活架构主要是为了提升系统的容灾能力,提高系统的可用性,保障业务持续可用。

    要实现多活架构,首先要进行分布式数据库的改造,然后是将不同数据分片的主服务器放到不同机房,最后是实现业务条带化的部署。如下面的这张图:

    可以看到,对于上一节的订单服务和订单数据分片,通过将其部署在不同的机房,使得订单服务1 部署在机房 1,可以对分片1进行读写;订单服务 2 部署在机房 1,可以对分片 2 进行读写;订单服务 3 部署在机房 3,可以对分片 3 进行读写。这样每个机房都可以有写入流量,每个机房都是“活”的,这就是多活架构设计。若一个机房发生宕机,如机房 1 宕机,则切换到另一个机房,上层服务和数据库跟着一起切换,切换后上层服务和数据库依然还是在一个机房,访问不用跨机房访问,依然能提供最好的性能和可用性保障。

    七、分布式事务解决方案

    学习了分布式数据库中数据的分片设计、索引设计、中间件选型,全链路的条带化设计分布式数据库中最令人头疼的问题,那就是分布式事务。详细的分布式事务解决方案在:高并发项目设计——分布式事务解决方案_庄小焱-CSDN博客

    7.1 分布式事务概念

    事务的概念相信你已经非常熟悉了,事务就是要满足 ACID 的特性,总结来说。

    • A(Atomicity) 原子性:事务内的操作,要么都做,要么都不做;
    • C(Consistency) 一致性:事务开始之前和事务结束以后,数据的完整性没有被破坏;如唯一性约束,外键约束等;
    • I(Isolation)隔离性:一个事务所做的操作对另一个事务不可见,好似是串行执行;
    • D(Durability)持久性:事务提交后,数据的修改是永久的。即使发生宕机,数据也能修复;

    特别需要注意的是,当前数据库的默认事务隔离级别都没有达到隔离性的要求,MySQL、Oracle、PostgreSQL等关系型数据库都是如此。大多数数据库事务隔离级别都默认设置为 READ-COMMITTED,这种事务隔离级别没有解决可重复度和幻读问题(除了Mysql以外,mysql已经在可重复读中解决了数据的幻读问题)。

    但由于在绝大部分业务中,都不会遇到这两种情况。若要达到完全隔离性的要求,性能往往又会比较低。因此在性能和绝对的隔离性前,大多数关系型数据库选择了一种折中。那什么是分布式事务呢?简单来说,就是要在分布式数据库的架构下实现事务的ACID特性。

    分布式数据库架构设计的一个原则,即大部分的操作要能单元化。即在一个分片中完成。如对用户订单明细的查询,由于分片键都是客户ID,因此可以在一个分片中完成。那么他能满足事务的ACID特性。

    但是,如果是下面的一个电商核心业务逻辑,那就无法实现在一个分片中完成,即用户购买商品,其大致逻辑如下所示:

    START TRANSATION;
    
    INSERT INTO orders VALUES (......);
    
    INSERT INTO lineitem VALUES (......);
    
    UPDATE STOCK SET COUNT = COUNT - 1 WHERE sku_id = ?
    
    COMMIT;

    可以看到,在分布式数据库架构下,表orders、linitem的分片键是用户ID。但是表stock是库存品,是商品维度的数据,没有用户ID的信息。因此stock的分片规则肯定与表orders和lineitem不同。所以,上述的事务操作大部分情况下并不能在一个分片中完成单元化,因此就是一个分布式事务,它要求用户维度的表 orders、lineitem 和商品维度的表 stock 的变更,要么都完成,要么都完成不了。常见的分布式事务的实现就是通过 2PC(two phase commit 两阶段提交)实现,接着我们来看下 2PC。

    7.2 2PC的分布式事务实现

    2PC 是数据库层面实现分布式事务的一种强一致性实现。在 2PC 中,引入事务协调者的角色用于协调管理各参与者(也可称之为各本地资源)的提交和回滚。而 2PC 所谓的两阶段是指parepare(准备)阶段和 commit(提交)两个阶段。在 2PC 的实现中,参与者就是分钟的 MySQL 数据库实例,那事务协调者是谁呢?这取决于分布式数据库的架构。若分布式数据库的架构采用业务通过分库分表规则直连分片的话,那么事务协调者就是业务程序本身。如下图所示:

     若采用数据库中间件的模式,那么事务协调者就是数据库中间件。如下图所示:

    从上图可以发现,使用分布式数据库中间件后,可以对上层服务屏蔽分布式事务的实现,服务不需要关心下层的事务是本地事务还是分布式事务,就好像是单机事务本身一样。2PC 要求第一段 prepare 的操作都成功,那么分布式事务才能提交,这样最终能够实现持久化,2PC 的代码逻辑如下所示:

    上面就是 2PC 的 Java 代码实现,可以看到只有2个参与者第一阶段 prepare 都成功,那么分布式事务才能提交。但是 2PC 的一个难点在于 prepare 都成功了,但是在进行第二阶段 commit 的时候,其中一个节点挂了。这时挂掉的那个节点在恢复后,或进行主从切换后,节点上之前执行成功的prepare 事务需要人为的接入处理,这个事务就称之为悬挂事务。

    用户可以通过命令 XA_RECOVER 查看节点上事务有悬挂事务:

    如果有悬挂事务,则这个事务持有的锁资源都是没有释放的。可以通过命令SHOW ENGINE INNODB STATUS 进行查看:

    从上图可以看到,事务 5136 处于 PREPARE状态,已经有 218 秒了,这就是一个悬挂事务,并且这个事务只有了两个行锁对象。

    可以通过命令 XA RECOVER 人工的进行提交:

    同学们应该都了了分布式事务的 2PC 实现和使用方法。它是一种由数据库层实现强一致事务解决方案。其优点是使用简单,当前大部分的语言都支持 2PC 的实现。若使用中间件,业务完全就不用关心事务是不是分布式的。

    然而,他的缺点是,事务的提交开销变大了,从 1 次 COMMIT 变成了两次 PREPARE 和COMMIT。而对于海量的互联网业务来说,2PC 的性能是无法接受。因此,这就有了业务级的分布式事务实现,即柔性事务。

    7.3 柔性事务

    柔性事务是指分布式事务由业务层实现,通过最终一致性完成分布式事务的工作。可以说,通过牺牲了一定的一致性,达到了分布式事务的性能要求。业界常见的柔性事务有 TCC、SAGA、SEATA 这样的框架、也可以通过消息表实现。它们实现原理本身就是通过补偿机制,实现最终的一致性。柔性事务的难点就在于对于错误逻辑的处理。

    为了讲述简单,这里用消息表作为柔性事务的案例分享。对于上述电商的核心电商下单逻辑,用消息表就拆分为 3 个阶段:

    阶段1:
    
    START TRANSACTION;
    
    # 订单号,订单状态
    
    INSERT INTO orders VALUES (...) 
    
    INSERT INTO lineitem VALUES (...)
    
    COMMIT;
    
    **********************************************************************
    
    阶段2:
    
    START TRANSACTION;
    
    UPDATE stock SET count = count -1 WHERE sku_id = ?
    
    # o_orderkey是消息表中的主键,具有唯一约束
    
    INSERT INTO stock_message VALUES (o_orderkey, ... )  
    
    COMMIT;
    
    **********************************************************************
    
    阶段3:
    
    UPDATE orders SET o_orderststus = 'F' WHERE o_orderkey = ?
    • 上面的柔性事务中,订单表中的列 o_orderstatus 用于记录柔性事务是否完成,初始状态都是未完成。表 stock_message 记录对应订单是否已经扣除过相应的库存。若阶段 2 完成,则柔性事务必须完成。阶段 3 就是将柔性事务设置为完成,最终一致性的确定。
    • 接着我们来下,若阶段 2 执行失败,即执行过程中节点发生了宕机。则后台的补偿逻辑回去扫描订单表中 o_orderstatus 为未完成的超时订单有哪些,然后看一下是否在对应的表stock_message 有记录,若有,则执行阶段 3。若无,可选择告知用户下单失败。
    • 若阶段 3 执行失败,处理逻辑与阶段 2 基本一致,只是这时 2 肯定是完成的,只需要接着执行阶段 3 即可。

    所以,这里的补偿逻辑程序就是实时/定期扫描超时订单,通过消息表判断这个柔性事务是继续执行还是执行失败,执行失败又要做哪些业务处理。上面介绍的框架实现的柔性事务原理大致如此,只不过对于补偿的逻辑处理有些不同,又或者使用上更为通用一些。对于海量的互联网业务来说,柔性事务性能更好,因此支付宝、淘宝等互联网业务都是使用柔性事务完成分布式事务的实现。

    博文参考

    展开全文
  • mixed_gauge 一个简单而健壮的ActiveRecord扩展,用于数据库分片。 mixed_gauge提供具有哈希槽和重新分片支持的分片管理。 它使您能够通过类似KVS的界面对单个节点执行高效的查询。 您甚至可以并行使用ActiveRecord...
  • 一、分区、分表、库的理解 1、什么是分区、分表、库 分区 就是把一张表的数据分成N个区块,在逻辑上看最终只是一张表,但底层是由N个物理区块组成的 分表 就是把一张表按一定的规则...单库单表是最常见的数据库设计

    一、分区、分表、分库的理解

    1、什么是分区、分表、分库

    分区
    就是把一张表的数据分成N个区块,在逻辑上看最终只是一张表,但底层是由N个物理区块组成的

    分表
    就是把一张表按一定的规则分解成N个具有独立存储空间的实体表。系统读写时需要根据定义好的规则得到对应的字表明,然后操作它。

    分库
    一旦分表,一个库中的表会越来越多
    将整个数据库比作图书馆,一张表就是一本书。当要在一本书中查找某项内容时,如果不分章节,查找的效率将会下降。而同理,在数据库中就是分区。

    二、数据存储的演进

    单库单表
    单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到。

    单库多表
    随着用户数量的增加,user表的数据量会越来越大,当数据量达到一定程度的时候对user表的查询会渐渐的变慢,从而影响整个DB的性能。如果使用mysql, 还有一个更严重的问题是,当需要添加一列的时候,mysql会锁表,期间所有的读写操作只能等待。

    可以通过某种方式将user进行水平的切分,产生两个表结构完全一样的user_0000,user_0001等表,user_0000 + user_0001 + …的数据刚好是一份完整的数据。

    多库多表
    随着数据量增加也许单台DB的存储空间不够,随着查询量的增加单台数据库服务器已经没办法支撑。这个时候可以再对数据库进行水平拆分。

    三、分区

    1、分区的概念

    数据分区是一种物理数据库的设计技术,它的目的是为了在特定的SQL操作中减少数据读写的总量以缩减响应时间。
    分区并不是生成新的数据表,而是将表的数据均衡分摊到不同的硬盘,系统或是不同服务器存储介子中,实际上还是一张表。另外,分区可以做到将表的数据均衡到不同的地方,提高数据检索的效率,降低数据库的频繁IO压力值,分区的优点如下:

    • 相对于单个文件系统或是硬盘,分区可以存储更多的数据;
    • 数据管理比较方便,比如要清理或废弃某年的数据,就可以直接删除该日期的分区数据即可;
    • 精准定位分区查询数据,不需要全表扫描查询,大大提高数据检索效率;
    • 可跨多个分区磁盘查询,来提高查询的吞吐量;
    • 在涉及聚合函数查询时,可以很容易进行数据的合并;

    2、水平分区

    这种形式分区是对表的行进行分区,通过这样的方式不同分组里面的物理列分割的数据集得以组合,从而进行个体分割(单分区)或集体分割(1个或多个分区)。所有在表中定义的列在每个数据集中都能找到,所以表的特性依然得以保持。
    举个简单例子:一个包含十年发票记录的表可以被分区为十个不同的分区,每个分区包含的是其中一年的记录。(朋奕注:这里具体使用的分区方式我们后面再说,可以先说一点,一定要通过某个属性列来分割,譬如这里使用的列就是年份)

    3、垂直分区

    这种分区方式一般来说是通过对表的垂直划分来减少目标表的宽度,使某些特定的列被划分到特定的分区,每个分区都包含了其中的列所对应的行。
    举个简单例子:一个包含了大text 和B L OB 列的表,这些text和BLOB列又不经常被访问,这时候就要把这些不经常使用的text和BLOB了划分到另一个分区,在保证它们数据相关性的同时还能提高访问速度。
    在数据库供应商开始在他们的数据库引擎中建立分区(主要是水平分区)时,DBA和建模者必须设计好表的物理分区结构,不要保存冗余的数据(不同表中同时都包含父表中的数据)或相互联结成一个逻辑父对象(通常是视图)。这种做法会使水平分区的大部分功能失效,有时候也会对垂直分区产生影响。

    四、分表

    什么时候考虑分表?

    • 一张表的查询速度已经慢到影响使用的时候。
    • sql经过优化
    • 数据量大
    • 当频繁插入或者联合查询时,速度变慢

    分表解决的问题

    • 分表后,单表的并发能力提高了,磁盘I/O性能也提高了,写操作效率提高了
    • 查询一次的时间短了
    • 数据分布在不同的文件,磁盘I/O性能提高
    • 读写锁影响的数据量变小
    • 插入数据库需要重新建立索引的数据减少

    分表的实现方式(复杂)
    需要业务系统配合迁移升级,工作量较大

    常见分区分表的规则策略(类似)

    • Range(范围)
    • Hash(哈希)
    • 按照时间拆分
    • Hash之后按照分表个数取模
    • 在认证库中保存数据库配置,就是建立一个DB,这个DB单独保存user_id到DB的映射关系

    五、分库

    什么时候考虑使用分库?

    • 单台DB的存储空间不够
    • 随着查询量的增加单台数据库服务器已经没办法支撑

    分库解决的问题
    其主要目的是为突破单节点数据库服务器的 I/O 能力限制,解决数据库扩展性问题。

    垂直拆分
    将系统中不存在关联关系或者需要join的表可以放在不同的数据库不同的服务器中。
    按照业务垂直划分。比如:可以按照业务分为资金、会员、订单三个数据库。
    需要解决的问题:跨数据库的事务、jion查询等问题。

    水平拆分
    例如,大部分的站点。数据都是和用户有关,那么可以根据用户,将数据按照用户水平拆分。
    按照规则划分,一般水平分库是在垂直分库之后的。比如每天处理的订单数量是海量的,可以按照一定的规则水平划分。需要解决的问题:数据路由、组装。

    读写分离
    对于时效性不高的数据,可以通过读写分离缓解数据库压力。需要解决的问题:在业务上区分哪些业务上是允许一定时间延迟的,以及数据同步问题。

    六、分片

    分片模式是什么?
    数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。

    (1)一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切分可以称之为数据的垂直(纵向)切分

    (2)另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。

    分片相关的概念

    (1)逻辑库(schema) :
    通常对实际应用来说,并不需要知道中间件的存在,业务开发人员只需要知道数据库的概念,所以数据库中间件可以被看做是一个或多个数据库集群构成的逻辑库。

    (2)逻辑表(table):
    既然有逻辑库,那么就会有逻辑表,分布式数据库中,对应用来说,读写数据的表就是逻辑表。逻辑表,可以是数据切分后,分布在一个或多个分片库中,也可以不做数据切分,不分片,只有一个表构成。

    (3)分片表:
    是指那些原有的很大数据的表,需要切分到多个数据库的表,这样,每个分片都有一部分数据,所有分片构成了完整的数据。 总而言之就是需要进行分片的表。

    (4)非分片表:
    一个数据库中并不是所有的表都很大,某些表是可以不用进行切分的,非分片是相对分片表来说的,就是那些不需要进行数据切分的表。

    (5)分片节点(dataNode)
    数据切分后,一个大表被分到不同的分片数据库上面,每个表分片所在的数据库就是分片节点(dataNode)。

    (6)节点主机(dataHost)
    数据切分后,每个分片节点(dataNode)不一定都会独占一台机器,同一机器上面可以有多个分片数据库,这样一个或多个分片节点(dataNode)所在的机器就是节点主机(dataHost),为了规避单节点主机并发数限制,尽量将读写压力高的分片节点(dataNode)均衡的放在不同的节点主机(dataHost)。

    (7)分片规则(rule)
    前面讲了数据切分,一个大表被分成若干个分片表,就需要一定的规则,这样按照某种业务规则把数据分到某个分片的规则就是分片规则,数据切分选择合适的分片规则非常重要,将极大的避免后续数据处理的难度。

    数据分片
    在分布式存储系统中,数据需要分散存储在多台设备上,数据分片(Sharding)就是用来确定数据在多台存储设备上分布的技术。数据分片要达到三个目的:

    1.分布均匀,即每台设备上的数据量要尽可能相近;

    2.负载均衡,即每台设备上的请求量要尽可能相近;

    3.扩缩容时产生的数据迁移尽可能少。

    数据分片方法
    数据分片一般都是使用Key或Key的哈希值来计算Key的分布,常见的几种数据分片的方法如下:

    1.划分号段。这种一般适用于Key为整型的情况,每台设备上存放相同大小的号段区间,如把Key为[1, 10000]的数据放在第一台设备上,把Key为[10001, 20000]的数据放在第二台设备上,依次类推。这种方法实现很简单,扩容也比较方便,成倍增加设备即可,如原来有N台设备,再新增N台设备来扩容,把每台老设备上一半的数据迁移到新设备上,原来号段为[1, 10000]的设备,扩容后只保留号段[1, 5000]的数据,把号段为[5001, 10000]的数据迁移到一台新增的设备上。此方法的缺点是数据可能分布不均匀,如小号段数据量可能比大号段的数据量要大,同样的各个号段的热度也可能不一样,导致各个设备的负载不均衡;并且扩容也不够灵活,只能成倍地增加设备。

    2.取模。这种方法先计算Key的哈希值,再对设备数量取模(整型的Key也可直接用Key取模),假设有N台设备,编号为0~N-1,通过Hash(Key)%N就可以确定数据所在的设备编号。这种方法实现也非常简单,数据分布和负载也会比较均匀,可以新增任何数量的设备来扩容。主要的问题是扩容的时候,会产生大量的数据迁移,比如从N台设备扩容到N+1台,绝大部分的数据都要在设备间进行迁移。

    3.检索表。在检索表中存储Key和设备的映射关系,通过查找检索表就可以确定数据分布,这里的检索表也可以比较灵活,可以对每个Key都存储映射关系,也可结合号段划分等方法来减小检索表的容量。这样可以做到数据均匀分布、负载均衡和扩缩容数据迁移量少。缺点是需要存储检索表的空间可能比较大,并且为了保证扩缩容引起的数据迁移量比较少,确定映射关系的算法也比较复杂。

    4.一致性哈希。一致性哈希算法(Consistent Hashing)在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot Spot)问题,该方法的详细介绍参考此处 http://blog.csdn.net/sparkliang/article/details/5279393。一致性哈希的算法简单而巧妙,很容易做到数据均分布,其单调性也保证了扩缩容的数据迁移是比较少的。
    通过上面的对比,在这个系统选择一致性哈希的方法来进行数据分片。

    展开全文
  • 数据库分片规则

    2020-07-20 13:30:35
    但是我们毕竟操作的不是 MySQL ,而是 MyCat ,MyCat 中的数据库并不真正存储数据,数据还是存储在 MySQL 中,因此,我们可以将 MyCat 看作是一个或者多个数据库集群构成的逻辑库 逻辑表 逻辑表又有几种不同的划分:...
  • 分布式数据库能实现高安全、高性能、高可用等特征,当然也带来了高成本(固定成本及运营成本),我们通过MongoDB及MySQLCluster从实现上来分析其中的设计思路,用以抽象我们在设计数据库时,可以引用的部分设计方法...
  • 数据库分片以及schema概念

    千次阅读 2020-04-18 21:46:59
    总结一下,总体来看,分片之路任重道远,需要投入大量人力物力去开发,而不分片又会导致流量过大瓶颈限制而造成的服务不可用,各大厂商为了合理设计数据库引入schema等一系列概念去做内层设计,外层只暴露sql指令给...
  • EfKwdFXZxg 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。...全新的界面设计 ...
  • 多租户SaaS平台的数据库设计方案

    千次阅读 2021-07-05 12:04:21
    多租户的数据库方案分析 目前基于多租户的数据库设计方案通常有如下三种: 独立数据库 共享数据库、独立 Schema 共享数据库、共享数据表 1) 独立数据库 独立数据库:每个租户一个数据库。 **优点:**为不同的租户...
  • 为了解决单臂式高速贴片机的元件数据库数据量大,数据库设计不合理会影响贴片效率的问题,通过对元件数据的分类表进行多层封装,实现从Access数据库中快速获取数据。基于贴片机元件数据库的框架,定义元件数据模块与...
  • 文章目录分库分表解决方案和数据库分片中常见的问题及其解决方案1、分库分表形式2、分库分表解决方案3、分库分表中存在的问题4、总结 分库分表解决方案和数据库分片中常见的问题及其解决方案 1、分库分表形式 水平...
  • 1. 什么是分片或者数据分区 数据分片(也称为数据分区)就是把一个巨大的数据集分为多个小分区的过程,这些小分区位于不同的机器上。...原来的数据库的大部分数据都分布在各个分片上,原来的数据库的表的每行数据...
  • 文章重点描述: MySQL数据库的架构选型和容量规划 MySQL数据库的服务器配置选型和部署规划 MySQL数据库的服务器处理能力和测试验证方法 分布式事务数据库的数据分片设计和优化 分布式事务数据库的行业案例剖析
  • 一、背景 随着零售门店数量的增长,库存表,优惠劵表,...支持全局表,数据自动分片到多个节点,用于高效表关联查询 支持独有的基于E-R 关系的分片策略,实现了高效的表关联查询 三、架构设计改造 目前生产数据库架构
  • 数据库分片简单认知

    千次阅读 2018-10-17 17:25:47
    一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分;另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)...
  • MySQL数据库分片技术调研

    千次阅读 2015-08-29 13:04:46
    将这段时间了解的MySQL分片技术和主从复制只是整理一下画了思维导图记录一下,希望能给需要的人一些帮助P.S.:个人整理,可能会有错误之处,还望指出~要解决的问题1、海量数据的操作超出单表、单库的最大限制2、访问...
  • 华为开源数据库openGauss

    万次阅读 2022-06-13 12:15:23
    openGauss是一款全面友好开放,携手伙伴共同打造的企业级开源关系型数据库。openGauss提供面向多核架构的极致性能、全链路的业务、数据安全、基于AI的调优和高效运维的能力。openGaus深度融合华为在数据库领域多年的...
  • 浅谈数据库分片技术

    千次阅读 2018-03-12 18:34:50
    把所有这些应用碎片分入不同的数据库实例是你首先应该考虑的,而不是直接考虑分片。客户定制系统经常有不同数据集的应用,所以这个分法很容易实现。 复制:许多应用都是“读操作”的压力大,而扩展读操作性能要比...
  • 数据库分区分片(Shards)技术概览

    万次阅读 2018-01-05 14:23:59
    Sharding的基本思想就要把一个数据库切分成多个部分放到不同的数据库 (server)上,从而缓解单一数据库的性能问题。不太严格的讲,对于海量数据的数据库,如果是因为表多而数据多,这时候适合使用垂直切分,即把关系...
  • 数据分片的原则和经验

    千次阅读 2021-11-16 10:49:11
    本文提供了一些数据分片的一些原则和经验,遵循这些提示,有助于确保数据正确的分片,而不是阻碍你的应用程序的可扩展性。 新的 SaaS 初创公司很少考虑如何扩展他们的应用程序。当然,他们会设想有一天他们会需要...
  • 分布式数据库设计系列将分为四个大的部分。将从以下四方面让大家对分布式数据库设计和使用有深入的理解。 模块一,分布式数据历史演变及其核心原理。从历史背景出发,讲解了分布式数据库要解决的问题、应用场景,...
  • 数据库主键的设计

    千次阅读 2022-02-11 13:58:04
    一、数据库主键的设计原则 主键和外键是把多个表组织为一个有效的关系数据库的粘合剂。主键和外键的设计对物理数据库的性能和可用性都有着决定性的影响。主键和外键的结构是将数据库模式从理论上的逻辑设计转换为...
  • mysql分片

    千次阅读 2021-01-21 00:14:40
    mysql向外扩展(横向扩展或者水平扩展)策略主要有三方面:复制、拆分、数据分片; 水平扩展的最简单的方式就是通过复制将数据分发到多个服务器上,然后将备库用于读查询。复制技术用于以读为主的服务效果最好;但是当...
  • 数据库数据库设计(需求,设计,运行,维护)

    万次阅读 多人点赞 2020-11-16 16:47:49
    1,数据库设计概述 1.1,数据库设计的基本概念 数据库设计是指对于一个给定的应用环境,构造(设计)优化的数据库逻辑模式和物理结构,并据此建立数据库及其应用系统,使之能够有效地存储和管理数据,满足各种...
  • 分片技术的由来 关系型数据库本身比较容易成为系统性能瓶颈,单机存储容量、连接数、处理能力等都很有限,数据库本身的“有状态性”导致了它并不像Web和应用服务器那么容易扩展。在互联网行业海量数据和高并发访问...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 95,413
精华内容 38,165
热门标签
关键字:

数据库分片如何设计