2018-09-06-淺談Redis與中間的一些設計策略

簡介

Redis是一個開源的高性能鍵值對數(shù)據(jù)庫,基于內(nèi)存且可持久化的日志,所以通常也說內(nèi)存數(shù)據(jù)庫,提供多種鍵值數(shù)據(jù)類型能夠適應不同的存儲需求,所以這兩塊構(gòu)成了Redis強大的基礎,基于內(nèi)存的高性能和支持多種數(shù)據(jù)類型的高可用。

可執(zhí)行文件

  • redis-server:Redis服務器
  • redis-cli:Redis命令行客戶端
  • redis-benchmark:Redis性能測試工具
  • redis-check-aof:AOF文件修復工具
  • redis-check-dump:RDB文件檢查工具

啟動和停止

  • 直接啟動:適用開發(fā)環(huán)境
    redis-server,默認使用6379端口,可以通過--port參數(shù)指定端口號
  • 初始化腳本啟動:生產(chǎn)環(huán)境
    Redis源代碼目錄utils文件中有一個redis_init_script的初始化腳本文件,使用腳本啟動需要配置Redis的運行方式和持久化文件、日志文件的存儲位置等。
  • 停止:強制終止進程可能會導致數(shù)據(jù)丟失,通常是像Redis發(fā)送SHUTDOWN命令,kill進程的PID效果一樣。
    redis-cli SHUTDOWN

多數(shù)據(jù)庫

Redis默認支持16個數(shù)據(jù)庫,可以通過配置參數(shù)databases來修改。Redis不支持自定義數(shù)據(jù)庫名字,也不支持為不同庫設置訪問密碼,都是以編號來命名的0~15,所以數(shù)據(jù)庫之間并不完全隔離。所以注意不同的應用要使用不同的Redis實例存儲數(shù)據(jù)。

5種數(shù)據(jù)類型

  • [string] 字符串類型:最基本的數(shù)據(jù)類型,能存儲任何形式的字符串,包括二進制數(shù)據(jù),允許最大容量是512MB

  • [string] 常用命令

    • [set/get] 賦值與取值
    • [incr] 遞增數(shù)字
    • [incrby] 增加指定的整數(shù),通過參數(shù)增加指定的數(shù)值
    • [decr] 減少指定的整數(shù)
    • [incrbyfloat] 增加指定的浮點數(shù)
    • [append] 向尾部追加值
    • [strlen] 獲取字符串長度
    • [mget/mset] 同時獲得/設置多個鍵值
    • [getbit/setbit/bitcount/bitop] 位操作
  • [hash] 散列類型:字典結(jié)構(gòu)以鍵值對的形式存儲數(shù)據(jù),可以包含232-1個字段

  • [hash] 常用命令

    • [hset/hget] 賦值與取值
    • [hexists] 判斷字段是否存在
    • [hsetnx] 當字段不存在時賦值
    • [hincrby] 增加數(shù)字
    • [hdel] 刪除字段
    • [hkeys/hvalus] 只獲取字段名或字段值
    • [hlen] 獲取字段數(shù)量
  • [list] 列表類型:存儲一個有序的字符串列表,常用的操作是向列表兩端添加元素,或者獲得列表的某一個片段,一個列表最多容納232-1個元素

  • [list] 常用命令

    • [lpush/rpush] 向列表兩端增加元素
    • [lpop/rpop] 從列表兩端彈出元素
    • [llen] 獲取列表中元素的個數(shù)
    • [lrange] 獲取列表片段
    • [lrem] 刪除列表中指定的值
    • [lindex/lset] 獲得/設置指定索引的元素值
    • [ltrim] 只保留列表指定片段
    • [linsert] 向列表中插入元素
    • [rpoplpush] 將元素從一個列表轉(zhuǎn)到另一個列表
  • [set] 集合類型:和列表類型的區(qū)別就是無序且去重

  • [set] 常用命令

    • [sadd/srem] 增加/刪除元素
    • [smembers] 獲得集合中的所有元素
    • [sismember] 判斷元素是否在集合中
    • [sdiff/sinter/sunion] 集合間運算
    • [scard] 獲得集合中元素個數(shù)
    • [sdiffstore/sinterstore/sunionstore] 進行集合運算并將結(jié)果存儲
    • [srandmember] 隨機獲得集合中的元素
    • [spop] 從集合中彈出一個元素
  • [sorted set] 有序集合類型:和集合類型的區(qū)別就在于有序

  • [sorted set] 常用命令

    • [zadd] 增加元素
    • [zscore] 獲得元素的分數(shù),也可做為判斷是否存在
    • [zrange/zreverange] 獲得排名在某個范圍的元素列表
    • [zrangebyscore] 獲得指定分數(shù)范圍的元素
    • [zincrby] 增加某個元素的分數(shù)
    • [zcard] 獲得集合中元素的數(shù)量
    • [zcount] 獲得指定分數(shù)范圍內(nèi)的元素個數(shù)
    • [zremrangebyrank] 按照排名范圍刪除元素
    • [zremrangebyscore] 按照分數(shù)范圍刪除元素
    • [zrank/zrevrank] 獲得元素的排名
    • [zinterstore] 計算有序集合的交集

其它應用場景

Redis不單單可以應用在緩存、數(shù)據(jù)庫、消息隊列等方面,根據(jù)本身的結(jié)構(gòu)和設計在一些其它的方面也有很好的體現(xiàn),這里舉例幾個場景。

  1. 統(tǒng)計活躍/在線用戶:可以通過bitmap實現(xiàn),能降低內(nèi)存的空間,包括可以使用bitop、bitcount做計算統(tǒng)計。
  2. 隊列:基于list類型,可以實現(xiàn)隊列。
  3. 消息通知:利用Redis的pub/sub模式,實現(xiàn)訂閱/通知,但是不支持分組。
  4. 延時隊列:基于zset類型,因為zset是通過score屬性來排序的,這樣的話可以通過設置score為時間,輪詢的方式來檢測消費刪除。
    redis的應用場景特別多,大多數(shù)應用場景是根據(jù)redis所支持的數(shù)據(jù)類型延伸出來的,包括還能通過redis的原子性操作、自增、過期時間等等實現(xiàn)分布式鎖。

事務

Redis中的事務(transaction)是一組命令的集合,Redis保證一個事務中的所有命令要么都執(zhí)行,要么都不執(zhí)行。 Redis事務不支持關系數(shù)據(jù)庫事務提供的回滾(rollback)功能。
過程:mulit-----待執(zhí)行命令----exec

錯誤處理

  1. 語法錯誤:事務中只要有一個命令有語法錯誤,執(zhí)行exec后就會直接返回錯誤,語法正確的命令也不會執(zhí)行。
  2. 運行錯誤:執(zhí)行時錯誤,如散列類型命令操作集合類型的鍵,這種錯誤執(zhí)行前redis是無法發(fā)現(xiàn)的,所以出現(xiàn)這類錯誤,事務中其它命令會繼續(xù)執(zhí)行。

WATCH命令
監(jiān)視一個(或多個) key ,監(jiān)控一直持續(xù)到exec命令(事務中的命令是在exec之后才執(zhí)行的,所以在mulit命令后可以修改watch監(jiān)控的鍵值),如果在事務執(zhí)行之前這個(或這些) key 被其他命令所改動,那么事務將被打斷。

UNWATCH
取消 WATCH命令對所有 key 的監(jiān)視。如果在執(zhí)行 WATCH命令之后, exec命令或 discard命令先被執(zhí)行了的話,那么就不需要再執(zhí)行 UNWATCH了。
因為 exec命令會執(zhí)行事務,而discard取消事務的同時也會取消對所有key的監(jiān)視。

DISCARD
取消事務,放棄執(zhí)行事務塊內(nèi)的所有命令。

EXPIRE生存時間

在Redis中可以使用expire命令設置一個鍵的生存時間,到時間后Redis會自動刪除它。
命令格式:expire key seconds,seconds參數(shù)表示鍵的生存時間,單位為秒。
取消使用persist命令

訪問頻率限制

通過使用incr命令遞增鍵值來描述次數(shù),通過設置生存時間來自動刪除,從而達到限制訪問的頻率,同時為了保證建立鍵和為鍵設置生存時間一起執(zhí)行(避免某些問題導致中間推出,避免未能設置生存時間而變成永久鍵),可以把事務也引用進來。

//偽代碼
isKeyExists = EXISTS limiting:IP
if isKeyExists is 1
   times = INCR limiting:ip
   if times >100
      print 訪問頻率超過限制
      exit
   else
      MULTI
      INCR limiting:ip
      EXPIRE keyName ,60
      exit

實現(xiàn)緩存

上面說到使用expire命令來設置了生存時間,就可以用來滿足緩存實現(xiàn)的要求,但是為了提高緩存的命用率以及更合理的使用內(nèi)存資源,需要讓Redis按照一定的規(guī)則淘汰不需要的緩存鍵
淘汰規(guī)則

規(guī)則 說明
volatile-lru 使用LRU算法刪除一個鍵(已設置生存時間的鍵)
allkeys-lru 使用LRU算法刪除一個鍵
volatile-random 隨機刪除一個鍵(已設置生存時間的鍵)
allkeys-random 隨機刪除一個鍵
volatile-ttl 刪除生存時間最近的一個鍵
noeviction 不刪除,只返回錯誤

排序

前面說到了列表類型,有序集合類型都是滿足有序的,這里講講怎么完成排序。
sort命令
sort命令可以對列表類型、集合類型和有序集合類型的鍵進行排序,還可以通過ALPHA參數(shù)實現(xiàn)按照字典順序排列,DESC參數(shù)倒序等。
by參數(shù)
如果提供了by參數(shù),sort命令將不再依據(jù)元素自身的值進行排序,而是對每個元素使用元素的值替換參考鍵中的第一個“*”并獲取其值,然后依據(jù)該值對元素排序

## 鍵名->字段名
sort tag:posts by  post:*->time desc

get參數(shù)
get參數(shù)不影響排序,它的作用是使sort命令的返回結(jié)果不再是元素自身的值,而是get參數(shù)中指定的鍵值。
store參數(shù)
默認情況下sort會直接返回排序結(jié)果,如果希望保存結(jié)果,需要使用store參數(shù)
性能優(yōu)化
sort是redis種最強大最復雜的命令之一,時間復雜度是O(n+mlogm),n表示排序的元素個數(shù),m表示返回的元素個數(shù)。

  1. 盡可能減少待排序鍵中元素的數(shù)量,n盡可能小。
  2. 使用limit參數(shù)只獲取需要的數(shù)據(jù),m盡可能小。
  3. 如果要排序的數(shù)據(jù)數(shù)量較大,盡可能使用store參數(shù)將結(jié)果緩存。

消息

任務隊列
說到隊列通常使用的是Redis的列表類型,使用lpush和rpop命令實現(xiàn)隊列。只需要讓生產(chǎn)者將任務使用lpush命令加入到某個鍵中,另一邊讓消費者不斷使用rpop命令從該鍵中取出任務即可。
優(yōu)先級隊列
可以通過brpop命令來解決,brpop可以同時接收多個鍵,意義是同時檢測多個鍵,如果所有鍵都沒有元素則阻塞,如果其中有一個鍵有元素則會從該鍵中彈出元素,如果多個鍵都有元素則按照從左到右的順序去第一個鍵中的元素。借此特性可以實現(xiàn)區(qū)分優(yōu)先級的任務隊列。
發(fā)布/訂閱
Redis提供了一組命令可以讓開發(fā)者實現(xiàn)發(fā)布/訂閱(publish/subscribe)模式。該模式包含了兩種角色,發(fā)布者和訂閱者;可以用來實現(xiàn)消息通知等。
訂閱者可以訂閱一個或者若干個頻道(channel),而發(fā)布者可以向指定的頻道發(fā)送消息,訂閱該頻道都會收到此消息。
管道
客戶端和Redis使用TCP協(xié)議連接,無論是發(fā)送命令還是返回執(zhí)行結(jié)果都需要經(jīng)過網(wǎng)絡傳輸,如果執(zhí)行的命令較多,延時的累加起來對性能還是有一定影響。
管道(pipelining)可以一次性發(fā)送多條命令并在執(zhí)行完后一次性將結(jié)果返回。

優(yōu)化空間

  1. 精簡鍵名和鍵值
  2. 內(nèi)部編碼優(yōu)化

持久化

Redis支持兩種方式的持久化,RDB方式、AOF方式,可單獨使用,也可兩者結(jié)合使用。

  1. RDB:通過快照(snapshotting)完成的,當符合一定條件時Redis自動將內(nèi)存中的所有數(shù)據(jù)進行快照并存儲到硬盤。由用戶在配置文件中自定義,兩個參數(shù)構(gòu)成:時間和改動的鍵個數(shù);指定時間內(nèi)更改的鍵大于這個數(shù)值就會進行快照。默認采用這種持久化方式
    缺點是定時持久化
  2. AOF:通過appendonly參數(shù)開啟,開啟后每執(zhí)行一條更改的命令,會將該命令寫入硬盤的AOF文件,AOF方式是將執(zhí)行過的寫指令記錄下來,在數(shù)據(jù)恢復時按照從前到后的順序再將指令都執(zhí)行一遍。AOF文件位置和RDB文件的位置相同,都是通過dir參數(shù)設置,將緩存內(nèi)容同步到硬盤中默認情況下采用everysec規(guī)則,即每秒執(zhí)行一次同步操作,always表示每次寫入都執(zhí)行同步,最安全也最慢,no表示不主動進行同步而交由操作系統(tǒng)(默認30秒一次),都是通過appendfsync參數(shù)設置。
  3. 備份/恢復:通過save命令就能生成dump.rdb文件,如果要恢復的話,把文件copy到安裝文件目錄下,啟動服務就可以了,可以通過config get dir查看目錄地址。

多機數(shù)據(jù)庫

主要是適用于大型分布式場景

  1. 復制:簡潔來說就是主從復制,主數(shù)據(jù)庫(master),從數(shù)據(jù)庫(slave);主數(shù)據(jù)庫可以進行讀寫操作,發(fā)生寫操作時候同步給從數(shù)據(jù)庫,從數(shù)據(jù)庫一般只負責讀。
  • 使用復制功能很簡單,在配置文件中加入“slaveof 主數(shù)據(jù) ip 主數(shù)據(jù)庫端口”即可。
  • 當從數(shù)據(jù)庫啟動后,會向主數(shù)據(jù)庫發(fā)送sync命令,主數(shù)據(jù)庫接收后開始后臺保存快照及期間接收的命令緩存起來,快照完成后,將快照文件和緩存的命令發(fā)送給從數(shù)據(jù)庫。從數(shù)據(jù)庫接收會載入快照文件及執(zhí)行緩存命令。不支持斷點續(xù)傳。
  • 基于復制可以實現(xiàn)讀寫分離,以及可以通過主從切換來提高數(shù)據(jù)庫的高可用。比如設置主禁用持久化,從開啟持久化,主數(shù)據(jù)庫掛掉后,可以把從數(shù)據(jù)庫提升為主數(shù)據(jù)庫,主數(shù)據(jù)庫重啟后設置為從數(shù)據(jù)庫即可將數(shù)據(jù)同步過來。這樣能保證數(shù)據(jù)不會丟失。
  1. sentinel:哨兵,Redis的高可用解決方案之一,由一個或多個sentinel實例組成的sentinel系統(tǒng)可以監(jiān)視多個主服務器,以及這些主服務器下的所有從服務器,主服務器下線后自動將下屬某個從服務器升級為新的主服務器。
  2. 集群:Redis提供的分布式數(shù)據(jù)庫方案,集群通過分片(sharding)來進行數(shù)據(jù)共享,并提供復制和故障轉(zhuǎn)移功能。
  • 客戶端數(shù)據(jù)分片,基于客戶端hash計算key的節(jié)點位置,可以降低服務端的集群復雜度,但是服務器集群資源難以管理,并且動態(tài)增刪節(jié)難以實現(xiàn),運維性差。
  • 服務端數(shù)據(jù)分配(路由),有服務端來計算key的位置,客戶端發(fā)送請求到任意節(jié)點,服務端接收請求后會將查詢請求發(fā)送到正確的節(jié)點上執(zhí)行。開源方案:redis-cluster,內(nèi)置數(shù)據(jù)自動分片機制,key的落點是通過一致性hash計算所在落點位置,以slot為單位,一共是一萬六千多個slot,關于負載,節(jié)點之間是可以遷移數(shù)據(jù)的,以slot為單位的遷移,但不是自動,需要外部命令觸發(fā),關于高可用,集群中采用主從,主宕機后會把從提升為主,但如果沒有設置從,會導致這個節(jié)點的讀寫服務不可用。


    路由方式
  • 使用代理方式(proxy),客戶端通過連接代理,代理計算出節(jié)點位置,并把請求發(fā)送到對應的集群節(jié)點,但是代理需要維護集群的節(jié)點信息,雖然解耦了,但會增加結(jié)構(gòu)的復雜度和運維的難度。

部分配置參數(shù)列表

參數(shù)名 默認值 使用config set設置
daemonize no no
pidfile /var/run/redis/pid no
port 6379 no
databases 16 no
save save 900 1/save 300 10/save 60 10000 yes
rdbcompression yes yes
rdbchecksum yes yes
dbfilename dump.rdb yes
dir ./ no
slaveof no
masterauth yes
slave-serve-stale-data yes yes
slave-read-only yes yes
requirepass yes
rename-command no
maxmemory yes
maxmemory-policy volatile-lru yes
maxmemory-samples 3 yes
appendonly no yes
appendfsync everysec yes
auto-aof-rewrite-percentage 100 yes
auto-aof-rewrite-min-size 64mb yes
lua-time-limit 5000 yes
slowlog-log-slower-than 10000 yes
slowlog-max-lan 128 yes
hash-max-ziplist-entries 512 yes
hash-max-ziplist-value 64 yes
list-max-ziplist-entries 512 yes
list-max-ziplist-value 64 yes
set-max-intset-entries 512 yes
zset-max-ziplist-entries 128 yes
zset-max-ziplist-value 64 yes

結(jié)語

有很多還不夠完善,未完待續(xù)吧~~~
個人博客~
簡書~

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 關于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,326評論 2 89
  • 《Redis 入門指南》(第二版) 第一章 Redis 是什么 Redis (REmote Dictionary ...
    EdenPP閱讀 67,463評論 3 10
  • 詩詞創(chuàng)作——對仗淺談 對仗:中古時詩歌格律的表現(xiàn)之一。對仗又稱對偶、隊仗、排偶。它是把同類或?qū)α⒏拍畹脑~語放...
    ZGb5cb閱讀 1,320評論 0 1
  • 我們永遠都無法將感情圈定在自己預設的情景里,讓故事按照我們所期待的軌跡發(fā)展??墒篱g沒有哪一種感情是可以被設計的,唯...
    LL光虹閱讀 551評論 0 1
  • 世界沒有那么好,也并不是那么糟,我們要做的,只不過是在環(huán)境允許的情況下,善意地對所有人。在環(huán)境不允許的情況下,保...
    談談愛吃肉閱讀 561評論 0 8

友情鏈接更多精彩內(nèi)容