狂神完整Redis筆記:https://blog.csdn.net/lijie0213/article/details/108129934
9、Linux下安裝Redis詳解
2020年9月26日 最新穩定版本是redis 6.0.6 http://www.redis.cn/
linux安裝redis-6.0.1單機和集群
數據庫事務正確執行的四個基本元素的縮寫:ACID
原子性(Atomicity),一致性(Consistency),隔離性(Isolation),持久性(Durability).一個支持事務(Transaction)的數據庫,必須要具有四種特性,否則在失誤過程當中無法保證數據的正確性,交易過程及可能達不到交易方的要求.
原子性(Atomicity)
原子性是指事務是一個不可分割的工作單位,事務中的操作要么都發生,要么都不發生。
一致性(Consistency)
事務前后數據的完整性必須保持一致。
隔離性(Isolation)
事務的隔離性是多個用戶並發訪問數據庫時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作數據所干擾,多個並發事務之間要相互隔離。
持久性(Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響.(數據永久性)
開發的原因,需要對吞吐量(TPS)、QPS、並發數、響應時間(RT)幾個概念做下了解,查自百度百科,記錄如下:
\1. 響應時間(RT)
響應時間是指系統對請求作出響應的時間。直觀上看,這個指標與人對軟件性能的主觀感受是非常一致的,因為它完整地記錄了整個計算機系統處理請求的時間。由於一個系統通常會提供許多功能,而不同功能的處理邏輯也千差萬別,因而不同功能的響應時間也不盡相同,甚至同一功能在不同輸入數據的情況下響應時間也不相同。所以,在討論一個系統的響應時間時,人們通常是指該系統所有功能的平均時間或者所有功能的最大響應時間。當然,往往也需要對每個或每組功能討論其平均響應時間和最大響應時間。
對於單機的沒有並發操作的應用系統而言,人們普遍認為響應時間是一個合理且准確的性能指標。需要指出的是,響應時間的絕對值並不能直接反映軟件的性能的高低,軟件性能的高低實際上取決於用戶對該響應時間的接受程度。對於一個游戲軟件來說,響應時間小於100毫秒應該是不錯的,響應時間在1秒左右可能屬於勉強可以接受,如果響應時間達到3秒就完全難以接受了。而對於編譯系統來說,完整編譯一個較大規模軟件的源代碼可能需要幾十分鍾甚至更長時間,但這些響應時間對於用戶來說都是可以接受的。
\2. 吞吐量(Throughput)
吞吐量是指系統在單位時間內處理請求的數量。對於無並發的應用系統而言,吞吐量與響應時間成嚴格的反比關系,實際上此時吞吐量就是響應時間的倒數。前面已經說過,對於單用戶的系統,響應時間(或者系統響應時間和應用延遲時間)可以很好地度量系統的性能,但對於並發系統,通常需要用吞吐量作為性能指標。
對於一個多用戶的系統,如果只有一個用戶使用時系統的平均響應時間是t,當有你n個用戶使用時,每個用戶看到的響應時間通常並不是n×t,而往往比n×t小很多(當然,在某些特殊情況下也可能比n×t大,甚至大很多)。這是因為處理每個請求需要用到很多資源,由於每個請求的處理過程中有許多不走難以並發執行,這導致在具體的一個時間點,所占資源往往並不多。也就是說在處理單個請求時,在每個時間點都可能有許多資源被閑置,當處理多個請求時,如果資源配置合理,每個用戶看到的平均響應時間並不隨用戶數的增加而線性增加。實際上,不同系統的平均響應時間隨用戶數增加而增長的速度也不大相同,這也是采用吞吐量來度量並發系統的性能的主要原因。一般而言,吞吐量是一個比較通用的指標,兩個具有不同用戶數和用戶使用模式的系統,如果其最大吞吐量基本一致,則可以判斷兩個系統的處理能力基本一致。
\3. 並發用戶數
並發用戶數是指系統可以同時承載的正常使用系統功能的用戶的數量。與吞吐量相比,並發用戶數是一個更直觀但也更籠統的性能指標。實際上,並發用戶數是一個非常不准確的指標,因為用戶不同的使用模式會導致不同用戶在單位時間發出不同數量的請求。一網站系統為例,假設用戶只有注冊后才能使用,但注冊用戶並不是每時每刻都在使用該網站,因此具體一個時刻只有部分注冊用戶同時在線,在線用戶就在瀏覽網站時會花很多時間閱讀網站上的信息,因而具體一個時刻只有部分在線用戶同時向系統發出請求。這樣,對於網站系統我們會有三個關於用戶數的統計數字:注冊用戶數、在線用戶數和同時發請求用戶數。由於注冊用戶可能長時間不登陸網站,使用注冊用戶數作為性能指標會造成很大的誤差。而在線用戶數和同事發請求用戶數都可以作為性能指標。相比而言,以在線用戶作為性能指標更直觀些,而以同時發請求用戶數作為性能指標更准確些。
\4. QPS每秒查詢率(Query Per Second)
每秒查詢率QPS是對一個特定的查詢服務器在規定時間內所處理流量多少的衡量標准,在因特網上,作為域名系統服務器的機器的性能經常用每秒查詢率來衡量。對應fetches/sec,即每秒的響應請求數,也即是最大吞吐能力。 (看來是類似於TPS,只是應用於特定場景的吞吐量)
單點登錄英文全稱Single Sign On,簡稱就是SSO。它的解釋是:在多個應用系統中,只需要登錄一次,就可以訪問其他相互信任的應用系統。
redis 6.0后版本為多線程.
13、String字符串類型詳解
ping ok #檢查redis是否安裝成功
set key1 v1 #設置值
get key1 #獲取值
keys * #獲得所有的key
EXISTS key1 #判斷某一個key是否存在
APPEND key1 hello #追加字符串,如果當前可以不存在就相當於setkey
STRLEN key1 #獲取字符串長度
INCR views #自增1
DECR views #自減1
incrby views 11 #設置步長,指定增量
flushdb #清空數據庫內容
flushall #清空所有數據庫內容
getrange key1 0 3 #截取字符串[0,3]
getrange key1 0 -1 #獲得全部的字符串
setrange key2 1 xxx #替換指定位置開始的字符串
#setex(set with expire) #設置過期時間
#setnx(set if not exist) #不存在再設置(分布式鎖中會常使用)
setex key3 30 hello #設置key3過期時間30s
ttl key3 #查看剩余的存活時間
setnx mykey "redis" #如果my可以不存在set mykey,否則原數據不變,創建失敗
mset k1 v1 k2 v2 k3 v3 #同時設置多個值
mget k1 k2 k3 #同時獲取多個值
msetnx mk1 mv1 mk2 mv2 #msetnx是一個原子性的操作,要么一起成功,要么一起失敗.
set user:1 {name:ling,age:3} #設置一個user:1對象 值為json字符來保存一個對象.
#這里的可以是一個巧妙的設置: user:{id}:{filed},如此設計在redis中是完全OK的.
mset user:1:name ling user:1:age 2
127.0.0.1:6379> mget user:1:name user:1:age
1) "ling"
2) "2"
getset #先get然后在set
127.0.0.1:6379> getset db redis #如果不存在值,則返回nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongedb #如果存在值,獲取原來的值,並設置新的值(用於更新操作)
"redis"
127.0.0.1:6379> get db
"mongedb"
B站獲取關注粉絲數,用uid incr.
14、List列表類型詳解
############################################################
127.0.0.1:6379> lpush list one #將一個值或多個值,插入到列表頭部(左)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1 #顯示列表所有的值(先進去的在最后)
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1 #redis不區分大小寫
1) "three"
2) "two"
127.0.0.1:6379> rpush list right #將一個值或多個值,插入到列表尾部(左)
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "five"
2) "four"
3) "three"
4) "two"
5) "one"
6) "right"
############################################################
lpop
rpop
127.0.0.1:6379> lpop list #移除list第一個值
"four"
127.0.0.1:6379> rpop list #移除list最后一個值
"one"
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
############################################################
lidex
127.0.0.1:6379> lrange list 0 -1 #顯示list全部值
1) "three"
2) "two"
127.0.0.1:6379> lindex list 1 #顯示list下標為1的值
"two"
127.0.0.1:6379> lindex list 0
"three"
127.0.0.1:6379> lindex list -1
"two"
############################################################
llen
127.0.0.1:6379> llen list #顯示list的長度
(integer) 2
############################################################
lrem:移除指定的值
127.0.0.1:6379> lrange list 0 -1
1) "six"
2) "two"
3) "one"
4) "one"
5) "three"
6) "two"
127.0.0.1:6379> lrem list 1 six #移除list集合中指定個數的value,精確匹配
(integer) 1
127.0.0.1:6379> lrem list 2 one
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "three"
3) "two"
############################################################
ltrim
127.0.0.1:6379> lrange list 0 -1
1) "six"
2) "five"
3) "one"
4) "three"
5) "two"
127.0.0.1:6379> ltrim list 1 2 #通過下標截取指定長度,這個list已經被改變了,截斷了只剩下截斷的元素.
OK
127.0.0.1:6379> lrange list 0 -1
1) "five"
2) "one"
############################################################
rpoplpush:#移除列表的最后一個元素,將他移動到新的列表中.
127.0.0.1:6379> lrange list 0 -1
1) "five"
2) "one"
127.0.0.1:6379> rpoplpush list mylist
"one"
127.0.0.1:6379> lrange list 0 -1
1) "five"
127.0.0.1:6379> lrange mylist 0 -1
1) "one"
############################################################
lset:#將列表中指定下標的指替換成另外一個值(更新操作)
127.0.0.1:6379> exists list #判斷這個列表是否存在
(integer) 1
127.0.0.1:6379> lset list 1 item #如果不存在列表我們去更新就會報錯
(error) ERR index out of range
127.0.0.1:6379>
127.0.0.1:6379> lset list 0 item #如果存在更新下標的指
OK
127.0.0.1:6379> lrange list 0 0
1) "item"
############################################################
linsert #將某個具體的值插入到指定元素的前面或后面
127.0.0.1:6379> linsert list before 1 2222
(integer) 5
127.0.0.1:6379> linsert list after 1 33333
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "2222"
2) "1"
3) "33333"
4) "two"
5) "one"
6) "item"
127.0.0.1:6379>
15、Set集合類型詳解
set中是不能重復讀的!
############################################################
127.0.0.1:6379> sadd myset hello ling china #set集合中添加勻速
(integer) 3
127.0.0.1:6379> SMEMBERS myset #查看指定set的所有值
1) "ling"
2) "hello"
3) "china"
127.0.0.1:6379> sismember myset hello #判斷一個值是不是在set集合中
(integer) 1
127.0.0.1:6379> sismember myset hello1
(integer) 0
127.0.0.1:6379> SMEMBERS myset
1) "ling"
2) "hello"
3) "china"
############################################################
127.0.0.1:6379> scard myset #獲取set集合中的內容元素個數
(integer) 3
############################################################
127.0.0.1:6379> SMEMBERS myset #查看指定集合set的所有值
1) "ling"
2) "hello"
3) "china"
127.0.0.1:6379> srem myset hello #移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "ling"
2) "china"
############################################################
set 無序不重復集合,抽隨機
127.0.0.1:6379> SRANDMEMBER myset #隨機抽出一個元素
"ling"
127.0.0.1:6379> SRANDMEMBER myset 2 #隨機抽出指定個數的元素
"china"
############################################################
刪除指定的key,隨機刪除key
127.0.0.1:6379> SMEMBERS myset
1) "shanghai"
2) "helll1"
3) "ling"
4) "china"
5) "java"
127.0.0.1:6379> spop myset #隨機刪除一個set集合中的元素
"ling"
127.0.0.1:6379> SMEMBERS myset
1) "java"
2) "helll1"
3) "shanghai"
4) "china"
############################################################
127.0.0.1:6379> smembers myset
1) "ling"
2) "hello"
3) "world"
4) "goodbye"
5) "seeyou"
127.0.0.1:6379> smove myset myset2 seeyou #將一個指定的值,移動到另一個set集合
(integer) 1
127.0.0.1:6379> smembers myset2
1) "seeyou"
############################################################
拼多多,抖音,qq,共同關注,共同好友
數字集合類:
-差集
-交集
-並集
127.0.0.1:6379> sadd user1 a b c d
(integer) 4
127.0.0.1:6379> sadd user2 b e f j
(integer) 4
127.0.0.1:6379> sdiff user1 user2 #差集
1) "a"
2) "c"
3) "d"
127.0.0.1:6379> sinter user1 user2 #交集,共同好友
1) "b"
127.0.0.1:6379> sunion user1 user2 #並集
1) "d"
2) "c"
3) "a"
4) "f"
5) "b"
6) "j"
7) "e"
微博,A用戶所有關注的人放在一個set集合中,將它的粉絲也放在一個集合中.
共同關注,共同愛好,二度好友(你可能認識),推薦好友(六度分隔理論)
16、Hash哈希類型詳解
Map集合,key-may,這個值是map集合,本質和string類型沒有太大區別,還是一個key-value
set myhash field ling
############################################################
127.0.0.1:6379> HSET myhash field1 ling #set一個具體key-value
(integer) 1
127.0.0.1:6379> hget myhash field1 #獲取一個字段值
"ling"
127.0.0.1:6379> hmset myhash field1 hello field2 redis #set多個key-value
OK
127.0.0.1:6379> hmget myhash field1 field2 #獲得多個字段值
1) "hello"
2) "redis"
127.0.0.1:6379> hgetall myhash #獲得全部數據
1) "field1"
2) "hello"
3) "field2"
4) "redis"
127.0.0.1:6379> hdel myhash field1 #刪除hash指定key字段,對應的value值也就消失了
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "redis"
############################################################
hlen
127.0.0.1:6379> HGETALL myhash
1) "field2"
2) "hi"
3) "field1"
4) "hello"
127.0.0.1:6379> keys *
1) "myhash"
127.0.0.1:6379> hlen myhash #獲取hash表的字段數量
(integer) 2
############################################################
127.0.0.1:6379> hexists myhash field1 #判斷hash中指定字段是否存在
(integer) 1
127.0.0.1:6379> hexists myhash fiel
(integer) 0
############################################################
127.0.0.1:6379> hkeys myhash #只獲得所有field
1) "field2"
2) "field1"
127.0.0.1:6379> hvals myhash #只獲得所有value
1) "hi"
2) "hello"
############################################################
incr decr
127.0.0.1:6379> hset myhash field3 5
(integer) 1
127.0.0.1:6379> hincrby myhash field3 1 #指定增量
(integer) 6
127.0.0.1:6379> hincrby myhash field3 -1 #指定減量
(integer) 5
127.0.0.1:6379> hsetnx myhash field4 hello #如果不存在則可以設置
(integer) 1
127.0.0.1:6379> hsetnx myhash field5 wor
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 adaw #如果存在則不能設置
hash
hash變更的數據user name age,尤其是用戶信息之類,經常變動的信息.
hash更適合存儲對象,string更適字符串存儲.
17、Zset有序集合詳解
在set的基礎上,增加了一個值,set k1 v1 zset k1 score1 v1
############################################################
127.0.0.1:6379> zadd myset 1 one #添加一個值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three #添加多個值
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
############################################################
127.0.0.1:6379> zadd salary 5000 zhangsan #添加三個值
(integer) 1
127.0.0.1:6379> zadd salary 3000 ling
(integer) 1
127.0.0.1:6379> zadd salary 10000 dalao
(integer) 1
#zrangebyscore key min max
127.0.0.1:6379> zrangebyscore salary -inf +inf #顯示全部用戶,從小到大
1) "ling"
2) "zhangsan"
3) "dalao"
127.0.0.1:6379> zrevrange salary 0 -1 #顯示全部用戶,從大到小排序
1) "dalao"
2) "zhangsan"
3) "ling"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores #顯示全部用戶並附帶成績
1) "ling"
2) "3000"
3) "zhangsan"
4) "5000"
5) "dalao"
6) "10000"
127.0.0.1:6379> zrangebyscore salary -inf 3000 withscores #顯示工資小於3000員工的升序排序
1) "ling"
2) "3000"
############################################################
#移除rem中的元素
127.0.0.1:6379> zrange salary 0 -1
1) "ling"
2) "zhangsan"
3) "dalao"
127.0.0.1:6379> zrem salary zhangsan #移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379> zcard salary #獲得有序集合中的個數
(integer) 2
############################################################
127.0.0.1:6379> zadd myset 1 hello 2 hi 3 ling
(integer) 3
127.0.0.1:6379> zcount myset 1 3 #獲取指定區間的成員數量
(integer) 3
127.0.0.1:6379> zcount myset 2 3
(integer) 2
按理思路:set 排序,儲存班級成績表,工資表排序.
普通消息,1,重要消息,2,帶權重進行判斷.
排行榜應用實現,取Top N測試.
三種特殊的數據類型
18、Geospatial地理位置詳解
geospatial地理位置,朋友的定位,附近的人,打車距離計算
redis的Geo在redis3.2版本就推出了!這個功能可以推算地理位置的信息,兩地之間的距離,方圓幾里的人!
只有六個命令
GEOADD
GEODIST
GEOHASH
GEOPOS
GOERADIUS
GEORADIUSBYMEMBER
geoadd
#geoadd 添加地理位置
#規則:兩極無法直接添加,一般會下載城市數據,通過Java程序一次性導入!
#參數key 值()
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijin
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing
(integer) 1
127.0.0.1:6379> geoadd china:city 114.05 22.52 shengzheng 120.16 30.24 hangzhou 102.96 34.26 xian
(integer) 3
geopos
127.0.0.1:6379> geopos china:city beijin #獲取指定的城市的精度和緯度
1) 1) "61.399999558925629"
2) "39.900000091670925"
127.0.0.1:6379> geopos china:city beijin shanghai
1) 1) "61.399999558925629"
2) "39.900000091670925"
2) 1) "121.47000163793564"
2) "31.229999039757836"
geodist
兩個人之間的距離!
單位:
- m表示單位為米.
- km表示單位為千米.
- mi表示單位為英里
- ft表示單位為英尺.
127.0.0.1:6379> geodist china:city beijin shanghai
"5417753.9752"
127.0.0.1:6379> geodist china:city beijin shanghai km #查看北京到上海的直線距離
"5417.7540"
127.0.0.1:6379> geodist china:city beijin xian km
"3706.9058"
127.0.0.1:6379> geodist china:city beijin chongqing km
"4233.6142"
georadius 以給定的經緯度為中心,找出某一半徑內的元素
我附近的人(獲得所有附近人的地址,定位)通過半徑來查詢!
獲得指定數量的人,200
所有數據都應該錄入:china:city ,才會讓結果更加真實.
127.0.0.1:6379> georadius china:city 110 30 1000 km #以110,30這個經緯度為中心,尋找房源1000公里內的城市
1) "chongqing"
2) "xian"
3) "shengzheng"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 700 km
1) "chongqing"
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist #顯示到中間距離的位置
1) 1) "chongqing"
2) "341.9374"
2) 1) "xian"
2) "814.6289"
3) 1) "shengzheng"
2) "924.6408"
4) 1) "hangzhou"
2) "977.5143"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord #顯示他人的定位信息
1) 1) "chongqing"
2) 1) "106.49999767541885"
2) "29.529999579006592"
2) 1) "xian"
2) 1) "102.96000212430954"
2) "34.2599996441893"
3) 1) "shengzheng"
2) 1) "114.04999762773514"
2) "22.520000087950386"
4) 1) "hangzhou"
2) 1) "120.16000002622604"
2) "30.240000322949022"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord withdist count 1 #篩選出指定的結果
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885"
2) "29.529999579006592"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord withdist count 2
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885"
2) "29.529999579006592"
2) 1) "xian"
2) "814.6289"
3) 1) "102.96000212430954"
2) "34.2599996441893"
georadiusbymember
#找出位於指定元素周圍的其他元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijin 1000 km
1) "beijin"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 500 km
1) "hangzhou"
2) "shanghai"
geohash命令 返回一個或者多個位置元素的Geohash表示
該命令將返回11個字符的geohash字符串
#將二維的經緯度轉換為一維的字符串,如果兩個字符串越接近,那么則距離越近.
127.0.0.1:6379> geohash china:city beijin shanghai
1) "tr5dvpw47e0"
2) "wtw3sj5zbj0"
GEO 底層的實現原理其實就是Zset,我們可以使用Zset命令來操作geo
127.0.0.1:6379> zrange china:city 0 -1 #查看地圖中的全部元素
1) "beijin"
2) "chongqing"
3) "xian"
4) "shengzheng"
5) "hangzhou"
6) "shanghai"
127.0.0.1:6379> zrem china:city beijin #移除指定元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shengzheng"
4) "hangzhou"
5) "shanghai"
19、Hyperloglog基數統計
什么是基數?
簡介
redis 2.8.9 版本就更新了Hyperloglog數據結構.
redis Hyperloglog 基數統計的算法.
優點:占用的內存時固定,2^64(long)不同的元素的技術,只需要12KB內存,如果要從內存角度Hyperloglog首選.
網頁的UV(一個人訪問一個網站多次,但還是算作一個個)
傳統的方式,set保存用戶的id,然后就可以統計set中的元素數量作為標准判斷.
這個方式如果保存大量的用戶id,就會比較麻煩,我們的目的是為了計數,而不是保存用戶id.
0.81%錯誤率,統計UV任務,可以忽略不計.
127.0.0.1:6379> pfadd mykey a b c d e f #創建一組元素 mykey
(integer) 1
127.0.0.1:6379> pfcount mykey #統計 mykey 元素的基數數量
(integer) 6
127.0.0.1:6379> pfadd mykey2 b c a j d w #創建二組元素 mykey
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 6
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 #合並兩組 mykey mykey2 =>mykey3 並集
OK
127.0.0.1:6379> pfcount mykey3 #看並集的數量
(integer) 8
如果允許容錯,那么一定可以使用Hyperloglog.
如果不允許容錯,就是用set或者自己的數據類型即可.
20、Bitmaps位圖場景詳解
位存儲
統計用戶信息,活躍,不活躍!登錄,未登錄!打卡,未打卡!兩個的狀態,都可以使用bitmaps
Bitmaps位圖,數據結構!都是操作二進制位來進行記錄,就只有0和1兩個狀態.
365天 = 365bit 1字節 = 8bit 46個字節左右!
周打卡
127.0.0.1:6379> setbit sign 6 0 #設置0~6一周,打卡為1,沒打卡為0
(integer) 0
127.0.0.1:6379> getbit sign 0 #獲取指定日期打卡狀態
(integer) 1
127.0.0.1:6379> bitcount sign #統計這周的打卡記錄,就可以看到是否有全勤
(integer) 6
21、Redis基本的事務操作
Redis事務本質:一組命令的集合.一個事務中的所有命令都會被序列化, 在事務執行過程中,會按照順序執行.
一次性,順序性,排他性!執行一系列命令.
------隊列 set set set get 執行-------
Redis事務沒有隔離級別的概念.
所有的命令在事務中,並沒有直接被執行.只有發起執行命令的時候才會執行.exec
Redis單條命令保存原子性,但事務不保證原子性.
redis事務:
- 開啟事務(multi)
- 命令入隊(寫命令...)
- 執行事務(exec)
所以事務中的命令在加入時都沒有被執行,直到提交時才會開始執行(Exec)一次性完成。
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> get k1
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "v1"
4) OK
5) 1) "k4"
2) "k1"
3) "k2"
4) "k3"
錯誤:一次只能添加一個值到事物中,ERR syntax error
127.0.0.1:6379> set k1 v1 k2 v2
QUEUED
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> exec
1) (error) ERR syntax error
2) (empty list or set)
取消事務(discard)
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> discard #放棄事務
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI #當前未開啟事務
127.0.0.1:6379> get k1 # 被放棄事務中命令並未執行
(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> error k1 #這是一條語法錯誤命令
(error) ERR unknown command 'error' #會報錯但是不影響后續命令入隊
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors. #執行報錯
127.0.0.1:6379> get k1
(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> incr k1 #這條命令邏輯錯誤(對字符串進行增量)
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range #報錯,不是int類型
4) "v2"
雖然中間有一條命令報錯了,但是后面的指令依舊正常執行成功了.
所有說Redis單條指令保證原子性,但是Redis事務不能保證原子性.
監控 watch
悲觀鎖:
- 很悲觀,認為什么時候都會出現問題,無論做什么都會加鎖 (synchronize 影響性能效率)
樂觀鎖:
- 很樂觀,認為什么時候都不會出現問題,所有不會上鎖.更新數據的時候去判斷一下,在此期間是否有人修改過這個數據.
- 獲取version
- 更新的時候比較version
使用watch key監控指定數據,相當於樂觀鎖加鎖.
正常執行
127.0.0.1:6379> set money 100 #設置余額:100
OK
127.0.0.1:6379> set use 0 #支出使用:0
OK
127.0.0.1:6379> watch money #監控money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby use 20
QUEUED
127.0.0.1:6379> exec #監視值沒有被中途修改
1) (integer) 80
2) (integer) 20
測試多線程修改值,使用watch可以當做redis的樂觀鎖操作(相當於getversion)
我們啟動另一個客戶端模擬插隊線程.
線程1:
127.0.0.1:6379> watch money #money上鎖
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby use 20
QUEUED
127.0.0.1:6379> #此時事務並沒有執行
模擬線程插隊,線程2:
127.0.0.1:6379> incrby money 500 #修改了線程1中監視的money
(integer) 580
回到線程1,執行事務:
127.0.0.1:6379> exec #執行之前,另一個線程修改了我們的值,這個時候就會導致事務執行失敗
(nil) #沒有結果,說明事務執行失敗
127.0.0.1:6379> get money #線程2 修改生效
"580"
127.0.0.1:6379> get use #線程1事務執行失敗,數值沒有被修改
"20"