首頁  >  文章  >  資料庫  >  一起聊聊Redis實現秒殺的問題

一起聊聊Redis實現秒殺的問題

WBOY
WBOY轉載
2022-05-27 11:40:343151瀏覽

這篇文章為大家帶來了關於Redis的相關知識,其中主要介紹了關於實現秒殺的相關內容,包括了秒殺邏輯、存在的連結超時、超賣和庫存遺留的問題,下面一起來看一下,希望對大家有幫助。

一起聊聊Redis實現秒殺的問題

推薦學習:Redis影片教學

#1、秒殺邏輯

##秒殺:解決計數器和人員記錄的事務操作

    1.uid和proid非空判斷
  1. 2.連接redis
  2. 3.拼接key
    • 庫存key
    • 秒殺成功用戶key
  3. #4.取得庫存,如果庫存為null,秒殺還沒開始
  4. 5.判斷使用者是否重複秒殺操作
  5. 6.判斷商品數量,庫存數量小於1,秒殺結束
  6. 7.秒殺過程
    • #庫存-1
    • 把秒殺成功使用者加入清單裡面
2、存在問題

2.1、連線逾時

原因:由於大量創建連接,十分消耗性能,有時獲取連接不及時,出現連接超時的情況

2.2、超賣

在並發的情況下發生的,就是在輸出沒有庫存(秒殺結束)後還有商品售出導致庫存數量為負數。


一起聊聊Redis實現秒殺的問題

2.3、庫存遺留

使用樂觀鎖定解決問題2之後,出現問題3

如果庫存數量相對並發更多,由於使用樂觀鎖,第一個用戶秒殺成功後會修改庫存鍵的版本號,其他搶到的用戶會因為版本號不同導致無法繼續購買,就會有庫存遺留問題

3、解決

3.1、連接逾時

使用連接池,工具類別如下:

public class JedisPoolUtil {
	private static volatile JedisPool jedisPool = null;
	private JedisPoolUtil() {
	}
	public static JedisPool getJedisPoolInstance() {
		if (null == jedisPool) {
			synchronized (JedisPoolUtil.class) {
				if (null == jedisPool) {
					JedisPoolConfig poolConfig = new JedisPoolConfig();
					poolConfig.setMaxTotal(200);
					poolConfig.setMaxIdle(32);
					poolConfig.setMaxWaitMillis(100 * 1000);
					poolConfig.setBlockWhenExhausted(true);
					poolConfig.setTestOnBorrow(true);
					jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379, 60000);
				}
			}
		}
		return jedisPool;
	}}//使用JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();Jedis jedis = jedisPoolInstance.getResource();
springBoot版本(pom.xml引入,application.yml配置,然後注入物件即可)

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-data-redis</artifactid></dependency><dependency>
    <groupid>redis.clients</groupid>
    <artifactid>jedis</artifactid>
    <version>3.2.0</version></dependency>
spring:
  redis:
    host: 127.0.0.1    port: 6379
    database: 0
    timeout: 1800000
    lettuce:
      pool:
        max-active: 20
        max-wait: -1
        max-idle: 5
        min-idle: 0
    @Autowired
    private RedisTemplate redisTemplate;
3.2、超賣問題

使用Redis事務,樂觀鎖定watch

//监视库存
jedis.watch(kcKey);//中间代码忽略

//7 秒杀过程
//使用事务
Transaction multi = jedis.multi();//组队操作
multi.decr(kcKey);multi.sadd(userKey,uid);//执行
List<object> results = multi.exec();if(results == null || results.size()==0) {
    System.out.println("秒杀失败了....");
    jedis.close();
    return false;}</object>
3.3、樂觀鎖導致的庫存遺留問題

#使用Lua嵌入式腳本語言

    將複雜的或多步驟的Redis 操作,寫為腳本,一次提交給Redis運行,減少重複連接reids的次數。提升性能。
  1. LUA腳本是類似redis 事務,有一定的原子性,不會被其他命令插隊,可以完成redis事務性的操作
  2. LUA腳本功能,在Redis 2.6以上的版本才可以使用
  3. 利用lua 腳本淘汰用戶,解決超賣問題。
  4. redis 2.6 版本以後,透過
  5. lua 腳本解決爭搶問題,實際上是 redis 利用其單執行緒的特性,用任務佇列的方式解決多任務並發問題
  6. local userid=KEYS[1];				//1、2行定义两个变量,					
    local prodid=KEYS[2];
    local qtkey="sk:"..prodid..":qt";	//3,4行定义拼接key
    local usersKey="sk:"..prodid..":usr";
    local userExists=redis.call("sismember",usersKey,userid); //5-8,判断用户是否存在,不存在return 2
    if tonumber(userExists)==1 then
        return2;
    end
    local num=redis.call("get",qtkey);	//9-11,判断商品是否存在
    if tonumber(num)
完整程式碼如下:

// 定义两段Lua脚本(使用Lua脚本可以解决乐观锁带来的库存遗留问题)
	static String secKillScript =
			"local userid=KEYS[1];\r\n" +
					"local prodid=KEYS[2];\r\n" +
					"local qtkey='sk:'..prodid..\":qt\";\r\n" +
					"local usersKey='sk:'..prodid..\":usr\";\r\n" +
					"local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" +
					"if tonumber(userExists)==1 then \r\n" +
					"   return 2;\r\n" +
					"end\r\n" +
					"local num= redis.call(\"get\" ,qtkey);\r\n" +
					"if tonumber(num)推薦學習:<p>Redis影片教學<a href="https://www.php.cn/course/list/54.html" target="_blank"></a></p>

以上是一起聊聊Redis實現秒殺的問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除