欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis增減庫(kù)存避坑的實(shí)現(xiàn)

 更新時(shí)間:2024年02月26日 11:29:03   作者:封閉火車  
在電商平臺(tái)或者倉(cāng)庫(kù)管理系統(tǒng)中,庫(kù)存的管理是非常重要的一項(xiàng)任務(wù),本文主要介紹了Redis增減庫(kù)存避坑的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下

Redis實(shí)現(xiàn)庫(kù)存管理

查詢商品庫(kù)存數(shù)量

首先,我們可以使用Redis的String類型來(lái)存儲(chǔ)商品的庫(kù)存數(shù)量。每個(gè)商品對(duì)應(yīng)一個(gè)key,其值為庫(kù)存數(shù)量。當(dāng)需要查詢商品庫(kù)存數(shù)量時(shí),只需要獲取相應(yīng)key的值即可。

# 獲取商品庫(kù)存數(shù)量
def get_stock(product_id):
    redis_conn = Redis()
    stock = redis_conn.get(f'stock:{product_id}')
    return int(stock) if stock else 0

更新商品庫(kù)存數(shù)量

當(dāng)有人購(gòu)買商品時(shí),我們需要更新商品的庫(kù)存數(shù)量。為了保證庫(kù)存的準(zhǔn)確性,我們可以使用Redis的原子操作INCRBY或者DECRBY來(lái)實(shí)現(xiàn)庫(kù)存數(shù)量的增減。

# 更新商品庫(kù)存數(shù)量
def update_stock(product_id, quantity):
    redis_conn = Redis()
    redis_conn.incrby(f'stock:{product_id}', quantity)

判斷商品庫(kù)存是否充足

為了判斷商品庫(kù)存是否充足,我們只需要查詢商品的庫(kù)存數(shù)量并與購(gòu)買數(shù)量進(jìn)行比較即可。

# 判斷商品庫(kù)存是否充足
def check_stock(product_id, quantity):
    stock = get_stock(product_id)
    return stock >= quantity

避免超賣問(wèn)題

在高并發(fā)的情況下,可能會(huì)出現(xiàn)超賣問(wèn)題,即多個(gè)用戶同時(shí)購(gòu)買了同一件商品,導(dǎo)致庫(kù)存數(shù)量出現(xiàn)負(fù)數(shù)。為了避免這個(gè)問(wèn)題,我們可以使用Redis的WATCH機(jī)制來(lái)保證庫(kù)存數(shù)量的原子性。

# 避免超賣問(wèn)題
def avoid_over_sell(product_id, quantity):
    redis_conn = Redis()
    with redis_conn.pipeline() as pipe:
        while True:
            try:
                pipe.watch(f'stock:{product_id}')
                stock = pipe.get(f'stock:{product_id}')
                stock = int(stock) if stock else 0
                if stock < quantity:
                    pipe.unwatch()
                    raise Exception('庫(kù)存不足')
                pipe.multi()
                pipe.decrby(f'stock:{product_id}', quantity)
                pipe.execute()
                break
            except WatchError:
                continue

問(wèn)題

先執(zhí)行g(shù)et獲取值,判斷符合條件再執(zhí)行incr、decr操作。在臨界緩存失效的情況下,會(huì)默認(rèn)賦值當(dāng)前key為永不過(guò)期的0,再執(zhí)行加減法,導(dǎo)致程序異常。

推薦解決方案

1、限制接口頻率:先incr,執(zhí)行后值為1,說(shuō)明是第一次執(zhí)行,需要額外設(shè)置過(guò)期時(shí)間,再判斷是否超過(guò)當(dāng)前接口頻率限制(注意上述步驟不可調(diào)換順序)

2、使用lua腳本完整提交一次操作,腳本中的key可以保證一致。以加減庫(kù)存為例,先查詢key存在的情況下,再進(jìn)行庫(kù)存變更,如果不存在無(wú)需處理,等待下次緩存加載即為最新的值

問(wèn)題描述

場(chǎng)景1:我們緩存了一個(gè)商品的庫(kù)存,過(guò)期時(shí)間為5分鐘,根據(jù)用戶的購(gòu)買和取消執(zhí)行 incr、decr 操作。代碼通常會(huì)這樣來(lái)編寫:

		// 庫(kù)存存在則加一
        if(redisService.get(prefix, key, Integer.class) != null){
            redisService.incr(prefix, key);
        }

場(chǎng)景2:對(duì)訪問(wèn)頻次進(jìn)行限流,我們可以通過(guò)redis簡(jiǎn)單實(shí)現(xiàn):

        // 首先獲取當(dāng)前訪問(wèn)頻次
		Integer count = redisService.get(prefix, key, Integer.class);
        // 如果頻次為空,則設(shè)置訪問(wèn)次數(shù)為1
		if (count == null) {
            redisService.set(prefix, key, 1);
        } else if (count < checkFrequencyCount) {
            // 如果頻次小于限制,則設(shè)置訪問(wèn)次數(shù)加1
            redisService.incr(prefix, key);
        } else {
			// 如果頻次超過(guò)限制,則限流
            throw new AppException("訪問(wèn)頻次過(guò)高,請(qǐng)稍候再試");
        }

兩種場(chǎng)景編碼看似都沒(méi)有問(wèn)題,但實(shí)際運(yùn)行中卻發(fā)現(xiàn)redis中有一些key變成了永不過(guò)期的key,而且值不正確。

原因是: 因?yàn)閞edis的incr操作,當(dāng)key不存在時(shí), 會(huì)生成這個(gè)key并將值初始化為0, 并且默認(rèn)設(shè)置key的有效時(shí)間為永久。

解決方案

1.優(yōu)化Java代碼,例如場(chǎng)景2。不論這個(gè)key是否存在都先加一,然后判斷其過(guò)期時(shí)間是否為永不過(guò)期,如果是永不過(guò)期則說(shuō)明是新生成的key,給它設(shè)置過(guò)期時(shí)間即可,如果非永不過(guò)期則無(wú)需操作。最后再判斷一下是否值已經(jīng)大于訪問(wèn)頻次了,是則限流。

		long count = redisService.incr(prefix, key);
		// 判斷必須放在后面,否則key沒(méi)有過(guò)期時(shí)間永遠(yuǎn)無(wú)法清除
        long expire = redisService.ttl(prefix, key);
        if (expire == -1) {
            redisService.setExpire(prefix, key, accessExpireSecond);
        }
        if (count > checkFrequencyCount) {
            throw new AppException("訪問(wèn)頻次過(guò)高,請(qǐng)稍候再試");
        }

2.使用lua腳本執(zhí)行,保證原子性。

腳本updateStore.lua

--- 獲取key
local key = KEYS[1]
--- 獲取參數(shù):incr、decr
local action = ARGV[1]
--- 如果key存在,再執(zhí)行增加或減少的操作
if redis.call('exists', key) == 1 
then redis.call(action, key)
    return true
end 
return false

配置LuaConfiguration.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;

@Configuration
public class LuaConfiguration {
    @Bean(name = "update")
    public DefaultRedisScript<Boolean> redisScript() {
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("luascript/updateStore.lua")));
        redisScript.setResultType(Boolean.class);
        return redisScript;
    }
}

使用方法:

    @Resource(name = "update")
    private DefaultRedisScript<Boolean> redisScript;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
	// 執(zhí)行腳本并傳參
	Boolean result = stringRedisTemplate.execute(redisScript, Arrays.asList(stockPrefix.getPrefix() + key), "incr");

到此這篇關(guān)于Redis增減庫(kù)存避坑的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Redis增減庫(kù)存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • Win10配置redis服務(wù)實(shí)現(xiàn)過(guò)程詳解

    Win10配置redis服務(wù)實(shí)現(xiàn)過(guò)程詳解

    這篇文章主要介紹了Win10配置redis服務(wù)實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • 在K8s上部署Redis集群的方法步驟

    在K8s上部署Redis集群的方法步驟

    這篇文章主要介紹了在K8s上部署Redis集群的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 如何查看redis服務(wù)的版本

    如何查看redis服務(wù)的版本

    這篇文章主要介紹了如何查看redis服務(wù)的版本問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 配置Redis序列化方式不生效問(wèn)題及解決

    配置Redis序列化方式不生效問(wèn)題及解決

    這篇文章主要介紹了配置Redis序列化方式不生效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 利用redis實(shí)現(xiàn)分布式鎖,快速解決高并發(fā)時(shí)的線程安全問(wèn)題

    利用redis實(shí)現(xiàn)分布式鎖,快速解決高并發(fā)時(shí)的線程安全問(wèn)題

    這篇文章主要介紹了利用redis實(shí)現(xiàn)分布式鎖,快速解決高并發(fā)時(shí)的線程安全問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01
  • Redis和Memcached的區(qū)別詳解

    Redis和Memcached的區(qū)別詳解

    這篇文章主要介紹了Redis和Memcached的區(qū)別詳解,本文從各方面總結(jié)了兩個(gè)數(shù)據(jù)庫(kù)的不同之處,需要的朋友可以參考下
    2015-03-03
  • Redis線程模型的原理分析

    Redis線程模型的原理分析

    Redis是一個(gè)高性能的數(shù)據(jù)存儲(chǔ)框架,在高并發(fā)的系統(tǒng)設(shè)計(jì)中,Redis也是一個(gè)比較關(guān)鍵的組件,是我們提升系統(tǒng)性能的一大利器,本文詳細(xì)的介紹了Redis線程模型,感興趣的可以了解一
    2021-11-11
  • Ubuntu系統(tǒng)中Redis的安裝步驟及服務(wù)配置詳解

    Ubuntu系統(tǒng)中Redis的安裝步驟及服務(wù)配置詳解

    本文主要記錄了Ubuntu服務(wù)器中Redis服務(wù)的安裝使用,包括apt安裝和解壓縮編譯安裝兩種方式,并對(duì)安裝過(guò)程中可能出現(xiàn)的問(wèn)題、解決方案進(jìn)行說(shuō)明,以及在手動(dòng)安裝時(shí),服務(wù)器如何添加自定義服務(wù)的問(wèn)題,需要的朋友可以參考下
    2024-12-12
  • 你真的了解redis為什么要提供pipeline功能

    你真的了解redis為什么要提供pipeline功能

    Redis本身是一個(gè)cs模式的tcp server, client可以通過(guò)一個(gè)socket連續(xù)發(fā)起多個(gè)請(qǐng)求命令。這篇文章帶領(lǐng)大家學(xué)習(xí)redis為什么要提供pipeline功能,需要的朋友可以參考下
    2021-06-06
  • Redis五種數(shù)據(jù)類型詳解

    Redis五種數(shù)據(jù)類型詳解

    Redis是基于內(nèi)存的 K-V 數(shù)據(jù)庫(kù),常用于緩存、消息隊(duì)列,分布式鎖等場(chǎng)景,并且提供了常見(jiàn)的數(shù)據(jù)結(jié)構(gòu):字符串、哈希、列表、集合、帶排序的集合,本文主要介紹了Redis的五種數(shù)據(jù)類型,感興趣的小伙伴可以參考閱讀本文
    2023-04-04

最新評(píng)論