亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Redis中的事務(wù)和Redis樂觀鎖詳解

 更新時(shí)間:2023年12月11日 10:27:45   作者:warybee  
這篇文章主要介紹了Redis中的事務(wù)和Redis樂觀鎖詳解,Redis事務(wù)是一個(gè)單獨(dú)的隔離操作:事務(wù)中的所有命令都會(huì)序列化、按順序地執(zhí)行,事務(wù)在執(zhí)行的過程中,不會(huì)被其他客戶端發(fā)送來的命令請(qǐng)求所打斷,需要的朋友可以參考下

1 Redis事務(wù)介紹

Redis事務(wù)是一個(gè)單獨(dú)的隔離操作:事務(wù)中的所有命令都會(huì)序列化、按順序地執(zhí)行。

事務(wù)在執(zhí)行的過程中,不會(huì)被其他客戶端發(fā)送來的命令請(qǐng)求所打斷。

  • Redis的事務(wù)是通過multi、exec、discard和watch這四個(gè)命令來完成的。
  • Redis的單個(gè)命令都是原子性的,所以這里需要確保事務(wù)性的對(duì)象是命令集合。
  • Redis將命令集合序列化并確保處于同一事務(wù)的命令集合連續(xù)且不被打斷的執(zhí)行。
  • Redis不支持回滾操作

1.1 命令介紹

  • multi:用于標(biāo)記事務(wù)塊的開始,Redis會(huì)將后續(xù)的命令逐個(gè)放入隊(duì)列中,然后使用exec原子化執(zhí)行這個(gè)命令隊(duì)列 。
  • exec:執(zhí)行命令隊(duì)列
  • discard:清除命令隊(duì)列
  • watch:在執(zhí)行multi之前,先執(zhí)行watch key1 [key2],可以監(jiān)視一個(gè)(或多個(gè)) key ,如果在事務(wù)執(zhí)行之前這個(gè)(或這些) key 被其他命令所改動(dòng),那么事務(wù)將被打斷(可以利用Watch特性實(shí)現(xiàn)Redis樂觀鎖)
  • unwatch:取消 WATCH 命令對(duì)所有 key 的監(jiān)視(如果在執(zhí)行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被執(zhí)行了的話,那么就不需要再執(zhí)行UNWATCH 了)。

1.2 事務(wù)流程

從輸入multi命令開始,輸入的命令都會(huì)依次進(jìn)入命令隊(duì)列中,但不會(huì)執(zhí)行,直到輸入exec命令后,redis會(huì)將之前的命令隊(duì)列中的命令依次執(zhí)行。

  • 組隊(duì)的過程中可以通過discard來放棄組隊(duì)。
  • 如果組隊(duì)中某個(gè)命令出現(xiàn)了報(bào)告錯(cuò)誤,執(zhí)行時(shí)整個(gè)的所有隊(duì)列都會(huì)被取消。
  • 如果執(zhí)行階段某個(gè)命令報(bào)出了錯(cuò)誤,則只有報(bào)錯(cuò)的命令不會(huì)被執(zhí)行,而其他的命令都會(huì)執(zhí)行,不會(huì)回滾。

在這里插入圖片描述

在這里插入圖片描述

上圖說明:

  • 1.客戶端1watch user:001 ,
  • 2.客戶端1 開啟事務(wù)multi
  • 3.客戶端1,執(zhí)行命令set user:001 lisi
  • 4.在客戶端1,執(zhí)行exec之前,客戶端2,執(zhí)行set user:001 xiaoming
  • 5.客戶端1,執(zhí)行exec出錯(cuò),事務(wù)被打斷

命令演示:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set user:001 zhangsan
QUEUED
127.0.0.1:6379> set user:002 lisi
QUEUED
127.0.0.1:6379> get user:001
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "zhangsan"


127.0.0.1:6379> multi
OK
127.0.0.1:6379> set user:001 xiaoming
QUEUED
127.0.0.1:6379> set user:002 xiaozhang
QUEUED
127.0.0.1:6379> discard    # 使用discard命令取消隊(duì)列
OK
127.0.0.1:6379> exec    # 執(zhí)行exec報(bào)錯(cuò)
(error) ERR EXEC without MULTI

# watch 命令演示

# 客戶端2,在客戶端1執(zhí)行exec之前,執(zhí)行 set user:001 xiaoming,客戶端1的事務(wù)被打斷
127.0.0.1:6379> get user:001
"zhangsan"
127.0.0.1:6379> watch user:001
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set user:001 lisi
QUEUED
127.0.0.1:6379> exec   # 客戶端1的事務(wù)被打斷
(nil)
127.0.0.1:6379> get user:001
"xiaoming"


2 Redis實(shí)現(xiàn)樂觀鎖

2.1 樂觀鎖與悲觀鎖介紹

悲觀鎖

悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)被阻塞直到它拿到鎖。

傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)里邊就用到了很多這種鎖機(jī)制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。

樂觀鎖

樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒有去更新這個(gè)數(shù)據(jù),可以使用版本號(hào)等機(jī)制。

樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量。Redis就是利用這種check-and-set機(jī)制實(shí)現(xiàn)事務(wù)的。

2.2 Redis樂觀鎖實(shí)現(xiàn)原理

Redis樂觀鎖的實(shí)現(xiàn),是利用watch命令特性。數(shù)據(jù)進(jìn)行提交更新的時(shí)候,對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè),如果發(fā)現(xiàn)沖突了,則讓返回用戶錯(cuò)誤的信息,讓用戶決定如何去做。

Redis通過數(shù)據(jù)版本(Version)記錄機(jī)制實(shí)現(xiàn)樂觀鎖,這是樂觀鎖最常用的一種實(shí)現(xiàn)方式。

客戶端1客戶端2
age字段初始版本為1age字段初始版本為1
watch age multi
set age 25 版本加一,目前數(shù)據(jù)庫(kù)版本為2
set age 30 exec 當(dāng)前操作版本為1,小于數(shù)據(jù)中版本,提交失敗。

客戶端2在客戶端1提交事務(wù)之前,對(duì)據(jù)庫(kù)版本version進(jìn)行更新一次,客戶端1事務(wù)提交的時(shí)候?qū)Ρ劝姹咎?hào),要是此次版本號(hào)低于數(shù)據(jù)庫(kù)當(dāng)前版本號(hào),就會(huì)提交失敗。

2.3 Redis樂觀鎖秒殺案例

創(chuàng)建Spring boot項(xiàng)目引入以下依賴

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

配置文件

spring:
  redis:
    host: 192.168.235.131
    port: 6379
    database: 0
    connect-timeout: 1800000
    password: 123456
    lettuce:
      pool:
        #連接池最大連接數(shù)(使用負(fù)值表示沒有限制)
        max-active: 20
        #最大阻塞等待時(shí)間(負(fù)數(shù)表示沒限制)
        max-wait: -1
        #連接池中的最大空閑連接
        max-idle: 8
        #連接池中的最小空閑連接
        min-idle: 0

service 代碼

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    //庫(kù)存
    private final String STOCK_KEY="stock:num";
    //秒殺成功的用戶
    private final String USER_KEY="success:user";
    /**
     * Redis樂觀鎖秒殺案例
     * @param userId  用戶ID
     * @return
     */
    @Override
    public boolean secKill(String userId) {
        Object stockObj = redisTemplate.opsForValue().get(STOCK_KEY);
        if (stockObj==null){
            log.info("庫(kù)存為空,秒殺還未開始!");
            return false;
        }
        int stockNum=Integer.parseInt(stockObj.toString());
        if (stockNum<=0){
            log.info("庫(kù)存為0,秒殺已經(jīng)結(jié)束!");
            return false;
        }
        //判斷當(dāng)前用戶是否已經(jīng)秒殺成功
        Boolean member = redisTemplate.opsForSet().isMember(USER_KEY, userId);
        if (member){
            log.info("您已經(jīng)秒殺成功,不能重復(fù)參與!");
            return false;
        }
        List txList =redisTemplate.execute(new SessionCallback<List<Object>>() {
           @Override
           public List<Object> execute(RedisOperations operations) throws DataAccessException {
               //監(jiān)聽?zhēng)齑?
                operations.watch(STOCK_KEY);
               //開啟事務(wù)
               operations.multi();
               //扣減庫(kù)存
               operations.opsForValue().decrement(STOCK_KEY);
               //把秒殺成功的用戶加入到set集合
               operations.opsForSet().add(USER_KEY,userId);
               //執(zhí)行事務(wù)
               List<Object> result=operations.exec();
               return result;
           }
       });
        if (txList==null||txList.size()==0){
            log.info("用戶:{},秒殺失敗",userId);
            return false;
        }
        log.info("用戶:{},秒殺成功",userId);
        return true;
    }
}

Controller代碼

/**
 * 秒殺
 * @return
 */
@RequestMapping("secKill")
public String secKill(){
    String userId= UUID.randomUUID().toString();
    boolean res = orderService.secKill(userId);
    if (res){
        return "秒殺成功";
    }else {
        return "秒殺失敗";
    }
}

使用linux上的ab進(jìn)行并發(fā)測(cè)試:

ab -n 500 -c 100  http://192.168.1.171/order/secKill

到此這篇關(guān)于Redis中的事務(wù)和Redis樂觀鎖詳解的文章就介紹到這了,更多相關(guān)Redis事務(wù)和樂觀鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解JAVA 函數(shù)式編程

    詳解JAVA 函數(shù)式編程

    這篇文章主要介紹了JAVA 函數(shù)式編程的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • Java經(jīng)理與員工的差異實(shí)現(xiàn)方法

    Java經(jīng)理與員工的差異實(shí)現(xiàn)方法

    這篇文章主要介紹了Java經(jīng)理與員工的差異實(shí)現(xiàn)方法,需要的朋友可以參考下
    2014-03-03
  • Java中Session的詳解

    Java中Session的詳解

    這篇文章主要介紹了了解java中的session的相關(guān)問題,什么是session,session怎么用等,具有一定參考價(jià)值,需要的朋友可以了解下。
    2021-10-10
  • 關(guān)于mybatis plus 中的查詢優(yōu)化問題

    關(guān)于mybatis plus 中的查詢優(yōu)化問題

    這篇文章主要介紹了關(guān)于mybatis plus 中的查詢優(yōu)化問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • java實(shí)現(xiàn)簡(jiǎn)單年齡計(jì)算器

    java實(shí)現(xiàn)簡(jiǎn)單年齡計(jì)算器

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單年齡計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • 解決static類使用@Value獲取yml文件獲取不到的問題

    解決static類使用@Value獲取yml文件獲取不到的問題

    在靜態(tài)類中直接使用@Value注解無法獲取yml文件中的配置,解決方案是在工具類Utils中創(chuàng)建靜態(tài)的setter方法,并從外部類ServiceClass中調(diào)用這個(gè)方法來設(shè)置值,這種方法通過外部調(diào)用來間接設(shè)置靜態(tài)變量的值,從而成功讀取yml配置
    2024-09-09
  • Java Socket編程(二) Java面向連接的類

    Java Socket編程(二) Java面向連接的類

    Java Socket編程(二) Java面向連接的類...
    2006-12-12
  • jetty運(yùn)行時(shí)無法保存文件的解決方法

    jetty運(yùn)行時(shí)無法保存文件的解決方法

    這篇文章主要為大家詳細(xì)介紹了jetty運(yùn)行時(shí)無法保存文件的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • java使用RabbitMQ實(shí)現(xiàn)延遲消息示例

    java使用RabbitMQ實(shí)現(xiàn)延遲消息示例

    本文介紹了在分布式系統(tǒng)中,使用RabbitMQ實(shí)現(xiàn)延遲消息處理,其中詳細(xì)闡述了RabbitMQ隊(duì)列和交換機(jī)的配置、消息的發(fā)送與接收以及死信隊(duì)列的處理,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-10-10
  • Java線程休眠之sleep方法詳解

    Java線程休眠之sleep方法詳解

    這篇文章主要介紹了Java線程休眠之sleep方法詳解,Thread?類中有一個(gè)靜態(tài)方法的sleep方法,當(dāng)該線程調(diào)用sleep方法后,就會(huì)暫時(shí)讓CPU的調(diào)度權(quán),但是監(jiān)視器資源比如鎖并不會(huì)釋放出去,需要的朋友可以參考下
    2024-01-01

最新評(píng)論