1.分布式锁
应用场景:多线程操作共享资源;系统是一个分布式系统,集群
mysql redis:类cas自旋式分布式锁,询问方式:尝试加锁
zk etcd:event事件通知后续锁的变化,轮询向外的过程
1.1 redis分布式锁
setnx key value
1.setnx 设置成功就是不存在 多机器部署的情况下一个进程挂了,会有死锁的可能
2.set key value nx ex expireTime 过期时间,防止死锁
3.客户端1操作太久,业务没有执行完,导致锁自动释放,客户端2这时拿到了锁,
开始操作共享资源,这时客户端1操作完成,释放了客户端2的锁
4.设置一个锁的标识,释放的时候先做一个判断,是否是自己的锁,比如uuid
执行get del命令会有原子性问题
5.但是还是会有原子性问题,写一个lua脚本
因为 Redis 处理每一个请求是「单线程」执行的,在执行一个 Lua 脚本时,其它请求必须等待,直到这个 Lua 脚本处理完成
6.过期时间问题,尽量冗余过期时间,但是还是解决不了问题
7.加锁时,先设置一个过期时间,然后我们开启一个「守护线程」,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还未完成,那么就自动对锁进行「续期」,重新设置过期时间,(Redisson框架)
8.主从集群,主库突然挂了,切换了主节点,set设置锁的命令还没有同步到从库,新的从库变成了主库,未同步到这个锁,其他客户端还是会拿到这个锁
1.2 redlock红锁
解决redis单机故障问题
(客户端实现的一个redlock算法,部署多台redis,同时向多台机器请求拿锁,如果超过半数以上拿到锁,则拿锁成功)
大致过程:
依次对多个 Redis 实例进行加锁(一般是3个或5个),加锁命令使用单实例 Redis 的加锁命令;
为了避免在某个节点长时间获取不到锁而阻塞,每次获取锁操作也有一个超时时间,远小于 TTL,超过超时时间则认为失败,继续向下一个节点获取锁;
计算整个获取多把锁的总消耗时间,只有在超过半数节点都成功获取锁,并且总消耗时间小于 TTL,则认为成功持有锁;
成功获取锁后,要重新计算 TTL = TTL - 总消耗时间;
如果获取锁失败,要向所有 redis 实例发送释放锁的命令。
释放锁操作就是向所有实例都发送删除 key 命令。
Redlock 容错性依赖于一个时间戳的计算,这在分布式系统中并不受待见,于是有了一场著名的论战。
1.3 ZooKeeper:分布式锁为什么要用到zk?
1.4 基于mysql数据库:主键或唯一索引冲突
分布式锁死锁情况:
1.加了锁没有删除锁,程序执行完成后删除锁
2.删除锁的时候程序挂了,导致删除失败,增加一个自动过期时间
2.分布式id
2.1 UUID
ID无序;字符串;长度过长;基于MAC生成可能造成MAC泄漏。
(对于数据库主键,最好的应该有序,短小,数值类型。尤其是无序会导致数据库聚簇索引结构频繁变动)
2.2 数据库自增ID
需要一个单独的MySQL实例用来生成ID,建表结构如下:
CREATE TABLE SEQUENCE_ID (
id bigint(20) unsigned NOT NULL auto_increment,
value char(10) NOT NULL default '', PRIMARY KEY (id)) ENGINE=MyISAM;
当我们需要一个ID的时候,向表中插入一条记录返回主键ID,但这种方式有一个比较致命的缺点,访问量激增时MySQL本身就是系统的瓶颈,用它来实现分布式服务风险比较大
优点:实现简单,ID单调自增,数值类型查询速度快。
缺点:DB单点存在宕机风险,高并发场景下DB本身将成为性能瓶颈,数据和数据量会泄密。
2.3 数据库多实例自增ID
设置固定步长,不同实例起点不一样
但是还是没办法解决高并发场景下
水平扩展的有利于解决数据库单点问题,同时为了ID生成特性,将自增步长按照机器数量来设置。
优点:解决了单点问题。
缺点:步长确定后无法扩容;高并发场景下DB本身仍将成为性能瓶颈。
2.4 生成号段,存在缓存中,不每次都去取数据库数据
CREATE TABLE id_generator (
id int(10) NOT NULL,
max_id bigint(20) NOT NULL COMMENT '当前最大id',
step int(20) NOT NULL COMMENT '号段的布长',
biz_typeint(20) NOT NULL COMMENT '业务类型',
version int(20) NOT NULL COMMENT '版本号', PRIMARY KEY (id
)
)
biz_type :代表不同业务类型
max_id :当前最大的可用id
step :代表号段的长度
version :乐观锁,每次都更新version,保证并发时数据的正确性
数据库服务挂了,重启服务后会导致当前id空洞,缺失一部分id
2.5 redis 时间戳+自增数
Redis 利用Redis的原子性操作自增,一般算法为:时间戳+ 自增Id, 补0
优点:有序递增,可读性强。
缺点:占用带宽,每次都要向Redis请求,容易猜到数据的意义,商业密码,涉及一些敏感数据的话不太合适,可能造成数据丢失。
注意:使用Redis需要考虑持久化问题,Redis有两种持久化方式RDB和AOF。
RDB可能会造成重复,某段时间可能没有持久化。AOF 不会丢失数据,但由于incr命令的特殊性,AOF恢复时间过长
2.6 雪花算法生成id:纯数字,按照时间生成
最高位是符号位,始终为0,不可用。
41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
10位的机器标识,10位的长度最多支持部署1024个节点。
12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。
优点:
毫秒数在高位,自增序列在低位,ID趋势递增。
不依赖第三方应用,本地生成,生成性能高。
可以根据自身业务特性分配bit位,非常灵活
缺点:强依赖机器时钟,时钟回拨可能会导致ID重复。
2.7美团(Leaf)
Leaf由美团开发,Leaf同时支持号段模式和Snowflake算法模式,可以切换使用。
3.分布式事务
CAP:一致性、可用性、分区容错性
CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。CAP原则是NOSQL数据库的基石。
分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
Base理论:
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写,BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
3.1两阶段提交:
只有协调者拥有超时机制
服务注册到事务协调器,分为事务协调者和参与者
第一次:预提交,占用数据库资源 (超时持有资源等待),做完业务,但是不提交
第二次:由协调者决定是提交或回滚(根据日志回滚),
缺点:
1.同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的,长时间占用资源。
2.单点故障问题,协调者可能会挂掉
3.可能有的参与者最终提交成功,有的参与者由于网络原因未收到提交请求,导致未提交事务,造成数据不一致,不能100%保证事务。
CAP(三者不可兼得)
3.2三阶段提交(tcc):
三阶段提交协议在协调者和参与者中都引入超时机制,将两阶段的第一阶段分为两步:询问,然后再锁资源
准备阶段(CanCommit):超时取消(询问参与者是否可以正常执行事务)
预提交阶段(PreCommit):失败不断重试(分布式事务开始执行,记录日志,先不提交)
提交阶段(DoCommit):超时默认提交(提交或者回滚)
Tcc (try confirm cancel):
try:插入数据,修改数据,直接提交本地事务(搞一个中间状态的数据)
confirm :协调者确认是否全部提交成功
cancel:取消,做逆操作,如果try是插入操作,则取消是删除数据;修改就修改回之前的数据
3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit
优点:效率高
缺点:事务补偿比较复杂,导致要写大量业务的补偿代码,对开发人员要求比较高,数据不一致,人工补偿(脚本),事务补偿,安全性,补偿可能会失败
3.3可靠消息服务(单独有一个消息服务)
对事务拆分成多个本地事务,通过mq发送消息,失败了不断重试
通过消息队列维护事件消息表,将消息持久化到db,失败了就不断重试,并更新消息状态。
缺点:无法回滚,从业务不能保证主业务的回滚,时效性差,比如网上转账这种。
rocketMQ 支持事务消息,搭建集群保证高可用。
3.4 AT模式(seata)
seata两阶段提交(第一阶段直接提交事务(包含日志信息和业务数据,如果出现问题,直接根据日志回滚业务数据),不占用资源)
拦截sql,查出指定条件的数据,保存结果镜像(类似undolog)
执行完业务sql,再次查询指定条件数据,保存结果(类似redolog),如果事务全部成功,删除结果镜像,否则根据镜像回滚。
也是分阶段提交,分了阶段提交
一阶段直接提交
二阶段提交,失败了框架直接自己会回滚
缺点:有性能的影响
TM:事务管理器(和中间件通信)
RM:资源管理器(数据库、依赖的服务等)