分库分表策略和id生成策略

前言

对于一家互联网公司,线上系统的分库分表大多是DBA参与完成。对于开发关注的问题莫过于主键id的生成策略,以及事务问题处理

0.1 主键id的生成策略(全局主键避重问题)

0.1.1 UUID

UUID标准形式包含32个16进制数字,分为5段,形式为8-4-4-4-12的36个字符,例如:550e8400-e29b-41d4-a716-446655440000
UUID由以下几部分的组合:
(1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
(2)时钟序列。
(3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
uuid优点:
UUID是主键是最简单的方案,本地生成,性能高,没有网络耗时。
uuid缺点:
缺点也很明显,由于UUID非常长,会占用大量的存储空间;
另外,作为主键建立索引和基于索引进行查询时都会存在性能问题,在InnoDB下,UUID的无序性会引起数据位置频繁变动,导致分页。

0.1.2 使用全局节点来生成ID(结合数据库维护主键ID表)

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

0.1.3 Snowflake(雪花算法)分布式自增ID算法

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

(1) 第一位未使用
(2) 接下来41位是毫秒级时间,41位的长度可以表示69年的时间
(3) 5位datacenterId,5位workerId。10位的长度最多支持部署1024个节点
(4) 最后12位是毫秒内的计数,12位的计数顺序号支持每个节点每毫秒产生4096个ID序列
在这里插入图片描述
好处是:毫秒数在高位,生成的ID整体上按时间趋势递增;不依赖第三方系统,稳定性和效率较高,理论上QPS约为409.6w/s(1000*2^12),并且整个分布式系统内不会产生ID碰撞;可根据自身业务灵活分配bit位。
不足就在于:强依赖机器时钟,如果时钟回拨,则可能导致生成ID重复。

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

0.1.4 在Redis等缓存服务中创建全局ID

0.1.5 使用auto_increment_increment和auto_increment_offset参数

0.2事务一致性问题

0.2.1 分布式事务

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

分布式事务能最大限度保证了数据库操作的原子性。
但在提交事务时需要协调多个节点,推后了提交事务的时间点,延长了事务的执行时间。导致事务在访问共享资源时发生冲突或死锁的概率增高。

随着数据库节点的增多,冲突或死锁这种趋势会越来越严重,从而成为系统在数据库层面上水平扩展的枷锁。

0.2.2 最终一致性(又称 事务补偿机制)

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

1 常用的分库分表的策略(如何分配分片中的数据,又叫路由策略)

HASH取模

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

范围分片

从1-10000一个表,10001-20000一个表。

地理位置分片

华南区一个表,华北一个表。

时间分片

按月分片,按季度分片等等,可以做到冷热数据。
分库分表后引入的问题

1.1路由原则(如何选择分区键)

分区键要能尽量避免跨分片查询的发生
分区键要能尽量使用各个分片中的数据平均

2 分库分表的几种方式

2.1 垂直(纵向)切分

垂直切分常见有垂直分库和垂直分表两种。

2.1.1 垂直分库

把一个实例中的多个数据库拆分到不同的实例
在这里插入图片描述
把一个库中的表分离到不同的数据库中
在这里插入图片描述

2.1.2 垂直分表

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

2.2 水平拆分

在数据库并发和负载没有达到限制时,不推荐水平拆分。
对一个库中的相关表进行水平拆分到不同的实例的数据库中
在这里插入图片描述

3 分布式事务问题

如果我们做了垂直分库或者水平分库以后,就必然会涉及到跨库执行SQL的问题,这样就引发了互联网界的老大难问题-“分布式事务”。
那要如何解决这个问题呢?
1.使用分布式事务中间件
2.使用MySQL自带的针对跨库的事务一致性方案(XA),不过性能要比单库的慢10倍左右。
3.能否避免掉跨库操作(比如将用户和商品放在同一个库中)
4.跨库join的问题
分库分表后表之间的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表, 结果原本一次查询能够完成的业务,可能需要多次查询才能完成。
粗略的解决方法: 全局表:基础数据,所有库都拷贝一份。 字段冗余:这样有些字段就不用join去查询了。
业务代码层组装:分别查询出所有,然后组装起来,较复杂。
5.横向扩容的问题
当我们使用HASH取模做分表的时候,针对数据量的递增,可能需要动态的增加表,此时就需要考虑因为reHash导致数据迁移的问题。
6.结果集合并、排序的问题
因为我们是将数据分散存储到不同的库、表里的,当我们查询指定数据列表时,数据来源于不同的子库或者子表,就必然会引发结果集合并、排序的问题。如果每次查询都需要排序、合并等操作,性能肯定会受非常大的影响。走缓存可能一条路!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值