美文网首页
关于数据库的分库分表

关于数据库的分库分表

作者: 雁阵惊寒_zhn | 来源:发表于2021-05-24 00:11 被阅读0次

    数据切分方式

    关系型数据库单表数据量增大,导致处理能力受限,成为业务系统的瓶颈。数据切分将单表的大量数据分为多张小表数据,或者分散存储在不同的数据库中,减少单表大数据导致的系统问题。
    在数据切分上,可以分成两种方式,垂直(纵向)切分和水平(横向)切分,两者各有优劣势,根据系统业务,选择切分方式,梳理架构。

    垂直(纵向)切分更像是基于领域驱动的设计方式,而水平(横向)切分或许才是我们默认讨论的分库分表操作。

    垂直(纵向)切分

    垂直切分是基于数据表的“列”进行的,数据表中字段较多可以根据业务耦合性,将不同的字段拆分开来,有一张多字段的表拆分为多个少字段的表。在查询过程中,因为数据库以行为单位进行查询并且加载到内存然后返回的,所以更少的字段访问的效率更高。显然垂直切分要依赖于业务的耦合性,将关系紧密的字段放在相同的表中,关系不密切的放入不同表中,这也是系统设计中的领域设计的思想。实践中,就是按照领域不同拆分为不同的微服务模块,每个微服务模块负责管理自己的数据表。

    垂直切分优点
    1. 业务层面的松耦合,逻辑清晰
    2. 不同业务的数据重构和升级之间互不影响
    3. 数据表字段较少,有效提高数据库的读写性能
    垂直切分缺点
    1. 如果不同的表在不同的数据库里,无法进行数据表的join操作,需要通过接口进行数据的聚合
    2. 增加了分布式事务的复杂性
    3. 如果业务数据量很大,例如记录用户信息的数据表,如果用户数量激增,垂直切分仍然会引起单表的数据量过大的问题
    水平(横向)切分

    水平切分是基于数据表的“行”进行的,可以进行库内分表和分库分表。将一张大数据表按照某种规则拆分成多个库的多张表中,或者一个库的多张表中。拆分后的小数据表只包含部分的数据。如果拆分为同一库中多张表,虽然表的数据量变小了,但是数据的请求仍然可能发送同一个物理机,这样分表的帮助不大。所以分库分表重要不仅仅使数据表变小了,还要将请求尽可能负载均匀的分配到不同的物理机上。

    水平切分缺点
    1. 水平切分可以切实改善单张数据表数据量过大的问题,提升系统稳定和负载的能力
    2. 与业务耦合性少,水平切分不需要考虑业务场景
    水平切分缺点
    1. 与垂直切分一样,多张表会引起分布式事务的复杂性
    2. join操作查询性能差
    3. 分页和排序查询更加复杂
    4. 不重复主键的复杂性,根据业务有确切的数据字段作为主键(例如,用户ID,订单ID等等)还好,如果主键在单表中设计的是自增ID,那么分库分表之后就需要更多的操作保证主键唯一

    总结分库分表带来的问题,并给出解决方案

    事务一致性问题

    多个库多张表同时更新,不可避免的会带来事务一致性的问题。分布式事务一般使用XA协议或者2PC处理。但是在提交事务的过程中需要协调多个节点,可能导致事务执行时间过长,并且分布式事务访问临界资源时可能会提高死锁的概率。涉及的节点越多这种情况就会越明显。
    当系统对于一致性要求并不那么高的时候,不要求实时的一致性,仅仅保证最终一致性即可,可以采用补偿事务的方式。例如,定期校验和同步标准数据源数据,或者记录出现的错误定时重试执行。

    不同节点的join操作

    当数据分布在不同的节点上时,此时join会带来性能问题。另外不同数据库的不同表之间也无法使用join操作。可以使用一些技术去避免使用join查询。

    • 对于所有系统都依赖的表,可以在每个数据库中都保存一份。当然前提这些表要通常很少会进行修改,否则会引起数据一致性问题。
    • 对于经常需要一起访问的数据可以冗余保存一份,例如网购的订单数据表保存下单用户ID的同时,也将用户昵称一起保存一份(用户昵称会在用户表中存放)。显然这种冗余数据的解决方式比较局限,需要相互依赖的数据较少,此外同样存在数据一致性问题。
    • 通过接口进行数据的聚合,查询出多张表的结果集,通过关联的字段拼装在一起。
    • 如果可以确定表之间的关系,将关联关系的表存放在相同的数据库中,并且存放在同一个节点上。
    不同节点的分页limit和排序order by查询

    在跨越节点进行分页或排序查询时,如果分页依赖的字段就是指定操作的字段时,可以比较容易定位到分片并返回结果集;如果指定操作的字段不是分页依赖的字段,就需要在不同的分片上将数据执行指定的查询操作,然后将不同分片的结果集合起来,再做一次相同的查询操作得到结果集,可参考ElasticSearch关于数据的操作中的搜索时的分页,这时如果页数很大,性能消耗很大。
    同样Max、Min、Sum、Count之类的函数也需要先在每个分片上执行,然后集合所有的数据后再次计算得到结果。

    全局主键避重问题

    在分库分表时不能在使用自增的主键ID,因为这样不同表的主键就会出现重复,需要根据业务设计唯一的业务ID,例如用户ID,订单ID等。

    1. 利用数据库生成唯一ID
    2. Snowflake等分布式唯一ID算法

    数据表切分时机

    基本原则是能不切分就不切分。只要当数据增长过快,数据量过大,或者业务发展需要垂直拆分字段时,才需要对数据进行切分。还有关于安全性和可用性的考虑,对数据拆分后可以保证数据的隔离性,这样一部分数据有问题不会影响到所有。

    几道面试题

    商品表的schema(product_id, shop_id, ....)需要进行切分,查询场景根据product_id查询商品详情,和根据shop_id查询商品的列表。如何操作分库分表?

    答:可以根据product_id取模进行水平切分,如果product_id生成算法可能出现热点分片,可以先对product_id进行hash取值再取模。创建shop_id和product_id的映射关系,缓存这个映射关系,通过shop_id可以查到所有关联的product_id,在通过product_id查询具体的库。

    如何不影响服务前提下无缝进行分库分表

    答:几种方法如下

    1. 创建分库分表,业务此时进行双写,然后同步双写时间戳之前的数据(保证分布式一致性),直到所有数据同步完成。
    2. 全局进行同步,记录开始同步后所有变化的数据,最后执行补偿操作。
    3. 监听原数据表的数据变化,将变化的数据发送到消息队列中,读取消息队列同步到分表中。触发原数据表中所有数据的变化,例如所有数据时间戳增加1毫秒,这样全局进行同步,消息队列可以保证数据的有序一致。

    补充:假如分表使用的是hash值,可以考虑利用一致性哈希技术,这样在分表时如果只涉及到一个分片,那么影响也仅仅是当前的分片,不需要全部进行再hash运算,影响最小。



    相关文章

      网友评论

          本文标题:关于数据库的分库分表

          本文链接:https://www.haomeiwen.com/subject/kazffltx.html