Nosql概述
為什么要用Nosql
現(xiàn)在處于大數(shù)據(jù)時代;大數(shù)據(jù)時代一般的數(shù)據(jù)庫無法進(jìn)行分析處理!
1.單機(jī)Mysql的年代!
2.Memcached(緩存) + MySQL + 垂直拆分
緩存并非一開始就出現(xiàn)的:
發(fā)展過程:優(yōu)化數(shù)據(jù)結(jié)構(gòu)和索引(IO)-----> Mencached(當(dāng)時最熱門的技術(shù))
3.分庫分表 + 水平拆分 + MySQL集群
技術(shù)和業(yè)務(wù)在發(fā)展的同時發(fā)展的同時,對人的要求越來越高!
本質(zhì):數(shù)據(jù)庫的讀寫
早些年MyISAM:表鎖,十分影響效率,高并發(fā)下出現(xiàn)嚴(yán)重的鎖問題
轉(zhuǎn)戰(zhàn)Innodb:行鎖
慢慢的.開始使用分庫分表來解決寫的壓力!MySQL在那個年代推出了表分區(qū),但使用不廣泛
MySQL的集群,很好滿足了那個年代的所有需求
4.如今最近的年代
為什么要用NoSQL
什么是NoSQL
NoSQL
NoSQL特點
- 傳統(tǒng)關(guān)系型數(shù)據(jù)庫和NoSQL
了解: 3V + 3高
阿里巴巴演進(jìn)分析:文檔
解決方案:
NoSQL四大分類
四者對比:
Redis入門
Redis是什么?
Redis 能干嘛?
Redis 特性
學(xué)習(xí)需要用的網(wǎng)站
Windows 版本在github上下載,但是停更很久了,redis建議在linux上學(xué)習(xí)
Windows下安裝
Linux下安裝
- 將redis配置文件拷貝到 /usr/local/bin/liubi-config自定義目錄下
- redis默認(rèn)不是后臺啟動的,需要修改配置文件!
- 啟動redis服務(wù)!通過指定的配置文件啟動服務(wù)
- 使用redis -cli 測試連接
- 查看redis的進(jìn)程是否開啟
- 關(guān)閉redis服務(wù)
在redis端口模式下輸入shutdown
使用kill -9 進(jìn)程id殺死對應(yīng)進(jìn)程
- 后面會使用單機(jī)多redis啟動集群測試
測試性能:redis-benchmark [option] [option value]
查看分析結(jié)果:get set incr lpush rpush lpop rpop sadd hset spop zadd zpopmin
lpush lrange mset等等信息
基礎(chǔ)知識
redis默認(rèn)有16個數(shù)據(jù)庫讼积,默認(rèn)使用的是第0個撞鹉,可以使用select進(jìn)行切換數(shù)據(jù)庫
dbsize 查看數(shù)據(jù)庫大小
keys * 查看所有鍵(key)
exists key 來判斷某個鍵是否存在
flushdb 清除當(dāng)前數(shù)據(jù)庫垂涯;flushall 清空所有數(shù)據(jù)庫
move key 目標(biāo)數(shù)據(jù)庫號 移動數(shù)據(jù)到目標(biāo)庫
redis是單線程的
redis基于內(nèi)存操作猛计,速度快,cpu不是redis的性能瓶頸绵估,主要取決于機(jī)器的內(nèi)存和網(wǎng)絡(luò)帶寬炎疆,既然能夠使用單線程來實現(xiàn),那就用單線程国裳。
redis使用c語言實現(xiàn)形入,QPS達(dá)到10w+,完全不必Memcache差
expire key 時間(單位為秒)來設(shè)置過期時間
ttl key 查看key剩余存活時間 返回值為數(shù)字(秒)缝左,-1說明沒有設(shè)置生存時間
type key 查看當(dāng)前key的類型
redis單線程為什么還這么快
- 誤區(qū)1:高性能的服務(wù)器一定是多線程嗎亿遂?
- 誤區(qū)2:多線程(有cpu上下文切換Eㄈ簟)一定比單線程效率高
快的原因:
- 完全基于內(nèi)存,數(shù)據(jù)存在內(nèi)存中蛇数,絕大部分請求是純粹的內(nèi)存操作挪钓,非常快速耳舅,跟傳統(tǒng)的磁盤文件數(shù)據(jù)存儲相比碌上,避免了通過磁盤IO讀取到內(nèi)存這部分的開銷。
- 數(shù)據(jù)結(jié)構(gòu)簡單浦徊,對數(shù)據(jù)操作也簡單馏予。Redis中的數(shù)據(jù)結(jié)構(gòu)是專門進(jìn)行設(shè)計的,每種數(shù)據(jù)結(jié)構(gòu)都有一種或多種數(shù)據(jù)結(jié)構(gòu)來支持盔性。Redis正是依賴這些靈活的數(shù)據(jù)結(jié)構(gòu)霞丧,來提升讀取和寫入的性能
- 采用單線程,省去了很多上下文切換的時間以及CPU消耗冕香,不存在競爭條件蛹尝,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作悉尾,也不會出現(xiàn)死鎖而導(dǎo)致的性能消耗
- 使用基于IO多路復(fù)用機(jī)制的線程模型突那,可以處理并發(fā)的連接
- Redis直接自己構(gòu)建了VM 機(jī)制 ,避免調(diào)用系統(tǒng)函數(shù)的時候焕襟,浪費(fèi)時間去移動和請求
Redis五大數(shù)據(jù)類型
命令:官網(wǎng)查詢http://www.redis.cn/commands.html
String
127.0.0.1:6379> set key1 v1 #設(shè)置值
OK
127.0.0.1:6379> get key1 #獲得值
"v1"
127.0.0.1:6379> append key1 "+v2" #給字符串追加字符串陨收,若當(dāng)前key不存在則相當(dāng)于新建字符串
(integer) 5
127.0.0.1:6379> get key1
"v1+v2"
127.0.0.1:6379> strlen key1 #求字符串長度
(integer) 5
127.0.0.1:6379> set key2 2 #設(shè)置數(shù)字字符串
OK
127.0.0.1:6379> incr key2 #自增1
(integer) 3
127.0.0.1:6379> get key2
"3"
127.0.0.1:6379> decr key2 #自減1
(integer) 2
127.0.0.1:6379> get key2
"2"
127.0.0.1:6379> incrby key2 5 #設(shè)置自增步長為5
(integer) 7
127.0.0.1:6379> get key2
"7"
127.0.0.1:6379> decrby key2 3 #設(shè)置自減步長為3
(integer) 4
127.0.0.1:6379> get key2
"4"
127.0.0.1:6379> get key1
"v1+v2"
127.0.0.1:6379> getrange key1 0 3 #取字符串key1的0到3位(0到-1指全部)
"v1+v"
127.0.0.1:6379> setrange key1 1 xx #替換從第一位開始的換為xx
(integer) 5
127.0.0.1:6379> get key1
"vxxv2"
127.0.0.1:6379> setex key3 30 "woshi" #設(shè)置過期時間及值
OK
127.0.0.1:6379> ttl key3
(integer) 23
127.0.0.1:6379> setnx key3 "hello" #設(shè)置值只有當(dāng)前key不存在時才成功
(integer) 1
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> setnx key3 "helo" #當(dāng)前key存在則失敗
(integer) 0
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #mset批量設(shè)置值
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> mget k1 k2 k3 #mget批量獲取值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k4 v4 k1 v1 #msetnx批量設(shè)置不存在的值饭豹,原子性操作鸵赖,有一個鍵存在則不成功
(integer) 0
對象
getset組合命令:先get再set,可用來作為更新操作拄衰;原子操作
String類似的使用場景:value除了字符串外還可以是數(shù)字
- 緩存: 經(jīng)典使用場景它褪,把常用信息,字符串翘悉,圖片或者視頻等信息放到redis中茫打,redis作為緩存層,mysql做持久化層妖混,降低mysql的讀寫壓力老赤。
- 計數(shù)器:redis是單線程模型,一個命令執(zhí)行完才會執(zhí)行下一個制市,同時數(shù)據(jù)可以一步落地到其他的數(shù)據(jù)源抬旺。
- session:常見方案spring session + redis實現(xiàn)session共享
List
在redis里面,可以將list作為棧祥楣,隊列开财,阻塞隊列使用汉柒,所有的list命令都是l開頭的
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> rpush list bingo #右邊壓入
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "bingo"
127.0.0.1:6379> lpop list #左邊彈出
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "bingo"
127.0.0.1:6379> rpop list #右邊彈出
"bingo"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1 #獲取下標(biāo)為1的值碾褂,-1表示最后一個
"one"
127.0.0.1:6379> llen list #獲取list的長度
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 three #移除一個three
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> ltrim list 0 1 #保留從0到1的元素,丟棄其他的
OK
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> exists list #查看隊列存不存在
(integer) 1
127.0.0.1:6379> exists list three #查看隊列元素存不存在
(integer) 1
127.0.0.1:6379> lset list 0 four #設(shè)置隊列對應(yīng)位置為新的值
OK
127.0.0.1:6379> lrange list 0 -1
1) "four"
2) "two"
127.0.0.1:6379> linsert list before four five #在four之前插入five历葛,若插入失敗返回-1
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "five"
2) "four"
3) "two"
127.0.0.1:6379> linsert list after five four #在five之后插入four正塌,若插入失敗返回-1
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "five"
2) "four"
3) "four"
4) "two"
rpoplpush組合命令: 先移除右邊的再壓入到左邊,原子操作
list 其實是一個鏈表恤溶。insert before 和 insert after都可以執(zhí)行传货,left right都可插入,取出
如果key不存在宏娄,創(chuàng)建新的鏈表问裕;如果key存在,新增內(nèi)容
如果移除了所有值孵坚,空鏈表粮宛,也代表不存在!
在兩邊插入或者改動值卖宠,效率最高巍杈,處理中間元素效率低一些
list使用場景:
- 微博TimeLine: 有人發(fā)布微博,用lpush加入時間軸扛伍,展示新的列表信息筷畦。
- 消息隊列
Set
set中的值是不能重復(fù)的,失敗操作返回0
127.0.0.1:6379> sadd myset "hello" #添加值刺洒,可以一次性添加多個
(integer) 1
127.0.0.1:6379> sadd myset "nihao" #添加值
(integer) 1
127.0.0.1:6379> sadd myset "kounijiwa" #添加值
(integer) 1
127.0.0.1:6379> smembers myset #查看集合元素
1) "kounijiwa"
2) "hello"
3) "nihao"
127.0.0.1:6379> sismember myset hello #判斷值是否在集合中
(integer) 1
127.0.0.1:6379> sismember myset byby
(integer) 0
127.0.0.1:6379> scard myset #獲取集合元素個數(shù)
(integer) 3
127.0.0.1:6379> srem myset kounijiwa #移除指定元素
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello"
2) "nihao"
127.0.0.1:6379> srandmember myset #隨機(jī)取出一個元素
"hello"
127.0.0.1:6379> srandmember myset
"hello"
127.0.0.1:6379> srandmember myset
"nihao"
127.0.0.1:6379> srandmember myset 2 #隨機(jī)取出指定個數(shù)個元素
1) "hello"
2) "nihao"
127.0.0.1:6379> spop myset 2 #隨機(jī)移除2個元素
1) "hello"
2) "nihao"
127.0.0.1:6379> smembers myset
(empty array)
127.0.0.1:6379> sadd myset hello
(integer) 1
127.0.0.1:6379> sadd myset nihao
(integer) 1
127.0.0.1:6379> sadd myset kounijiawa
(integer) 1
127.0.0.1:6379> sadd myset2 byebye
(integer) 1
127.0.0.1:6379> smove myset myset2 nihao #把myset的nihao移到myset2中
(integer) 1
127.0.0.1:6379> smembers myset
1) "kounijiawa"
2) "hello"
127.0.0.1:6379> smembers myset2
1) "byebye"
2) "nihao"
127.0.0.1:6379> sadd myset nihao
(integer) 1
127.0.0.1:6379> sdiff myset myset2 #做myset和myset2的差集
1) "kounijiawa"
2) "hello"
127.0.0.1:6379> sinter myset myset2 #做myset和myset2的交集
1) "nihao"
127.0.0.1:6379> sunion myset myset2 #做myset和myset2的并集
1) "kounijiawa"
2) "byebye"
3) "hello"
4) "nihao"
set使用場景
- 共同好友鳖宾,共同關(guān)注:使用并集實現(xiàn)
- 點贊,或點踩逆航,收藏等鼎文,可以放到set中實現(xiàn)
Hash(哈希)
Redis hash 是一個 string 類型的 field(字段) 和 value(值) 的映射表即key-map!hash 特別適合用于存儲對象。
127.0.0.1:6379> hset myhash name liubi #添加鍵值對
(integer) 1
127.0.0.1:6379> hget myhash name #獲取指定鍵的值
"liubi"
127.0.0.1:6379> hset myhash age 21 gender man #同時添加多個鍵值對
(integer) 2
127.0.0.1:6379> hmget myhash age gender #同時獲取多個鍵的值hmget
1) "21"
2) "man"
127.0.0.1:6379> hgetall myhash #獲取所有的鍵值對
1) "name"
2) "liubi"
3) "age"
4) "21"
5) "gender"
6) "man"
127.0.0.1:6379> hdel myhash gender #刪除指定鍵的鍵值對
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "name"
2) "liubi"
3) "age"
4) "21"
127.0.0.1:6379> hlen myhash #獲取hash中有多少個鍵值對
(integer) 2
127.0.0.1:6379> hexists myhash name #判斷指定的鍵是否存在
(integer) 1
127.0.0.1:6379> hkeys myhash #獲取所有的鍵
1) "name"
2) "age"
127.0.0.1:6379> hvals myhash #獲取所有的值
1) "liubi"
2) "21"
127.0.0.1:6379> hincrby myhash age 1 #給對應(yīng)鍵的值自增1因俐,注意沒有自減只能用自增負(fù)數(shù)實現(xiàn)
(integer) 22
127.0.0.1:6379> hsetnx myhash age 25 #指定鍵不存在則創(chuàng)建拇惋,存在則失敗返回0
(integer) 0
hash使用場景:
*緩存:相比string更節(jié)省空間, 能直觀的維護(hù)緩存信息抹剩,如用戶信息撑帖,視頻信息等。更適合存儲對象澳眷,string更加適合字符串的存儲
Zset(有序集合)
Redis 有序集合和集合一樣也是 string 類型元素的集合,且不允許重復(fù)的成員胡嘿。不同的是每個元素都會關(guān)聯(lián)一個 double 類型的分?jǐn)?shù)。redis 通過分?jǐn)?shù)來為集合中的成員進(jìn)行從小到大的排序境蔼。
127.0.0.1:6379> zadd myzset 1 one #添加元素及其分?jǐn)?shù)
(integer) 1
127.0.0.1:6379> zadd myzset 2 two 3 three #添加多個元素及其分?jǐn)?shù)
(integer) 2
127.0.0.1:6379> zrange myzset 0 -1 #按分?jǐn)?shù)從小到大取出元素
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zrangebyscore myzset -inf +inf #-inf表無窮小灶平,+inf表無窮大伺通,兩者區(qū)間之間升序
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zrangebyscore myzset 1 100 withscores #帶上分?jǐn)?shù)正序序輸出
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
127.0.0.1:6379> zrevrangebyscore myzset +inf -inf #降序輸出
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> zrem myzset one #移除指定元素
(integer) 1
127.0.0.1:6379> zrange myzset 0 -1
1) "two"
2) "three"
127.0.0.1:6379> zcard myzset #獲取有序集合的元素個數(shù)
(integer) 2
127.0.0.1:6379> zcount myzset 0 3 #獲取值為0到3之間的元素個數(shù)
(integer) 2
zset應(yīng)用場景
- 排行榜:有序集合經(jīng)典使用場景。例如小說視頻等網(wǎng)站需要對用戶上傳的小說視頻做排行榜逢享,榜單可以按照用戶關(guān)注數(shù)罐监,更新時間,字?jǐn)?shù)等打分瞒爬,做排行弓柱。
Redis三種特殊數(shù)據(jù)類型
geospatial地理位置
geoadd:添加位置命令
規(guī)則
兩級無法直接添加,我們一般會下載城市數(shù)據(jù)(這個網(wǎng)址可以查詢 GEO:http://www.jsons.cn/lngcode)侧但!
- 有效的經(jīng)度從-180度到180度矢空。
- 有效的緯度從-85.05112878度到85.05112878度。
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing #添加經(jīng)緯度地方
(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 55.55 33.33 nowhere 44.32 33.65 somewhere #同時加多個經(jīng)緯度
(integer) 2
geopos:獲取指定的成員的經(jīng)度和緯度命令
127.0.0.1:6379> geopos china:city beijing chongqing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
getdist:獲取兩位置之間距離禀横,默認(rèn)單位為m米屁药,不存在返回空
單位如下:
- m
- km
- mi 英里
- ft 英尺
127.0.0.1:6379> geodist china:city beijing shanghai
"1067378.7564"
georadius:附近的人(獲得所有附近的人的地址, 定位, 通過半徑來查詢)
georadius key 經(jīng)度 緯度 半徑 單位 [顯示結(jié)果的經(jīng)度和緯度 withcoord] [顯示結(jié)果的距離 withdist] [顯示的結(jié)果的數(shù)量 count n ]
127.0.0.1:6379> georadius china:city 110 30 1000 km
#以 100,30 這個坐標(biāo)為中心, 尋找半徑為1000km的城市
1) "chongqing"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord withdist count 2
#以 100,30 這個坐標(biāo)為中心, 尋找半徑為1000km的城市2個,并顯示其對應(yīng)經(jīng)緯度
1) 1) "xian"
2) "483.8340"
3) 1) "108.96000176668167114"
2) "34.25999964418929977"
2) 1) "manjing"
2) "864.9816"
3) 1) "118.75999957323074341"
2) "32.03999960287850968"
georadiusbymember:顯示與指定成員一定半徑范圍內(nèi)的其他成員柏锄,與georadius類似
127.0.0.1:6379> georadiusbymember china:city taiyuan 1000 km withcoord withdist count 2
1) 1) "taiyuan"
2) "0.0000"
3) 1) "112.54999905824661255"
2) "37.86000073876942196"
2) 1) "xian"
2) "514.2264"
3) 1) "108.96000176668167114"
2) "34.25999964418929977"
geohash:該命令返回11個字符的hash字符串酿箭,很少使用
#將二維的經(jīng)緯度轉(zhuǎn)換為一維的字符串秆撮,字符越相似越接近
127.0.0.1:6379> geohash china:city taiyuan shenyang
1) "ww8p3hhqmp0"
2) "wxrvb9qyxk0"
geo底層實現(xiàn)原理:使用了Zset
127.0.0.1:6379> type china:city #查看類型為zset
zset
127.0.0.1:6379> zrange china:city 0 -1 withscores #查看地圖所有的元素
1) "somewhere"
2) "3512854875600444"
3) "nowhere"
4) "3609594737223037"
5) "chongqing"
6) "4026042091628984"
7) "shanghai"
8) "4054803462927619"
9) "beijing"
10) "4069885360207904"
127.0.0.1:6379> zrem china:city nowhere #刪除指定位置元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1 withscores
1) "somewhere"
2) "3512854875600444"
3) "chongqing"
4) "4026042091628984"
5) "shanghai"
6) "4054803462927619"
7) "beijing"
8) "4069885360207904"
Hyperloglogs(基數(shù) 統(tǒng)計)
什么是基數(shù)
A = {1, 2, 3, 4, 5}准验, B = {3, 5, 6, 7, 9};那么基數(shù)(不重復(fù)的元素)= 1, 2, 4, 6, 7, 9惜颇; (允許容錯抬闷,即可以接受一定誤差)
Hyperloglogs解決什么問題
- 這個結(jié)構(gòu)可以非常省內(nèi)存的去統(tǒng)計各種計數(shù)妇蛀,比如注冊 IP 數(shù)、每日訪問 IP 數(shù)笤成、頁面實時UV评架、在線用戶數(shù),共同好友數(shù)等疹启。
- 傳統(tǒng)的方式古程,set保護(hù)用戶的id蔼卡,然后就可以統(tǒng)計set中的元素數(shù)量作為標(biāo)準(zhǔn)判斷喊崖,這個方式如果存入大量的用戶id,則比較麻煩雇逞,而且我們目的是計數(shù)荤懂。
- HyperLogLog 在 Redis 中每個鍵占用的內(nèi)容都是 12K,理論存儲近似接近 2^64 個值塘砸,不管存儲的內(nèi)容是什么节仿,它一個基于基數(shù)估算的算法,只能比較準(zhǔn)確的估算出基數(shù)掉蔬,可以使用少量固定的內(nèi)存去存儲并識別集合中的唯一元素廊宪。而且這個估算的基數(shù)并不一定準(zhǔn)確矾瘾,是一個帶有 0.81% 標(biāo)準(zhǔn)錯誤的近似值(對于可以接受一定容錯的業(yè)務(wù)場景,比如IP數(shù)統(tǒng)計箭启,UV等壕翩,是可以忽略不計的)。
命令 pf開頭
127.0.0.1:6379> pfadd myhp a b c d e f #創(chuàng)建第一組元素
(integer) 1
127.0.0.1:6379> pfcount myhp #統(tǒng)計元素個數(shù)
(integer) 6
127.0.0.1:6379> pfadd myhp2 a d m n l j #創(chuàng)建第二組元素
(integer) 1
127.0.0.1:6379> pfmerge myhp3 myhp myhp2 #合并兩個hplog到myhp3傅寡,去重復(fù)值
OK
127.0.0.1:6379> pfcount myhp3
(integer) 10 #不重復(fù)值為10個
Bitmaps(位圖數(shù)據(jù)結(jié)構(gòu)放妈,都是操作二進(jìn)制位來進(jìn)行記錄,只有0 和 1 兩個狀態(tài)荐操。)
Bitmaps解決什么問題芜抒?
- 統(tǒng)計用戶信息,活躍托启,不活躍宅倒! 登錄,未登錄屯耸! 打卡唉堪,不打卡! 兩個狀態(tài)的肩民,都可以使用 Bitmaps唠亚!存儲一年的打卡狀態(tài)只需要 365 天 = 365 bit 1字節(jié) = 8bit 46 個字節(jié)左右!
命令
127.0.0.1:6379> setbit sign 0 1 #設(shè)置當(dāng)天是否打卡
(integer) 0
127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> getbit sign 2 #獲取當(dāng)天是否打卡
(integer) 1
127.0.0.1:6379> bitcount sign #獲取所有打卡天數(shù)
(integer) 3
127.0.0.1:6379> bitcount sign 0 3 #獲取0到3天內(nèi)的打卡數(shù)
(integer) 3
事務(wù)
- redis單條命令是保證原子性的持痰,但是事務(wù)不保證原子性(事務(wù)可以部分成功)灶搜!
- redis能保證一致性,因為命令編譯時異彻で希可以使得事務(wù)回滾
- redis的事務(wù)沒有隔離級別的概念割卖,但是單線程保證了隔離
- redis事務(wù)不保證持久性,aof和rbd都是異步執(zhí)行的患雏,主要是出于對性能的考慮
- redis 事務(wù)的本質(zhì)是一組命令的集合鹏溯。事務(wù)支持一次執(zhí)行多個命令,一個事務(wù)中所有命令都會被序列化淹仑。在事務(wù)執(zhí)行過程丙挽,會按照順序串行化執(zhí)行隊列中的命令,其他客戶端提交的命令請求不會插入到事務(wù)執(zhí)行命令序列中匀借。
- redis事務(wù)就是一次性颜阐、順序性、排他性的執(zhí)行一個隊列中的一系列命令
redis事務(wù):
- 開啟事務(wù)(mutil)
- 命令入隊
- 執(zhí)行事務(wù)(exec)
- 取消事務(wù)(dicard)
- 監(jiān)視事務(wù)(watch):監(jiān)視一個或多個key,如果事務(wù)在執(zhí)行前吓肋,這個key(或多個key)被其他命令修改凳怨,則事務(wù)被中斷,不會執(zhí)行事務(wù)中的任何命令。
- 取消監(jiān)視(unwatch):取消watch對所有key的監(jiān)視
開啟執(zhí)行事務(wù)
127.0.0.1:6379> set k1 0 #先插入k1值
OK
127.0.0.1:6379> set k2 1 #先插入k2值
OK
127.0.0.1:6379> multi #開啟事務(wù)
OK
127.0.0.1:6379(TX)> set k1 8 #修改k1值操作進(jìn)隊列
QUEUED
127.0.0.1:6379(TX)> set k2 9 #修改k1值操作進(jìn)隊列
QUEUED
127.0.0.1:6379(TX)> exec #執(zhí)行事務(wù)
1) OK
2) OK
127.0.0.1:6379> get k1 #發(fā)現(xiàn)k1值被更改
"8"
127.0.0.1:6379> get k2 #發(fā)現(xiàn)k2值被更改
"9"
放棄事務(wù)
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 555
QUEUED
127.0.0.1:6379(TX)> discard #放棄事務(wù)
OK
127.0.0.1:6379> get k1 #k1值沒有變肤舞,說明事務(wù)失效
"8"
redis事務(wù)異常
- 編譯型異常:使用的命令或者寫的代碼有問題紫新,事務(wù)會失效,其中所有命令都不會被執(zhí)行李剖!
127.0.0.1:6379> multi #開啟事務(wù)
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> getset k2
(error) ERR wrong number of arguments for 'getset' command #錯誤的命令
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> exec #事務(wù)執(zhí)行失敗
(error) EXECABORT Transaction discarded because of previous errors.
- 運(yùn)行時異常:
127.0.0.1:6379> set k1 "v1" #設(shè)置k1為字符串
OK
127.0.0.1:6379> multi #開啟事務(wù)
OK
127.0.0.1:6379(TX)> incr k1 #給字符串加1(為運(yùn)行時錯誤)
QUEUED
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec #執(zhí)行事務(wù)弊琴,后面的操作成功
1) (error) ERR value is not an integer or out of range
2) OK
3) "2"
CAS操作實現(xiàn)樂觀鎖
使用watch命令實現(xiàn)redis事務(wù)的check-and-set(cas)操作
被 WATCH 的鍵會被監(jiān)視,并會發(fā)覺這些鍵是否被改動過了杖爽。 如果有至少一個被監(jiān)視的鍵在 EXEC 執(zhí)行之前被修改了敲董, 那么整個事務(wù)都會被取消, EXEC 返回nil-reply來表示事務(wù)已經(jīng)失敗慰安。
正常執(zhí)行
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 #監(jiān)視money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec #事務(wù)正常結(jié)束腋寨,數(shù)據(jù)在執(zhí)行期間未發(fā)生變動
1) (integer) 80
2) (integer) 20
- 啟動另外一個redis-cli進(jìn)行多線程測試
使用link2的redis客戶線程修改了money的值
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 250 #修改money的值為250
OK
然后在link1的redis客戶線程進(jìn)行操作
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec #發(fā)現(xiàn)執(zhí)行失敗,因為money的值被改了
(nil)
- 出現(xiàn)錯誤則先用unwatch解鎖化焕,重新進(jìn)行watch
127.0.0.1:6379> unwatch #先解鎖
OK
127.0.0.1:6379> watch money #然后重新加鎖
OK
127.0.0.1:6379> multi #重新開啟事務(wù)
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec #事務(wù)執(zhí)行成功
1) (integer) 240
2) (integer) 30
watch是如何監(jiān)視實現(xiàn)的呢萄窜?
- Redis使用watch命令來決定事務(wù)是繼續(xù)執(zhí)行還是回滾,那就需要在multi之前使用watch來監(jiān)控某些鍵值對撒桨,然后使用multi命令來開啟事務(wù)查刻,執(zhí)行對數(shù)據(jù)結(jié)構(gòu)操作的各種命令,此時這些命令入隊列凤类。
- 當(dāng)使用exec執(zhí)行事務(wù)時穗泵,首先會比對watch所監(jiān)控的鍵值對,如果沒發(fā)生改變谜疤,它會執(zhí)行事務(wù)隊列中的命令佃延,提交事務(wù);如果發(fā)生變化夷磕,將不會執(zhí)行事務(wù)中的任何命令履肃,同時事務(wù)回滾。當(dāng)然無論是否回滾坐桩,Redis都會取消執(zhí)行事務(wù)前的watch命令
為什么redis不支持回滾尺棋?
Redis 在事務(wù)失敗時不進(jìn)行回滾,而是繼續(xù)執(zhí)行余下的命令绵跷;回滾并不能解決編程錯誤帶來的問題
優(yōu)點:
- Redis 命令只會因為錯誤的語法而失敱烀(并且這些問題不能在入隊時發(fā)現(xiàn)),或是命令用在了錯誤類型的鍵上面:這也就是說抖坪,從實用性的角度來說萍鲸,失敗的命令是由編程錯誤造成的,而這些錯誤應(yīng)該在開發(fā)的過程中被發(fā)現(xiàn)擦俐,而不應(yīng)該出現(xiàn)在生產(chǎn)環(huán)境中。
- 因為不需要對回滾進(jìn)行支持握侧,所以 Redis 的內(nèi)部可以保持簡單且快速