每一秒钟的时间都值得铭记

0%

Redis实现商品秒杀

商品秒杀是一个非常常见的场景,今天我们就使用Redis来实现商品秒杀功能。

为什么使用Redis?

  • Redis是一款非关系数据库,数据存储在内存中,存取数据速度非常快!
  • Redis是单线程的,即使在同一时间有多条命令操作数据库,这些命令依然只能排队等候。

SpringDataRedis

SpringDataRedis是一款Java语言实现的Redis数据库的操作API,它是SpringData系列的框架之一,专门用于操作Redis,而且可以和SpringBoot进行无缝整合。

怎么实现?

Redis可以存储五种数据结构,我们使用列表数据来实现商品秒杀功能。

  • 我们将商品信息作为列表结构的key值,给列表push商品库存个数的value。
  • 当商品秒杀的时候,我们使用pop命令从列表中取出数据即可。

具体实现

搭建开发环境

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
<!--SpringBoot为父项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>

<dependencies>
<!--测试功能的启动器,用于整合junit测试功能-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Spring-Data-Redis的启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--lombox插件的依赖,SpringBoot内置,无须标注版本号-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

SpringBoot项目的启动类

1
2
3
4
5
6
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class);
}
}

Dao层

  • Dao层接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface CommodityDao {
/**
* 将商品加入库存
* @param key
* @param value
*/
void addStock(String key,String value);

/**
* 秒杀商品
* @param key
* @return
*/
String spike(String key);
}
  • Dao层实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Repository
public class CommodityDaoImpl implements CommodityDao {

@Autowired
private RedisTemplate redisTemplate;

@Override
public void addStock(String key,String value) {
redisTemplate.boundListOps(key).leftPush(value);
}

@Override
public String spike(String key) {
return (String) redisTemplate.boundListOps(key).leftPop();
}
}

Service层

  • Service层接口
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface CommodityService {
/**
* 将商品加入库存
* @param stock
*/
void addStock(int stock);

/**
* 秒杀商品
* @return
*/
String spike() ;
}
  • Service层实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
public class CommodityServiceImpl implements CommodityService {

@Autowired
private CommodityDao commodityDao;

private final static String KEY = "commodity";

@Override
public void addStock(int stock) {
for(int i = 0; i < stock; i++) {
commodityDao.addStock(KEY,"智能手表");
}
}

@Override
public String spike() {
return commodityDao.spike(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
56
57
58
59
60
61
62
63
64
@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisTest {

@Autowired
private CommodityService commodityService;

/**
* 执行商品秒杀前,将商品存入数据库中,已存入5件商品
*/
@Before
public void addStock() {
commodityService.addStock(5);
}

/**
* 商品秒杀测试,使用两个线程一起秒杀
*/
@Test
public void spike() {
ExecutorService es = Executors.newFixedThreadPool(3);
Object obj = new Object();
es.submit(() -> {
System.out.println(Thread.currentThread().getName() + "正在秒杀");
while (true) {
String commodity = commodityService.spike();
if (commodity != null) {
System.out.println(Thread.currentThread().getName() + "抢到了商品" + commodity);
} else {
System.out.println("商品被抢完了");
break;
}
}
});
es.submit(() -> {
System.out.println(Thread.currentThread().getName() + "正在秒杀");
while (true) {
String commodity = commodityService.spike();
if (commodity != null) {
System.out.println(Thread.currentThread().getName() + "抢到了商品" + commodity);
} else {
System.out.println("商品被抢完了");
break;
}
}
});
}

/**
* 单线程环境秒杀
*/
@Test
public void del() {
while (true) {
String commodity = commodityService.spike();
if (commodity != null) {
System.out.println(Thread.currentThread().getName() + "抢到了商品" + commodity);
} else {
System.out.println("商品被抢完了");
break;
}
}
}
}
坚持原创技术分享,您的支持将鼓励我继续创作!
-------------这是我的底线^_^-------------