- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Redis锁完美解决高并发秒杀问题由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
场景:一家网上商城做商品限量秒杀.
将商品的数量存到Redis中。每个用户抢购前都需要到Redis中查询商品数量(代替mysql数据库。不考虑事务),如果商品数量大于0,则证明商品有库存。然后我们在进行库存扣减和接下来的操作。因为多线程并发问题,我们不得不在get()方法内部使用同步代码块。这样可以保证查询库存和减库存操作的原子性.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package
springbootdemo.demo.controller;
/*
* @auther 顶风少年
* @mail dfsn19970313@foxmail.com
* @date 2020-01-13 11:19
* @notify
* @version 1.0
*/
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.data.redis.core.RedisTemplate;
import
org.springframework.web.bind.annotation.GetMapping;
import
org.springframework.web.bind.annotation.RestController;
@RestController
public
class
RedisLock {
@Autowired
private
RedisTemplate<String, String> redisTemplate;
@GetMapping
(value =
"buy"
)
public
String get() {
synchronized
(
this
) {
String phone = redisTemplate.opsForValue().get(
"phone"
);
Integer count = Integer.valueOf(phone);
if
(count >
0
) {
redisTemplate.opsForValue().set(
"phone"
, String.valueOf(count -
1
));
System.out.println(
"抢到了"
+ count +
"号商品"
);
}
return
""
;
}
}
}
|
但是由于业务上升,并发数量变大。公司不得不将原有系统复制一份,放到新的服务器。然后使用nginx做负载均衡。为了模拟高并发环境这里使用了 Apache JMeter工具.
很明显,现在的线程锁不管用了。于是我们需要换一把锁,这把锁必须和两套系统没有任何的耦合度.
使用Redies的API如果key不存在,则设置一个key。这个key就是我们现在使用的一把锁。每个线程到此处,先设置锁,如果设置锁失败,则表明当前有线程获取到了锁,就返回。最后我们为了减库存和其他业务抛出异常,而没有释放锁。把释放锁的操作放到了finally代码块中。看起来是比较完美了.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package
springbootdemo.demo.controller;
/*
* @auther 顶风少年
* @mail dfsn19970313@foxmail.com
* @date 2020-01-13 11:19
* @notify
* @version 1.0
*/
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.data.redis.core.RedisTemplate;
import
org.springframework.web.bind.annotation.GetMapping;
import
org.springframework.web.bind.annotation.RestController;
@RestController
public
class
RedisLock {
@Autowired
private
RedisTemplate<String, String> redisTemplate;
@GetMapping
(value =
"buy"
)
public
String get() {
Boolean phoneLock = redisTemplate.opsForValue().setIfAbsent(
"phoneLock"
,
""
);
if
(!phoneLock) {
return
""
;
}
try
{
String phone = redisTemplate.opsForValue().get(
"phone"
);
Integer count = Integer.valueOf(phone);
if
(count >
0
) {
redisTemplate.opsForValue().set(
"phone"
, String.valueOf(count -
1
));
System.out.println(
"抢到了"
+ count +
"号商品"
);
}
}
finally
{
redisTemplate.delete(
"phoneLock"
);
}
return
""
;
}
}
|
如果try中抛出了异常,进入finally,这把锁依然会释放,不会影响其他线程获取锁,那么如果在finally也抛出了异常,或者在finally中服务直接关闭了,那其他的服务再也获取不到锁。最终导致商品卖不出去.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package
springbootdemo.demo.controller;
/*
* @auther 顶风少年
* @mail dfsn19970313@foxmail.com
* @date 2020-01-13 11:19
* @notify
* @version 1.0
*/
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.data.redis.core.RedisTemplate;
import
org.springframework.web.bind.annotation.GetMapping;
import
org.springframework.web.bind.annotation.RestController;
@RestController
public
class
RedisLock {
@Autowired
private
RedisTemplate<String, String> redisTemplate;
@GetMapping
(value =
"buy"
)
public
String get() {
int
i =
0
;
Boolean phoneLock = redisTemplate.opsForValue().setIfAbsent(
"phoneLock"
,
""
);
if
(!phoneLock) {
return
""
;
}
try
{
String phone = redisTemplate.opsForValue().get(
"phone"
);
Integer count = Integer.valueOf(phone);
if
(count >
0
) {
i = count;
redisTemplate.opsForValue().set(
"phone"
, String.valueOf(count -
1
));
System.out.println(
"抢到了"
+ count +
"号商品"
);
}
}
finally
{
if
(i ==
20
) {
System.exit(
0
);
}
redisTemplate.delete(
"phoneLock"
);
}
return
""
;
}
}
|
问题就出现在如果出现意外,这把锁无法释放。这里我们在引入Redis的API,对key进行过期时间的设置。这样如果拿到锁的线程,在任何情况下没有来得及释放锁,当Redis的key时间到,也会自动释放锁。但是这样还是存在问题 。
如果在key过期后,锁释放了,但是当前线程没有执行完毕。那么其他线程就会拿到锁,继续抢购商品,而这个较慢的线程则会在执行完毕后,释放别人的锁。导致锁失效.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package
springbootdemo.demo.controller;
/*
* @auther 顶风少年
* @mail dfsn19970313@foxmail.com
* @date 2020-01-13 11:19
* @notify
* @version 1.0
*/
import
javafx.concurrent.Task;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.data.redis.core.RedisTemplate;
import
org.springframework.web.bind.annotation.GetMapping;
import
org.springframework.web.bind.annotation.RestController;
import
java.util.Timer;
import
java.util.TimerTask;
import
java.util.concurrent.TimeUnit;
@RestController
public
class
RedisLock {
@Autowired
private
RedisTemplate<String, String> redisTemplate;
@GetMapping
(value =
"buy"
)
public
String get() {
Boolean phoneLock = redisTemplate.opsForValue().setIfAbsent(
"phoneLock"
,
""
,
3
, TimeUnit.SECONDS);
if
(!phoneLock) {
return
""
;
}
try
{
String phone = redisTemplate.opsForValue().get(
"phone"
);
Integer count = Integer.valueOf(phone);
if
(count >
0
) {
try
{
Thread.sleep(99999999999L);
}
catch
(Exception e) {
}
redisTemplate.opsForValue().set(
"phone"
, String.valueOf(count -
1
));
System.out.println(
"抢到了"
+ count +
"号商品"
);
}
}
finally
{
redisTemplate.delete(
"phoneLock"
);
}
return
""
;
}
}
|
问题的出现就是,当一条线程的key已经过期,但是这个线程的任务确确实实没有执行完毕,这个交易没有结束。但是锁没了。现在我们必须对锁的时间进行延长。在判断商品有库存时,第一时间创建一个线程不停的给key续命, 。
防止key过期。然后在交易结束后,停止定时器,释放锁.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package
springbootdemo.demo.controller;
/*
* @auther 顶风少年
* @mail dfsn19970313@foxmail.com
* @date 2020-01-13 11:19
* @notify
* @version 1.0
*/
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.data.redis.core.RedisTemplate;
import
org.springframework.web.bind.annotation.GetMapping;
import
org.springframework.web.bind.annotation.RestController;
import
java.util.Timer;
import
java.util.TimerTask;
import
java.util.concurrent.TimeUnit;
@RestController
public
class
RedisLock {
@Autowired
private
RedisTemplate<String, String> redisTemplate;
@GetMapping
(value =
"buy"
)
public
String get() {
Boolean phoneLock = redisTemplate.opsForValue().setIfAbsent(
"phoneLock"
,
""
,
3
, TimeUnit.SECONDS);
if
(!phoneLock) {
return
""
;
}
Timer timer =
null
;
try
{
String phone = redisTemplate.opsForValue().get(
"phone"
);
Integer count = Integer.valueOf(phone);
if
(count >
0
) {
timer =
new
Timer();
timer.schedule(
new
TimerTask() {
@Override
public
void
run() {
redisTemplate.opsForValue().set(
"phoneLock"
,
""
,
3
, TimeUnit.SECONDS);
}
},
0
,
1
);
redisTemplate.opsForValue().set(
"phone"
, String.valueOf(count -
1
));
System.out.println(
"抢到了"
+ count +
"号商品"
);
}
}
finally
{
if
(timer !=
null
) {
timer.cancel();
}
redisTemplate.delete(
"phoneLock"
);
}
return
""
;
}
}
|
在步骤5我们的代码已经很完善了,不会出现高并发问题。但是代码确过于冗余,我们为了使用Redis锁,我们需要设置一个定长的key,然后当购买完成后,将key删除。但为了防止key提前过期,我们不得不新增一个线程执行定时任务。下面我们可以使用Redissson框架简化代码。getLock()方法代替了Redis的setIfAbsent(),lock()设置过期时间。最终我们在交易结束后释放锁。延长锁的操作则有Redisson框架替我们完成,它会使用轮询去查看key是否过期, 。
在交易没有完成时,自动重设Redis的key过期时间 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
package
springbootdemo.demo.controller;
/*
* @auther 顶风少年
* @mail dfsn19970313@foxmail.com
* @date 2020-01-13 11:19
* @notify
* @version 1.0
*/
import
org.redisson.Redisson;
import
org.redisson.api.RLock;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.data.redis.core.RedisTemplate;
import
org.springframework.web.bind.annotation.GetMapping;
import
org.springframework.web.bind.annotation.RestController;
import
java.util.Timer;
import
java.util.TimerTask;
import
java.util.concurrent.TimeUnit;
@RestController
public
class
RedissonLock {
@Autowired
private
RedisTemplate<String, String> redisTemplate;
@Autowired
private
Redisson redisson;
@GetMapping
(value =
"buy2"
)
public
String get() {
RLock phoneLock = redisson.getLock(
"phoneLock"
);
phoneLock.lock(
3
, TimeUnit.SECONDS);
try
{
String phone = redisTemplate.opsForValue().get(
"phone"
);
Integer count = Integer.valueOf(phone);
if
(count >
0
) {
redisTemplate.opsForValue().set(
"phone"
, String.valueOf(count -
1
));
System.out.println(
"抢到了"
+ count +
"号商品"
);
}
}
finally
{
phoneLock.unlock();
}
return
""
;
}
}
|
到此这篇关于Redis锁完美解决高并发秒杀问题的文章就介绍到这了,更多相关Redis锁高并发秒杀内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.cnblogs.com/zumengjie/p/12187669.html 。
最后此篇关于Redis锁完美解决高并发秒杀问题的文章就讲到这里了,如果你想了解更多关于Redis锁完美解决高并发秒杀问题的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在寻找一种方法来创建根据价格选择我的产品的过滤器(选择下拉菜单)。 我知道这样的查询是完全可能的: SELECT * FROM products ORDER BY price ASC SELECT
函数参数中或显示尺寸时(高度,宽度)的顺序是否有约定? 最佳答案 我不知道大量的语言,但我使用过的语言(宽度,高度)。它更适合沿着 (x, y) 坐标线。 关于language-agnostic -
在我的表单中,我让用户输入房间的长度高度和宽度以获得 m2、m3 和瓦特的计算值。但是用户也应该能够直接输入 height 和 m2 来获取值。我尝试了很多语法,但 if else 不能正常工作。我知
我在 Elasticsearch 中创建了一个索引,看起来像 {"amazingdocs":{"aliases":{},"mappings":{"properties":{"Adj Close":{"
我有以下功能,我需要清除数据库中的所有图片列并移动到文件系统。当我一次性完成这一切时,内存太多并且会崩溃。我切换到递归函数并执行 20 次写入和批量操作。 我需要为大约 6 个表执行此操作。我的 Re
我正在编写一个函数来计算 PI 的值,并将其作为 double 值返回。到目前为止,一切都很好。但是一旦函数到达小数点后14位,它就不能再保存了。我假设这是因为 double 有限。我应该怎么做才能继
2020年是中国CDN行业从98年诞生到今天快速发展的第二十四年,相关数据显示,全国感知网速持续上扬,达到了3.29兆/秒,标志着在宽带中国的政策指导下,中国的网速水平正在大步赶上世界发达国家的水平
在 aerospike 集合中,我们有四个 bin userId、adId、timestamp、eventype,主键是 userId:timestamp。在 userId 上创建二级索引以获取特定用
$('#container').highcharts('Map', { title : { text : 'Highmaps basic demo'
有没有办法显示自定义宽度/高度的YouTube视频? 最佳答案 在YouTube网站上的this link中: You can resize the player by editing the obj
我使用 Highcharts ,我想在 Highcharts 状态下悬停时制作动态不同的颜色。 正如你可以看到不同的颜色,这就是我做的 var usMapChart , data = [] ; va
在所有节点上运行 tpstats 后。我看到很多节点都有大量的 ALL TIME BLOCKED NTR。我们有一个 4 节点集群,NTR ALL TIME BLOCKED 的值为: 节点 1:239
我发现 APC 上存在大量碎片 (>80%),但实际上性能似乎相当不错。我有 read another post这建议在 wordpress/w3tc 中禁用对象缓存,但我想知道减少碎片是否比首先缓存
对于我的脚本类(class),我们必须制作更高/更低的游戏。到目前为止,这是我的代码: import random seedVal = int(input("What seed should be u
我发现 APC 上存在大量碎片 (>80%),但实际上性能似乎相当不错。我有 read another post这建议在 wordpress/w3tc 中禁用对象缓存,但我想知道减少碎片是否比首先缓存
对于我的脚本类(class),我们必须制作更高/更低的游戏。到目前为止,这是我的代码: import random seedVal = int(input("What seed should be u
我已经 seen >2 字节的 unicode 代码点,如 U+10000 可以成对编写,如 \uD800\uDC00。它们似乎以半字节 d 开头,但我只注意到了这一点。 这个 split Actio
有人可以帮我理解为什么我的饼图百分比计算不正确吗?看截图: 根据我的计算,如 RHS 上所示,支出百分比应为 24.73%。传递给 Highcharts 的值如下:- 花费:204827099.36-
我阅读了有关该问题的所有答案,但我还没有找到任何解决方案。 我有一个应用程序,由我的 api 服务器提供。 Wildfly 8.1 和 Mysql 5.6。当查看时间到来时(Wildfly 服务器连接
我正在用选定的项目创建圆形导航。当用户单击任何项目时,它将移动到定义的特定点。一切都很好,除了当你继续点击项目时,当动画表现不同并且项目在 360 度圆中移动并且它被重置直到你重复场景时,我希望它
我是一名优秀的程序员,十分优秀!