慢查詢、pipline、釋出訂閱、 bitmap點陣圖、 hyperloglog、geo、持久化

认真的六六發表於2024-07-01

【慢查詢】

 1 # 1 我們配置一個時間,如果查詢時間超過了我們設定的時間,我們就認為這是一個慢查詢
 2 # 2 慢查詢是一個先進先出的佇列,固定長度,儲存在記憶體中--->透過設定慢查詢,以後超過我們設定時間的命令,就會放在這個佇列中
 3 # 3 後期我們透過查詢這個佇列,過濾出 慢命令--》最佳化慢命令
 4 
 5 
 6 # 4 實操:
 7     1 配置慢查詢
 8     # 設定記錄所有命令
 9     config set slowlog-log-slower-than 0
10     # 最多記錄100條
11     config set slowlog-max-len 100
12     # 持久化到本地配置檔案
13     config rewrite
14     
15     2 檢視慢查詢佇列
16     slowlog get [n]  #獲取慢查詢佇列
17         '''
18         日誌由4個屬性組成:
19         1)日誌的標識id
20         2)發生的時間戳
21         3)命令耗時
22         4)執行的命令和引數
23         '''
24      slowlog len #獲取慢查詢佇列長度
25     slowlog reset #清空慢查詢佇列
26     
27     
28 # mysql : slow_query_log

【pipline命令管道】

  1 #1 Redis的pipeline(管道)功能在命令列中沒有,但redis是支援pipeline的,而且在各個語言版的client中都有相應的實現
  2 
  3 # 2 pipeline期間將“獨佔”連結,多個命令,放到一個pipline(管道中),要麼都執行,要麼都不執行
  4 
  5 # 3 python中使用pipline
  6 import redis
  7 
  8 pool = redis.ConnectionPool(host='192.168.241.129', port=6379, password='jh123')
  9 conn = redis.Redis(connection_pool=pool)
 10 # pipe = r.pipeline(transaction=False)
 11 # 建立pipeline
 12 pipe =conn.pipeline(transaction=True)
 13 # 開啟事務
 14 pipe.multi()
 15 
 16 pipe.set('name', 'jh')
 17 # 其他程式碼,可能出異常
 18 pipe.set('role', 'nb')
 19 
 20 pipe.execute()
 21 
 22 
 23 # 4 事務四大特性
 24     -原子性
 25     -永續性
 26     -一致性
 27     -隔離性
 28 # 6 redis有沒有事務,能不能有這四大特性?
 29     -因為redis,能實現事務的四大特性---》咱們說它支援
 30     -透過pipline實現原子性--》透過pipline可以實現事務
 31     -pipline只支援單例項,叢集環境,不支援---》叢集環境不支援事務
 32     
 33     
 34 # 7 原生操作,實現樂觀鎖
 35 
 36 # 7.1 mutil  開啟事務,放到管道中一次性執行
 37 multi   # 開啟事務
 38 set name xxxx
 39 set age 18
 40 exec ,然後再去查,就發現資料改了
 41 
 42 
 43 # 7.2 模擬事務回顧 ,樂觀鎖
 44 # 在開啟事務之前,先watch
 45 watch age
 46 multi
 47 decr age
 48 exec
 49 
 50 # 另一臺機器
 51 multi
 52 decr age
 53 exec  # 先執行,上面的執行就會失敗(樂觀鎖,被wathc的事務不會執行成功)
 54 
 55 
 56 
 57 # 7.3 使用redis實現樂觀鎖
 58 
 59 
 60 ## 利用redis的樂觀鎖,實現秒殺系統的資料同步(基於watch實現),
 61 ## 使用者1 
 62 import redis
 63 conn = redis.Redis(host='192.168.241.129',port=6379)
 64 with conn.pipeline() as pipe:
 65     # 先監視,自己的值沒有被修改過
 66     conn.watch('count')
 67     # 事務開始
 68     pipe.multi()
 69     old_count = conn.get('count')
 70     count = int(old_count)
 71     input('我考慮一下')
 72     if count > 0:  # 有庫存
 73         pipe.set('count', count - 1)
 74 
 75     # 執行,把所有命令一次性推送過去
 76     ret = pipe.execute()
 77     print(type(ret))
 78     print(ret)
 79 ## 使用者2
 80 import redis
 81 conn = redis.Redis(host='127.0.0.1',port=6379)
 82 with conn.pipeline() as pipe:
 83     # 先監視,自己的值沒有被修改過
 84     conn.watch('count')
 85     # 事務開始
 86     pipe.multi()
 87     old_count = conn.get('count')
 88     count = int(old_count)
 89     if count > 0:  # 有庫存
 90         pipe.set('count', count - 1)
 91     # 執行,把所有命令一次性推送過去
 92     ret=pipe.execute()
 93     print(type(ret))
 94 
 95 # 注:windows下如果資料被修改了,不會拋異常,只是返回結果的列表為空,mac和linux會直接拋異常
 96 
 97 # 秒殺系統核心邏輯測試,建立100個執行緒併發秒殺(程式碼有問題)
 98 import redis
 99 from threading import Thread
100 def choose(name, conn):
101     with conn.pipeline() as pipe:
102         # 先監視,自己的值沒有被修改過
103         conn.watch('count')
104         # 事務開始
105         pipe.multi()
106         old_count = conn.get('count')
107         count = int(old_count)
108         # input('我考慮一下')
109         # time.sleep(random.randint(1, 2))
110         if count > 0:  # 有庫存
111             pipe.set('count', count - 1)
112 
113         # 執行,把所有命令一次性推送過去
114         ret = pipe.execute()
115         print(ret)
116         if len(ret) > 0:
117             print('第%s個人搶購成功' % name)
118         else:
119             print('第%s個人搶購失敗' % name)
120 
121 
122 if __name__ == '__main__':
123     conn = redis.Redis(host='192.168.241.129', port=6379)
124     for i in range(100):
125 
126         t = Thread(target=choose, args=(i, conn))
127         t.start()

【釋出訂 1 # 釋出訂閱(觀察者模式):釋出者釋出了訊息,所有的訂閱者都可以收到,就是生產者消費者模型

 2     -生產者消費者 釋出訂閱模型
 3     -生產者消費者 佇列
 4     
 5 # 原生實現
 6     -釋出訊息:
 7         publish channel message 
 8     -訂閱(現有訂閱者,然後釋出者才有訊息)
 9         subscribe channel
10     -接收訊息
11         -只要釋出者一發布,訂閱者都會受到
12         
13         
14 # python+redis實現
15 import redis
16 # 連線到Redis伺服器
17 r = redis.Redis(host='localhost', port=6379, db=0)
18 # 釋出者
19 20 r.publish('channel', 'message')
21  
22     
23     
24 import redis
25 # 連線到Redis伺服器
26 r = redis.Redis(host='localhost', port=6379, db=0)  
27 # 訂閱者
28 sub = r.pubsub()
29 sub.subscribe('channel')
30 for message in sub.listen():
31     print(message)
    

if isinstance(message['data'],bytes):
print(message['data'].decode('utf-8'))
if message['data'].decode('utf-8')['type']=='下單':
print(message['data'].decode('utf-8')['user'],'傳送簡訊')

【bitmap點陣圖】

 1 #1 本質是字串
 2 
 3 #2 操作位元位
 4   b         i        g
 5 01100011  01101001   01100111
 6 set hello big #放入key位hello 值為big的字串
 7 getbit hello 0 #取點陣圖的第0個位置,返回0
 8 getbit hello 1 #取點陣圖的第1個位置,返回1 如上圖
 9 
10 # 我們可以直接操縱位
11 setbit key offset value #給點陣圖指定索引設定值
12 setbit hello 7 1 #把hello的第7個位置設為1 這樣,big就變成了cig
13 
14 
15 # 3 獨立使用者統計---》統計日活---》使用者量足夠大--》節約記憶體
16     -10億使用者  使用者id1 2  3  10億
17     
18     1個bytes--》8個位元位---》能表示多少種變化?
19         2的7次方-1 :-128 到127之間
20     8個bytes--》64個位元位--》9223372036854775808
21     
22     如果以集合儲存:存一個使用者,就要8個bytes---》存1億使用者 8 bytes*1億
23     
24     -統計日活,只要使用者登入,就把使用者id放到集合中
25     -晚上只需要統計一下 集合大小---》就能統計出日活   
26     -使用集合儲存---》1--》32位--》1億使用者 5千萬左右---》需要 200MB空間
27         int8個位元位表示範圍 -128--127之間
28         int16 
29         int32 個位元位表示範圍 -2的31次方---2的31次方  2147483648
30         
31     -使用點陣圖---》12.5MB空間
32 
33     
34 # 獲取前3位中1的個數
35 bitcount name 0 3 

【hyperloglog】

 1 # 1 基於HyperLogLog演算法:極小的空間完成獨立數量統計,本質還是字串
 2 
 3 # 2 
 4     -放值:pfadd uuids "uuid1" "uuid2" "uuid3" "uuid4"
 5     -統計個數:pfcount uuids
 6     -判斷一個值是否在裡面:pfadd uuids "uuid1"(返回1/0)
 7       
 8 # 3 統計日活使用者
 9 
10 # 4 去重:爬過的網址,就不爬了--》存一下爬過的
11 'www.baidu.com'--->放到 pfadd 中--》返回1,說明沒爬過--》繼續爬取--》如果返回0,不爬了
12 
13 # 5 黑白名單
14 
15 # 6 注意
16 百萬級別獨立使用者統計,百萬條資料只佔15k
17 錯誤率 0.81%
18 無法取出單條資料,只能統計個數
19 
20 # 7 類似於布隆過濾器,演算法不一樣

【geo】

 1 # 1 geo 地理位置資訊
 2 GEO(地理資訊定位):儲存經緯度,計算兩地距離,範圍等
 3 北京:116.28,39.55
 4 天津:117.12,39.08
 5 可以計算天津到北京的距離,天津周圍50km的城市,外賣等
 6 
 7 # 2 經緯度哪裡來?
 8     - web端:js獲取--》掉介面,傳給後端
 9     - 安卓,ios端:執行緒的程式碼
10     -微信小程式
11           
12 # 3 js獲取經緯度
13 if (navigator.geolocation) {
14   navigator.geolocation.getCurrentPosition(function(position) {
15     var latitude = position.coords.latitude;
16     var longitude = position.coords.longitude;
17     console.log("經度:" + longitude);
18     console.log("緯度:" + latitude);
19   });
20 } else {
21   console.log("瀏覽器不支援Geolocation API");
22 }
23 
24 
25 # 4 增加地理位置資訊
26 geoadd key longitude latitude member #增加地理位置資訊
27 
28 geoadd cities:locations 116.28 39.55 beijing #把北京地理資訊天津到cities:locations中
29 geoadd cities:locations 117.12 39.08 tianjin
30 geoadd cities:locations 114.29 38.02 shijiazhuang
31 geoadd cities:locations 118.01 39.38 tangshan
32 geoadd cities:locations 115.29 38.51 baoding
33 
34 
35 # 5 獲取天津的經緯度  geopos key member #獲取地理位置資訊
36 geopos cities:locations tianjin
37 # 根據經緯度--》文字
38 from geopy.geocoders import Nominatim
39 geolocator = Nominatim(user_agent="my_application")
40 def geolocate_point(latitude, longitude):
41     location = geolocator.reverse(f"{latitude}, {longitude}")
42     return location.address
43 # 示例使用經緯度
44 latitude = 39.983424
45 longitude = 116.307224
46 address = geolocate_point(latitude, longitude)
47 print(address)
48 
49 # 6 統計兩個地理位置之間的距離
50 geodist cities:locations beijing tianjin km #北京到天津的距離,89公里
51 
52 # 7 統計某個地理位置方圓xx公里,有xx
53 georadiusbymember cities:locations beijing 150 km

【持久化】

 1 # 1 什麼是持久化
 2 redis的所有資料儲存在記憶體中,對資料的更新將非同步的儲存到硬碟上
 3 
 4 # 2 持久化的實現方式
 5 快照:某時某刻資料的一個完成備份,
 6      -mysql的Dump:寫一個mysql自動定時備份和清理前後端程式
 7     -redis的RDB:某一刻,把記憶體中得資料,儲存到硬碟上這個操作就是rbd的持久化
 8 寫日誌:任何操作記錄日誌,要恢復資料,只要把日誌重新走一遍即可
 9     -mysql的 Binlog
10     -Redis的 AOF
11     
12 # 3 redis有三種持久化方案
13     -rdb:快照
14     -aof:日誌
15     -混合持久化:rdb+aof混合方案

rdb

 1 # 4 rdb方案 :三種方式
 2     1 人工同步:客戶端 : save  # 同步操作,會阻塞其他命令--》單執行緒架構
 3     2 人工非同步:客戶端:bgsave  # 非同步操作,不會阻塞其他命令
 4     3 配置檔案
 5         save 900 1 #配置一條
 6         save 300 10 #配置一條
 7         save 60 10000 #配置一條
 8 
 9         save 60 5
10 
11 # 5 最佳配置
12 #最佳配置
13 save 900 1 
14 save 300 10 
15 save 60 10000 
16 dbfilename dump-6379.rdb  #以埠號作為檔名,可能一臺機器上很多reids,不會亂
17 dir ./bigdiskpath #儲存路徑放到一個大硬碟位置目錄
18 stop-writes-on-bgsave-error yes #出現錯誤停止
19 rdbcompression yes #壓縮
20 rdbchecksum yes #校驗
21 
22 
23 # 6 確定 RDB問題
24     耗時,耗效能:
25     不可控,可能會丟失資料

aof

1 # 1 客戶端每寫入一條命令,都記錄一條日誌,放到日誌檔案中,如果出現當機,可以將資料完全恢復
2 # 2 AOF的三種策略日誌不是直接寫到硬碟上,而是先放在緩衝區,緩衝區根據一些策略,寫到硬碟上always:redis–》寫命令重新整理的緩衝區—》每條命令fsync到硬碟—》AOF檔案everysec(預設值):redis——》
寫命令重新整理的緩衝區—》每秒把緩衝區fsync到硬碟–》AOF檔案no:redis——》寫命令重新整理的緩衝區—》作業系統決定,緩衝區fsync到硬碟–》AOF檔案​
3 # 3 配置檔案appendonly yes #將該選項設定為yes,開啟appendfilename "appendonly-${port}.aof" 4 #檔案儲存的名字appendfsync everysec 5 #採用第二種策略dir /bigdiskpath 6 #存放的路徑no-appendfsync-on-rewrite yes​​appendonly yesappendfilename "appendonly.aof"appendfsync everysecno-appendfsync-on-rewrite yes​​ 7 8 # 4 放在了檔案中appendonly.aof.1.base.rdb :永久的appendonly.aof.1.incr.aof :臨時的appendonly.aof.manifest: 哪些檔案存了資料

相關文章