# redis緩存
## 一、redis介紹
### 1.redis簡介
- redis是一個開源的使用ANSI C語言編寫的Key-Value內存數據庫
- 讀寫性能強,支持多種數據類型
- 把數據存儲在內存中的高速緩存
### 2.redis特點
- 速度快
- 支持多種數據結構(string list hash set storted set)
- 持久化
- 主從復制(集群)
- 支持過期時間
- 支持事務
- 消息訂閱
- 官方不支持windows
### 3.redis和memcache的對比
| 項目 | Redis | memcached |
| -------- | ---------------------- | :--------------------: |
| 過期策略 | 支持 | 支持 |
| 數據類型 | 五種數據類型 | 單一數據類型 |
| 持久化 | 支持 | 不支持 |
| 主從復制 | 支持 | 不支持 |
| 虛擬內存 | 支持 | 不支持 |
| 性能 | 強,多線程寫入效果明顯 | 強,單線程寫入效果明顯 |
### 4.Redis應用場景
- 數據緩存
提高訪問性能,使用方式與memcache相同
- 會話緩存(session cache)
保存web會話信息(判斷用戶是否是登錄狀態)
- 排行榜/計數器
nginx+lua+Redis計數器進行IP自動封禁
- 消息隊列
構建實時的消息系統,聊天,群聊
## 二、安裝配置
### 1.安裝(編譯安裝)
```
tar xf redis-3.2.6.tar.gz && cd redis-3.2.6/ && make && cd src/ && make install PREFIX=/application/redis
cp redis-3.2.6/redis.conf /application/redis/
```
### 2.啟動
```
/application/redis/bin/redis-server /application/redis/redis.conf
```
### 3.配置文件
```
bind 192.168.1.4
protected-mode yes #redis的安全機制
requirepass 199747 #設置登錄密碼,需要上一項為yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes #允許后臺啟動,改為yes
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile "" #后面跟日志路徑,放引號里面
databases 16
always-show-logo yes
save 900 1 #在900s之內,有一次操作,就保存到硬盤
save 300 10 #300s之內,有10次操作,就保存到硬盤
save 60 10000 #60s之內,有1w次操作,就保存到硬盤
stop-writes-on-bgsave-error yes
rdbcompression yes #保存本地的數據文件是否開啟壓縮,默認yes
rdbchecksum yes
dbfilename dump.rdb #保存在硬盤的數據文件(持久化)
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no #日志開關,aof持久化
appendfilename "appendonly.aof"
appendfsync everysec #默認everysec每秒同步一次,no表示操作系統進行數據緩存同步到磁盤,linux約30s,always表示每次更新操作后調用fsync將書記寫入到硬盤
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000 #慢日志查詢,超過多少微秒,才認定為是慢查詢
slowlog-max-len 128 #保存多少慢日志
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
```
### 4.連接redis-server
```
redis-cli -h 192.168.1.4
```
## 三、常用操作
### 1.認證密碼
```
192.168.1.4:6379> auth 199747
OK
```
### 2.設置鍵值,并獲取
```
192.168.1.4:6379> set shz 21
OK
192.168.1.4:6379> get shz
"21"
```
### 3.獲取系統中所有key
```
192.168.1.4:6379> KEYS *
1) "oldboy"
2) "shz"
```
### 4.獲取當前所有配置
```
192.168.1.4:6379> CONFIG GET *
.............
```
### 5.變更運行配置(只修改當前內存中的配置,重啟失效)
```
192.168.1.4:6379> CONFIG SET loglevel 'notice'
OK
```
## 四、redis數據類型
### 1.redis數據存儲
- 內存
- 硬盤 :數據文件.rdb 日志文件.aof
### 2.持久化
- RDB持久化可以在指定的時間間隔內生成數據集的時間點快照
- AOF持久化記錄服務器執行的所有寫操作命令,并在服務器啟動時,通過重新執行這些命令來還原數據集。AOF文件的命令全部以Redis協議的格式來保存,新命令會被追加到文件的末尾。Redis還可以在后臺對AOF文件進行重寫,使得AOF文件的體積不會超出保存數據集狀態所需的實際大小
- Redis還可以同時使用AOF和RDB持久化。在這種情況下,當Redis重啟時,它會優先使用AOF文件來還原數據集,因為AOF文件保存的數據集通常比RDB文件所保存的數據集更加完整。
- 你甚至可以關閉持久化功能,讓數據只在服務器運行時存在
## 五、核心實踐,操作
### 1.redis保存的數據類型
- 字符串(REDIS_STRING)
- 列表(REDIS_LIST)
- 有序集合(REDIS_ZSET)
- 哈希表(REDIS_HASH)
- 集合(REDIS_SET)
### 2.常規操作
- KEYS *查看KEY
- DEL刪除給定的一個過多個KEY
- EXISTS檢查是否存在
- EXPIRE設置生存時間
- TTL以秒為單位返回過期時間
- DUMP RETORE序列化與反序列化
- EXPIRE PTTL PERSIST以毫秒為單位
- RENAME 變更KEY名
- SORT鍵值排序,有序數字時報錯
- TYPE返回鍵所存儲的類型
### 3.數據類型詳解
#### 字符串(string)
- SET name shz
- GET name
- 一個鍵最大能存儲512M
- append可以將value追加到key原來值得末尾
- Mget mset同時設置一個或者多個鍵值對
- STRLEN返回字符串長度
- INCR DECR將值增或減1
- INRBY DECRBY 減去指定量
- DECRBY count 20
#### hash(哈希)
- redis hash是一個鍵值對的集合
- redis hash是一個string類型的field和value的映射表
- hash特別適合用于存儲對象
- 每個hash可以存儲2^32-1個鍵值對
- HSET HGET 設置返回單個值
- HMSET HMGET 設置和獲取多個值
- HGETALL 返回key的所有鍵值
- HEXSITS HLEN
- HLEYS HVALS 獲取所有字段或值
- HDEL 刪除key中的一個或者多個指定值
```
192.168.1.4:6379> HSET user1 name shz
(integer) 1
192.168.1.4:6379> hset user1 age 21
(integer) 1
192.168.1.4:6379> HGET user1 name
"shz"
192.168.1.4:6379> HGET user1 age
"21"
192.168.1.4:6379> HGETALL user1
1) "name"
2) "shz"
3) "age"
4) "21"
192.168.1.4:6379> HMGET user1 name age
1) "shz"
2) "21"
```
#### 列表
- Redis列表是簡單的字符串列表
- 按照插入順序排序每個
- list可以以存儲2^32-1個鍵值對
- LPUSH 將一個或多個值插入到列表頭部
- RPUSH 將一個或多個值插入到列表尾部
- LPOP/RPOP 一處表頭/尾的元素
- LLEN返回列表長度
- LRANGE 返回指定的元素
- LREM greet 2 morning 刪除前兩個morning
- LREM greet -1 morning 刪除后一個morning
- LREM greet 0 hello 刪除所有hello
- Lindex 返回列表key中下標為index的元素
- LSET key index value 將列表key下標位index的元素的值為value
- LINSERT 插入數據位于某元素之前或之后(LINSERT key BEFORE XXX value)
```
插入表格
192.168.1.4:6379> LPUSH list1 name age sex
(integer) 3
查看類型
192.168.1.4:6379> type list1
list
查看表格數據
192.168.1.4:6379> LRANGE list1 0 10
1) "sex"
2) "age"
3) "name"
往前面出入數據
192.168.1.4:6379> LPUSH list1 phone
(integer) 4
192.168.1.4:6379> LRANGE list1 0 19
1) "phone"
2) "sex"
3) "age"
4) "name"
取出數據(從上面刪除),且有返回值
192.168.1.4:6379> RPOP list1
"name"
192.168.1.4:6379> LRANGE list1 0 10
1) "phone"
2) "sex"
3) "age"
往后面插入數據
192.168.1.4:6379> RPUSH list1 from
(integer) 4
192.168.1.4:6379> LRANGE list1 0 10
1) "phone"
2) "sex"
3) "age"
4) "from"
取出數據(從下面刪除),且有返回值
192.168.1.4:6379> RPOP list1
"from"
192.168.1.4:6379> LRANGE list1 0 10
1) "phone"
2) "sex"
3) "age"
直接刪除,沒有返回值
192.168.1.4:6379> LREM list1 1 phone
(integer) 1
192.168.1.4:6379> LRANGE list1 0 10
1) "sex"
2) "age"
設定一個值(根據前面的數標判斷)
192.168.1.4:6379> LSET list1 1 name
OK
192.168.1.4:6379> LRANGE list1 0 10
1) "sex"
2) "name"
往指定位置插入數據
192.168.1.4:6379> LINSERT list1 AFTER name from
(integer) 3
192.168.1.4:6379> LRANGE list1 0 10
1) "sex"
2) "name"
3) "from"
```
## 六、redis高級應用
### 1. 生產消費模型
### 2.消息模式
- 發布消息通常有兩種模式:隊列模式(queuing)和發布-訂閱模式(publish-subscibe)。隊列模式中,consumers可以同時從服務端讀取消息,每個消息只被其中一個consumer讀到
- 發布-訂閱模式中消息被廣播到所有的consumer中,topic中的消息將被分發到組中的一個成員中。同一組中的consumer可以在不同的程序中,也可以在不同的機器上
### 3.Redis發布訂閱
- Redis發布訂閱(pub/sun)是一種消息通信模式:發送者(pub)發送消息,訂閱者(sub)接受消息。
- Redis客戶端可以訂閱任意數量的頻道
### 4.訂閱發布實例
復制3個ssh會話
```
192.168.1.4:6379> PUBLISH mq1 "i love u guys"
(integer) 2
```
```
192.168.1.4:6379> SUBSCRIBE mq1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "mq1"
3) (integer) 1
1) "message"
2) "mq1"
3) "i love u guys"
```
### 5.Redis事務
- Redis事務可以一次執行多個命令
事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序執行。事務在執行的過程中,不會被其他客戶端發送的命令請求打斷
原子性:事務中的命令要么全部被執行,要么全部不執行
- 執行過程:
開始事務
命令入隊
執行事務
- 事務命令
DISCARD :取消事務,放棄執行事務塊內的所有命令
EXEC :執行所有事務塊內的命令
MULTI:標記一個事務塊的開始
UNWATCH:取消WATCH命令對所有key的監視
WATCH key :監視一個或多個key,如果在事務執行之前這個key被其他命令所改動,那么事務將被打斷
模擬轉賬(multi標記一個事務,確保轉賬扣錢和增加是同時操作的)
這個數據類型是 有序集合
```
192.168.1.4:6379> ZADD salary 3000 shz 5000 ll
(integer) 2
開啟隊列
192.168.1.4:6379> MULTI
OK
將下面命令添加到隊列
192.168.1.4:6379> ZINCRBY salary 1000 shz
QUEUED
192.168.1.4:6379> ZINCRBY salary -1000 ll
QUEUED
執行命令
192.168.1.4:6379> EXEC
1) "4000"
2) "4000"
查看數據
192.168.1.4:6379> ZRANGE salary 0 -1 withscores
1) "ll"
2) "4000"
3) "shz"
4) "4000"
```
### 6.服務器命令
- INFO 服務器信息
- CLIENT LIST 客戶端列表,當前登錄的客戶,通過redis-cli連接的
- FLUSHALL 清空所有數據
- FLUSHDB 清空當前庫
- MONITOR 監控實時指令
- SHUTDOWN 關閉服務器redis-server
- SAVE 保存數據
- SLAVEOF host port 主從的配置
- SLAVEOF NO ONE 關閉主從復制
- SYNC 主從同步
- ROLE 返回主從角色
### 7.慢日志查詢
在配置文件中開啟并配置
```
192.168.1.4:6379> CONFIG GET slow*
1) "slowlog-log-slower-than"
2) "10000"
3) "slowlog-max-len"
4) "128"
```
### 8.數據備份
- CONFIG GET dir 獲取當前目錄
- Save備份(無持久化策略時),生成時在Redis當前目錄中
- 恢復時只需將dump.rdb放入redis當前目錄
## 七、主從復制
在從服務器上執行
```
192.168.1.5:6379> SLAVEOF 192.168.1.4 6379
OK
查看
192.168.1.5:6379> INFO Replication
# Replication
role:slave
master_host:192.168.1.4
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1543653264
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:5a217d0959762c50b2ad0e289124da1e35b01c6e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
```
```
從升級為master
192.168.1.5:6379> SLAVEOF no one
OK
192.168.1.5:6379> INFO Replication
# Replication
role:master
connected_slaves:0
master_replid:a3a481c40cda3452b297b3040a153ace36abd5c0
master_replid2:5a217d0959762c50b2ad0e289124da1e35b01c6e
master_repl_offset:0
second_repl_offset:1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
```
## 八、Redis 高可用(redis sentnel)
### 1.簡介
- redis-sentinel時Redis官方推薦的好可用性的解決方案,當用Redis做master-slave的高可用方案時,假如master宕機了,Redis本身(包括它的很多客戶端)都沒有實現自動進行主備切換,兒Redis-sentinel本身也是一個獨立運行的進程,他能監控多個master-slave集群,發現master宕機后進行自動切換。
### 2.功能
- 監控:sentinel會不斷地檢查你的主服務器和從服務器是否運作正常
- 提醒:當被監控的某個Redis服務器出現問題時,sentinel可以通過api向管理員或者其他應用程序發送通知
- 自動故障遷移:當一個主服務器不能正常工作時,sentinel會開始一次自動故障遷移操作,它會將失效主服務器的其中一個從服務器升級為新的主服務器,并讓失效的主服務器額其他從服務器改為新的主服務器;當客戶端試圖連接失效的主服務時,集群也會向客戶端返回新主服務器的地址,使得集群可以使用新主服務器代替失效服務器
### 3.配置和啟動
配置文件
```
port 26379
daemonize no
pidfile /var/run/redis-sentinel.pid
logfile ""
dir /tmp
sentinel monitor mymaster 127.0.0.1 6379 1 #指定監控master的bind,1指需要多少臺sentinel同意才能切換master
sentinel down-after-milliseconds mymaster 30000 #超過30000毫秒后認為主機宕機
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000 #當主從切換多久后認為主從切換失敗
sentinel deny-scripts-reconfig yes
```
啟動
```
redis-sentinel /application/redis/sentinel.conf
```
## 九、redis集群
### 1.簡介
- redis集群是一個可以在多個Redis節點之間進行數據共享的設施
- Redis集群不支持那些需要同時處理多個鍵的Redis命令,因為執行這些命令需要在多個Redis節點之間移動數據,并且在高負載的情況下,這些命令將降低Redis集群的性能,并導致不可預測的行為
- Redis集群通過分區來提供一定程度的可用性:即使集群中有一部分節點失效或者無法進行通訊,集群也可以繼續處理命令請求
- 將數據自動劃分到多個節點的能力
- 當集群中的一部分節點失效或者無法進行通訊是,仍然可以繼續處理命令請求的能力
### 2.redis集群數據共享
- Redis集群使用數據分片而非一致性哈希來實現:一個Redis集群包含16384個哈希槽,數據庫中的每個鍵都屬于這16384個哈希槽的其中一個,集群使用公式CRC(key)%16384來計算鍵key屬于哪個槽,其中CRC16(key)語句用于計算鍵(key)語句用于計算鍵key的CRC16校驗
如:
- 節點A負責處理0號至5500號哈希槽
- 節點B負責處理5501至11000號哈希槽
- 節點C負責處理11001至16384號哈希槽
### 3.集群的復制
- 為了使得集群在一部分節點下線或者無法與集群的大多數節點進行通訊的情況下,仍然可以正常運作,Redis集群節點使用了主從復制的功能:集群中的每個節點都有1個至N個復制品,其中一個復制品為主節點,而其余的N-1個復制品為從節點。
- 在之前舉例的節點A、B、C的例子中,如果節點B下線了,那么集群將無法正常運行,以為集群找不到節點來處理5501到11000號的哈希槽
- 假如在創建集群的時候(或者至少在節點B下線之前),我們為主節點B添加了從節點B1,那么當主節點B下線的時候,集群就會將B1設置為新的主節點,并讓它代替下線的主節點B,繼續處理5501-11000號的哈希槽,這樣集群就不會因為主節點B的下線而無法正常運作了
- 不過如果節點B和B1都下線的話,Redis集群還是會停止工作
### 4.運行機制
- 所有的Redis節點彼此互聯,內部使用二進制協議優化傳輸速度和帶寬
- 節點的失敗是通過集群中超過半數的master幾點檢測失效時才失效
- 客戶端與Redis節點直連,不需要任何中間proxy層,客戶端不需要連接集群所有節點,連接集群的任何一個可用節點即可
- 把所有的物理節點映射到[0-16384]slot上,cluster負責維護node<>slot<>key
### 5.配置集群
#### 安裝所需依賴
```
yum install -y ruby rubygems
```
```
gem install redis
```
如果卡住,使用國內鏡像
```
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
```
```
gem sources -l
```
執行`gem install redis`報錯redis requires Ruby version >= 2.2.2.解決辦法
```
gpg2 --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
```
安裝rvm
```
curl -L get.rvm.io | bash -s stable
```
source變量
```
source /usr/local/rvm/scripts/rvm
```
查看rvm庫中已知的ruby版本
```
rvm list known
```
安裝一個ruby版本
```
rvm install 2.4.0
```
使用一個ruby版本
```
rvm use 2.4.0
```
卸載一個已知版本
```
rvm remove 2.0.0
```
查看版本
```
ruby --version
```
#### 創建集群
可用以下腳本
cluster-create.sh
```shell
#!/bin/bash
for n in 79 80 81 82 83 84;
do
cp -a redis redis63$n/
rm -rf redis63$n/dump.rdb
sed -i "s/port 6379/port 63$n/g" redis63$n/redis.conf
sed -i "s#pidfile /var/run/redis_6379.pid#pidfile /application/redis63$n/redis_63$n.pid#g" redis63$n/redis.conf
echo "cluster-enabled yes" >> redis63$n/redis.conf
echo "cluster-config-file nodes_63$n.conf" >> redis63$n/redis.conf
echo "cluster-node-timeout 5000" >> redis63$n/redis.conf
sed -i "s/appendonly no/appendonly yes/g" redis63$n/redis.conf
sed -i "/^dir/d" redis63$n/redis.conf
echo 'dir "./"' >> redis63$n/redis.conf
done
```
#### 啟動集群
cluster-start.sh
```shell
#!/bin/bash
for n in {79..84};
do
cd /application/redis63$n && bin/redis-server redis.conf
done
/application/redis/bin/redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384
```
#### 登錄集群
可從以上創建的任意節點登錄,本例都是監聽127.0.0.1
```
redis-cli -c -p 6379
```