推薦學習:
秒殺:解決計數器和人員記錄的事務操作
原因:由於大量建立連線,十分消耗效能,並且有時獲取連線不及時,出現連線超時的情況
在並行的情況下發生的,就是在輸出沒有庫存(秒殺結束)後還有商品售出導致庫存數量為負數。
使用樂觀鎖解決問題2之後,出現問題3
如果庫存數量相對並行更多,由於使用樂觀鎖,第一個使用者秒殺成功後會修改庫存鍵的版本號,其他搶到的使用者會因為版本號不同導致無法繼續購買,就會有庫存遺留問題
使用連線池,工具類如下:
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;
使用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;}
使用Lua嵌入式指令碼語言
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)<=0 then return 0; else //12-15,使用者和商品操作 redis.call("decr",qtkey); redis.call("sadd",usersKey,userid); end return1; //最後一行return 1; 秒殺成功
完整程式碼如下:
// 定義兩段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)<=0 then \r\n" + " return 0;\r\n" + "else \r\n" + " redis.call(\"decr\",qtkey);\r\n" + " redis.call(\"sadd\",usersKey,userid);\r\n" + "end\r\n" + "return 1" ; public static boolean doSecKill(String uid,String prodid) throws IOException { JedisPool jedispool = JedisPoolUtil.getJedisPoolInstance(); Jedis jedis=jedispool.getResource(); jedis.select(2); // 通過jedis的scriptLoad方法載入Lua指令碼 String sha1= jedis.scriptLoad(secKillScript); //通過jedis的evalsha方法呼叫Lua指令碼 Object result= jedis.evalsha(sha1, 2, uid,prodid); String reString=String.valueOf(result); if ("0".equals( reString ) ) { System.err.println("已搶空!!"); }else if("1".equals( reString ) ) { System.out.println("搶購成功!!!!"); }else if("2".equals( reString ) ) { System.err.println("該使用者已搶過!!"); }else{ System.err.println("搶購異常!!"); } jedis.close(); return true; }
推薦學習:
以上就是一起聊聊Redis實現秒殺的問題的詳細內容,更多請關注TW511.COM其它相關文章!