一、概述
在如今高并发,分布式大行其道的今天,如果你还只会单体项目,那未免也太落伍了。撇开技术落伍,受人耻笑外(脸皮厚的人根本不在乎耻笑),更为现实的问题是:如果你是刚进入职场的新人,即将面临找工作,估计连面试机会都没有,如果你是已经在职的人士,不知晓分布式的各种成人姿势,那你也只有在公司任人玩弄的份。说到分布式这么重要,那今天我作为一个潜伏它圈多年的老将,跟大家分享下分布式下的分布式锁的各种成人姿势,注意是成人哦,未成年人勿入。
二,问题现场还原——秒杀系统下单功能
1。mysql数据库有2张表:股票(库存表),stock_order(订单表)。
2。后台通过弹簧引导构建下单的业务接口(下单流程=查库存——下单——减库存)。
3。打开浏览器正常业务流程再现,刷新多少次,卖出多少份皮蛋粥,没毛病。
4。使用压测工具(ab/jemter/loaderrunner)进行压力测试ab - n 100 - c 100 http://127.0.0.1:8080 second-kill3/技能/订单/123456
5。再次打开浏览器查看库存
各位看官,看到这个结果是不是有一种蛋碎一地的感觉! ! !怎么可能10000份皮蛋粥可以卖出(9989 + 109),如果你感觉奇怪,那说明你的技术已经了。好了到此场景还原就到此结束。接下来给各位介绍下解决这种问题的各种姿势。
三,姿势一:同步
了解多线程的同学肯定会想的到,并发线程安全问题,可以用jdk同步工具同步解决。正确的说法给大家更正下,叫做数据库丢失更新。能想到这里的我算你有点社会实践姿势,但是效果如何,请看:
1。给下单方法加同步,做同步。
2。继续压力测试
ab - n 100 - c 100 http://127.0.0.1:8080 second-kill3/技能/订单/123456
3。同步姿势总结:
1)是一种解决方案。
2)同步无法实现细粒度的锁。
在下单的方法中加同步会将所有商品下单都做同步,如果另外一件商品并没有很高并发量。也会导致很请求很慢,锁的粒度太大。
3)只适合单点情况。(而现实是高并发,分布式集群当道)
四,姿势二:分布式锁
接下来是我们的主角:分布式锁登场了。
分布式锁的三种实现方式:数据库分布式锁,复述,分布式锁,动物园管理员分布式锁。今天打算给大家介绍下复述分布式锁的实现方式。
1。安装复述,【不知道怎么安装的,请咨询我的官方秘书度娘】
2。maven工程中导入spring-redis依赖。
org.springframework。启动
spring-boot-starter-data-redis
3。编写redisLock实现加锁与解锁
org.springframework。引导spring-boot-starter-data-redis
3。编写redisLock实现加锁与解锁
/* *
加锁
@param关键
@param价值当前时间+超时时间
@return
*/
公共静态逻辑锁(字符串,字符串值){
//setIfAbsent=setNX如果不存在,就设置值并返回真,否返回假
//1,加锁成功如果(redisTemplate.opsForValue () .setIfAbsent(键,值)){返回true;}
//2,避免死锁(线程1加锁成功,结果在解锁前出现异常,没有解锁,导致死锁)
//2.1获取过期时间StringcurrentValue=https://www.yisu.com/zixun/redisTemplate.opsForValue () . get(关键);
//2.2判断过期时间于当前时间的关系
如果(! StringUtils.isEmpty (currentValue)
,, Long.parseLong (currentValue) & lt; System.currentTimeMillis()){字符串oldValue=https://www.yisu.com/zixun/redisTemplate.opsForValue () .getAndSet(键,值);如果(! StringUtils.isEmpty oldValue &&oldValue.equals (currentValue)){返回true;
}
}
//3,加锁失败t2结束返回假
返回false;
}
/* *
解锁
@param关键
@param值
*/
公共空间解锁(字符串,字符串值){