Redis基礎(chǔ)知識

Nosql概述

為什么要用Nosql

現(xiàn)在處于大數(shù)據(jù)時代;大數(shù)據(jù)時代一般的數(shù)據(jù)庫無法進(jìn)行分析處理!

1.單機(jī)Mysql的年代!

image.png
image.png

2.Memcached(緩存) + MySQL + 垂直拆分

緩存并非一開始就出現(xiàn)的:
發(fā)展過程:優(yōu)化數(shù)據(jù)結(jié)構(gòu)和索引(IO)-----> Mencached(當(dāng)時最熱門的技術(shù))

image.png

3.分庫分表 + 水平拆分 + MySQL集群

技術(shù)和業(yè)務(wù)在發(fā)展的同時發(fā)展的同時,對人的要求越來越高!
本質(zhì):數(shù)據(jù)庫的讀寫
早些年MyISAM:表鎖,十分影響效率,高并發(fā)下出現(xiàn)嚴(yán)重的鎖問題
轉(zhuǎn)戰(zhàn)Innodb:行鎖
慢慢的.開始使用分庫分表來解決寫的壓力!MySQL在那個年代推出了表分區(qū),但使用不廣泛
MySQL的集群,很好滿足了那個年代的所有需求

image.png

4.如今最近的年代

image.png
image.png

為什么要用NoSQL

image.png

什么是NoSQL

NoSQL

image.png

NoSQL特點

image.png
  1. 傳統(tǒng)關(guān)系型數(shù)據(jù)庫和NoSQL
image.png
image.png

了解: 3V + 3高

image.png
image.png

阿里巴巴演進(jìn)分析:文檔

image.png
image.png

解決方案:

image.png

NoSQL四大分類

image.png
image.png
image.png
image.png

四者對比:

image.png

Redis入門

Redis是什么?

image.png

Redis 能干嘛?

image.png

Redis 特性

image.png

學(xué)習(xí)需要用的網(wǎng)站

image.png

Windows 版本在github上下載,但是停更很久了,redis建議在linux上學(xué)習(xí)

Windows下安裝

image.png
image.png
image.png

Linux下安裝

image.png
image.png
image.png
  1. 將redis配置文件拷貝到 /usr/local/bin/liubi-config自定義目錄下
image.png
  1. redis默認(rèn)不是后臺啟動的,需要修改配置文件!
image.png
  1. 啟動redis服務(wù)!通過指定的配置文件啟動服務(wù)
image.png
  1. 使用redis -cli 測試連接
image.png
  1. 查看redis的進(jìn)程是否開啟
image.png
  1. 關(guān)閉redis服務(wù)

在redis端口模式下輸入shutdown

image.png

使用kill -9 進(jìn)程id殺死對應(yīng)進(jìn)程

image.png
  1. 后面會使用單機(jī)多redis啟動集群測試

測試性能:redis-benchmark [option] [option value]

image.png
image.png
image.png
image.png

查看分析結(jié)果:get set incr lpush rpush lpop rpop sadd hset spop zadd zpopmin
lpush lrange mset等等信息

image.png
image.png

基礎(chǔ)知識

redis默認(rèn)有16個數(shù)據(jù)庫讼积,默認(rèn)使用的是第0個撞鹉,可以使用select進(jìn)行切換數(shù)據(jù)庫

image.png
image.png

dbsize 查看數(shù)據(jù)庫大小

image.png

keys * 查看所有鍵(key)

image.png

exists key 來判斷某個鍵是否存在

image.png

flushdb 清除當(dāng)前數(shù)據(jù)庫垂涯;flushall 清空所有數(shù)據(jù)庫

image.png

move key 目標(biāo)數(shù)據(jù)庫號 移動數(shù)據(jù)到目標(biāo)庫

image.png

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è)置過期時間

image.png

ttl key 查看key剩余存活時間 返回值為數(shù)字(秒)缝左,-1說明沒有設(shè)置生存時間

image.png

type key 查看當(dāng)前key的類型

image.png

redis單線程為什么還這么快

  1. 誤區(qū)1:高性能的服務(wù)器一定是多線程嗎亿遂?
  2. 誤區(qū)2:多線程(有cpu上下文切換Eㄈ簟)一定比單線程效率高

快的原因:

  1. 完全基于內(nèi)存,數(shù)據(jù)存在內(nèi)存中蛇数,絕大部分請求是純粹的內(nèi)存操作挪钓,非常快速耳舅,跟傳統(tǒng)的磁盤文件數(shù)據(jù)存儲相比碌上,避免了通過磁盤IO讀取到內(nèi)存這部分的開銷。
  2. 數(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)霞丧,來提升讀取和寫入的性能
  3. 采用單線程,省去了很多上下文切換的時間以及CPU消耗冕香,不存在競爭條件蛹尝,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作悉尾,也不會出現(xiàn)死鎖而導(dǎo)致的性能消耗
  4. 使用基于IO多路復(fù)用機(jī)制的線程模型突那,可以處理并發(fā)的連接
  5. Redis直接自己構(gòu)建了VM 機(jī)制 ,避免調(diào)用系統(tǒng)函數(shù)的時候焕襟,浪費(fèi)時間去移動和請求

Redis五大數(shù)據(jù)類型

命令:官網(wǎng)查詢http://www.redis.cn/commands.html

image.png
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

對象

image.png

getset組合命令:先get再set,可用來作為更新操作拄衰;原子操作

image.png

String類似的使用場景:value除了字符串外還可以是數(shù)字

  1. 緩存: 經(jīng)典使用場景它褪,把常用信息,字符串翘悉,圖片或者視頻等信息放到redis中茫打,redis作為緩存層,mysql做持久化層妖混,降低mysql的讀寫壓力老赤。
  2. 計數(shù)器:redis是單線程模型,一個命令執(zhí)行完才會執(zhí)行下一個制市,同時數(shù)據(jù)可以一步落地到其他的數(shù)據(jù)源抬旺。
  3. 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組合命令: 先移除右邊的再壓入到左邊,原子操作

image.png

list 其實是一個鏈表恤溶。insert before 和 insert after都可以執(zhí)行传货,left right都可插入,取出
如果key不存在宏娄,創(chuàng)建新的鏈表问裕;如果key存在,新增內(nèi)容
如果移除了所有值孵坚,空鏈表粮宛,也代表不存在!
在兩邊插入或者改動值卖宠,效率最高巍杈,處理中間元素效率低一些

list使用場景:

  1. 微博TimeLine: 有人發(fā)布微博,用lpush加入時間軸扛伍,展示新的列表信息筷畦。
  2. 消息隊列
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使用場景

  1. 共同好友鳖宾,共同關(guān)注:使用并集實現(xiàn)
  2. 點贊,或點踩逆航,收藏等鼎文,可以放到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地理位置
image.png

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)行多線程測試
image.png

使用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)部可以保持簡單且快速

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蚯瞧,一起剝皮案震驚了整個濱河市嘿期,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌埋合,老刑警劉巖备徐,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異甚颂,居然都是意外死亡蜜猾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門振诬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蹭睡,“玉大人,你說我怎么就攤上這事赶么〖缁恚” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵辫呻,是天一觀的道長清钥。 經(jīng)常有香客問我,道長放闺,這世上最難降的妖魔是什么祟昭? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮怖侦,結(jié)果婚禮上从橘,老公的妹妹穿的比我還像新娘。我一直安慰自己础钠,他們只是感情好恰力,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著旗吁,像睡著了一般踩萎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上很钓,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天香府,我揣著相機(jī)與錄音,去河邊找鬼码倦。 笑死企孩,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的袁稽。 我是一名探鬼主播勿璃,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了补疑?” 一聲冷哼從身側(cè)響起歧沪,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎莲组,沒想到半個月后诊胞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡锹杈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年撵孤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竭望。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡邪码,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出市框,到底是詐尸還是另有隱情霞扬,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布枫振,位于F島的核電站喻圃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏粪滤。R本人自食惡果不足惜斧拍,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杖小。 院中可真熱鬧肆汹,春花似錦、人聲如沸予权。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扫腺。三九已至岗照,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間笆环,已是汗流浹背攒至。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留躁劣,地道東北人迫吐。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像账忘,于是被迫代替她去往敵國和親志膀。 傳聞我的和親對象是個殘疾皇子熙宇,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容