一、使用lua脚本扣减单个商品的库存
@SpringBootTest
class LuaTests { @Autowired StringRedisTemplate stringRedisTemplate; @Test void test3 ( ) { for ( int i = 1 ; i <= 5 ; i++ ) { stringRedisTemplate. opsForValue ( ) . set ( "product." + i, String . valueOf ( i) ) ; } } @Test void test2 ( ) { StringBuilder sb = new StringBuilder ( ) ; sb. append ( " local key = KEYS[1] " ) ; sb. append ( " local qty = ARGV[1] " ) ; sb. append ( " local redis_qty = redis.call('get',key) " ) ; sb. append ( " if tonumber(redis_qty) >= tonumber(qty) then " ) ; sb. append ( " redis.call('decrby',key,qty) " ) ; sb. append ( " return -1 " ) ; sb. append ( " else " ) ; sb. append ( " return tonumber(redis_qty) " ) ; sb. append ( " end " ) ; RedisScript < Long > luaScript = RedisScript . of ( sb. toString ( ) , Long . class ) ; ExecutorService executorService = Executors . newFixedThreadPool ( 5 ) ; for ( int i = 1 ; i <= 5 ; i++ ) { executorService. execute ( ( ) -> { int qty = RandomUtil . randomInt ( 1 , 6 ) ; Long count = stringRedisTemplate. execute ( luaScript, CollUtil . newArrayList ( "product.5" ) , String . valueOf ( qty) ) ; if ( count == - 1L ) { System . out. println ( Thread . currentThread ( ) . getId ( ) + " 扣减成功,扣减了-> " + qty) ; } else { System . out. println ( Thread . currentThread ( ) . getId ( ) + "扣减失败,需求量是:" + qty+ ",剩余库存量:" + count) ; } } ) ; } ThreadUtil . safeSleep ( 3000 ) ; }
}
二、使用lua脚本扣减多个商品的库存
@Test void test4 ( ) { StringBuilder sb = new StringBuilder ( ) ; sb. append ( " local table = {} " ) ; sb. append ( " local values = redis.call('mget', unpack(KEYS) )" ) ; sb. append ( " for i = 1, #KEYS do " ) ; sb. append ( " if tonumber(ARGV[i]) > tonumber(values[i]) then " ) ; sb. append ( " table[#table + 1] = KEYS[i] .. '=' .. values[i] " ) ; sb. append ( " end " ) ; sb. append ( " end " ) ; sb. append ( " if #table > 0 then " ) ; sb. append ( " return table " ) ; sb. append ( " end " ) ; sb. append ( " for i = 1 , #KEYS do " ) ; sb. append ( " redis.call('decrby',KEYS[i],ARGV[i]) " ) ; sb. append ( " end " ) ; sb. append ( " return {} " ) ; RedisScript < List > luaScript = RedisScript . of ( sb. toString ( ) , List . class ) ; List < StockProduct > stockProducts = new ArrayList < > ( ) ; stockProducts. add ( new StockProduct ( 5 , 1 ) ) ; stockProducts. add ( new StockProduct ( 4 , 2 ) ) ; List < String > keys = stockProducts. stream ( ) . map ( it -> "product." + it. getId ( ) ) . collect ( Collectors . toList ( ) ) ; Object [ ] qtys = stockProducts. stream ( ) . map ( it -> it. getQty ( ) + "" ) . toArray ( ) ; List < String > list = stringRedisTemplate. execute ( luaScript, keys, qtys) ; if ( list. isEmpty ( ) ) { System . out. println ( "库存冻结成功" ) ; } else { for ( String key_qty : list) { String [ ] split = key_qty. split ( "=" ) ; System . out. println ( split[ 0 ] + "库存不足,剩余库存量:" + split[ 1 ] ) ; } } ThreadUtil . safeSleep ( 3000 ) ; }
三、通过分布式锁,扣减商品的库存
package com. by ; import cn. hutool. core. date. DateUtil ;
import cn. hutool. core. thread. ThreadUtil ;
import cn. hutool. core. util. RandomUtil ;
import com. by. moder. Student ;
import org. junit. jupiter. api. Test ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. boot. test. context. SpringBootTest ;
import org. springframework. data. redis. core. RedisTemplate ;
import org. springframework. data. redis. core. StringRedisTemplate ;
import org. springframework. data. redis. core. ValueOperations ; import javax. annotation. Resource ;
import java. util. concurrent. Executor ;
import java. util. concurrent. ExecutorService ;
import java. util. concurrent. Executors ; @SpringBootTest
class SetNXTests { @Autowired StringRedisTemplate stringRedisTemplate; @Resource ( name = "redisTemplate" ) ValueOperations < String , String > valueOperations; @Test void Test ( ) { ExecutorService executorService = Executors . newFixedThreadPool ( 5 ) ; for ( int i = 1 ; i <= 5 ; i++ ) { executorService. execute ( ( ) -> { String ioId = "IO" + RandomUtil . randomInt ( 1 , 1000 ) ; while ( true ) { Boolean b = valueOperations. setIfAbsent ( "lock.product.1" , ioId+ ":" + DateUtil . now ( ) ) ; if ( b) { System . out. println ( Thread . currentThread ( ) . getId ( ) + "获取到了分布式锁" ) ; ThreadUtil . safeSleep ( 3000 ) ; stringRedisTemplate. delete ( "lock.product.1" ) ; System . out. println ( Thread . currentThread ( ) . getId ( ) + "释放了分布式锁" ) ; break ; } else { System . out. println ( Thread . currentThread ( ) . getId ( ) + "没有获取到分布式锁" ) ; ThreadUtil . safeSleep ( 1000 ) ; } } } ) ; } ThreadUtil . safeSleep ( 100000 ) ; }
}