思路:
1.加锁:INSERT INTO tb_lock(lock_name) values ('lock') 执行成功代表获取锁成功
2.释放锁:获取锁成功的请求执行业务操作,执行完成之后通过delete删除对应记录
3.重试:递归
@Data @TableName("tb_lock") public class Lock { private Long id; private String lockName; }
public void deduct() { try { //加锁 Lock lock = new Lock(); lock.setLockName("lock"); this.lockMapper.insert(lock); //1.查询库存信息 String stock = redisTemplate.opsForValue().get("stock").toString(); //2.判断库存是否充足 if (stock != null && stock.length() != 0) { Integer st = Integer.valueOf(stock); if (st > 0) { //3.扣减库存 redisTemplate.opsForValue().set("stock", String.valueOf(--st)); } } //解锁 this.lockMapper.deleteById(lock.getId()); }catch (Exception e) { e.printStackTrace(); //重试 try{ Thread.sleep(50); this.deduct(); }catch (Exception exception){ exception.printStackTrace(); } } }
mysql分布式锁的性能比较差
lock_time 需要在数据库中加一个字段,给锁记录添加一个获取锁时间的列,防止死锁
server_id varchar 通过uuid给服务器生成一个uuid
thread_id 那个线程的id
count 重入次数减为0
防死锁:客户端程序获取到锁之后,客户端程序的服务器宕机。给锁记录添加一个获取锁时间的列。
防误删:借助于id的唯一性来防止误删
原子性:通过一个写操作 如果是多个操作可以借助于mysql的乐观锁或者悲观锁
可重入:可重入 ,记录服务信息以及线程信息,重入次数
自动续期:通过定时任务,只要服务活着,就去重置获取锁的时间。使用服务器内的定时器重置获取锁的系统时间。额外的定时器检查获取锁的系统时间和当前系统时间的差值是否超过了阈值。
单机故障:搭建mysql主备模式
集群情况下锁机制失效问题(依然存在问题,mysql中用分布式锁不太容易实现)
分布式锁(mysql、zookeeper 、redis)
1.简易程序: mysql 》redis(lua脚本) 》zk
2.性能:redis 》zk 》mysql
3.可靠性:zk 》redis =mysql
仅仅满足独占排他和对性能和可靠性要求不高的情况下,选择mysql分布式锁
如果追求极致性能选择:redis分布式锁
追求可靠性:zk