这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战
分布式是单体项目往微服务演进中必然会涉及到的技术,我分享下我在这块的积累和见解。
分布式ID
在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。如在美团点评的金融、支付、餐饮、酒店、猫眼电影等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库的自增ID显然不能满足需求;特别一点的如订单、骑手、优惠券也都需要有唯一ID做标识。此时一个能够生成全局唯一ID的系统是非常必要的。
业务系统对ID号的要求有哪些呢?
- 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。
- 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
- 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求。
- 信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。
常见的实现方案有:
UUID
- 优点:性能非常高:本地生成,没有网络消耗。
- 缺点:空间占用较多
- 不能生成递增有序的数字,索引效率下降
数据库主键自增
- 非常简单,利用现有数据库系统的功能实现,成本小,有DBA专业维护。
- ID号单调自增,可以实现一些对ID有特殊要求的业务。
- 缺点:并发性能不高,受限于数据库性能
- 分库分表,需改造,较复杂
- 自增:数据量泄露
Redis自增
- 优点:相对于MySQL,并发性能会高很多
- 缺点:数据丢失
- 自增:数据量泄露
雪花算法:
这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等,比如在snowflake中的64-bit分别表示如下图(图片来自网络)所示:
41-bit的时间可以表示(1L<<41)/(1000L360024*365)=69年的时间,10-bit机器可以分别表示1024台机器。如果我们对IDC划分有需求,还可以将10-bit分5-bit给IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,可以根据自身需求定义。12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。
- 缺点:时钟回拨
方案总结:
- 号段模式
- 1..100,101..200
- 性能提高,自增
- 雪花算法
- 时间戳(毫秒)+ 随机数,但这样容易重,考虑把随机数换成自增数(Automic,而不是i++),能解决单台,但是不能解决多台服务器,于是加机器号
- 1bit符号位+41bit时间戳位+10bit工作进程位(5+5,区域+服务器标识)+12bit序列号位
- 这些位数都可以根据实际情况改
典型的开源组件:
- 百度(uid-generator)
- 滴滴(Tinyid)
- 美团(Leaf),同时支持号段和雪花算法
参考资料:
分布式session
在传统的项目里,session是由tomcat来管理的。但是在集群常见下,需要集中式地管理登录信息,于是就有了分布式session,通常是借助Redis集群来实现。
此外,也有一种不依赖session的登录方式,jwt,使用的也比较广泛。
分布式任务调度
分布式调度即执行一些定时任务。有一些单机的定时任务框架,不能满足集群需求,除非加分布式锁,而分布式调度工具即为了解决这一痛点。
典型的框架包括Python的celery框架,Java的XXL框架。
分布式限流
常见限流算法包括漏桶算法和令牌桶算法。漏桶保护第三方系统,保证流出的流量匀速,令牌桶比较适合秒杀。
有一些单机版限流工具,如guava RateLimiter
分布式常见下可选用sentinel。
分库分表
当数据库性能瓶颈时,需要进行分库分表。
拆分方式
- 水平拆分:每个表结构都一样
- 垂直拆分:一个多字段的表拆分多个表
分库分表解决方案
- shardingsphere.apache.org
- www.mycat.org.cn
shardingsphere社区维护的比较好,是业内比较主流的分库分表开源组件。
分布式事务
分布式事务分为强一致性和最终一致性。
强一致性的只有二阶段提交。最终一致性的思路有很多,比如TCC,事务性消息,本地消息表等。
另外推荐一款开源的分布式解决方案seata
分布式事务 seata及其三种模式,可参考这篇文章: seata.io/zh-cn/blog/…