Redis 介紹安裝配置, 使用場景, redis-API的使用(通用命令, 五大資料型別), redis高階用法(慢查詢優化, 管道[實現事務功能], 釋出訂閱, Bitmap點陣圖, HyperLo
Redis 介紹安裝配置
#1 只有5種資料結構: -多種資料結構:字串,hash,列表,集合,有序集合 #2 單執行緒,速度為什麼這麼快? -本質還是因為是記憶體資料庫 -epoll模型(io多路複用) -單執行緒,沒有執行緒,程序間的通訊 # 3 linux上 安裝redis#下載 (測試放在/home/ldc路徑下) wget http://download.redis.io/releases/redis-5.0.7.tar.gz #解壓 tar -xzf redis-5.0.7.tar.gz #建立軟連線 (相當於建立快捷方式,直接cd就能進去) ln -s redis-5.0.7 redis # 當前路徑下會有redis資料夾 cd redis make&&make install # 編譯和安裝兩步並一步 # bin路徑下幾個命令:redis-cli,redis-server,redis-sentinel (cd到redis裡的src資料夾中,能看到命令,可以敲命令使用) # 在任意位置能夠執行redis-server 如何做?配置環境變數 (經測試這裡redis自動加入環境變數中) #4 啟動redis的三種方式 -方式一:(一般不用,沒有配置檔案) -redis-server -方式二:(用的也很少) # 以6380埠開啟 redis-server --port 6380 -方式三:(都用這種,配置檔案) # cat redis.conf檢視配置檔案,下面為內部重要引數 daemonize yes#是否以守護程序啟動 pidfile /var/run/redis.pid#程序號的位置,刪除 port 6379#埠號 dir "/opt/soft/redis/data"#工作目錄(資料存放的目錄) logfile 6379.log#日誌位置 # vim redis.conf1 編寫新的配置檔案,寫入上面5行重要引數 (注意命令結尾不要空格,可以寫註釋。經過測試,刪掉註釋才能識別。。。) # 啟動:redis-server redis.conf1(Can't chdir to '/opt/soft/redis/data': No such file or directory報錯。建立對應的資料夾路徑,然後回到redis.conf1路徑下) #5 客戶端連線 redis-cli -h 127.0.0.1 -p 6379 # 檢視程序是否存在 ps -ef |grep redis-server |grep 6379 # 參看日誌 cat 6379.log
使用場景
快取系統:使用最廣泛的就是快取
計數器:網站訪問量,轉發量,評論數(文章轉發,商品銷量,單執行緒模型【一個個執行,不會出現併發問題】)
訊息佇列:釋出訂閱,阻塞佇列實現(簡單的分散式,blpop:阻塞佇列,生產者消費者)
排行榜:有序集合(閱讀排行,點贊排行,推薦(銷量高的,推薦))
社交網路:很多特效跟社交網路匹配,粉絲數,關注數
實時系統:垃圾郵件處理系統,布隆過濾器
redis-API的使用
# 介紹,安裝,啟動,應用場景 在配置檔案中新增:允許遠端登陸(windows連線測試,redis沒設密碼,不用新增下面引數也能連) bind 0.0.0.0 protected-mode no requirepass 123456 # 可加可不加 #1 通用命令 ####1-keys #打印出所有key keys * (少用,慢查詢。資料多,會特別慢) #keys命令一般不在生產環境中使用,生產環境key很多,時間複雜度為o(n),用scan命令 ####2-dbsize 計算key的總數 dbsize #redis內建了計數器,插入刪除值該計數器會更改,所以可以在生產環境使用,時間複雜度是o(1) ###3-exists key 時間複雜度o(1) exists key # 檢視key是否存在 #設定a set a b #檢視a是否存在 exists a (integer) 1 # 返回結果 #存在返回1 不存在返回0 ###4-del key 時間複雜度o(1) 刪除成功返回1,key不存在返回0 ###5-expire key seconds 時間複雜度o(1) expire name 時間 # 給name這個key設定一個超時時間 expire name 3 #3s 過期 ttl name # 還剩多長時間過期 persist name # 去掉超時時間,沒有過期時間。如果過期了,就不存在了,返回(integer) 0 ###6-type key 時間複雜度o(1) type name # 檢視key 對應資料的型別 # 2 資料結構:5大資料型別(字串,列表,hash,集合,有序集合 他們底層有更豐富的型別儲存) # 3 單執行緒架構,同一時刻,只有一個命令(不會出現併發問題) -其實並不是真正的單執行緒,執行命令是單執行緒,可能會持久化資料
字串型別
# 4 字串型別 ##常用命令 ###1---基本使用get,set,del get name #時間複雜度 o(1) set name lqz #時間複雜度 o(1) del name #時間複雜度 o(1) ###2---其他使用incr,decr,incrby,decrby incr age #對age這個key的value值自增1 decr age #對age這個key的value值自減1 incrby age 10 #對age這個key的value值增加10 decrby age 10 #對age這個key的value值減10 #統計網站訪問量(單執行緒無競爭,天然適合做計數器) #快取mysql的資訊(json格式) #分散式id生成(多個機器同時併發著生成,不會重複) ###3---set,setnx,setxx set name lqz #不管key是否存在,都設定 setnx name lqz #key不存在時才設定(新增操作) set name lqz nx #同上 set name lqz xx #key存在,才設定(更新操作) ###4---mget mset mget key1 key2 key3 #批量獲取key1,key2.。。時間複雜度o(n) mset key1 value1 key2 value2 key3 value3 #批量設定時間複雜度o(n) #n次get和mget的區別 #n次get時間=n次命令時間+n次網路時間 #mget時間=1次網路時間+n次命令時間 ###5---其他:getset,append,strlen getset name lqznb #設定新值並返回舊值 時間複雜度o(1) append name 666 #將value追加到舊的value 時間複雜度o(1) strlen name #計算字串長度(注意中文) 時間複雜度o(1) ###6---其他:incrybyfloat,getrange,setrange increbyfloat age 3.5 #為age自增3.5,傳負值表示自減 時間複雜度o(1) getrange key start end #獲取字串制定下標所有的值 時間複雜度o(1) #例 getrange name 1 3 取第1到第3位(從0開始),前閉後閉區間 setrange key index value #從指定index開始設定value值 時間複雜度o(1) #例 setrange name 1 ppp 從第1位開始替換為ppp 例如返回"lpppb666" -incrby 分散式id的生成 -mget mset 批量設定值,取值 (生產環境中儘量少用)
分散式id的生成
雜湊型別
雜湊值結構
# 5 hash型別
##常用命令 ###1---hget,hset,hdel hget key field #獲取hash key對應的field的value 時間複雜度為 o(1) hset key field value #設定hash key對應的field的value值 時間複雜度為 o(1) hdel key field #刪除hash key對應的field的值 時間複雜度為 o(1) #測試 hset user:1:info age 23 #使用:分層方便層級檢視 hget user:1:info age hset user:1:info name lqz hgetall user:1:info hdel user:1:info age ###2---hexists,hlen hexists key field #判斷hash key 是否存在field 時間複雜度為 o(1) hlen key #獲取hash key field的數量 時間複雜度為 o(1) hexists user:1:info name hlen user:1:info #返回數量 ###3---hmget,hmset hmget key field1 field2 ...fieldN #批量獲取hash key 的一批field對應的值 時間複雜度是o(n) hmset key field1 value1 field2 value2 #批量設定hash key的一批field value 時間複雜度是o(n) ###4--hgetall,hvals,hkeys hgetall key #返回hash key 對應的所有field和value 時間複雜度是o(n) hvals key #返回hash key 對應的所有field的value 時間複雜度是o(n) hkeys key #返回hash key對應的所有field 時間複雜度是o(n) ###小心使用hgetall ##1 計算網站每個使用者主頁的訪問量 hincrby user:1:info pageview count #例hincrby user:1:info age 3 #這個key中user代表使用者,1代表使用者id,info代表使用者資訊。使用:分層方便軟體檢視 #另外一般使用這種方法,好處是同一個key下。如果使用incrby key來自增,會導致key很多不方便,並影響效率 ##2 快取mysql的資訊,直接設定hash格式(對比存string型別,key會少一些) hset hget hdel hexists --下方5個都是長慢命令 hmget hmset hgetall 獲取所有 #長慢命令,出現卡頓情況。用hscan一點一點取,做生成器。hscan底層生成器實現 hvals 獲取所有value值 hkeys 獲取所有key值 hincrby user:group { #此處虛擬碼,hincrby使用情況。 userid1:88 userid2:100 userid3:22 } ####注意:雜湊型別只支援一層,裡面不能套字典,列表 ##其他操作 hsetnx,hincrby,hincrbyfloat hestnx key field value #設定hash key對應field的value(如果field已存在,則失敗),時間複雜度o(1) hincrby key field intCounter #hash key 對英的field的value自增intCounter 時間複雜度o(1) hincrbyfloat key field floatCounter #hincrby 浮點數 時間複雜度o(1)
# 面試官:你在使用redis的時候,有沒有要注意的點?-----》拒絕長慢命令
# 在用redis的過程中遇到什麼問題?-----》同事寫的命令,慢,我去排查,修改hgeall---一次性獲取出來 用hscan代替,一點點取,做成生成器
列表型別
# 6 列表型別 -有序佇列:從左側新增,右側新增,左側彈出,右側彈出,可以重複 -實現佇列和棧(爬蟲深度優先和廣度優先[先進先出還是後進後出]) ##插入操作 #rpush 從右側插入 rpush key value1 value2 ...valueN #時間複雜度為o(1~n) 例rpush ll 1 2 3 #lpush 從左側插入 #linsert linsert key before|after value newValue #從元素value的前或後插入newValue 時間複雜度o(n) ,需要便利列表 例linsert ll before 2 888 linsert listkey before b java linsert listkey after b php ##刪除操作 lpop key #從列表左側彈出一個item 時間複雜度o(1) rpop key #從列表右側彈出一個item 時間複雜度o(1) lrem #從指定位置刪除 lrem key count value #根據count值,從列表中刪除所有value相同的項 時間複雜度o(n) 1 count>0 從左到右,刪除最多count個value相等的項 2 count<0 從右向左,刪除最多 Math.abs(count)個value相等的項 3 count=0 刪除所有value相等的項 lrem listkey 0 a #刪除列表中所有值a lrem listkey -1 c #從右側刪除1個c ltrim key start end #按照索引範圍修剪列表 o(n) 前後都為閉區間 ltrim ll 1 4 #修剪,只保留下表1-4的元素 ##查詢操作 lrange key start end #包含end獲取列表指定索引範圍所有item o(n) end如果超出範圍不報錯 lrange listkey 0 2 lrange listkey 1 -1 #獲取第一個位置到倒數第一個位置的元素 lindex key index #獲取列表指定索引的item o(n) lindex listkey 0 lindex listkey -1 # 獲取最後一個 llen key #獲取列表長度 ##修改操作 lset key index newValue #設定列表指定索引值為newValue o(n) lset listkey 2 ppp #把第二個位置設為ppp lset ll 1 99 #使用場景 --時間軸:微信朋友圈狀態,列表有順序 -關注的人 列表的key 你的id號[每次關注一個插入一條] ##其他操作 blpop key timeout #lpop的阻塞版,timeout是阻塞超時時間,timeout=0為擁有不阻塞 o(1) brpop key timeout #rpop的阻塞版,timeout是阻塞超時時間,timeout=0為擁有不阻塞 o(1) #要實現棧的功能 lpush+lpop # 後進先出 #實現佇列功能 lpush+rpop # 先進先出 #固定大小的列表 lpush+ltrim # 插進去修剪下 #訊息佇列 lpush+brpop # 插入,右側取的時候是阻塞狀態 達到類似celery的效果 # 佇列用來幹啥? -流量削峰 # 處理能力有限,一點點處理 -解耦 # 本來一個程式,變為go專案給資料到python處理 -非同步
集合型別
# 6 集合型別 -無序,不重複(去重),社交相關(交叉並補),同時關注劉亦菲的,你們公共關注的好友 -你關注的人張三 喜歡了linux,python, -你喜歡了linux---》給你推薦python -推薦系統---》通過大資料分析的出來的---》 -上海有個張三,年齡,性別,身高,職業。。。 -跟北京的王五,,年齡,性別,身高,職業。。。都類似 -忽然有一天張三在你們平臺上買了綠色短褲(linux的課程) -可以推斷出王五也可能喜歡綠色短褲,給王五推 # 基本操作 sadd key element #向集合key新增element(如果element存在,新增失敗) o(1) srem key element #從集合中的element移除掉 o(1) scard key #計算集合大小 sismember key element #判斷element是否在集合中 srandmember key count #從集合中隨機取出count個元素,不會破壞集合中的元素 srandmember key count 從集合中隨機取出幾個元素(抽獎), -汽車商場(抽獎功能),連續登陸14天---》獲得抽獎資格---》7天后開獎---》送個頭盔 spop key #從集合中隨機彈出一個元素 smembers key 獲取集合中所有元素,無序 o(n) # 要小心,可能會阻塞 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 newkey key1 key2 ... # 把key1於key2的差集存入newkey中 # 實戰 抽獎系統 :通過spop來彈出使用者的id,活動取消,直接刪除 點贊,點踩,喜歡等,使用者如果點了贊,就把使用者id放到該條記錄的集合中 # 不會出現重複點的情況 標籤:給使用者/文章等新增標籤,sadd user:1:tags 標籤1 標籤2 標籤3 # 如果交集匹配超過30%就推薦 給標籤新增使用者,關注該標籤的人有哪些 共同好友:集合間的操作
有序集合
# 7 有序集合 ## 特點 #有一個分值欄位,來保證順序 key score value user:ranking 1 lqz user:ranking 99 lqz2 user:ranking 88 lqz3 #集合有序集合 集合:無重複元素,無序,element 有序集合:無重複元素,有序,element+score #列表和有序集合 列表:可以重複,有序,element 有序集合:無重複元素,有序,element+score ### 基本操作 zadd key score element #score(必須有)可以重複,可以多個同時新增,element不能重複 o(logN) # zadd key grade1 member1 grade2 member2 ... zrem key element #刪除元素,可以多個同時刪除 o(1) zscore key element #獲取元素的分數 o(1) zincrby key increScore element #增加或減少元素的分數increScore o(1) zcard key #返回元素總個數 o(1) zrank key element #返回element元素所在的排名(從小到大排) zrange key 0 -1 #返回排名,不帶分數 o(log(n)+m) n是元素個數,m是要獲取的值 #zrange zset1 0 1獲取排名倒數第一與倒數第二 #zrange zset1 -2 -1 獲取排名第二與第一 #zrange key start end # 從低到高 #zrevrange key start end # 從高到底 zrange player:rank 0 -1 withscores #返回排名,帶分數 zrangebyscore key minScore maxScore #返回指定分數範圍內的升序元素 o(log(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之間的元素 文章閱讀數 zincrby key increScore element zcard key 返回排名:過了一天了,管理員登陸到後臺,統計一下上新的產品,瀏覽量(收藏量)排名 zrank key element 管理員統計瀏覽量在2w到3w之間的商品 zrangebyscore key minScore maxScore -實戰:排行榜相關的操作 排行榜:音樂排行榜,銷售榜,關注榜,遊戲排行榜 ### 其他操作 zrevrank #從高到低排序 zrevrange #從高到低排序取一定範圍 zrevrangebyscore #返回指定分數範圍內的降序元素 zinterstore #對兩個有序集合交集 zunionstore #對兩個有序集合求並集 # 面試官:你在使用redis的時候,有沒有要注意的點?-----》拒絕長慢命令 # 在用redis的過程中遇到什麼問題?-----》同事寫的命令,慢,我去排查,修改hgeall---一次性獲取出來 用hscan代替,一點點取,做成生成器
總結
操作型別 | 命令 |
---|---|
基本操作 | zadd/ zrem/ zcard/ zincrby/ zscore |
範圍操作 | zrange/ zrangebyscore/ zcount/ zremrangebyrank |
集合操作 | zunionstore/ zinterstore |
redis高階用法
慢查詢
我們配置一個時間,如果查詢時間超過了我們設定的時間,我們就認為這是一個慢查詢.
慢查詢發生在第三階段
客戶端超時不一定慢查詢,但慢查詢是客戶端超時的一個可能因素
# 1 慢查詢優化 一:修改配置問題 slowlog-max-len # 固定長度 # 後面慢的命令會被丟棄 二:通過記錄日誌,後期分析日誌,或者通過命令,定位出哪些命令慢,改掉它 # 慢查詢閾值(單位:微秒) (1秒等於1000 000微秒) slowlog-log-slower-than=0 # 超過設定微秒數的命令會被記錄下來,如果=0記錄所有命令 slowlog-log-slower-than <0,不記錄任何命令 面試官問:在使用redis時有沒有碰到什麼問題? 執行慢了---》通過分析日誌,解決掉 celery使用碰到過什麼問?redis大家都用---》有的同事寫了一些慢命令導致redis阻塞了,導致我的celery專案 執行慢---》清空慢查詢的佇列
配置方法
1 預設配置
config get slowlog-max-len=128
Config get slowly-log-slower-than=10000
2 修改配置檔案重啟
3 動態配置
客戶端的方式寫入,一重啟就失效了
# 設定記錄所有命令 config set slowlog-log-slower-than 0 # 最多記錄100條 config set slowlog-max-len 100 # 持久化到本地配置檔案 config rewrite ''' config set slowlog-max-len 1000 config set slowlog-log-slower-than 1000 '''
三個命令
通過命令檢視慢查詢日誌,也可以直接通過日誌檔案檢視
slowlog get [n] #獲取慢查詢佇列 ''' 日誌由4個屬性組成: 1)日誌的標識id 2)發生的時間戳 3)命令耗時 4)執行的命令和引數 ''' slowlog len #獲取慢查詢佇列長度 slowlog reset #清空慢查詢佇列,速度就變快了
經驗
1 slowlog-max-len 不要設定過大,預設10ms,通常設定1ms 2 slowlog-log-slower-than不要設定過小,通常設定1000左右 3 理解命令生命週期 4 定期持久化慢查詢(也就是清理慢查詢)
管道
-redis是不支援事務的(網上會說,redis支援事務) -要麼都成功要麼都失敗(斷電後面就不執行了)---》mysql通過回滾實現的 -客戶端的命令先放到管道中,---》一次性發送到服務端執行---》保證了要麼都成功,要麼都失敗 ### 客戶端實現 import redis pool = redis.ConnectionPool(host='10.211.55.4', 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() # 把上面的命令一起執行 ### 與原生操作對比 通過pipeline提交的多次命令,在服務端執行的時候,可能會被拆成多次執行,而mget等操作,是一次性執行的,所以,pipeline執行的命令並非原子性的。 但是因為執行速度很快,基本一瞬間完成,除非極端情況(斷電等),可能出現有操作未完成 ### 使用建議 1 注意每次pipeline攜帶的資料量 2 pipeline每次只能作用在一個Redis的節點上 3 M(mset,mget....)操作和pipeline的區別
# 使用場景 轉賬的人特別多。先把轉賬的人資料拿到redis中,張三減,李四加。然後做定時任務,把資料再同步到mysql中去
釋出訂閱
釋出者釋出了訊息,所有的訂閱者都可以收到,就是生產者消費者模型(後訂閱了,無法獲取歷史訊息)
模型
publish souhu:tv "hello world" # 釋出命令 subscribe souhu:tv # 訂閱頻道 訂閱之後,再在這個頻道上釋出訊息,只要訂閱了,都能收到
釋出者訂閱者
publish channel message #釋出命令 publish souhu:tv "hello world" #在souhu:tv頻道釋出一條hello world 返回訂閱者個數 subscribe [channel] #訂閱命令,可以訂閱一個或多個 subscribe souhu:tv #訂閱sohu:tv頻道 unsubscribe [channel] #取消訂閱一個或多個頻道(需要在程式碼中進行使用,客戶端連上無法退出) unsubscribe sohu:tv #取消訂閱sohu:tv頻道 psubscribe [pattern...] #訂閱模式匹配 訂閱多個頻道 psubscribe c* #訂閱以c開頭的頻道 unpsubscribe [pattern...] #按模式退訂指定頻道 pubsub channels #列出至少有一個訂閱者的頻道,列出活躍的頻道 pubsub numsub [channel...] #列出給定頻道的訂閱者數量 pubsub numpat #列出被訂閱模式的數量
--釋出訂閱和訊息佇列的區別? 釋出訂閱數全收到,訊息佇列有個搶的過程,只有一個搶到 微信公眾號訂閱:公眾號發了一篇文章,只要訂閱了的,都可以收到更新。屬於主動傳送,不算這裡說的釋出訂閱(釋出訂閱模式,觀察者模式) -自己實現的,寫了新文章,主動給所有訂閱的使用者推送 設計模式:單例模式,工廠模式; 觀察者模式,代理模式(flask原始碼部分,request物件,用的是代理模式),迭代器模式
Bitmap點陣圖(本質是字串)
點陣圖是什麼
下面是字串big對應的二進位制(b是98)
# 4 bitmap 點陣圖(字串) ## 相關命令 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 setbit test 50 1 #test不存在,在key為test的value的第50位設為1,那其他位都以0補 # getbit test 1 #返回0 # getbit test 2 #返回0 # getbit test 50 #返回1 bitcount key [start end] #獲取點陣圖指定範圍(start到end,單位為位元組,注意按位元組一個位元組8個bit為,如果不指定就是獲取全部)位值為1的個數 bitop op destkey key [key...] #做多個Bitmap的and(交集)/or(並集)/not(非)/xor(異或),操作並將結果儲存在destkey中 bitop and after_lqz lqz lqz2 #把lqz和lqz2按位與操作,放到after_lqz中 bitpos key targetBit start end #計算點陣圖指定範圍(start到end,單位為位元組,如果不指定是獲取全部)第一個偏移量對應的值等於targetBit的位置 bitpos lqz 1 #big 對應點陣圖中第一個1的位置,在第二個位置上,由於從0開始返回1 bitpos lqz 0 #big 對應點陣圖中第一個0的位置,在第一個位置上,由於從0開始返回0 bitpos lqz 1 1 2 #返回9:返回從第一個位元組到第二個位元組之間 第一個1的位置,看上圖,為9
### 使用場景: 獨立使用者統計 1 使用set和Bitmap對比 2 1億使用者,5千萬獨立(1億使用者量,約5千萬人訪問,統計活躍使用者數量)
資料型別 | 每個userid佔用空間 | 需要儲存使用者量 | 全部記憶體量 |
---|---|---|---|
set | 32位(假設userid是整形,佔32位) | 5千萬 | 32位*5千萬=200MB |
bitmap | 1位 | 1億 | 1位*1億=12.5MB |
假設有10萬獨立使用者,使用點陣圖還是佔用12.5mb,使用set需要32位*1萬=4MB
### 總結 1 點陣圖型別是string型別,最大512M (1億才12.5MB,絕對夠了) 2 使用setbit時偏移量如果過大,會有較大消耗(setbit 1,2沒問題。如果是1億,10億會有點慢) 3 點陣圖不是絕對好用,需要合理使用 4 和布隆過濾器比較,因為布隆過濾器是經過運算取點,會比bitmap的方法更省空間
HyperLogLog(日活統計) 幾百人集合就行
基於HyperLogLog演算法:極小的空間完成獨立數量統計(比點陣圖佔的空間還要小)
本質還是字串
## 三個命令 pfadd key element #向hyperloglog新增元素,可以同時新增多個 pfcount key #計算hyperloglog的獨立總數 pfmerge destroy sourcekey1 sourcekey2#合併多個hyperloglog,把sourcekey1和sourcekey2合併為destroy pfadd uuids "uuid1" "uuid2" "uuid3" "uuid4" #向uuids中新增4個uuid pfcount uuids #返回4 pfadd uuids "uuid1" "uuid5"#有一個之前存在了,其實只把uuid5添加了,去重 pfcount uuids #返回5 pfadd uuids1 "uuid1" "uuid2" "uuid3" "uuid4" pfadd uuids2 "uuid3" "uuid4" "uuid5" "uuid6" pfmerge uuidsall uuids1 uuids2 #合併 pfcount uuidsall #統計個數 返回6 ## 記憶體消耗&總結 百萬級別獨立使用者統計,百萬條資料只佔15k 錯誤率 0.81% 無法取出單條資料,只能統計個數 當時在做這個功能的時候,專門去調研了,想用HyperLogLog,發現沒多少使用者,直接用了集合,後期如果使用者量增大,再用HyperLogLog (日活幾百人,用集合就可以了) 問題:日活5000左右,但是將阿里雲的頻寬200m全佔滿了,有什麼解決方案 收穫:5000使用者---》同時在訪問你---》程式沒有及時的響應回來(非同步[儘量快速把請求處理完,mysql做主從],cdn) 服務降級,熔斷,api閘道器