簡介
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),這里舉例幾個場景。
- 統(tǒng)計活躍/在線用戶:可以通過bitmap實現(xiàn),能降低內(nèi)存的空間,包括可以使用bitop、bitcount做計算統(tǒng)計。
- 隊列:基于list類型,可以實現(xiàn)隊列。
- 消息通知:利用Redis的pub/sub模式,實現(xiàn)訂閱/通知,但是不支持分組。
- 延時隊列:基于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
錯誤處理
- 語法錯誤:事務中只要有一個命令有語法錯誤,執(zhí)行exec后就會直接返回錯誤,語法正確的命令也不會執(zhí)行。
- 運行錯誤:執(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ù)。
- 盡可能減少待排序鍵中元素的數(shù)量,n盡可能小。
- 使用limit參數(shù)只獲取需要的數(shù)據(jù),m盡可能小。
- 如果要排序的數(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)化空間
- 精簡鍵名和鍵值
- 內(nèi)部編碼優(yōu)化
持久化
Redis支持兩種方式的持久化,RDB方式、AOF方式,可單獨使用,也可兩者結(jié)合使用。
-
RDB:通過快照(snapshotting)完成的,當符合一定條件時Redis自動將內(nèi)存中的所有數(shù)據(jù)進行快照并存儲到硬盤。由用戶在配置文件中自定義,兩個參數(shù)構(gòu)成:
時間和改動的鍵個數(shù);指定時間內(nèi)更改的鍵大于這個數(shù)值就會進行快照。默認采用這種持久化方式
缺點是定時持久化 -
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ù)設置。 - 備份/恢復:通過save命令就能生成dump.rdb文件,如果要恢復的話,把文件copy到安裝文件目錄下,啟動服務就可以了,可以通過config get dir查看目錄地址。
多機數(shù)據(jù)庫
主要是適用于大型分布式場景
- 復制:簡潔來說就是主從復制,主數(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ù)不會丟失。
- sentinel:哨兵,Redis的高可用解決方案之一,由一個或多個sentinel實例組成的sentinel系統(tǒng)可以監(jiān)視多個主服務器,以及這些主服務器下的所有從服務器,主服務器下線后自動將下屬某個從服務器升級為新的主服務器。
- 集群: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 |
