精华内容
下载资源
问答
  • 如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点...

    数据库分片(Sharding):分表+分库+分片+分区

    1数据切分:

    数据库分布式核心内容无非就是数据切分(Sharding)

    将一个数据库比喻成一个大任务,将这任务分散给不同的人去执行,那么每一个执行的人就称为数据库的碎片(DatabaseShard)。将整个数据库拆散成多份的过程就叫做sharding,中文叫做分片技术。

    Sharding的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上,从而缓解单一数据库的性能问题。

    数据切分根据其切分类型,可以分为两种方式:垂直(纵向)切分和水平(横向)切分

    可以这么去理解:
    1.如果是因为表多而数据多,这时候适合使用垂直切分,即把关系紧密(比如同一模块)的表切分出来放在一个server上
    2.如果表并不多,但每张表的数据非常多,这时候适合水平切分,即把表的数据按某种规则(比如按ID散列)切分到多个数据库(server)上
    3.现实中更多是这两种情况混杂在一起,这时候需要根据实际情况做出选择,也可能会综合使用垂直与水平切分,从而将原有数据库切分成类似矩阵一样可以无限扩充的数据库(server)阵列

    如下为综合使用垂直与水平切分:
    在这里插入图片描述

    1.1垂直切分(纵向)

    • 垂直切分常见有垂直分库垂直分表

    1.1.1.垂直分库(对象是整个表)
    就是根据业务耦合性,将关联度不同的表存储在不同的数据库。做法与大系统拆分为多个小系统类似,按业务分类进行独立划分。与"微服务治理"的做法相似,每个微服务使用单独的一个数据库

    在这里插入图片描述
    将不同模块的数据表分库存储,模块间不相互关联查询。如果有,就必须通过数据冗余或营业层二次加工来解决。这种方法业务和数据结构最清晰,但若不能杜绝跨库关联查询,则宣告此路不通。

    1.1.2.垂直分表(对象是列/字段)
    是基于数据库中的"列"进行,某个表字段较多,可以新建一张扩展表,将不经常用或字段长度较大的字段拆分出去到扩展表中。在字段很多的情况下(例如一个大表有100多个字段),通过"大表拆小表",更便于开发与维护,也能避免跨页问题,MySQL底层是通过数据页存储的,一条记录占用空间过大会导致跨页,造成额外的性能开销。另外数据库以行为单位将数据加载到内存中,这样表中字段长度较短且访问频率较高,内存能加载更多的数据,命中率更高,减少了磁盘IO,从而提升了数据库性能。
    在这里插入图片描述
    垂直切分的优点:

    • 解决业务系统层面的耦合,业务清晰
    • 与微服务的治理类似,也能对不同业务的数据进行分级管理、维护、监控、扩展等
    • 高并发场景下,垂直切分一定程度的提升IO、数据库连接数、单机硬件资源的瓶颈

    缺点:

    • 部分表无法join,只能通过接口聚合方式解决,提升了开发的复杂度
    • 分布式事务处理复杂
    • 依然存在单表数据量过大的问题(需要水平切分)

    1.2水平切分(横向)

    从1.1的垂直切分缺点可以看到,当一个应用难以再细粒度的垂直切分,或切分后数据量行数巨大,存在单库读写、存储性能瓶颈,水平切分这时候就显得格外重要

    根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果

    • 水平切分分为库内分表分库分表
      在这里插入图片描述

    相对纵向切分这一将表分类的作法,此法是按表内某个字段的某种规则来将数据分散存储于不同的数据库(或不同的表),也就是按数据行来切分数据

    水平切分后同一张表会出现在多个数据库/表中,每个库/表的内容不同

    水平切分的优点:

    • 不存在单库数据量过大、高并发的性能瓶颈,提升系统稳定性和负载能力
    • 应用端改造较小,不需要拆分业务模块

    缺点:

    • 跨分片的事务一致性难以保证
    • 跨库的join关联查询性能较差
    • 数据多次扩展难度和维护量极大

    那么水平切分过程,一定会遇到:怎么分?的问题,如下:

    1.2.1.根据数值范围:

    • 按照时间区间或ID区间来切分

    优点:

    • 单表大小可控
    • 天然便于水平扩展,后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数据进行迁移
    • 使用分片字段进行范围查找时,连续分片可快速定位分片进行快速查询,有效避免跨分片查询的问题。

    缺点:

    • 热点数据成为性能瓶颈。连续分片可能存在数据热点,例如按时间字段分片,有些分片存储最近时间段内的数据,可能会被频繁的读写,而有些分片存储的历史数据,则很少被查询

    1.2.2.根据数值取模:

    • 一般采用hash取模mod的切分方式

    举例子:假设有用户表user,将其分成3个表user0,user1,user2.路由规则是对3取模,当uid=1时,对应到的是user1,uid=2时,对应的是user2

    在这里插入图片描述
    优点:

    • 数据分片相对比较均匀,不容易出现热点和并发访问的瓶颈

    缺点:

    • 后期分片集群扩容时,需要迁移旧的数据(使用一致性hash算法能较好的避免这个问题)
    • 容易面临跨分片查询的复杂问题。比如上例中,如果频繁用到的查询条件中不带user时,将会导致无法定位数据库,从而需要同时向4个库发起查询,再在内存中合并数据,取最小集返回给应用,分库反而成为拖累。

    1.2.3.地理位置分片

    举例子:华南区一个表,华北一个表

    1.3分库分表带来的问题

    分库分表能有效的环节单机和单库带来的性能瓶颈和压力,突破网络IO、硬件资源、连接数的瓶颈,同时也带来了一些问题

    1.3.1事务一致性问题

    • 分布式事务:

    当更新内容同时分布在不同库中,不可避免会带来跨库事务问题。跨分片事务也是分布式事务,没有简单的方案,一般可使用**“XA协议""两阶段提交”**处理。

    分布式事务能最大限度保证了数据库操作的原子性。但在提交事务时需要协调多个节点,推后了提交事务的时间点,延长了事务的执行时间。导致事务在访问共享资源时发生冲突或死锁的概率增高。随着数据库节点的增多,这种趋势会越来越严重,从而成为系统在数据库层面上水平扩展的枷锁。

    • 最终一致性

    对于那些性能要求很高,但对一致性要求不高的系统,往往不苛求系统的实时一致性,只要在允许的时间段内达到最终一致性即可,可采用事务补偿的方式。与事务在执行中发生错误后立即回滚的方式不同,事务补偿是一种事后检查补救的措施,一些常见的实现方法有:对数据进行对账检查,基于日志进行对比,定期同标准数据来源进行同步等等。事务补偿还要结合业务系统来考虑

    1.3.2.跨节点关联查询 join 问题

    切分之前,系统中很多列表和详情页所需的数据可以通过sql join来完成

    切分之后,数据可能分布在不同的节点上,此时join带来的问题就比较麻烦了,考虑到性能,尽量避免使用join查询

    解决方法:

    • 全局表

    全局表,也可看做是"数据字典表",就是系统中所有模块都可能依赖的一些表,为了避免跨库join查询,可以将这类表在每个数据库中都保存一份。这些数据通常很少会进行修改,所以也不担心一致性的问题

    • 字段冗余

    一种典型的反范式设计,利用空间换时间,为了性能而避免join查询。例如:订单表保存userId时候,也将userName冗余保存一份,这样查询订单详情时就不需要再去查询"买家user表"了。

    但这种方法适用场景也有限,比较适用于依赖字段比较少的情况。而冗余字段的数据一致性也较难保证,就像上面订单表的例子,买家修改了userName后,是否需要在历史订单中同步更新呢?这也要结合实际业务场景进行考虑

    • 数据组装

    在系统层面,分两次查询,第一次查询的结果集中找出关联数据id,然后根据id发起第二次请求得到关联数据。最后将获得到的数据进行字段拼装

    • ER分片

    关系型数据库中,如果可以先确定表之间的关联关系,并将那些存在关联关系的表记录存放在同一个分片上,那么就能较好的避免跨分片join问题。在1:1或1:n的情况下,通常按照主表的ID主键切分

    Data Node1上面的order订单表与orderdetail订单详情表就可以通过orderId进行局部的关联查询了,Data Node2上也一样
    在这里插入图片描述

    1.3.3.跨节点分页、排序、函数问题

    跨节点多库进行查询时,会出现limit分页、order by排序等问题。分页需要按照指定字段进行排序,当排序字段就是分片字段时,通过分片规则就比较容易定位到指定的分片;当排序字段非分片字段时,就变得比较复杂了。需要先在不同的分片节点中将数据进行排序并返回,然后将不同分片返回的结果集进行汇总和再次排序,最终返回给用户
    在这里插入图片描述

    上图中只是取第一页的数据,对性能影响还不是很大。但是如果取得页数很大,情况则变得复杂很多,因为各分片节点中的数据可能是随机的,为了排序的准确性,需要将所有节点的前N页数据都排序好做合并,最后再进行整体的排序,这样的操作时很耗费CPU和内存资源的,所以页数越大,系统的性能也会越差

    在使用Max、Min、Sum、Count之类的函数进行计算的时候,也需要先在每个分片上执行相应的函数,然后将各个分片的结果集进行汇总和再次计算,最终将结果返回

    1.3.4.全局主键避重问题

    在分库分表环境中,由于表中数据同时存在不同数据库中,主键值平时使用的自增长将无用武之地,某个分区数据库自生成的ID无法保证全局唯一

    因此需要单独设计全局主键,以避免跨库主键重复问题。有一些常见的主键生成策略:

    • UUID

    UUID标准形式包含32个16进制数字,分为5段,形式为8-4-4-4-12的36个字符,例如:550e8400-e29b-41d4-a716-446655440000

    UUID是主键是最简单的方案,本地生成,性能高,没有网络耗时。但缺点也很明显,由于UUID非常长,会占用大量的存储空间;另外,作为主键建立索引和基于索引进行查询时都会存在性能问题,在InnoDB下,UUID的无序性会引起数据位置频繁变动,导致分页。

    • 结合数据库维护主键ID表

    在数据库中建立 sequence 表:

    CREATE TABLE `sequence` (  
      `id` bigint(20) unsigned NOT NULL auto_increment,  
      `stub` char(1) NOT NULL default '',  
      PRIMARY KEY  (`id`),  
      UNIQUE KEY `stub` (`stub`)  
    ) ENGINE=MyISAM;
    

    stub字段设置为唯一索引,同一stub值在sequence表中只有一条记录,可以同时为多张表生成全局ID。sequence表的内容,如下所示:

    +-------------------+------+  
    | id                | stub |  
    +-------------------+------+  
    | 72157623227190423 |    a |  
    +-------------------+------+  
    

    使用 MyISAM 存储引擎而不是 InnoDB,以获取更高的性能。MyISAM使用的是表级别的锁,对表的读写是串行的,所以不用担心在并发时两次读取同一个ID值。

    当需要全局唯一的64位ID时,执行:

    REPLACE INTO sequence (stub) VALUES ('a');  
    SELECT LAST_INSERT_ID(); 
    

    这两条语句是Connection级别的,select last_insert_id() 必须与 replace into 在同一数据库连接下才能得到刚刚插入的新ID。

    使用replace into代替insert into好处是避免了表行数过大,不需要另外定期清理。

    此方案较为简单,但缺点也明显:存在单点问题,强依赖DB,当DB异常时,整个系统都不可用。配置主从可以增加可用性,但当主库挂了,主从切换时,数据一致性在特殊情况下难以保证。另外性能瓶颈限制在单台MySQL的读写性能。

    flickr团队使用的一种主键生成策略,与上面的sequence表方案类似,但更好的解决了单点和性能瓶颈的问题。

    这一方案的整体思想是:建立2个以上的全局ID生成的服务器,每个服务器上只部署一个数据库,每个库有一张sequence表用于记录当前全局ID。表中ID增长的步长是库的数量,起始值依次错开,这样能将ID的生成散列到各个数据库上。如下图所示:

    在这里插入图片描述

    由两个数据库服务器生成ID,设置不同的auto_increment值。第一台sequence的起始值为1,每次步长增长2,另一台的sequence起始值为2,每次步长增长也是2。结果第一台生成的ID都是奇数(1, 3, 5, 7 …),第二台生成的ID都是偶数(2, 4, 6, 8 …)。

    这种方案将生成ID的压力均匀分布在两台机器上。同时提供了系统容错,第一台出现了错误,可以自动切换到第二台机器上获取ID。但有以下几个缺点:系统添加机器,水平扩展时较复杂;每次获取ID都要读写一次DB,DB的压力还是很大,只能靠堆机器来提升性能。

    可以基于flickr的方案继续优化,使用批量的方式降低数据库的写压力,每次获取一段区间的ID号段,用完之后再去数据库获取,可以大大减轻数据库的压力。

    在这里插入图片描述

    还是使用两台DB保证可用性,数据库中只存储当前的最大ID。ID生成服务每次批量拉取6个ID,先将max_id修改为5,当应用访问ID生成服务时,就不需要访问数据库,从号段缓存中依次派发0-5的ID。当这些ID发完后,再将max_id修改为11,下次就能派发6-11的ID。于是,数据库的压力降低为原来的1/6。

    • Snowflake分布式自增ID算法

    Twitter的snowflake算法解决了分布式系统生成全局ID的需求,生成64位的Long型数字,组成部分:

    • 第一位未使用
    • 接下来41位是毫秒级时间,41位的长度可以表示69年的时间
    • 5位datacenterId,5位workerId。10位的长度最多支持部署1024个节点
    • 最后12位是毫秒内的计数,12位的计数顺序号支持每个节点每毫秒产生4096个ID序列

    这样的好处是:毫秒数在高位,生成的ID整体上按时间趋势递增;不依赖第三方系统,稳定性和效率较高,理论上QPS约为409.6w/s(1000*2^12),并且整个分布式系统内不会产生ID碰撞;可根据自身业务灵活分配bit位。

    不足就在于:强依赖机器时钟,如果时钟回拨,则可能导致生成ID重复。

    结合数据库和snowflake的唯一ID方案,可以参考业界较为成熟的解法:Leaf——美团点评分布式ID生成系统,并考虑到了高可用、容灾、分布式下时钟等问题

    1.3.5.数据迁移、扩容问题

    当业务高速发展,面临性能和存储的瓶颈时,才会考虑分片设计,此时就不可避免的需要考虑历史数据迁移的问题。一般做法是先读出历史数据,然后按指定的分片规则再将数据写入到各个分片节点中。此外还需要根据当前的数据量和QPS,以及业务发展的速度,进行容量规划,推算出大概需要多少分片(一般建议单个分片上的单表数据量不超过1000W)

    如果采用数值范围分片,只需要添加节点就可以进行扩容了,不需要对分片数据迁移。如果采用的是数值取模分片,则考虑后期的扩容问题就相对比较麻烦。

    1.4什么时候考虑切分

    下面讲述一下什么时候需要考虑做数据切分。

    1、能不切分尽量不要切分

    并不是所有表都需要进行切分,主要还是看数据的增长速度。切分后会在某种程度上提升业务的复杂度,数据库除了承载数据的存储和查询外,协助业务更好的实现需求也是其重要工作之一。

    不到万不得已不用轻易使用分库分表这个大招,避免"过度设计"和"过早优化"。分库分表之前,不要为分而分,先尽力去做力所能及的事情,例如:升级硬件、升级网络、读写分离、索引优化等等。当数据量达到单表的瓶颈时候,再考虑分库分表。

    2、数据量过大,正常运维影响业务访问

    这里说的运维,指:

    1)对数据库备份,如果单表太大,备份时需要大量的磁盘IO和网络IO。例如1T的数据,网络传输占50MB时候,需要20000秒才能传输完毕,整个过程的风险都是比较高的

    2)对一个很大的表进行DDL修改时,MySQL会锁住全表,这个时间会很长,这段时间业务不能访问此表,影响很大。如果使用pt-online-schema-change,使用过程中会创建触发器和影子表,也需要很长的时间。在此操作过程中,都算为风险时间。将数据表拆分,总量减少,有助于降低这个风险。

    3)大表会经常访问与更新,就更有可能出现锁等待。将数据切分,用空间换时间,变相降低访问压力

    3、随着业务发展,需要对某些字段垂直拆分

    举个例子,假如项目一开始设计的用户表如下:

    id                   bigint             #用户的ID
    name                 varchar            #用户的名字
    last_login_time      datetime           #最近登录时间
    personal_info        text               #私人信息
    .....                                   #其他信息字段
    

    在项目初始阶段,这种设计是满足简单的业务需求的,也方便快速迭代开发。而当业务快速发展时,用户量从10w激增到10亿,用户非常的活跃,每次登录会更新 last_login_name 字段,使得 user 表被不断update,压力很大。而其他字段:id, name, personal_info 是不变的或很少更新的,此时在业务角度,就要将 last_login_time 拆分出去,新建一个 user_time 表。

    personal_info 属性是更新和查询频率较低的,并且text字段占据了太多的空间。这时候,就要对此垂直拆分出 user_ext 表了。

    4、数据量快速增长

    随着业务的快速发展,单表中的数据量会持续增长,当性能接近瓶颈时,就需要考虑水平切分,做分库分表了。此时一定要选择合适的切分规则,提前预估好数据容量

    5、安全性和可用性

    鸡蛋不要放在一个篮子里。在业务层面上垂直切分,将不相关的业务的数据库分隔,因为每个业务的数据量、访问量都不同,不能因为一个业务把数据库搞挂而牵连到其他业务。利用水平切分,当一个数据库出现问题时,不会影响到100%的用户,每个库只承担业务的一部分数据,这样整体的可用性就能提高。

    1.5案例分析

    • 1.用户中心业务场景

    用户中心是一个非常常见的业务,主要提供用户注册、登录、查询/修改等功能,其核心表为:

    User(uid, login_name, passwd, sex, age, nickname)
    
    uid为用户ID,  主键
    login_name, passwd, sex, age, nickname,  用户属性
    

    任何脱离业务的架构设计都是耍流氓,在进行分库分表前,需要对业务场景需求进行梳理:

    • 用户侧:前台访问,访问量较大,需要保证高可用和高一致性。主要有两类需求:

      • 用户登录:通过login_name/phone/email查询用户信息,1%请求属于这种类型
      • 用户信息查询:登录之后,通过uid来查询用户信息,99%请求属这种类型
    • 运营侧:后台访问,支持运营需求,按照年龄、性别、登陆时间、注册时间等进行分页的查询。是内部系统,访问量较低,对可用性、一致性的要求不高

    • 2.水平切分方法

    当数据量越来越大时,需要对数据库进行水平切分,上文描述的切分方法有"根据数值范围"和"根据数值取模"。

    “根据数值范围”:以主键uid为划分依据,按uid的范围将数据水平切分到多个数据库上。例如:user-db1存储uid范围为01000w的数据,user-db2存储uid范围为1000w2000wuid数据。

    • 优点是:扩容简单,如果容量不够,只要增加新db即可。

    • 不足是:请求量不均匀,一般新注册的用户活跃度会比较高,所以新的user-db2会比user-db1负载高,导致服务器利用率不平衡

    “根据数值取模”:也是以主键uid为划分依据,按uid取模的值将数据水平切分到多个数据库上。例如:user-db1存储uid取模得1的数据,user-db2存储uid取模得0的uid数据。

    • 优点是:数据量和请求量分布均均匀

    • 不足是:扩容麻烦,当容量不够时,新增加db,需要rehash。需要考虑对数据进行平滑的迁移。

    • 3.非uid的查询方法

    水平切分后,对于按uid查询的需求能很好的满足,可以直接路由到具体数据库。而按非uid的查询,例如login_name,就不知道具体该访问哪个库了,此时需要遍历所有库,性能会降低很多。

    对于用户侧,可以采用"建立非uid属性到uid的映射关系"的方案;对于运营侧,可以采用"前台与后台分离"的方案。

    • 建立非uid属性到uid的映射关系

    1)映射关系

    例如:login_name不能直接定位到数据库,可以建立login_name→uid的映射关系,用索引表或缓存来存储。当访问login_name时,先通过映射表查询出login_name对应的uid,再通过uid定位到具体的库。

    映射表只有两列,可以承载很多数据,当数据量过大时,也可以对映射表再做水平切分。这类kv格式的索引结构,可以很好的使用cache来优化查询性能,而且映射关系不会频繁变更,缓存命中率会很高。

    2)基因法

    分库基因:假如通过uid分库,分为8个库,采用uid%8的方式进行路由,此时是由uid的最后3bit来决定这行User数据具体落到哪个库上,那么这3bit可以看为分库基因。

    上面的映射关系的方法需要额外存储映射表,按非uid字段查询时,还需要多一次数据库或cache的访问。如果想要消除多余的存储和查询,可以通过f函数取login_name的基因作为uid的分库基因。生成uid时,参考上文所述的分布式唯一ID生成方案,再加上最后3位bit值=f(login_name)。当查询login_name时,只需计算f(login_name)%8的值,就可以定位到具体的库。不过这样需要提前做好容量规划,预估未来几年的数据量需要分多少库,要预留一定bit的分库基因。

    在这里插入图片描述

    • 前台与后台分离

    对于用户侧,主要需求是以单行查询为主,需要建立login_name/phone/email到uid的映射关系,可以解决这些字段的查询问题。

    而对于运营侧,很多批量分页且条件多样的查询,这类查询计算量大,返回数据量大,对数据库的性能消耗较高。此时,如果和用户侧公用同一批服务或数据库,可能因为后台的少量请求,占用大量数据库资源,而导致用户侧访问性能降低或超时。

    这类业务最好采用"前台与后台分离"的方案,运营侧后台业务抽取独立的service和db,解决和前台业务系统的耦合。由于运营侧对可用性、一致性的要求不高,可以不访问实时库,而是通过binlog异步同步数据到运营库进行访问。在数据量很大的情况下,还可以使用ES搜索引擎或Hive来满足后台复杂的查询方式。

    1.6支持分库分表中间件

    站在巨人的肩膀上能省力很多,目前分库分表已经有一些较为成熟的开源解决方案:

    参考

    [1]数据库分布式架构扫盲——分库分表(及银行核心系统适用性思考)

    [2]分库分表的思想

    [3]水平分库分表的关键步骤以及可能遇到的问题

    [4]从原则、方案、策略及难点阐述分库分表

    [5]Leaf——美团点评分布式ID生成系统

    [6]数据库水平切分架构实践-【架构师之路】公众号

    展开全文
  • AntDB 如何合理选择分片键 本文主要探讨AntDB 分片设计规则。 通过本文阐述下列功能: AntDB 分片介绍 AntDB 分片要解决的问题 常用分片字段优缺点分析 AntDB 分片 分片键(sharding Key) 分片是将一张分布式表...

    AntDB 如何合理选择分片键


    本文主要探讨AntDB 分片键设计规则。

    通过本文阐述下列功能:

    • AntDB 分片介绍
    • AntDB 分片要解决的问题
    • 常用分片字段优缺点分析

    AntDB 分片

    分片键(sharding Key)

    分片是将一张分布式表按照指定的分片键(sharding Key)和分片模式(sharding Mode)水平拆分成多个数据片,分散在多个数据存储节点中。对于分片的表,要选取一个分片键。一张分布式表只能有一个分片键,分片键是用于划分和定位表的列,不能修改。

    分片模式(sharding Mode)

    • 散列Hash

      即按表的分片键(sharding Key)来计算哈希值,并将取模后的值(哈希值%1024)与数据节点建立映射关系,从而将哈希值不同的数据分布到不同的数据节点上。

    分片目的

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

    分片策略

    1. 1000万以内的表,不建议分片. 通过合适的索引,可以更好地解决性能问题。

    2. 分片键不能修改。

    3. 分片键选择时,尽量保证数据能够均匀分布。

    4. 分片键选择时,尽量保证事务控制在分片范围内(本节点),避免出现跨分片(跨节点)。

    5. 继承表的分片键,子表保持和父表一致。

    总体上来说,分片键盘的选择是取决于最频繁的查询 SQL 的条件。找出每个表最频繁的 SQL,分析其查询条件,以及相互的关系,就能比较准确的选择每个表的分片策略。

    分片要解决的问题

    跨节点join问题

    只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分可以减少此类情况的发生。

    解决这一问题,要求分片字段和join连接字段保持一致,当查询可以下推至节点运算时,可以有效解决此类问题。

    如果运算不允许下推至节点,可以将数据量较小的表修改为复制表后(数据量不超过1000万),也能实现本节点运算的能力。

    跨节点order by,group by,count等聚合函数问题

    这些是一类问题,因为它们都需要基于全部数据集合进行计算。

    解决这一问题,要求分片字段和聚合字段保持一致,当聚合可以下推至节点运算时,可以有效解决此类问题。

    常用分片字段优缺点分析

    考虑使用 phone-number(唯一值) 字段作为分片键

    每个人通常都有自己独一无二的电话号码,这类重复率低的字段作为分片键 能够保证集群中数据的均匀分布,是一种推荐的分片方式。

    考虑使用 areacode(区号) 字段作为分片键

    这个字段的取值范围是给定的一个列表,由于总的区号的个数并不多,所有areacode相同的数据都会分布在同一个数据节点中.由于各个地区的数据量不均匀,导致数据分布产生不均衡,这会带来一些影响:

    当按区号聚合查询时,SQL整体执行效率完全取决于 数据量较大的区号 的响应时间,产生圆桶效应。

    考虑使用 date(日期) 字段作为分片键

    这个字段和 areacode 类似,也是一个范围区间,但是可以有效避免数据分布不均匀的问题。但当查询的日期区间不是数据节点倍数的时候,查询目标结果集的数据量却产生了不均匀,也会产生圆通效应。

    如一个8节点的集群,某表按日期分片后,只查询连续12天的数据,此类场景,将产生计算不均匀的现象。

    展开全文
  • 文章目录前言数据拆分垂直切分水平切分拆分阶段如何操作拆分的数据库(客户端分片)程序自定义规则实现通过ORM框架实现通过JDBC协议实现通过代理分片实现 前言   在当今互联网的发展下,很多软件拥有大量用户的...

    前言

      在当今互联网的发展下,很多软件拥有大量用户的使用。用户每天产生大量数据。这些数据我们会保存到关系型数据库中,而如何设计一个可以快速响应的并且能够容易容纳大量数据的数据库便显得很重要了。此篇讲述如何通过分库分表设计解决以上问题。 一切技术都是为了解决存在的问题。

    数据拆分

      数据量大,解决的办法就是进行数据拆分到不同的数据库。而数据切分的两种常见的方式就是垂直切分与水平切分。

    垂直切分

      垂直切分又称为纵向切分,根据业务的维度(模块),将原本的数据库中的表进行分类,分到不同的数据库。如下图

    优点:

    • 最直观的就是业务清晰。
    • 简单,容易扩展
    • 方便实现冷热数据设计
    • 数据维护简单

    缺点:

    • 无法解决单数据库性能问题。
    • 部分业务无法join(即关联),可以编写接口进行访问,但增加程序复杂度
    • 事务管理复杂

    水平切分

      水平切分又称为横向切分。即就是把单个数据库的结构不改变。按照数据进行切分,每个数据库存放一部分数据,达到数据分服务器存储。如下图:

      我们想一个场景,当一个系统有1500万用户使用,未切分时数据库承受1500万的请求压力,切分成10个,则每个数据库承受150万的压力。成倍减少了访问压力。
    优点:

    • 降低数据库访问压力,提高性能
    • 表结构相同,只需要合适的路由规则

    缺点:

    • 数据分散,不易管理。
    • 拆分规则需要一个好的设计
    • 会出现事务一致性问题
    • 分页,排序等难以解决
    • 存在多数据源

    拆分阶段

    1. 单库单表
        这个阶段没什么讲的,比如用户表,则所有数据都存到该表。
    2. 单库多表
        当数据量增大时,此时我们进行表的拆分。把用户表拆分多个用户表,用户表结构相同。比如user表拆分成user1表和user2表,则user1表和user2表合起来的数据就 是user表的所有数据。
    3. 多库多表
        当数据库服务器存储数量过大,并且影响到查询效率时,此时应该把表存放到多个数据库。

    如何操作拆分的数据库(客户端分片)

    程序自定义规则实现

      我们通过自己编写指定的规则来实现访问哪一个表或数据库。这种方式具有侵入性,但是简单。比如我们设计user拆分为4个表然后对id进行取模存储到不同数据库,这是保存到数据库,当我们进行读取时采用相同算法进行获取即可。但是有时候切分规则并不会这么简单。

    通过ORM框架实现

      比如使用mybatis进行查询的时候指定一个参数,这个参数是表的索引,例如user表分为user0,user1,0和1代表的索引值,查询的时候传递表索引参数指定操作的表。

    <select>
          select * from user#{index} where id = #{userID}
    </select>
    

    通过JDBC协议实现

      定制特定的JDBC协议实现,优点就是开发人员不必关心分库分表的实现,对开发人员透明,无侵入性,流行的有sharding JDBC。

    通过代理分片实现

      代理分片,就是通过在数据和业务逻辑之间提供一个代理层,分片规则在代理层实现。代理层提供接口供开发人员使用。缺点,增加了代理层。对数据库的操作增加一层网络传输。影响性能。并且增加我们的维护成本,比如Mycat,我们要配置高可用方案保证系统稳定性。

    问题

      当我们使用分库分表这种设计虽然解决了我们的大问题,但其带来的问题也不小。当把一个数据集拆分成多份时,会打破数据完整性,这个事实上是反数据库设计的。

    1. 数据库的扩容和迁移操作变得更难。不小心就会导致数据丢失,重复数据问题。

    2. 数据库跨维度查询问题
        我们想一个场景,当数据库1(db1)中user1购买商品A时,此时我们把购买记录保存到user1所在的db1库中。现在db2中的user2购买商品A,交易记录保存到db2中,即购买记录存到买家的维度上。我们现在如果查询商品A的购买记录。发现我们要遍历两个库才能查询所有的记录。反之交易记录存到商品的库。则用户查询记录要遍历所有的商品的库。
        - 在用户和商品各自在的库都保存交易记录的信息
        - 查询所有分片数据,合并。效率太低,不是太建议
        - 搜索引擎实现

    3. 数据库事务问题
        当进行跨库操作时,很难保证其事务性。这其实是个很大的问题,比如在金融方面的系统上,我们必须保证其操作的准确性和数据的完整性。则多处会使用事务。

    结语

      此篇只讲了一些理念,未涉及到实际操作,后面会出。

    展开全文
  • 上篇文章讲解了传统数据库的一些设计注意点。本篇为第二篇,在大数据量的情况下,如何去提前设计这个表结构,来达到一个比较好的效果。对于团队,对于后续的维护和扩展都带来更大的便利。自增id自增id还是可以有,...

    上篇文章讲解了传统数据库的一些设计注意点。
    本篇为第二篇,在大数据量的情况下,如何去提前设计这个表结构,来达到一个比较好的效果。对于团队,对于后续的维护和扩展都带来更大的便利。

    自增id

    自增id还是可以有,但是不是必须的了。但是建议还是每张表中有一个自增id。为什么,还是那句话,做数据查询,迁移,排序的时候,有着天然的一些优势。

    唯一标识

    这个标识无论是token,还是其他例如订单的订单号或者其他唯一标识都行。重点是唯一,不只是在单系统中唯一,而是需要在并发的情况,也能够保持唯一。

    关于分布式id的生成方案,网上已经有很多了,这里就不重复了。谷歌搜索搜索,自己看下原理,跑跑demo,能够满足自己业务的最大并发情况下的唯一即可。

    比如说你未来几年的最大并发也就是100,搞个能支持在几千并发下不会出现标识重复的实现方案即可,并发几万,几十万,真的需要吗?

    后续如果真的能够达到几万并发,那说明什么?说明业务火爆了啊。难道还抽不出时间,抽不出人来做一个id生成方案的改造?这里有比较重要的一点,不要太过超前设计,没必要,也没那个时间。

    创建时间&修改时间

    创建时间和修改时间还是要有的,而且建议时间精确到毫秒级别,在上一篇,我没有说精确多少,那是因为并发不高,秒级完全够了。但是在大数据量的情况下,可能一秒有几十、几百、上千、上万的数据新增都是有可能的。那么秒级在这种情况下完全就不够看了,选择毫秒级别是一个比较好的选择。

    分库分表

    前面的唯一标识/创建时间可以说就是为了这步准备的。

    但是怎么来设计分库分表,选择什么方式,范围还是hash,选择哪个字段,还是选择几个字段。平滑迁移还是停机迁移。

    这些都没有唯一答案。只能是根据场景来区分不同的情况。下面举几个例子来进行一个讲解,不是标准答案,同一个场景为了满足不同的需求,也可能有不同的一个设计。

    1:支付订单的场景

    例如,订单每日新增千万级,那么在这个情况下。我们还需要区分一下。

    1.1 订单号包含了时间戳

    那么强烈建议按照时间维度进行分库分表。也强烈建议在订单号中将时间戳放进去。
    优点:

    • 单表的大小是可以预知的,一天多少订单量,一个月多少订单量
      非常便于水平扩展,后期如果想对整个分片集群扩容时,只需要添加库表即可,不需要对已经存在的分片数据进行迁移。使用订单号进行范围查找时,可以快速定位查询,避免了跨片查询的问题。

    缺点:

    • 最近的订单会存在着热点数据,可以通过其他方式进行解决,例如缓存等

    1.2 订单号不包含时间戳

    不包含时间戳,你可以选择创建时间来做范围分片。或者使用订单号来做hash分片,也就是取模运算分片。

    hash分片的缺点就是后期扩容会涉及到老数据的迁移,但是现在有一种方案可以避免该缺点,那就是使用虚节点,先占位,但不使用,需要的节点需要是2的次方个才行。大家可以去网上了解一下,这里就不展开了。另外一个缺点就是跨片查询的性能问题,当查询条件中没有订单号的时候,会无法定位到数据库表,所以会遍历所有的库表,进行查询,再在内存中合并数据,取最小集返回,在这种情况下,分库分表反而会成为累赘。

    其他一些分库分表带来的事务问题大家可以看看现在的一些分布式事务解决方案,都还挺不错的。阿里的Seata可以了解一下。

    最后,分库分表,并不是一定要分库的,也可以只分表,这样很多分库分表的问题就不存在了。分库分表还是要跟进实际的数据增长速度来评估,比如说,每年数据才几十万或者百万,那么没有必要进行一个过渡设计,单表即可。等数据库到了瓶颈,可以再考虑优化。

    很多时候,瓶颈也不一定是在数据库。

    性能优化

    在这里,对于性能优化提一句,因为自己也刚完成一个性能优化的需求不久,提升性能2倍左右。这次优化完全没有动数据库。主要优化点在:同步方法异步调用第三方服务、计算异步处理、批量单次调用、部分不变数据缓存 重点:拿资源(空间、线程)换时间。

    总结

    当数据量大了之后,其实很多设计和传统的数据库还是没有很大变化的。

    主要是要考虑到数据量大之后,该表如果分库分表,那么怎么设计更加合理一点,也许当下不需要分库分表,但是可以给以后少埋点坑。

    但是注意,还是那句话,不要过度设计,也不要不去设计。简单的说,可以预估到以后的业务每日数据量新增是万级几十万以上的,就可以考虑下以后的分表,但是当期并不需要做。但如果是日增百万千万级别,那么这个分库分表肯定是当期就需要进行的。假如是日增几百几千的表,那么就不要花过多时间去考虑什么分库分表的方案了,真的用不上。

    建议的一个提前思考时间,1年左右的思考维度设计比较好。即不会超前,也不会因为迭代了一两次业务就有人提出,不知道哪个**设计的结构,又得重新来设计。

    最后,能够在技术方案时就明确的事情,绝不留到写代码的时候再去明确!技术方案越清晰(注意:不是说超前设计,这里面有个度,只可意会不可言传),写代码越轻松,团队协作越流畅。

    e45a1bb07f39f882b6bf6af51f5d6f55.gif

    5c15b9398ccb1bb68ed3a726050fa2b5.png

     动动小手指 了解更多详情 !faee4ca63fc6b7f3fc2d2deb10184b9e.gif
    展开全文
  • 导读:数据总线(DBus)专注于数据的实时采集与实时分发,可以对IT...在上一篇关于DBus的文章(DBus 数据库表结构变更处理方案)中,我们主要介绍了在DBus的设计中,表结构变更及其带来的各种问题是如何处理的。本...
  • Mycat 的分片规则设计

    2016-07-02 17:03:00
    分布式数据库系统中,分片规则用于定义数据与分片的路由关系,也就是insert,delete,update,select的基本sql操作中,如何将sql路由到对应的分片执行。 Mycat的总体路由图为: 如图所示分片规则是最终解析sql到...
  • 数据库分片架构 数据库垂直切分 二、实践一 场景:单key业务,如何做到数据库无限容量 文章:《用户中心,数据库架构优化与实践》 内容: 用户中心业务分析 用户中心水平切分方案 “前台与...
  • 花了不少时间,把自己曾经做...一、总起内容:单库体系架构数据库分组架构数据库分片架构数据库垂直切分二、实践一场景:单key业务,如何做到数据库无限容量内容:用户中心业务分析用户中心水平切分方案“前台与后台...
  • 本文来自用户投稿,作者陈浩翔上篇文章讲解了传统数据库的一些设计注意点。本篇为第二篇,在大数据量的情况下,如何去提前设计这个表结构,来达到一个比较好的效果。对于团队,对于后续的维护和扩展都带来更大的便利...
  • 刘竞,初文科 青岛农业大学信息科学与工程学院,山东青岛(266109) ... 摘 要:提出了如何充分利用 MS SQL Server 2000 的数据库管理特性...实验实现了分布式数据库的水平分片、垂直分片和混合式数据分布。 关键词:
  • datanode用来做数据分片的,mycat会根据分片规则将数据写入datanode对应的数据库中,不同的datanode中可以使用相同的datahost。 用mycat一直是用编号作为分片依据,最近一个项目中数据库结构不适...
  • 在保障数据层的高性能与高稳定方面,最容易想到的方式就是对数据进行分片、多份、冗余等,很多架构的本质其实也是基于这几点来实现的。 这里先不看细节,即先不管底层数据源是什么数据库,我们先只聊架构方案,因为...
  • MyCat分布式数据库如何解决分库JOIN一、全局表。该模式适用于不分片表。MyCat全局表在进行更新、添加、删除操作时会在每一个节点执行 一次以保证数据一致性MyCat全局表在设计时需要额外添加一个字段_mycat_op_time...
  • 数据库中某表存储买卖双方交易的数据信息,对于一条交易来说,买卖双方数据具有一定程度的耦合性,比如卖家的状态更新对应买家的状态也会更新,对于一个中大规模的电子商务网站,架构师在设计如何考虑数据分片的...
  • 在前面我们已经实现了我们的强制分片算法的使用了,那么在本章我们将为大家讲解如何实现我们的哈希取模分片算法的使用。 1、查看源码,追根溯源 通过源码我们可以看到他的分片是根据字段的哈希值以后然后进行分片...
  • 分布式数据库2

    千次阅读 2016-06-24 12:29:01
    1.分片设计 在分布式数据库系统设计中,最基本的问题就是数据的分布问题,即如何对全局数据进行逻辑划分和实际的物理分配。逻辑划分成为分片,实际的物理分配则是分配内容。一般的设计策略我们有自顶向下和自底向上...
  • 在互联网项目中,当业务规模越来越大,数据越来越多,随之而来的...在保障数据层的高性能与高稳定方面,最容易想到的方式就是对数据进行分片、多份、冗余等,很多架构的本质其实也是基于这几点来实现的。 这里先...
  • 在互联网项目中,当业务规模越来越大,数据越来越多,随之而来的...在保障数据层的高性能与高稳定方面,最容易想到的方式就是对数据进行分片、多份、冗余等,很多架构的本质其实也是基于这几点来实现的。 这里先...
  • [分享]浅谈分布式数据库

    万次阅读 多人点赞 2017-06-14 10:50:36
    文章集中整理总结mysql分库分表开源产品,分布式数据库的...1 谈数据库分片需要首先确定以下概念 2 分片 1 水平拆分垂直拆分都是什么 2 为什么分表 3 为什么分库 4 分布式事务 5 小结 6 如何自己实现分库分表 7 SO不需
  • 通过优锐课核心java学习笔记中,我们可以看到,码了很多专业的相关知识...作为参考,以下是有关MySQL分片的简要说明:MySQL分片是将MySQL应用程序工作负载划分到多个不同的MySQL数据库服务器上的策略,从而允许查询...
  • 9.2 分布式数据库设计的数据分片、复制和分配技术 182 9.2.1 数据分片 182 9.2.2 数据复制和分配 184 9.2.3 分片、分配和复制示例 185 9.3 分布式数据库系统的类型 187 9.4 分布式数据库中的...
  • 全书共4个部分,第一部分介绍最基本的概念、术语及建模原则,第二部分描述了关系数据模型和关系型DBMS,第三部分讨论数据库设计,第四部分主要描述数据库系统中使用的物理文件结构和存取方法。书中涉及的内容非常...

空空如也

空空如也

1 2 3 4 5 6
收藏数 104
精华内容 41
关键字:

数据库分片如何设计