redis——集合,有序,慢查詢, pipline與事務, bitmap ,HyperLogLog geo

拆尼斯、帕丁顿發表於2024-03-21

集合型別(set)

sadd key element #向集合key新增element(如果element存在,新增失敗) o(1)
srem key element #從集合中的element移除掉 o(1)
scard key #計算集合大小
sismember key element #判斷element是否在集合中
srandmember key count #從集合中隨機取出count個元素,不會破壞集合中的元素 (抽獎)
spop key #從集合中隨機彈出一個元素
smembers key #獲取集合中所有元素 ,無序,小心使用,會阻塞住 

sdiff user:1:follow user:2:follow  #計算user:1:follow和user:2:follow的差集
sinter user:1:follow user:2:follow  #計算user:1:follow和user:2:follow的交集        
sunion user:1:follow user:2:follow  #計算user:1:follow和user:2:follow的並集   



sdiff|sinter|suion + store destkey... #將差集,交集,並集結果儲存在destkey集合中
sdiffstore xxx number1 number2
SUNIONSTORE myset myset1 myset2



### 總結
###  實戰
抽獎系統 :透過spop來彈出使用者的id,活動取消,直接刪除
點贊,點踩,喜歡等,使用者如果點了贊,就把使用者id放到該條記錄的集合中
標籤:給使用者/文章等新增標籤,sadd user:1:tags 標籤1 標籤2 標籤3
給標籤新增使用者,關注該標籤的人有哪些
共同好友:集合間的操作

# 總結4
sadd:可以做標籤相關
spop/srandmember:可以做隨機數相關
sadd/sinter:社交相關

有序集合(zset)

#### 特點   有一個分值欄位,來保證順序
key                  score                value
user:ranking           1                   lqz
user:ranking           99                  lqz2
user:ranking           88                  lqz3


#集合有序集合
集合:無重複元素,無序,element
有序集合:無重複元素,有序,element+score


#列表和有序集合
列表:可以重複,有序,element
有序集合:無重複元素,有序,element+score



# API使用    zset
zadd key score element #score可以重複,可以多個同時新增,element不能重複 o(logN) 

zrem key element #刪除元素,可以多個同時刪除 o(1)

zscore key element #獲取元素的分數 o(1)

zincrby key increScore element #增加或減少元素的分數  o(1)

zcard key #返回元素總個數 o(1)

zrank key element #返回element元素的排名(從小到大排)

zrange key 0 -1 #返回排名,不帶分數  o(log(n)+m) n是元素個數,m是要獲取的值
zrange player:rank 0 -1 withscores #返回排名,帶分數

zrangebyscore key minScore maxScore #返回指定分數範圍內的升序元素 o(log(n)+m) n是元素個數,m是要獲取的值
zrangebyscore user:1:ranking 90 210 withscores #獲取90分到210分的元素

zcount key minScore maxScore #返回有序集合內在指定分數範圍內的個數 o(log(n)+m)

zremrangebyrank key start end #刪除指定排名內的升序元素 o(log(n)+m)
zremrangebyrank user:1:rangking 1 2 #刪除升序排名中1到2的元素
        
zremrangebyscore key minScore maxScore #刪除指定分數內的升序元素 o(log(n)+m)
zremrangebyscore user:1:ranking 90 210 #刪除分數90到210之間的元素
        
 

# 其他操作
zrevrank #從高到低排序
zrevrange #從高到低排序取一定範圍
zrevrangebyscore #返回指定分數範圍內的降序元素
zinterstore #對兩個有序集合交集
zunionstore #對兩個有序集合求並集

# 實戰
排行榜:音樂排行榜,銷售榜,關注榜,遊戲排行榜

慢查詢(排查reids慢的問題)

# MySQL 中的 7 種日誌介紹

# 生命週期
客戶端編寫命令  get name---》透過網路 ---》到服務端---》服務端執行命令【查詢,修改。。】---》返回資料透過網路返回給客戶端

我們配置一個時間,如果查詢時間超過了我們設定的時間,我們就認為這是一個慢查詢.

慢查詢發生在第三階段

客戶端超時不一定慢查詢,但慢查詢是客戶端超時的一個可能因素



 # 兩個配置 配置慢查詢
   -只要是超過慢查詢時間的命令,都會被記錄
   -後期透過記錄分析,哪些命令是慢的,儘量在生成環境中避免
    
   -慢查詢是一個佇列,裡面記錄了,超過你設定時間的命令
   - 透過兩個配置:
    slowlog-log-slower-than  慢於多少微秒的都會被記錄
    slowlog-max-len          佇列長度是多少
    
    
  -配置檔案直接配置
    # 設定記錄所有命令
    config set slowlog-log-slower-than 0
    # 最多記錄100條
    config set slowlog-max-len 100
    # 持久化到本地配置檔案
    config rewrite
    
    
  -檢視慢查詢佇列
    slowlog get 100
    slowlog len #獲取慢查詢佇列長度
    slowlog reset #清空慢查詢佇列
    
    
    
# 總結:
    開啟慢查詢記錄後---》只要超過某個時間的命令,都會被記錄到慢查詢佇列中
    後期我們可以透過慢查詢佇列中的命令,最佳化程式,提高redis使用效率

pipline與事務

# redis 其實不支援事務,但是可以透過pipline來模擬事務,pipline只支援單例項redis,如果做叢集,沒有pipiline

# redis 支援事務嗎?
    不支援,可以透過pipline實現,但是叢集環境不能用pipline





### python客戶端實現
import redis
pool = redis.ConnectionPool(host='10.0.0.111', port=6379)
r = redis.Redis(connection_pool=pool)
# pipe = r.pipeline(transaction=False)
#建立pipeline
pipe = r.pipeline(transaction=True)
#開啟事務
pipe.multi()
pipe.set('name', 'lqz')
#其他程式碼,可能出異常

pipe.set('role', 'nb')
 
pipe.execute()


####### 原生redis操作,模擬事務##########
# 0 開啟兩個客戶端

# 在第一個客戶端執行 mutil  開啟事務,放到管道中一次性執行
multi     # 開啟事務
set name lqz
set age 18
exec

# 在第二個客戶端去查詢,如果第一個客戶端沒有執行exec ,查詢不到第一個事務未提交的資料

# 隔離級別---》讀已提交級別





########## 原生redis  透過watch+pipline(multi)  模擬樂觀鎖##########
### 0 開啟兩個客戶端
### 1 第一個客戶端上 在開啟事務之前,先watch
wathc age  # 看了一眼,是10
multi
decr age   # 它有可能被別人操作了
exec       

# 另一臺機器
multi
decr age
exec  # 先執行,上面的執行就會失敗(樂觀鎖,被wathc的事務不會執行成功)


#### 整合到python專案中實現基於redis利用redis的樂觀鎖,實現秒殺系統的資料同步(基於watch實現),
import redis
from threading import Thread

def choose(name, conn):
    # conn.set('count',10)
    with conn.pipeline() as pipe:
        # 先監視,自己的值沒有被修改過
        conn.watch('count')
        # 事務開始
        pipe.multi()
        old_count = conn.get('count')
        count = int(old_count)
        # input('我考慮一下')
        # time.sleep(random.randint(1, 2))
        if count > 0:  # 有庫存
            pipe.set('count', count - 1)

        # 執行,把所有命令一次性推送過去
        ret = pipe.execute()
        print(ret)
        if len(ret) > 0:
            print('第%s個人搶購成功' % name)
        else:
            print('第%s個人搶購失敗' % name)


if __name__ == '__main__':
    conn = redis.Redis(host='10.0.0.111', port=6379,password='654321')
    for i in range(100):

        t = Thread(target=choose, args=(i, conn))
        t.start()

釋出訂閱

# 釋出者釋出了訊息,所有的訂閱者都可以收到,就是生產者消費者模型(後訂閱了,無法獲取歷史訊息)

# 如果是訊息佇列
    生產者生產了一條訊息---》只會有一個消費者消費
# 如果是釋出定義---》釋出訂閱---》觀察者模式
    生產者生產了一條訊息---》所有訂閱生產者的消費者都會收到訊息
    
    
    
  
# 實際操作
    -釋出者釋出訊息
    publish channel01 "hello world"
    
    -訂閱者01訂閱頻道 channel01 
        subscribe channel01 
    -訂閱者02訂閱頻道 channel01
        subscribe channel01 
        
        
        
# 實際用途
    -只要設計到,一個人發生變化,其他人都收到通知的情況,就可以使用釋出訂閱
    
    
    -如何用,python?
       員工1 ,員工2 關注了 張三
    張三傳送一篇文章---》文章儲存到資料庫了---》訊號繫結一個函式--》在函式中釋出訊息
        conn.publish('user_zhangsan_new_article','文章id')
        
    啟動一個程式---》等待別人釋出訊息---》只要別人釋出了訊息---》取出頻道--》去mysql檢視哪些員工訂閱了這個頻道---[員工1 ,員工2]--->發郵件通知

bitmap

# 操作位元位
  c         i        g
01100011  01101001   01100111
set hello big #放入key位hello 值為big的字串
getbit hello 0 #取點陣圖的第0個位置,返回0
getbit hello 1 #取點陣圖的第1個位置,返回1 如上圖

# 我們可以直接操縱位
setbit key offset value #給點陣圖指定索引設定值
setbit hello 7 1 #把hello的第7個位置設為1 這樣,big就變成了cig


# 獨立使用者統計---》統計日活---》使用者量足夠大--》節約記憶體
    -10億使用者  使用者id1 2  3  10億
    -統計日活,只要使用者登入,只要使用者登入,就把使用者id放到集合中
    -晚上只需要統計一下 集合大小---》就能統計出日活
   
    
   
    -使用集合儲存---》1--》32位--》1億使用者 5千萬左右---》需要 200MB空間
        int8個位元位表示範圍 -128--127之間
        int16 
        int32 個位元位表示範圍 -2的31次方---2的31次方  2147483648
        
    -使用點陣圖---》12.5MB空間

HyperLogLog

基於HyperLogLog演算法:極小的空間完成獨立數量統計,極小記憶體實現去重

pfadd urls "www.baidu.com" "www.cnblogs.com" "www.lqz.com"

pfcount urls

pfadd urls 值 # 返回0表示在,返回1 表示不在


# 總結
百萬級別獨立使用者統計,百萬條資料只佔15k
錯誤率 0.81%
無法取出單條資料,只能統計個數

geo

# GEO(地理資訊定位):儲存經緯度,計算兩地距離,範圍等
    
 # 經緯度如何獲取?
    -不是後端做的
    -手機端---》手機有定位--》申請了定位---》手機端能拿到經緯度---》提供介面--->提交到後端
    -web端---》js獲取經緯度---》提供介面---》提交到後端
    
    
# 後端只需要儲存
geoadd key longitude latitude member #增加地理位置資訊
geoadd cities:locations 116.28 39.55 beijing #把北京地理資訊天津到cities:locations中
geoadd cities:locations 117.12 39.08 tianjin
geoadd cities:locations 114.29 38.02 shijiazhuang
geoadd cities:locations 118.01 39.38 tangshan
geoadd cities:locations 115.29 38.51 baoding
    
# 統計兩個經緯度距離
geodist cities:locations beijing baoding km
    
# 統計北京方圓 100公里內的城市
georadiusbymember cities:locations beijing 100 km

相關文章