初识 Redis
Redis概述
Redis簡介
- Redis:Remote Dictionary Server(遠程字典服務器)
- Redis是當前比較熱門的NOSQL系統之一,它是一個開源的使用ANSI C語言編寫的key-value存儲系統(區別于MySQL的二維表格的形式存儲。)。和Memcache類似,但很大程度補償了Memcache的不足。和Memcache一樣,Redis數據都是緩存在計算機內存中,不同的是,Memcache只能將數據緩存到內存中,無法自動定期寫入硬盤,這就表示,一斷電或重啟,內存清空,數據丟失。所以Memcache的應用場景適用于緩存無需持久化的數據。而Redis不同的是它會周期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,實現數據的持久化。
Redis特點
- Redis是一款內存型非關系型數據庫,速度快;官方測試Redis讀取的速度是110000次/s,寫的速度是81000次/s
- 支持多種數據結構:string(字符串);list(列表);hash(哈希),set(集合);zset(有序集合);以及三種特殊類型
- 支持過期時間,支持事務,消息訂閱
- redis 單條命令是保證原子性的,但是整個事務不保證原子性 沒有事務隔離級別的概念
- 持久化,主從復制(集群)
Redis應用場景
- 數據緩存,提高性能
- 會話緩存,保存web會話信息
- 排行榜/計數器
- 消息隊列
Redis的安裝
-
首先上傳Redis壓縮包到自定義文件夾,并解壓到對應文件夾
tar -zvxf redis-6.0.8.tar.gz -
注意:redis6的編譯需要gcc9版本,centos的gcc是4.8版本,因此需要更新成gcc9版本
#首先可以查看gcc 的版本 gcc -v #升級到gcc 9 yum -y install centos-release-scl yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils scl enable devtoolset-9 bash #需要注意的是scl命令啟用只是臨時的,退出shell或重啟就會恢復原系統gcc版本。 #如果要長期使用gcc 9.3的話,執行以下命令,這樣退出shell重新打開就是新版的gcc了 echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile -
進入到Redis的解壓目錄,執行make命令
[root@localhost redis-6.0.8]# make cd src && make all make[1]: 進入目錄“/home/programs/Redis/redis-6.0.8/src”CC Makefile.dep make[1]: 離開目錄“/home/programs/Redis/redis-6.0.8/src” make[1]: 進入目錄“/home/programs/Redis/redis-6.0.8/src” ......Hint: It's a good idea to run 'make test' ;)make[1]: 離開目錄“/home/programs/Redis/redis-6.0.8/src” [root@localhost redis-6.0.8]# -
make命令執行完后同樣在解壓目錄下執行make install命令
[root@localhost redis-6.0.8]# make install cd src && make install make[1]: 進入目錄“/home/programs/Redis/redis-6.0.8/src”CC Makefile.dep make[1]: 離開目錄“/home/programs/Redis/redis-6.0.8/src” make[1]: 進入目錄“/home/programs/Redis/redis-6.0.8/src”Hint: It's a good idea to run 'make test' ;)INSTALL installINSTALL installINSTALL installINSTALL installINSTALL install make[1]: 離開目錄“/home/programs/Redis/redis-6.0.8/src” [root@localhost redis-6.0.8]# -
復制redis.conf文件到指定目錄,這樣解壓目錄下的redis.conf文件就不會被影響
cp redis.conf /usr/local/bin/myconfig -
修改redis.conf文件,讓redis以守護線程的方式啟動(即后臺運行)
-
在/usr/local/bin目錄下指定配置文件啟動服務端 redis-server
redis-server myconfig/redis.conf -
在/usr/local/bin目錄下啟動客戶端
redis-cli -p 6379 -
測試
127.0.0.1:6379> ping PONG 127.0.0.1:6379> set "name" "zs" OK 127.0.0.1:6379> get "name" -
重新開啟一個連接查看redis的進程是否啟動
[root@localhost bin]# ps -ef|grep redis root 37678 1 0 16:50 ? 00:00:03 redis-server 127.0.0.1:6379 root 71203 11881 0 17:21 pts/0 00:00:00 redis-cli -p 6379 root 71703 69756 0 17:22 pts/2 00:00:00 grep --color=auto redis -
關閉redis服務
127.0.0.1:6379> shutdown not connected> exit -
再次查看進程
[root@localhost bin]# ps -ef|grep redis root 77478 69756 0 17:24 pts/2 00:00:00 grep --color=auto redis -
docker部署的redis進入bash命令
-
首先進入redis的安裝目錄下
-
進入bash命令行
docker exec -it redis redis-cli #進入后如果提示 127.0.0.1:6379> (error) NOAUTH Authentication required 127.0.0.1:6379> auth "yourpassword" #輸入你的密碼即可 127.0.0.1:6379> ping 127.0.0.1:6379> pong
-
-
使用RedisDesktopManager連接Redis
-
修改redis.conf文件
#修改bind 原來: bind 127.0.0.1 代表本地回環地址,訪問redis服務只能通過本機的客戶端連接,而無法通過遠程連接 #修改為:將此行注釋或者講ip改成0.0.0.0 這樣就能接受所有來自于可用網絡接口的連接 bind 0.0.0.0 #修改protected mode 保護模式 protected-mode no #補充 redis.conf 中的daemonize 配置daemonize yes #代表開啟守護進程模式。此時是單進程多線程的模式,redis將在后臺運行,并將pid寫入redis.conf--pidfile文件中,此時redis將一直運行,除非手動killdaemonize no #當前界面將進入redis的命令行界面,exit強制退出或者關閉連接工具(xshell等)都會導致redis進程退出#修改配置文件后需要重啟redis!!!#注:啟動redis 一定要指定配置文件,否則配置文件不生效的 -
連接
-
注意:安裝完后,記得在防火墻配置端口
-
Redis基礎知識
-
Redis是單進程單線程(redis6版本采用了多線程)
- 采用 I/O 多路復用技術可以讓單個線程高效的處理多個連接請求(盡量減少網絡IO的時間消耗)
- 多線程處理可能涉及到鎖,多線程處理會涉及到線程切換而消耗CPU
- 單進程不存在線程安全問題
- 缺點:無法發揮多核CPU性能,不過可以通過在單機開多個Redis實例來完善
-
Redis默認有16個數據庫,類似數組下表從零開始,初始默認使用零號庫
#切換到下標為1的數據庫 127.0.0.1:6379> select 1 OK 127.0.0.1:6379[1]> set name zs #往下標為1的庫中設值 OK 127.0.0.1:6379[1]> keys * #查看下標為1的庫下所有的key 1) "name" 127.0.0.1:6379[1]> get name "zs" -
常用基本命令
127.0.0.1:6379[1]> dbsize #查看當前庫的key數量 (integer) 1 127.0.0.1:6379[1]> flushdb #清空當前庫 OK 127.0.0.1:6379[1]> flushall #清空所有庫 OK -
key常用命令
#該命令用于在 key 存在時刪除 key del key #序列化給定 key ,并返回被序列化的值 dump key #檢查給定 key 是否存在 exists key #為給定 key 設置過期時間,以秒計 expire key seconds #expireat 的作用和 expire 類似,都用于為 key 設置過期時間。 不同在于 expireat 命令接受的時間參數是 unix 時間戳(unix timestamp) expireat key timestamp #設置 key 的過期時間以毫秒計 pexpire key milliseconds #設置 key 過期時間的時間戳(unix timestamp) 以毫秒計 pexpireat key milliseconds-timestamp #查找所有符合給定模式( pattern)的 key keys pattern #將當前數據庫的 key 移動到給定的數據庫 db 當中 move key db #移除 key 的過期時間,key 將持久保持 persist key #以毫秒為單位返回 key 的剩余的過期時間 pttl key #以秒為單位,返回給定 key 的剩余生存時間(ttl, time to live) ttl key #從當前數據庫中隨機返回一個 key randomkey #修改 key 的名稱 rename key newkey #僅當 newkey 不存在時,將 key 改名為 newkey renamenx key newkey #返回 key 所儲存的值的類型 type key
Redis的五大基本數據類型
String(字符串類型)
-
字符串類型是編程語言和應用程序中最常見和最有用的數據類型,也是Redis的基本數據類型之一,事實上,Redis中所有鍵都必須是字符串。
127.0.0.1:6379> set name zs #設置一個值 OK 127.0.0.1:6379> get name # 取值 "zs" 127.0.0.1:6379> getrange name 0 1 #取出key中下標 0~1的字符 閉區間 [] "zs" 127.0.0.1:6379> set email 12323343@qq.com OK 127.0.0.1:6379> getrange email 2 5 #取出key中下標2~5的字符 "3233" 127.0.0.1:6379> set name 1234 OK 127.0.0.1:6379> setrange name 2 x #將下標為2的字符替換成x (integer) 4 127.0.0.1:6379> get name "12x4" 127.0.0.1:6379> del name #刪除一個鍵 (integer) 1 127.0.0.1:6379> get name (nil) 127.0.0.1:6379> exists name #判斷一個鍵是否存在 (integer) 0 127.0.0.1:6379> exists email (integer) 1 127.0.0.1:6379> set name lisi OK 127.0.0.1:6379> expire name 30 #設置一個鍵的過期時間 單位秒 (integer) 1 127.0.0.1:6379> ttl name#查看一個鍵還有多久過期 (integer) 27 #27秒 127.0.0.1:6379> set name wer OK 127.0.0.1:6379> getset name abc #將給定 key 的值設為 value ,并返回 key 的舊值(old value) "wer" 127.0.0.1:6379> strlen name #返回key所存儲值的長度 (integer) 3 127.0.0.1:6379> setex age 10 18 #將值 value 關聯到 key ,并將 key 的過期時間設為 seconds (以秒為單位) OK 127.0.0.1:6379> setnx age 22 #只有在 key 不存在時設置 key 的值 (integer) 1 127.0.0.1:6379> setnx name ttt #key存在時 設置失敗 (integer) 0 127.0.0.1:6379> mset k1 vi k2 v2 k3 v3 #批量設置鍵值 OK 127.0.0.1:6379> mget k1 k2 k3 #批量取值 1) "vi" 2) "v2" 3) "v3" 127.0.0.1:6379> msetnx k4 v4 k5 v5 k6 v6 #批量設置多個不存在的KV對 (integer) 1 127.0.0.1:6379> psetex oo 100 abc #與setex 命令相似,但它以毫秒為單位設置 key 的生存時間 OK 127.0.0.1:6379> set age 1 OK 127.0.0.1:6379> get age "1" 127.0.0.1:6379> incr age #自增一 (integer) 2 127.0.0.1:6379> incr age (integer) 3 127.0.0.1:6379> incrby age 3 #設置自增的增量為3 (integer) 6 127.0.0.1:6379> incrby age 3 (integer) 9 127.0.0.1:6379> incrbyfloat age 0.5 #設置自增的增量為浮點類型 0.5 "9.5" 127.0.0.1:6379> incrbyfloat age 0.5 "10" 127.0.0.1:6379> set class 10 OK 127.0.0.1:6379> decr class #自減一 (integer) 9 127.0.0.1:6379> decr class (integer) 8 127.0.0.1:6379> decrby class 2 #設置自減的減量為2 (integer) 6 127.0.0.1:6379> decrby class 2 (integer) 4 127.0.0.1:6379> set dept it OK 127.0.0.1:6379> get dept "it" 127.0.0.1:6379> append dept info #如果 key 已經存在并且是一個字符串, append 命令將指定的 value 追加到該 key 原來值(value)的末尾 (integer) 6 127.0.0.1:6379> get dept "itinfo" -
String類型的應用場景:計數器,如微博的評論數、點贊數、分享數,抖音作品的收藏數,京東商品的銷售量、評價數等。
List(列表類型)
-
列表是應用我只是應該程序開發中非常有用的數據類型之一,列表能存在一組對象,因此它也可以被用于棧或者隊列,在Redis中,與鍵相關的聯的值可以是字符串組成的列表,Redis中的列表更像是數據結構中的雙向鏈表。
127.0.0.1:6379> lpush name v1 v2 v3 v4 #往列中添加4個值 (integer) 4 127.0.0.1:6379> llen name #查看列表長度 (integer) 4 127.0.0.1:6379> lrange name 0 -1 #查看列表中所有的值 1) "v4" 2) "v3" 3) "v2" 4) "v1" 127.0.0.1:6379> lpop name #移除最后進的一個元素 先進后出,后進先出 "v4" 127.0.0.1:6379> lrange name 0 -1 1) "v3" 2) "v2" 3) "v1" 127.0.0.1:6379> rpop name #移除最早進的一個元素 "v1" 127.0.0.1:6379> lrange name 0 -1 1) "v3" 2) "v2" 127.0.0.1:6379> lpush name v1 v2 v3 v4 v5 v6 (integer) 6 127.0.0.1:6379> lrange name 0 -1 1) "v6" 2) "v5" 3) "v4" 4) "v3" 5) "v2" 6) "v1" 127.0.0.1:6379> lindex name 2 #取出下標為2的元素 "v4" 127.0.0.1:6379> lindex name 1 #取出下標為1的元素 "v5" 127.0.0.1:6379> lindex name 0 #取出下標為0的元素,最后進的下標為0 "v6" 127.0.0.1:6379> lrem name 2 v3 #移除列表中的元素 語法 lrem key count value #count不同則代表不同的含義,詳情見https://blog.csdn.net/qq_41384743/article/details/98211366 (integer) 1 127.0.0.1:6379> lrange name 0 -1 1) "v6" 2) "v5" 3) "v4" 4) "v2" 5) "v1" 127.0.0.1:6379> lrange name 0 -1 1) "v6" 2) "v5" 3) "v4" 127.0.0.1:6379> lset name 0 v1 #根據索引設置元素的值 存在就更新 不存在就報錯 OK 127.0.0.1:6379> lrange name 0 -1 1) "v1" 2) "v5" 3) "v4" 127.0.0.1:6379> ltrim name 1 2 #截取下標從1 ~ 2的元素,類似于subString截取字符串 OK 127.0.0.1:6379> lrange name 0 -1 1) "v5" 2) "v4" 127.0.0.1:6379> rpush name k1 #在尾部追加一個元素 (integer) 3 127.0.0.1:6379> lrange name 0 -1 1) "v5" 2) "v4" 3) "k1" 127.0.0.1:6379> lrange name 0 -1 1) "v6" 2) "v6" 3) "v5" 4) "v4" 5) "k1" 127.0.0.1:6379> linsert name before v5 a1 #在v5前插入值 a1 (integer) 6 127.0.0.1:6379> lrange name 0 -1 1) "v6" 2) "v6" 3) "a1" 4) "v5" 5) "v4" 6) "k1"
Set(集合)
-
集合類型是由唯一,無序對象組成的集合(collection).它經常用于測試某個成員是集合中,重復項刪除和集合運算(求并,交,差集),Redis的值對象可以是字符串集合。
127.0.0.1:6379> sadd name v1 v2 v3 v4 v5 v6 #往集合中添加值 (integer) 6 127.0.0.1:6379> scard name #獲取集合中元素的數量 (integer) 6 127.0.0.1:6379> smembers name #查看集合中所有的成員 1) "v3" 2) "v4" 3) "v1" 4) "v6" 5) "v2" 6) "v5" 127.0.0.1:6379> sadd name1 v5 v6 v7 #新建一個集合 (integer) 3 127.0.0.1:6379> sdiff name name1 #取name和name1的差集(就是指前面那個集合有后面那個集合沒有的元素) 1) "v2" 2) "v3" 3) "v1" 4) "v4" 127.0.0.1:6379> sdiff name1 name 1) "v7" 127.0.0.1:6379> Smembers name 1) "v6" 2) "v4" 127.0.0.1:6379> Smembers name1 1) "v6" 2) "v7" 3) "v5" 127.0.0.1:6379> sinter name name1 #取兩個集合的交集 1) "v6" 127.0.0.1:6379> sunion name name1 #取兩個集合的并集 1) "v6" 2) "v7" 3) "v4" 4) "v5" 127.0.0.1:6379> SISMember name v2 #判斷某個元素是否存在集合中 (integer) 1 127.0.0.1:6379> srem name v1 v2 #移除一個或多個元素 (integer) 2 127.0.0.1:6379> Smembers name 1) "v6" 2) "v3" 3) "v4" 4) "v5" 127.0.0.1:6379> srem name v3 #移除指定元素 (integer) 1 127.0.0.1:6379> Smembers name 1) "v6" 2) "v4" 3) "v5" 127.0.0.1:6379> srandmember name #隨機出一個元素 "v5" 127.0.0.1:6379> srandmember name 2 #隨機出多個元素 1) "v6" 2) "v4" 127.0.0.1:6379> spop name #隨機刪除一個 "v5" 127.0.0.1:6379> Smembers name 1) "v6" 2) "v4" 127.0.0.1:6379> smove name1 name v7 #將集合name1中的元素v7移動到name集合中 (integer) 1 127.0.0.1:6379> Smembers name1 1) "v6" 2) "v5" 127.0.0.1:6379> Smembers name 1) "v6" 2) "v7" 3) "v4" -
Set集合應用場景:粉絲列表,好友,感興趣的人集合,共同關注
ZSet(sortset)有序集合
-
有序集合是一個類似于set但是更復雜的數據類型,單詞sorted意為著這種集合中的每個元素都有一個可用于排序的權重,并且我們可以按順序從集合中得到元素在某些需要一個保持數據有序的場景中,使用這種原生的序的特性是很方便的。
127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four #往集合中添加值 (integer) 4 127.0.0.1:6379> zrange myzset 0 -1 #查看集合中所有的值 1) "one" 2) "two" 3) "three" 4) "four" 127.0.0.1:6379> zrange myzset 0 -1 withscores #加上withscores就可以顯示出一一對應關系 1) "one" 2) "1" 3) "two" 4) "2" 5) "three" 6) "3" 7) "four" 8) "4" 127.0.0.1:6379> zcard myzset #獲取集合長度 (integer) 4 127.0.0.1:6379> zrangebyscore myzset -inf +inf #從小到大排序 1) "one" 2) "two" 3) "three" 4) "four" 127.0.0.1:6379> zrevrange myzset 0 -1 #從大到小排序 1) "four" 2) "three" 3) "two" 4) "one" 127.0.0.1:6379> zrem myzset four #移除指定元素 (integer) 1 127.0.0.1:6379> zrevrange myzset 0 -1 1) "three" 2) "two" 3) "one" 127.0.0.1:6379> zcount myzset 0 2 #獲取指定區間的元素個數 (integer) 2 -
Zset 應用場景:1.工資表排序; 2.成績表排序;3.帶權重進行判斷;4.排行榜;
Hash(哈希)
-
哈希表示字段和值之間的映射關系,與JAVA中的Map類似,Redis數據集本身就可以看做一個哈希,其中字符串類型的鍵關聯到如字符串和列表之類的數據對象,而Reidis的數據對象也可以再次使用哈希,其字段和值必須是字符串。
127.0.0.1:6379> hset name k1 v1 k2 v2 k3 v3 k4 v4 k5 v5 k6 v6 #設置KV對 (integer) 6 127.0.0.1:6379> hgetall name #取除所有的鍵值對1) "k1"2) "v1"3) "k2"4) "v2"5) "k3"6) "v3"7) "k4"8) "v4"9) "k5" 10) "v5" 11) "k6" 12) "v6" 127.0.0.1:6379> hget name k1 #獲取某一個鍵下的值 "v1" 127.0.0.1:6379> hexists name k2 #判斷是否有這個鍵 (integer) 1 127.0.0.1:6379> hdel name k5 v5 #刪除某一個鍵值對 (integer) 1 127.0.0.1:6379> hkeys name #獲取哈希表中的所有的key 1) "k1" 2) "k2" 3) "k3" 127.0.0.1:6379> hvals name #獲取哈希表中的所有的value 1) "v1" 2) "v2" 3) "v3" 127.0.0.1:6379> hlen name #獲取哈希表中字段的數量 (integer) 3 -
Hash的應用場景:對象信息的保存,經常變更的數據
三種特殊數據類型
Geospatial 地理位置
-
應用場景:朋友的定位,附近的人,打車距離計算。
-
geo底層其實就是Zset,可以使用Zset命令來操作geo
#geoadd key 經度 緯度 城市名 注意:兩極無法直接添加,一般會下載城市數據,通過java程序一次性導入 127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing #添加一個城市經緯度記錄 (integer) 1 127.0.0.1:6379> geopos china:city beijing #獲取一個城市的經緯度,注意:必須得存在你這個key中 1) 1) "116.39999896287918091"2) "39.90000009167092543" 127.0.0.1:6379> geodist china:city beijing shanghai km #獲取兩個地點之間的距離 可指定單位 "1068.7817" 127.0.0.1:6379> georadius china:city 121 31 1100 km [withdist withcoord count 1] #獲取以121,31經緯度為中心,尋找方圓1100km的城市,withdist 顯示距離 withcoord顯示坐標 count 限制數量 1) "changsha" 2) "shanghai" 3) "beijing" 127.0.0.1:6379> geohash china:city hangzhou shanghai # 將二維坐標轉換為一維字符串 ,兩個字符串越接近,距離越近 1) "wtmknuxtmb0" 2) "wtw3s77j9j0" 127.0.0.1:6379> georadiusbyMember china:city shanghai 200 km #獲取以上海為中心半徑為200km周圍的城市,注意:只獲取到key中存在的 1) "hangzhou" 2) "shanghai"
Hyperloglog 基數
-
基數:基數就是集合中不重復的元素
-
應用場景:可以統計網頁的訪問次數
127.0.0.1:6379> pfadd name java html sql #添加元素到key中 (integer) 1 127.0.0.1:6379> pfcount name #統計數量 (integer) 3 127.0.0.1:6379> pfadd age 18 (integer) 1 127.0.0.1:6379> pfmerge age name #合并,將name中的元素合并到age中 OK 127.0.0.1:6379> pfcount name (integer) 3 127.0.0.1:6379> pfcount age (integer) 4
Bitmap 位圖
-
Bitmap:只有兩個狀態 非0即1
-
應用場景:可以統計用戶信息: 活躍 不活躍 登錄 未登錄 打卡 未打卡 簽到 未簽到
#使用bitmap記錄周一到周日的打卡情況0~6代表周一到周日 后面的那個1或0代表打卡和沒打卡 127.0.0.1:6379> setbit week 0 1 (integer) 0 127.0.0.1:6379> setbit week 1 1 (integer) 0 127.0.0.1:6379> setbit week 2 1 (integer) 0 127.0.0.1:6379> setbit week 3 0 (integer) 0 127.0.0.1:6379> setbit week 4 1 (integer) 0 127.0.0.1:6379> setbit week 5 1 (integer) 0 127.0.0.1:6379> setbit week 6 0 (integer) 0 127.0.0.1:6379> getbit week 1 #查看周二是否打卡 (integer) 1 127.0.0.1:6379> getbit week 6 #查看周日是否打卡 (integer) 0 127.0.0.1:6379> bitcount week #統計一周打卡的數量 (integer) 5
事務
-
可以一次執行多個命令,本質是一組命令的集合。一個事務中的所有命令都會序列化,按順序地串行化執行而不會被其它命令插入,不許加塞
-
Redis事務的特性:一次性,順序性,排他性。
-
redis的單條命令是保證原子性的,但是Redis的事務不保證原子性,沒有隔離級別的概念
-
所有的命令在事務中,并沒有直接被執行,而是當我們發起執行命令的時候才會執行
-
redis事務的三個階段:
- 開啟事務:multi
- 命令入隊
- 執行事務:exec
-
放棄事務:discard 類似于回滾
-
正常執行一段事務
127.0.0.1:6379> multi #開啟事務 OK 127.0.0.1:6379> set k1 v1 QUEUED #進入隊列 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> get k1 QUEUED 127.0.0.1:6379> exec #執行事務 1) OK 2) OK 3) OK 4) "v1"#測試回滾事務 127.0.0.1:6379> multi OK 127.0.0.1:6379> mset k1 v1 k2 v2 k4 v4 QUEUED 127.0.0.1:6379> discard #取消事務,類似于回滾事務 OK 127.0.0.1:6379> get k4 #事務中的命令都不會執行 (nil) -
編譯型異常(代碼有問題,命令有錯),事務中命令都不會執行
127.0.0.1:6379> multi OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> getset k3 #錯誤的命令 (error) ERR wrong number of arguments for 'getset' command 127.0.0.1:6379> set k4 v4 QUEUED 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. #執行事務報錯 127.0.0.1:6379> get k1 #所有命令都不會執行 (nil) -
運行時異常(類似于1/0) 如果事務隊列中存在語法性錯誤,只有錯的不執行會拋出異常,其他命令正常執行
127.0.0.1:6379> set k1 v1 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr k1 #k1是字符串不能自增,執行失敗 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> exec 1) (error) ERR value is not an integer or out of range # k1是字符串不能自增,其他正常執行 2) OK 3) OK 4) "v2"
監控
-
悲觀鎖:認為什么時候都會出問題,無論做什么都會加鎖,會影響性能
-
樂觀鎖:認為什么時候都不會出問題,不會加鎖,更新數據的時候去判斷一下,在此期間是否有人修改過這個數據
-
watch 監視
#正常執行 127.0.0.1:6379> set money 100 OK 127.0.0.1:6379> set out 0 OK 127.0.0.1:6379> watch money #監視這個對象 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby money 20 QUEUED 127.0.0.1:6379> incrby out 20 QUEUED 127.0.0.1:6379> exec #事務正常結束 1) (integer) 80 2) (integer) 20 -
多線程測試,類似于樂觀鎖的操作
#線程一 127.0.0.1:6379> watch money #監視對象 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby money 10 QUEUED 127.0.0.1:6379> incrby out 10 #此時并未執行事務,這時線程二對數據進行修改 QUEUED#線程二 127.0.0.1:6379> get money "80" 127.0.0.1:6379> set money 1000 OK#此時線程一再去執行時 127.0.0.1:6379> exec #執行失敗 (nil)#此時可以先放棄監視,然后重新監視,再開啟事務重新操作 127.0.0.1:6379> unwatch OK 127.0.0.1:6379> watch money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby money 200 QUEUED 127.0.0.1:6379> incrby out 200 QUEUED 127.0.0.1:6379> exec #比對監視的值是否發生變化,如果沒變化就執行成功,反之失敗 1) (integer) 800 2) (integer) 220
Jedis
-
Jedis是Redis官方推薦的Java連接開發工具
-
基本使用
-
導入相關依賴
<!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.3.0</version> </dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.73</version> </dependency> -
測試
import redis.clients.jedis.Jedis;public class JedisDemo {public static void main(String[] args) {//連接Redis 服務,注意連接遠程的要保證遠程的開啟了,本地的不用Jedis jedis = new Jedis("192.168.2.130",6379);// 如果 Redis 服務設置來密碼,需要下面這行,沒有就不需要// jedis.auth("123456"); System.out.println("連接成功");//查看服務是否運行System.out.println("服務正在運行: "+jedis.ping());//所有的API就是對應的命令...自行測試} }//Jedis配合事務測試 import com.alibaba.fastjson.JSONObject; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction;public class JedisDemo {public static void main(String[] args) {Jedis jedis = new Jedis("192.168.2.130",6379);System.out.println(jedis.ping());JSONObject jsonObject = new JSONObject();jsonObject.put("hello","world");jsonObject.put("nihao","shijie");//開啟事務Transaction transaction = jedis.multi();String result = jsonObject.toJSONString();try{transaction.set("str1",result);transaction.set("str2",result);int i = 1/0; //模擬異常事務}catch (Exception e){transaction.discard(); //放棄事務e.printStackTrace();}finally {System.out.println(jedis.get("str1"));System.out.println(jedis.get("str2"));jedis.close(); //關閉連接}} }
-
SpringBoot整合Redis
-
創建SpringBoot項目,引入相關依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> -
編寫主配置文件
spring.redis.host=192.168.2.130 spring.redis.port=6379 -
測試
@Autowired private RedisTemplate redisTemplate; //注入RedisTemplate @Test void contextLoads() {//獲得連接redisTemplate.getConnectionFactory();//set一個字段redisTemplate.opsForValue().set("name","zs");//注意:當我們傳遞對象的使用都需要將對象序列化//實體類對象直接實現Serializable接口,其他的可以通過下列方式,將user對象序列化//String u = new ObjectMapper().writeValueAsString(user);System.out.println(redisTemplate.opsForValue().get("name"));//其余的API與命令形式類似...自行測試//企業一般會對原生的API進行封裝,封裝成一個RedisUtils或RedisTools } -
自定義RedisTemplate,開發中使用自定義的RedisTemplate
package com.hr.springbootredis.config;import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig {// 這是我給大家寫好的一個固定模板,大家在企業中,拿去就可以直接使用!// 自己定義了一個 RedisTemplate@Bean@SuppressWarnings("all")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactoryfactory) {// 我們為了自己開發方便,一般直接使用 <String, Object>RedisTemplate<String, Object> template = new RedisTemplate<String,Object>();template.setConnectionFactory(factory);// Json序列化配置Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = newJackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// String 的序列化StringRedisSerializer stringRedisSerializer = newStringRedisSerializer();// key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);// hash的key也采用String的序列化方式template.setHashKeySerializer(stringRedisSerializer);// value序列化方式采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的value序列化方式采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;} }
?
?
?
總結
- 上一篇: 预告:无穷小微积分改版,寻找接班人
- 下一篇: 一千零一夜的观后感(一)