1. 程式人生 > 資料庫 >Redis 介紹安裝配置, 使用場景, redis-API的使用(通用命令, 五大資料型別), redis高階用法(慢查詢優化, 管道[實現事務功能], 釋出訂閱, Bitmap點陣圖, HyperLo

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閘道器