日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

Redis-09Redis的基础事务

發(fā)布時(shí)間:2025/3/21 数据库 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis-09Redis的基础事务 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 概述
  • Redis 事務(wù)命令
  • Redis 的基礎(chǔ)事務(wù)
  • 在 Spring 中使用 Redis 事務(wù)命令
  • 代碼

概述

和其他大部分的 NoSQL 不同,Redis 是存在事務(wù)的,盡管它沒(méi)有數(shù)據(jù)庫(kù)那么強(qiáng)大,但是它還是很有用的,尤其是在那些需要高并發(fā)的網(wǎng)站當(dāng)中 ,使用 Redis 讀/寫(xiě)數(shù)據(jù)要比數(shù)據(jù)庫(kù)快得多,如果使用 Redis 事務(wù)在某種場(chǎng)合下去替代數(shù)據(jù)庫(kù)事務(wù),則可以在保證數(shù)據(jù)一致性的同時(shí),大幅度提高數(shù)據(jù)讀/寫(xiě) 的響應(yīng)速度。

互聯(lián)網(wǎng)系統(tǒng)很多用戶同時(shí)訪問(wèn)服務(wù)器的可能性很大,尤其在一些商品搶購(gòu)、搶紅包等場(chǎng)合,對(duì)性能和數(shù)據(jù)的一致性有著很高的要求,而存儲(chǔ)系統(tǒng)的讀/寫(xiě)響應(yīng)速度對(duì)于這類場(chǎng)景的性能的提高是十分重要的 。

在 Redis 中,也存在多個(gè)客戶端同時(shí)向 Redis 系統(tǒng)發(fā)送命令的并發(fā)可能性,因此同一個(gè)數(shù)據(jù),可能在不同的時(shí)刻被不同的線程所操縱,這樣就出現(xiàn)了并發(fā)下的數(shù)據(jù)一致的問(wèn)題。為了保證異性數(shù)據(jù)的安全性, Redis 為提供了事務(wù)方案。而 Redis 的事務(wù)是使用 MULTI-EXEC的命令組合,使用它可以提供兩個(gè)重要的保證 :

  • 事務(wù)是一個(gè)被隔離的操作,事務(wù)中的方法都會(huì)被 Redis 進(jìn)行序列化并按順序執(zhí)行,事務(wù)在執(zhí)行的過(guò)程中不會(huì)被其他客戶端發(fā)生的命令所打斷。
  • 事務(wù)是一個(gè)原子性的操作,它要么全部執(zhí)行,要么就什么都不執(zhí)行。
  • 在一個(gè) Redis 的連接中,請(qǐng)注意要求是一個(gè)連接,所以更多的時(shí)候在使用 Spring 中會(huì)使用 SessionCallback 接口進(jìn)行處理,在 Redis 中使用事務(wù)會(huì)經(jīng)過(guò) 3 個(gè)過(guò)程

  • 開(kāi)啟事務(wù)
  • 命令進(jìn)入隊(duì)列
  • 執(zhí)行事務(wù)

  • Redis 事務(wù)命令

    官網(wǎng): https://redis.io/commands#transactions

    命令說(shuō)明備注
    multi開(kāi)啟事務(wù)命令,之后的命令就進(jìn)入隊(duì)列,而不會(huì)馬上被執(zhí)行在事務(wù)生存期間,所有的 Redis 關(guān)于數(shù)據(jù)結(jié)構(gòu)的命令都會(huì)入隊(duì)
    watch key1 [key2 …]監(jiān)聽(tīng)某些鍵,當(dāng)被監(jiān)聽(tīng)的鍵在事務(wù)執(zhí)行前被修改,則事務(wù)會(huì)被回滾使用樂(lè)觀鎖
    unwatch key1 [key2 …]取消監(jiān)聽(tīng)某些鍵-----
    exec執(zhí)行事務(wù),如果被監(jiān)聽(tīng)的鍵沒(méi)有被修改,則采用執(zhí)行命令,否則就回滾命令在執(zhí)行事務(wù)隊(duì)列存儲(chǔ)的命令前, Redis 會(huì)檢測(cè)被監(jiān)聽(tīng)的鍵值對(duì)有沒(méi)有發(fā)生變化,如果沒(méi)有則執(zhí)行命令 ,否則就回滾事務(wù)
    discard回滾事務(wù)回滾進(jìn)入隊(duì)列的事務(wù)命令,之后就不能再用 exec命令提交了

    Redis 的基礎(chǔ)事務(wù)

    在 Redis 中開(kāi)啟事務(wù)是 multi 命令,而執(zhí)行事務(wù)是 exec 命令。 multi 到 exec 命令之間的Redis 命令將采取進(jìn)入隊(duì)列的形式,直至 exec 命令的出現(xiàn),才會(huì)一次性發(fā)送隊(duì)列里的命令去執(zhí)行,而在執(zhí)行這些命令的時(shí)候其他客戶端就不能再插入任何命令了,這就是 Redis 的事務(wù)機(jī)制。

    [redis@artisan bin]$ ./redis-cli 127.0.0.1:6379> auth artisan OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set key1 value1 QUEUED 127.0.0.1:6379> get key1 QUEUED 127.0.0.1:6379> exec 1) OK 2) "value1" 127.0.0.1:6379>

    從上述命令中可以看到,先使用 multi 啟動(dòng)了 Redis 的事務(wù),因此進(jìn)入了 set 和 get 命令,我們可以發(fā)現(xiàn)它并未馬上執(zhí)行,而是返回了 一個(gè)飛回歸D”的結(jié)果。這說(shuō)明 Redis 將其放入隊(duì)列中,并不會(huì)馬上執(zhí)行,當(dāng)命令執(zhí)行到 exec 的時(shí)候它就會(huì)把隊(duì)列中的命令發(fā)送給Redis 服務(wù)器 , 這樣存儲(chǔ)在隊(duì)列中的命令就會(huì)被執(zhí)行了,所以才會(huì)"OK"和"value1"的輸出返回 。


    如果回滾事務(wù),則可以使用 discard 命令,它就會(huì)進(jìn)入在事務(wù)隊(duì)列中的命令,這樣事務(wù)中的方法就不會(huì)被執(zhí)行了,使用 discard 命令取消事務(wù)如下所示

    127.0.0.1:6379> MULTI OK 127.0.0.1:6379> SET key2 value2 QUEUED 127.0.0.1:6379> GET key2 QUEUED 127.0.0.1:6379> DISCARD OK 127.0.0.1:6379> EXEC (error) ERR EXEC without MULTI 127.0.0.1:6379>

    當(dāng)使用了 discard 命令后 ,再使用 exec 命令時(shí)就會(huì)報(bào)錯(cuò),因?yàn)?discard 命令已經(jīng)取消了事務(wù)中的命令,而到了 exec 命令時(shí),隊(duì)列里面己經(jīng)沒(méi)有命令可以執(zhí)行了,所以就出現(xiàn)了報(bào)錯(cuò)的情況。


    在 Spring 中使用 Redis 事務(wù)命令

    在 Spring 中要使用同一個(gè)連接操作 Redis 命令的場(chǎng)景,這個(gè)時(shí)候我們借助的是 Spring 提供的 SessionCallback 接口,采用 Spring 去實(shí)現(xiàn)上述的命令.

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="classpath:redis/redis.properties" /><!--2,注意新版本2.3以后,JedisPoolConfig的property name,不是maxActive而是maxTotal,而且沒(méi)有maxWait屬性,建議看一下Jedis源碼或百度。 --><!-- redis連接池配置 --><bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"><!--最大空閑數(shù) --><property name="maxIdle" value="${redis.maxIdle}" /><!--連接池的最大數(shù)據(jù)庫(kù)連接數(shù) --><property name="maxTotal" value="${redis.maxTotal}" /><!--最大建立連接等待時(shí)間 --><property name="maxWaitMillis" value="${redis.maxWaitMillis}" /><!--逐出連接的最小空閑時(shí)間 默認(rèn)1800000毫秒(30分鐘) --><property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" /><!--每次逐出檢查時(shí) 逐出的最大數(shù)目 如果為負(fù)數(shù)就是 : 1/abs(n), 默認(rèn)3 --><property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" /><!--逐出掃描的時(shí)間間隔(毫秒) 如果為負(fù)數(shù),則不運(yùn)行逐出線程, 默認(rèn)-1 --><property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" /><property name="testOnBorrow" value="true"></property><property name="testOnReturn" value="true"></property><property name="testWhileIdle" value="true"></property></bean><!--redis連接工廠 --><bean id="jedisConnectionFactory"class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"destroy-method="destroy"><property name="poolConfig" ref="jedisPoolConfig"></property><!--IP地址 --><property name="hostName" value="${redis.host.ip}"></property><!--端口號(hào) --><property name="port" value="${redis.port}"></property><!--如果Redis設(shè)置有密碼 --><property name="password" value="${redis.password}" /> <!--客戶端超時(shí)時(shí)間單位是毫秒 --><property name="timeout" value="${redis.timeout}"></property><property name="usePool" value="true" /><!--<property name="database" value="0" /> --></bean><!-- 鍵值序列化器設(shè)置為String 類型 --><bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/><!-- redis template definition --><bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"p:connection-factory-ref="jedisConnectionFactory"p:keySerializer-ref="stringRedisSerializer"p:valueSerializer-ref="stringRedisSerializer"></bean></beans> package com.artisan.redis.transaction;import java.util.List;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SessionCallback;public class SpringRedisTransaction {@SuppressWarnings({ "unchecked", "rawtypes", "resource" })public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring/spring-redis-string.xml");RedisTemplate<String, String> redisTemplate = ctx.getBean(RedisTemplate.class);// 清掉keyredisTemplate.delete("key1");SessionCallback sessionCallback = (SessionCallback) (RedisOperations ops) -> {// 開(kāi)啟事務(wù)ops.multi();// 設(shè)置值ops.boundValueOps("key1").set("artisan");// 注意由于命令只是進(jìn)入隊(duì)列 ,而沒(méi)有被執(zhí)行,所以此處采用 get 命令 ,而 value 卻返回為nullString value = (String) ops.boundValueOps("key1").get();System.out.println("事務(wù)執(zhí)行過(guò)程中 , 命令入隊(duì)列,而沒(méi)有被執(zhí)行,所以 value 為空 :value=" + value);// 此時(shí) list 會(huì)保存之前進(jìn)入隊(duì)列的所有命令的結(jié)果List list = ops.exec();for (int i = 0; i < list.size(); i++) {System.out.println("隊(duì)列中的命令返回的結(jié)果:" + list.get(i).toString());}// 事務(wù)結(jié)束后 , 獲取 valuelvalue = (String) redisTemplate.opsForValue().get("key1");System.out.println("----:" + value);return value;};// 執(zhí)行Redis命令String value = (String) redisTemplate.execute(sessionCallback);System.out.println("value:" + value);}}

    執(zhí)行結(jié)果

    INFO : org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@73a8dfcc: startup date [Thu Sep 27 12:10:45 CST 2018]; root of context hierarchy INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/spring-redis-string.xml] 事務(wù)執(zhí)行過(guò)程中 , 命令入隊(duì)列,而沒(méi)有被執(zhí)行,所以 value 為空 :value=null 隊(duì)列中的命令返回的結(jié)果:true 隊(duì)列中的命令返回的結(jié)果:artisan ----:artisan value:artisan

    采用了 Lambda 表達(dá)式( Java 8 以后才引入 Lambda 表達(dá)式)來(lái)為 SessionCallBack 接口 實(shí)現(xiàn)了業(yè)務(wù)邏輯.

    從代碼看,使用了 SessionCallBack 接口,從而保證所有的命令都是通過(guò)同一個(gè) Redis 的連接進(jìn)行操作的。

    在使用 multi 命令后 , 要特別注意的是,使用 get 等返回值的方法一律返回為空 ,因?yàn)樵?Redis 中它只是把命令緩存到隊(duì)列中,而沒(méi)有去執(zhí)行 。

    使用 exec 后就會(huì)執(zhí)行事務(wù),行行完了事務(wù)后,執(zhí)行 get 命令就能正常返回結(jié)果了。

    最后使用 redisTemplate.execute(callBack);就能執(zhí)行我們?cè)?SessionCallBack 接口定義Lambda 表達(dá)式的業(yè)務(wù)邏輯,并將獲得其返回值。

    需要再?gòu)?qiáng)調(diào)的是 : 這里打印出來(lái)的 value=null,是因?yàn)樵谑聞?wù)中,所有的方法都只會(huì)被
    緩存到 Redis 事務(wù)隊(duì)列中,而沒(méi)有立即執(zhí)行,所以返回為 null,

    如果我們希望得到 Redis 執(zhí)行事務(wù)各個(gè)命令的結(jié)果,可以用這行代碼 :

    List list = ops.exec();

    這段代碼將返回之前在事務(wù)隊(duì)列中所有命令的執(zhí)行結(jié)果,并保存在一個(gè) List 中,只要在 SessionCallback 接口的 execute 方法中將 list 返回,就可以在程序中獲得各個(gè)命令執(zhí)行的結(jié)果了 .


    代碼

    代碼托管到了 https://github.com/yangshangwei/redis_learn

    《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結(jié)

    以上是生活随笔為你收集整理的Redis-09Redis的基础事务的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。