Redis
NoSOL特點(diǎn)
方便擴(kuò)展
大數(shù)據(jù)量性能高
數(shù)據(jù)類型多樣
-
RDBMS與NoSQL的區(qū)別
RDMBS -結(jié)構(gòu)化組織 -SQL -數(shù)據(jù)和關(guān)系都存在單獨(dú)的表中 -嚴(yán)格的一致性 -基礎(chǔ)的事務(wù)
NoSQL -不僅僅是數(shù)據(jù)庫 -沒有固定的查詢語言 -鍵值對存儲(chǔ)答憔,列存儲(chǔ),文檔存儲(chǔ),圖形數(shù)據(jù)庫 -最終一致性 -CAP定理和BASE (異地多活) -高性能俺祠,高可用帖世,高可擴(kuò)
3V+3高
3V
? 海量Velume
? 多樣Variety
? 實(shí)時(shí)Velocity
3高
? 高并發(fā)
? 高可擴(kuò)
? 高性能
NoSQL四大分類
KV鍵值對:
- Redis 杀怠、Tair母截、memcache
文檔型數(shù)據(jù)庫(bson格式)
- MongoDB
- MongoDB是一個(gè)基于分布式文件存儲(chǔ)的數(shù)據(jù)庫惜互,主要用來處理大量的文檔
- MongoDB是一個(gè)介于關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫中間的產(chǎn)品蹋订,MongoDB是非關(guān)系型數(shù)據(jù)庫中功能最豐富率挣,最像關(guān)系型數(shù)據(jù)庫的!
- ConthDB
列存儲(chǔ)數(shù)據(jù)庫
- HBase
- 分布式文件系統(tǒng)
圖關(guān)系數(shù)據(jù)庫
- Neo4j露戒、infoGrid;
Redis概述
==什么是Redis椒功?==
Redis(Remote Dictionary Server ),即遠(yuǎn)程字典服務(wù) 智什。是一個(gè)開源的使用ANSI C語言編寫动漾、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型荠锭、Key-Value數(shù)據(jù)庫旱眯,并提供多種語言的API。
redis會(huì)周期性的把更新的數(shù)據(jù)寫入磁盤或者把修改操作寫入追加的記錄文件,并且在此基礎(chǔ)上實(shí)現(xiàn)了master-slave(主從)同步删豺。 是當(dāng)下最熱門的NoSQL技術(shù)之一础爬。
==Redis能做什么==
- 內(nèi)存存儲(chǔ)、持久化
- 效率高吼鳞,可以用于高速緩存
- 發(fā)布訂閱系統(tǒng)
- 地圖信息分析
- 計(jì)時(shí)器看蚜、計(jì)數(shù)器
==特性==
- 多樣化的數(shù)據(jù)類型
- 持久化
- 集群
- 事務(wù)
官網(wǎng):https://redis.io/
中文網(wǎng):http://www.redis.cn/
下載地址:
Redis推薦在Linux服務(wù)器上搭建
Linux安裝
1、下載安裝包
2赔桌、將安裝包上傳到linux下 放到opt目錄下
? 解壓
3供炎、進(jìn)入解壓后的文件,標(biāo)紅的就是Redis的配置文件
4疾党、基本的環(huán)境安裝
yum install gcc-c++
安裝6.0以上版本需要升級gcc到5.3及以上,如下:升級到gcc 9.3:
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash
make
make install
5音诫、redis默認(rèn)安裝路徑==/usr/local/bin==
6、將redis配置文件復(fù)制到我們當(dāng)前的目錄下
7雪位、redis默認(rèn)不是后臺(tái)啟動(dòng)的竭钝,修改配置文件
8、啟動(dòng)redis服務(wù)
9雹洗、使用redis-cli進(jìn)行連接測試
10香罐、查看redis進(jìn)程是否開啟
ps -ef|grep redis
11、關(guān)閉Redis服務(wù)
shutdown
12时肿、再次查看進(jìn)程是否存在
性能測試
redis-benchmark是壓力測試工具庇茫,官方自帶的性能測試工具
redis-benchmark 命令參數(shù)!
圖片截取自菜鳥教程
實(shí)例測試:
#測試100個(gè)并發(fā)連接 100000請求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
基礎(chǔ)知識(shí)
redis默認(rèn)有16個(gè)數(shù)據(jù)庫
默認(rèn)使用的是第0個(gè)
可以使用select進(jìn)行切換數(shù)據(jù)庫
127.0.0.1:6379> select 3# 切換數(shù)據(jù)庫
OK
127.0.0.1:6379[3]> DBSIZE #查看數(shù)據(jù)庫大小
(integer) 0
127.0.0.1:6379[3]> keys * #查看所有的key
1) "name"
清除當(dāng)前數(shù)據(jù)庫==FLUSHDB==
清除全部數(shù)據(jù)庫內(nèi)容==flushall==
127.0.0.1:6379[3]> FLUSHDB
OK
127.0.0.1:6379[3]> keys *
(empty array)
redis6.0之前是單線程的螃成,6.0引入了多線程
Redis 的瓶頸并不在 CPU旦签,而在內(nèi)存和網(wǎng)絡(luò)。內(nèi)存不夠的話寸宏,可以加內(nèi)存或者做數(shù)據(jù)結(jié)構(gòu)優(yōu)化和其他優(yōu)化等宁炫,但網(wǎng)絡(luò)的性能優(yōu)化才是大頭,網(wǎng)絡(luò) IO 的讀寫在 Redis 整個(gè)執(zhí)行期間占用了大部分的 CPU 時(shí)間氮凝,如果把網(wǎng)絡(luò)處理這部分做成多線程處理方式羔巢,那對整個(gè) Redis 的性能會(huì)有很大的提升。
Redis為什么單線程還這么快覆醇?
核心:redis是將所有的數(shù)據(jù)全部放在內(nèi)存中的朵纷,所以說使用單線程進(jìn)行操作效率就高,多線程(CPU上下文會(huì)且換:比較耗時(shí))
五大數(shù)據(jù)類型
官方文檔
Redis-Key
127.0.0.1:6379> keys * #查看所有的key
(empty array)
127.0.0.1:6379> set name lijie #添加一個(gè)key
OK
127.0.0.1:6379> get name
"lijie"
127.0.0.1:6379> set age 56
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> EXISTS name #判斷當(dāng)前key是否存在
(integer) 1
127.0.0.1:6379> exists user
(integer) 0
127.0.0.1:6379> move name 1 #移動(dòng)當(dāng)前數(shù)據(jù)庫的key到指定地?cái)?shù)據(jù)庫中
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> set name lijie
OK
127.0.0.1:6379>
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> expire name 10 #設(shè)置key的過期時(shí)間單位是秒
(integer) 1
127.0.0.1:6379> ttl name #查看當(dāng)前可以的剩余時(shí)間
(integer) -2
127.0.0.1:6379> get name #獲得key
(nil)
127.0.0.1:6379> del name #刪除key
(integer) 0
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> type name #查看當(dāng)前key的類型
string
127.0.0.1:6379> type age
string
String(字符串)
127.0.0.1:6379> set key1 v1
OK
127.0.0.1:6379> get key1
"v1"
127.0.0.1:6379> keys *
1) "key1"
127.0.0.1:6379> EXISTS key1 #判斷某一個(gè)key是否存在
(integer) 1
127.0.0.1:6379> APPEND key1 leave #追加字符串永脓,如果當(dāng)前key不存在,就相當(dāng)于增加一個(gè)key
(integer) 7
127.0.0.1:6379> get key1
"v1leave"
127.0.0.1:6379> STRLEN key1 #獲取字符串的長度
(integer) 7
127.0.0.1:6379> APPEND key1 pdd
(integer) 10
127.0.0.1:6379> STRLEN key1
(integer) 10
127.0.0.1:6379> get key1
"v1leavepdd"
##################################################
#步長
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views#自增1
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> decr views#自減1
(integer) 1
127.0.0.1:6379> INCRBY views 10 #設(shè)置步長鞋仍,指定增量
(integer) 11
127.0.0.1:6379> DECRBY views 4 #設(shè)置步長常摧,指定減量
(integer) 7
##################################################
字符串范圍 range
127.0.0.1:6379> set key1 "hello Tom" #key的值
OK
127.0.0.1:6379> get key1
"hello Tom"
127.0.0.1:6379> GETRANGE key1 0 2 #截取字符串 [0,3]
"hel"
127.0.0.1:6379> GETRANGE key1 0 -1 #獲取全部的字符串 相當(dāng)于get key1
"hello Tom"
#替換
127.0.0.1:6379> set key2 123456
OK
127.0.0.1:6379> get key2
"123456"
127.0.0.1:6379> SETRANGE key2 2 b #替換指定位置開始的字符串
(integer) 6
127.0.0.1:6379> get key2
"12b456"
#########################################################
# setex (set with expire) 設(shè)置過期時(shí)間
# setnx (set if not exist) 不存在再設(shè)置(在分布式鎖中會(huì)經(jīng)常使用)
127.0.0.1:6379> SETEX key3 30 "history" #設(shè)置key3得值為hello,30秒后過期
OK
127.0.0.1:6379> ttl key3
(integer) 19
127.0.0.1:6379> get key3
"history"
127.0.0.1:6379> setnx mykey "redis" #如果mykey 不存在,創(chuàng)建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "key2"
2) "mykey"
3) "key1"
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> SETNX mykey "hello" #如果mykey存在落午,創(chuàng)建失敗
(integer) 0
127.0.0.1:6379> get mykey
"redis"
########################################################
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同時(shí)設(shè)置多個(gè)值
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> mget k1 k2 k3 #同時(shí)獲取多個(gè)值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 #msetnx 是一個(gè)原子性的操作谎懦,要么一起成功,要么一起失敗
(integer) 0
127.0.0.1:6379> get k4
(nil)
#########################################################
set user:1 {name: lijie,age:3} #設(shè)置一個(gè)user:1對象 值為json字符串來保存一個(gè)對象
127.0.0.1:6379> mset user:1:name lijie user:1:age 60
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "lijie"
2) "60"
#########################################################
getset #先get再set
127.0.0.1:6379> getset db redis #如果不存在值溃斋,則創(chuàng)建值返回nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> GETSET db LeetCode #如果存在值界拦,則替換原來的值返回nil
"redis"
127.0.0.1:6379> get db
"LeetCode"
String類型的使用場景:
- 計(jì)數(shù)器
- 統(tǒng)計(jì)多單位的數(shù)量
- 粉絲數(shù)
- 對象緩存存儲(chǔ)
List (列表)
Redis列表是簡單的字符串列表,按照插入順序排序梗劫。你可以添加一個(gè)元素到列表的頭部(左邊)或者尾部(右邊)
一個(gè)列表最多可以包含 232 - 1 個(gè)元素 (4294967295, 每個(gè)列表超過40億個(gè)元素)享甸。
127.0.0.1:6379> lpush list one #將一個(gè)值或者多個(gè)值,插入到列表頭部(左)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1 # 獲取list中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1 #通過區(qū)間來獲取具體的值
1) "three"
2) "two"
127.0.0.1:6379> RPUSH list four # #將一個(gè)值或者多個(gè)值梳侨,插入到列表尾部(右)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
#########################################################
lpop
rpop
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379> LPOP list #移除list的第一個(gè)元素
"three"
127.0.0.1:6379> Rpop list #移除list的最后一個(gè)元素
"four"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
#########################################################
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1 # 通過下標(biāo)獲得list中的某一個(gè)值
"one"
127.0.0.1:6379> lindex list 0
"two"
#########################################################
LLen
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> llen list #返回列表的長度
(integer) 3
#########################################################
移除指定的值
LREM
127.0.0.1:6379> LRANGE list 0 -1
^[[3~1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 one #移除list集合中指定個(gè)數(shù)的value蛉威,精確匹配
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379> lrem list 1 three
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> lrem list 2 three
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
#########################################################
trim
127.0.0.1:6379> lpush list "hello1"
(integer) 1
127.0.0.1:6379> lpush list "hello2"
(integer) 2
127.0.0.1:6379> lpush list "hello3"
(integer) 3
127.0.0.1:6379> lpush list "hello4"
(integer) 4
127.0.0.1:6379> ltrim list 1 2 #通過下標(biāo)截取指定的長度
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "hello3"
2) "hello2"
#########################################################
rpoplpush #移動(dòng)列表的最后一個(gè)元素 到新的列表中
127.0.0.1:6379> RPUSH mylist "hello"
(integer) 1
127.0.0.1:6379> RPUSH mylist "hello1"
(integer) 2
127.0.0.1:6379> RPUSH mylist "hello2"
(integer) 3
127.0.0.1:6379> RPOPLPUSH mylist otherlist#移動(dòng)列表的最后一個(gè)元素 到新的列表中
"hello2"
127.0.0.1:6379> LRANGE mylist 0 -1 #查看原來的列表
1) "hello"
2) "hello1"
127.0.0.1:6379> LRANGE otherlist 0 -1 #查看目標(biāo)列表,確實(shí)存在值
1) "hello2"
#########################################################
LSET #將列表中指定下標(biāo)的值替換為另外一個(gè)值走哺,更新操作
127.0.0.1:6379> EXISTS list#判斷這個(gè)列表是否存在
(integer) 0
127.0.0.1:6379> lpush list value1
(integer) 1
127.0.0.1:6379> LRANGE list 0 0
1) "value1"
127.0.0.1:6379> LSET list 0 tian #如果存在蚯嫌,更新當(dāng)前下標(biāo)的值
OK
127.0.0.1:6379> LRANGE list 0 0
1) "tian"
127.0.0.1:6379> LSET list 1 flag #如果不存在,則會(huì)保存
(error) ERR index out of range
#########################################################
LINSERT #將某個(gè)具體的value插入到列表中某個(gè)元素的前面或者后面
127.0.0.1:6379> RPUSH mylist "hello"
(integer) 1
127.0.0.1:6379> RPUSH mylist "world"
(integer) 2
127.0.0.1:6379> LINSERT mylist before "world" tian
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "tian"
3) "world"
127.0.0.1:6379> LINSERT mylist after "world" flag
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "tian"
3) "world"
4) "flag"
==小結(jié)==
- 實(shí)際上是一個(gè)鏈表丙躏,before Node after 择示,left,right都可以插入值
- 如果key不存在晒旅,則創(chuàng)建新的鏈表
- 如果key存在对妄,新增內(nèi)容
- 如果移除了所有的值,就變成了空鏈表敢朱,代表不存在
- 在兩邊插入或者改動(dòng)值剪菱,效率最高拴签!操作中間元素效率會(huì)低一點(diǎn)
消息排隊(duì)奕短!消息隊(duì)列 (Lpush Rpop)昆雀,棧(lpush Lpop)
Set(集合)
==set是無序不重復(fù)集合==
127.0.0.1:6379> SADD myset god #往set集合中添加元素
(integer) 1
127.0.0.1:6379> SADD myset oldxu
(integer) 1
127.0.0.1:6379> SADD myset wabibabo
(integer) 1
127.0.0.1:6379> SMEMBERS myset #查看指定集合中的所有值
1) "wabibabo"
2) "oldxu"
3) "god"
127.0.0.1:6379> SISMEMBER myset god #判斷某一個(gè)值是否在set集合中
(integer) 1
127.0.0.1:6379> SISMEMBER myset ooo
(integer) 0
#########################################################
127.0.0.1:6379> SCARD myset #獲取set集合中的內(nèi)容元素個(gè)數(shù)
(integer) 3
127.0.0.1:6379> sadd myset oldxu
(integer) 0
127.0.0.1:6379> sadd myset baozi
(integer) 1
127.0.0.1:6379> scard myset
(integer) 4
#########################################################
127.0.0.1:6379> srem myset god #移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> SCARD myset
(integer) 3
127.0.0.1:6379> SMEMBERS myset
1) "baozi"
2) "wabibabo"
3) "oldxu"
#########################################################
127.0.0.1:6379> SRANDMEMBER myset #隨機(jī)抽選出一個(gè)元素
"baozi"
127.0.0.1:6379> SRANDMEMBER myset
"baozi"
127.0.0.1:6379> SRANDMEMBER myset
"wabibabo"
127.0.0.1:6379> SRANDMEMBER myset 2隨機(jī)抽選出指定個(gè)數(shù)的元素
1) "baozi"
2) "oldxu"
127.0.0.1:6379> SRANDMEMBER myset 2
1) "wabibabo"
2) "oldxu"
#########################################################
刪除指定的key,隨機(jī)刪除key
127.0.0.1:6379> SMEMBERS myset
1) "baozi"
2) "wabibabo"
3) "oldxu"
127.0.0.1:6379> spop myset #隨機(jī)刪除set集合中的元素
"wabibabo"
127.0.0.1:6379> spop myset
"baozi"
127.0.0.1:6379> SMEMBERS myset
1) "oldxu"
#########################################################
#將一個(gè)指定的元素,移動(dòng)到另外一個(gè)集合中
127.0.0.1:6379> sadd myset qwert
(integer) 1
127.0.0.1:6379> sadd myset asdfg
(integer) 1
127.0.0.1:6379> sadd myset zxcvb
(integer) 1
127.0.0.1:6379> sadd name dingding
(integer) 1
127.0.0.1:6379> Smove myset name qwert #將一個(gè)指定的元素戴而,移動(dòng)到另外一個(gè)集合中
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "zxcvb"
2) "asdfg"
127.0.0.1:6379> SMEMBERS name
1) "qwert"
2) "dingding"
#########################################################數(shù)字集合類:
- 差集 SDIFF
- 交集 SINTER
- 并集 sunion
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key2 e
(integer) 1
127.0.0.1:6379> SDIFF key1 key2 #差集
1) "a"
2) "b"
127.0.0.1:6379> SINTER key1 key2 #交集
1) "c"
127.0.0.1:6379> sunion key1 key2 #并集
1) "a"
2) "c"
3) "e"
4) "b"
5) "d"
Hash(哈希)
Map集合,key-map 它的值是一個(gè)Map集合犬钢,本質(zhì)和String類型沒有太大區(qū)別篷扩,還是一個(gè)簡單的key-value!
127.0.0.1:6379> hset myhash field1 bingb #set一個(gè)具體的key-value
(integer) 1
127.0.0.1:6379> hget myhash field1 #獲取一個(gè)字段值
"bingb"
127.0.0.1:6379> hset myhash field1 tiantian field2 libai #set多個(gè) key-value
(integer) 1
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> hset myhash field1 binggong
(integer) 1
127.0.0.1:6379> hget myhash field1
"binggong"
127.0.0.1:6379> hmset myhash field1 dingding field2 libai #獲取多個(gè)字段值
OK
127.0.0.1:6379> hmget myhash field1 field2
1) "dingding"
2) "libai"
127.0.0.1:6379> HGETALL myhash #獲取全部數(shù)據(jù)
1) "field1"
2) "dingding"
3) "field2"
4) "libai"
127.0.0.1:6379> HDEL myhash field1 #刪除hash指定key字段曹阔,對應(yīng)的value值也就消失了
(integer) 1
127.0.0.1:6379> HGETALL myhash
1) "field2"
2) "libai"
#########################################################
hlen
127.0.0.1:6379> HGETALL myhash
1) "field2"
2) "libai"
127.0.0.1:6379> hlen myhash #獲取hash表中字段的數(shù)量
(integer) 1
#########################################################
HEXISTS
127.0.0.1:6379> HEXISTS myhash field1 #判斷hash中指定字段是否存在
(integer) 0
127.0.0.1:6379> HEXISTS myhash field2
(integer) 1
#########################################################
# 獲得所有field HKEYS
# 獲得所有value HVALS
127.0.0.1:6379> HKEYS myhash # 獲得所有的字段
1) "field2"
2) "filed1"
127.0.0.1:6379> HVALS myhash # 獲得所有value
1) "libai"
2) "hello"
#########################################################
127.0.0.1:6379> hset myhash field3 5 #指定增量
(integer) 1
127.0.0.1:6379> HINCRBY myhash field3 1
(integer) 6
127.0.0.1:6379> HINCRBY myhash field3 -1
(integer) 5
127.0.0.1:6379> HSETNX myhash field4 bbbb #如果不存在則新增
(integer) 1
127.0.0.1:6379> HSETNX myhash field4 bbbb #如果存在則不能設(shè)置
(integer) 0
hash適合存儲(chǔ)經(jīng)常變動(dòng)的信息半开,尤其是用戶信息之類的。hash適合與對象的存儲(chǔ)赃份,String更加適合字符串存儲(chǔ)
Zset(有序集合)
在set的基礎(chǔ)上寂拆,增加了一個(gè)用來排序的值
127.0.0.1:6379> zadd myzset 1 one #添加一個(gè)值
(integer) 1
127.0.0.1:6379> zadd myzset 2 two 3 three #添加多個(gè)值
(integer) 2
127.0.0.1:6379> ZRANGE myzset 0 -1
1) "one"
2) "two"
3) "three"
#########################################################
實(shí)現(xiàn)排序
127.0.0.1:6379> zadd salary 2500 libai #添加三個(gè)用戶以及薪水
(integer) 1
127.0.0.1:6379> zadd salary 4500 dufu
(integer) 1
127.0.0.1:6379> zadd salary 200 AQ
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf #顯示全部的用戶從小到大
1) "AQ"
2) "libai"
3) "dufu"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores #顯示全部的用戶并附帶薪水
1) "AQ"
2) "200"
3) "libai"
4) "2500"
5) "dufu"
6) "4500"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores #顯示工資小于等于2500的員工按升序排列
1) "AQ"
2) "200"
3) "libai"
4) "2500"
127.0.0.1:6379> ZREVRANGE salary 0 -1 #從大到小進(jìn)行排序
1) "dufu"
2) "libai"
#########################################################
127.0.0.1:6379> ZRANGE salary 0 -1
1) "AQ"
2) "libai"
3) "dufu"
127.0.0.1:6379> zrem salary AQ #移除元素
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "libai"
2) "dufu"
127.0.0.1:6379> zcard salary #獲取有序集合中的個(gè)數(shù)
(integer) 2
#########################################################
127.0.0.1:6379> zadd myzset 1 qwert 2 asdfg 3 zxcvb
(integer) 3
127.0.0.1:6379> zcount myzset 1 3 #獲取指定區(qū)間的成員數(shù)量
(integer) 3
127.0.0.1:6379> zcount myzset 1 2
(integer) 2
三種特殊數(shù)據(jù)類型
geospatial 地理位置
微信定位奢米,附近的人,打車距離計(jì)算
Redis的Geo功能可以推算地理位置的信息纠永,兩地之間的距離鬓长,方圓幾里的人
geoadd
#geoadd 添加地理位置
# 規(guī)則:兩極無法直接添加
# 參數(shù) key value(經(jīng)度、緯度尝江、名稱)
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqin 114.05 22.52 shenzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
geopos
獲得當(dāng)前定位:一定是一個(gè)坐標(biāo)值
127.0.0.1:6379> geopos china:city beijing #獲取指定的城市的精度和緯度
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city beijing chongqin
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
GEODIST
兩人之間的距離
單位:
- m 表示單位為米涉波。
- km 表示單位為千米。
- mi 表示單位為英里炭序。
- ft 表示單位為英尺啤覆。
127.0.0.1:6379> GEODIST china:city beijing shanghai
"1067378.7564"
127.0.0.1:6379> GEODIST china:city beijing shanghai km #查看北京到上海的直線距離
"1067.3788"
127.0.0.1:6379> GEODIST china:city beijing chongqin km
"1464.0708"
GEORADIUS
以給定的經(jīng)緯度為中心, 返回鍵包含的位置元素當(dāng)中少态, 與中心的距離不超過給定最大距離的所有位置元素城侧。
獲取附近的人的地址,定位彼妻,通過半徑來查詢
127.0.0.1:6379> GEORADIUS china:city 110 20 1000 km
1) "shenzhen"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km #以110嫌佑,30 這個(gè)經(jīng)緯度為中心,尋找方圓1000km內(nèi)的城市
1) "chongqin"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist #顯示到中心位置的距離
1) 1) "chongqin"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord #顯示他人的定位信息
1) 1) "chongqin"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 1
1) 1) "chongqin"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 2 #篩選出指定的結(jié)果
1) 1) "chongqin"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) "483.8340"
3) 1) "108.96000176668167114"
2) "34.25999964418929977"
GEORADIUSBYMEMBER
#找出位于指定元素周圍的其他元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city xian 500 km
1) "xian"
GEOHASH 返回一個(gè)或多個(gè)位置元素的 Geohash 表示
該命令將返回11個(gè)字符的Geohash字符串
# 將二維的經(jīng)緯度轉(zhuǎn)換為一維的字符串
127.0.0.1:6379> GEOHASH china:city beijing chongqin
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
GEO底層的實(shí)現(xiàn)原理為Zset 侨歉,可以使用Zset命令來操作geo
127.0.0.1:6379> ZRANGE china:city 0 -1 #查看地圖中全部的元素
1) "chongqin"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> zrem china:city xian #移除指定元素
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqin"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
5) "beijing"
Hyperloglog
什么是基數(shù)屋摇?
不重復(fù)的元素
A{1 ,2幽邓, 3 炮温,4 ,5牵舵,5} B{1柒啤,2,3畸颅,4担巩,5}
基數(shù)為5
簡介
Redis Hyperloglog 基數(shù)統(tǒng)計(jì)的算法。
優(yōu)點(diǎn):占用的內(nèi)存是固定的没炒,2^64不同元素的技術(shù)涛癌,只需要消耗12kb內(nèi)存
網(wǎng)頁的UV(一個(gè)人訪問一個(gè)網(wǎng)站多次。但還是算作一個(gè)人)
0.81%錯(cuò)誤率送火!對于UV任務(wù) 拳话,可以忽略不計(jì)
測試使用
127.0.0.1:6379> pfadd mykey a b c d e f g h i j #創(chuàng)建第一組元素 mykey
(integer) 1
127.0.0.1:6379> PFCOUNT mykey #統(tǒng)計(jì)mykey 元素的基數(shù)數(shù)量
(integer) 10
127.0.0.1:6379> pfadd mykey2 a j j o f n q m #創(chuàng)建第二組元素 mykey2
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 7
127.0.0.1:6379> PFMERGE mykey3 mykey2 mykey #合并兩組 mykey mykey2 =》mykey3
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 14
Bitmap
位存儲(chǔ)
統(tǒng)計(jì)用戶信息,活躍种吸,不活躍弃衍!打卡。兩個(gè)狀態(tài)的骨稿,都可以使用Bitmap笨鸡!
Bitmap位圖姜钳,都是操作二進(jìn)制位來進(jìn)行記錄坦冠,就只有0和1兩個(gè)狀態(tài)
測試
#使用bitmap來記錄 周一到周日的打卡形耗,1代表打卡 0代表未打卡
127.0.0.1:6379> setbit sign 0 0
(integer) 0
127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0
####################################################
#查看某一天是否打卡
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 4
(integer) 0
##########################################################統(tǒng)計(jì)操作,統(tǒng)計(jì)打卡的天數(shù)
127.0.0.1:6379> bitcount sign
(integer) 3
事務(wù)
Redis事務(wù)本質(zhì):一組命令的集合辙浑!一個(gè)事務(wù)中的所有命令都會(huì)被序列化激涤,在事務(wù)執(zhí)行的過程中,會(huì)按照順序執(zhí)行
一次性判呕、順序性倦踢、排他性!執(zhí)行一系列的命令
------ 隊(duì)列 set set set 執(zhí)行 ------
Redis事務(wù)沒有隔離級別的概念
所有的命令在事務(wù)中侠草,并沒有直接被執(zhí)行! 只有發(fā)起執(zhí)行命令的時(shí)候才會(huì)執(zhí)行
Redis單條命令是保證原子性的辱挥,但事務(wù)不保證原子性
redis的事務(wù):
- 開啟事務(wù)(multi)
- 命令入隊(duì)()
- 執(zhí)行事務(wù)(exec)
正常開啟事務(wù)
127.0.0.1:6379> multi #開啟事務(wù)
OK
127.0.0.1:6379> set k1 v1 #命令入隊(duì)
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec #執(zhí)行事務(wù)
1) OK
2) OK
3) "v1"
4) OK
放棄事務(wù)
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> discard #放棄事務(wù) 事務(wù)隊(duì)列中的命令都不會(huì)被執(zhí)行
OK
編譯型異常(代碼有問題!命令錯(cuò)誤1咛椤)晤碘,事務(wù)中所有的命令都不會(huì)被執(zhí)行,錯(cuò)誤命令拋出異常
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> adahjj k3 #錯(cuò)誤的命令
(error) ERR unknown command `adahjj`, with args beginning with: `k3`,
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec #執(zhí)行事務(wù)報(bào)錯(cuò)
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k4 #所有的命令都不會(huì)被執(zhí)行
(nil)
運(yùn)行時(shí)異常(1/0)功蜓,如果事務(wù)隊(duì)列中存在語法性錯(cuò)誤园爷,那么執(zhí)行命令的時(shí)候,其他命令是可以正常執(zhí)行的
127.0.0.1:6379> set name "lijie"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr name
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range #雖然第一條命令報(bào)錯(cuò)了式撼,但事務(wù)依舊執(zhí)行成功了
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
監(jiān)控童社!Watch
悲觀鎖:
- 很悲觀,認(rèn)為什么時(shí)候都會(huì)出問題著隆,無論做什么都會(huì)加鎖
樂觀鎖:
- 很樂觀扰楼,認(rèn)為什么時(shí)候都不會(huì)出問題,所以不會(huì)上鎖美浦!更新數(shù)據(jù)的時(shí)候判斷一下弦赖,在此期間是否有人修改過這個(gè)數(shù)據(jù)
- 獲取version
- 更新的時(shí)候比較version
Redis監(jiān)視測試
正常執(zhí)行成功
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #監(jiān)視 money 對象
OK
127.0.0.1:6379> multi #數(shù)據(jù)執(zhí)行期間沒有發(fā)生變動(dòng),這個(gè)時(shí)候就正常執(zhí)行成功
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
測試多線程修改值抵代,使用watch可以當(dāng)做redis的樂觀鎖操作
127.0.0.1:6379> watch money #監(jiān)視 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec #執(zhí)行之前腾节,另外一個(gè)線程,修改了我們的值荤牍,這個(gè)時(shí)候就會(huì)導(dǎo)致事務(wù)執(zhí)行失敗
(nil)
如果修改失敗案腺,獲取最新的值就好
127.0.0.1:6379> unwatch # 如果發(fā)現(xiàn)事務(wù)執(zhí)行失敗,就先解鎖
OK
127.0.0.1:6379> watch money #獲取最新的值康吵,再次監(jiān)視
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 1
QUEUED
127.0.0.1:6379> INCRBY money 1
QUEUED
127.0.0.1:6379> exec #對比監(jiān)視的值是否發(fā)生了變化劈榨,如果沒有變化,那么可以執(zhí)行成功晦嵌,如果產(chǎn)生了變化就執(zhí)行失敗
1) (integer) 999
2) (integer) 1000
Jedis
Jedis是Redis官方推薦的java連接開發(fā)工具同辣!使用Java操作Redis中間件
測試
1拷姿、導(dǎo)入對應(yīng)的依賴
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<!-- fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
</dependencies>
2、編碼測試:
連接數(shù)據(jù)庫
操作命令
-
斷開連接
public class TestPing { public static void main(String[] args) { Jedis jedis = new Jedis("localhost",6379); jedis.auth("dengzhuo"); System.out.println(jedis.ping()); } }
常用的API
String
List
Set
Hash
Zset
測試事務(wù)
public class TestTX {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost",6379);
jedis.auth("dengzhuo");
jedis.flushDB();
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","lijie");
//開啟事務(wù)
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
try {
multi.set("user1",result);
multi.set("user2",result);
int i =1/0;
multi.exec();//執(zhí)行事務(wù)
} catch (Exception e) {
multi.discard();//放棄事務(wù)
e.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();//關(guān)閉連接
}
}
}
SpringBoot整合
在SpringBoot2.x之后旱函,原來使用的jedis被替換成了lettuce
jedis:采用的直連技術(shù)响巢,多個(gè)線程操作的話,是不安全的棒妨,為了避免這種情況踪古,會(huì)使用jedis pool連接池,類型BIO模式
lettuce:采用netty券腔,實(shí)例可以在多個(gè)線程中進(jìn)行共享伏穆,不存在線程不安全的情況!可以減少線程數(shù)據(jù)纷纫,更像NIO 模式
源碼分析
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}//我們可以用自定義的redisTemplate來替換這個(gè)默認(rèn)的
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
//默認(rèn)的redisTemplate沒有過多設(shè)置枕扫,redis對象都是需要序列化的
//兩個(gè)泛型都是object,object的類型辱魁,后續(xù)使用需要強(qiáng)制轉(zhuǎn)換<String,object>
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean //由于String是redis中最常使用的類型烟瞧,所以單獨(dú)提出一個(gè)bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
整合測試
1、導(dǎo)入依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
2商叹、配置連接
spring.redis.host= localhost
spring.redis.port=6379
spring.redis.password=******
3燕刻、測試
@SpringBootTest
class Redis02SpringbootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//redisTemplate 操作不同的數(shù)據(jù)類型
//opsForValue //操作字符串
//opsForList
//opsForSet
//opsForHash
//opsForZSet
//opsForGeo
//opsForHyperLogLog
//除了基本的操作,常用的方法都可以直接通過redisTemplate操作剖笙,例如事務(wù)和卵洗,基本的CRUD
//獲取redis的連接對象
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// connection.flushAll();
// connection.flushDb();
redisTemplate.opsForValue().set("name","lijie");
System.out.println(redisTemplate.opsForValue().get("name"));
}
}
自定義RedisTemplate
@Configuration
public class RedisConfig {
//自己定義了一個(gè)RedisTemplate
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) throws UnknownHostException {
//為了開發(fā)方便,一般直接使用<String, Object>
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(factory);
//序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(om.getPolymorphicTypeValidator());
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
//hash采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
Redis.conf詳解
啟動(dòng)的時(shí)候弥咪,就通過配置文件來啟動(dòng)过蹂!
單位
1、配置文件unit單位對大小寫不敏感
包含
網(wǎng)絡(luò)
bind 127.0.0.1 #綁定的IP
protected-mode no #保護(hù)模式
port 6379 #端口設(shè)置
通用GENERAL
daemonize yes #以守護(hù)進(jìn)程的方式運(yùn)行聚至,默認(rèn)是no酷勺,我們需要自己開啟默認(rèn)為yes
pidfile /var/run/redis_6379.pid #如果以后臺(tái)的方式運(yùn)行,就需要指定一個(gè)pid文件
#日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
logfile "" #日志文件的位置
databases 16 #數(shù)據(jù)庫的數(shù)量扳躬,默認(rèn)是16個(gè)
always-show-logo yes #是否總是顯示LOGO
快照
redis是內(nèi)存數(shù)據(jù)庫脆诉,如果沒有持久化,那么數(shù)據(jù)斷電即失
#如果900s內(nèi)贷币,如果至少有一個(gè)key進(jìn)行了修改击胜,將自動(dòng)進(jìn)行持久化操作
save 900 1
#如果300s內(nèi),如果至少10個(gè)key進(jìn)行了修改役纹,將自動(dòng)進(jìn)行持久化操作
save 300 10
#如果60s內(nèi)偶摔,如果至少10000個(gè)key進(jìn)行了修改,將自動(dòng)進(jìn)行持久化操作
save 60 10000
stop-writes-on-bgsave-error yes #持久化如果出錯(cuò)促脉,是否還繼續(xù)工作
rdbcompression yes #是否壓縮 rdb 文件辰斋,策州,需要消耗一些cpu資源
rdbchecksum yes #保存rdb文件的時(shí)候,進(jìn)行錯(cuò)誤的檢查校驗(yàn)
dir ./ #rdb文件保存的目錄
SECURITY 安全
requirepass dengzhuo #密碼設(shè)置
maxclients 10000 #設(shè)置能連接上redis的最大客戶端的數(shù)量
maxmemory <bytes> #redis 配置最大的內(nèi)存容量
maxmemory-policy noeviction #內(nèi)存到達(dá)上限之后的處理策略
volatile-lru:從設(shè)置了過期時(shí)間的數(shù)據(jù)集中,選擇最近最久未使用的數(shù)據(jù)釋放;
allkeys-lru:從數(shù)據(jù)集中(包括設(shè)置過期時(shí)間以及未設(shè)置過期時(shí)間的數(shù)據(jù)集中)衣吠,選擇最近最久未使用的數(shù)據(jù)釋放;
volatile-random:從設(shè)置了過期時(shí)間的數(shù)據(jù)集中下硕,隨機(jī)選擇一個(gè)數(shù)據(jù)進(jìn)行釋放丁逝;
allkeys-random:從數(shù)據(jù)集中(包括了設(shè)置過期時(shí)間以及未設(shè)置過期時(shí)間)隨機(jī)選擇一個(gè)數(shù)據(jù)進(jìn)行入釋放汁胆;
volatile-ttl:從設(shè)置了過期時(shí)間的數(shù)據(jù)集中,選擇馬上就要過期的數(shù)據(jù)進(jìn)行釋放操作霜幼;
noeviction:不刪除任意數(shù)據(jù)(但redis還會(huì)根據(jù)引用計(jì)數(shù)器進(jìn)行釋放),這時(shí)如果內(nèi)存不夠時(shí)嫩码,會(huì)直接返回錯(cuò)誤。
APPENDONLY ONLY 模式 aof配置
appendonly no #默認(rèn)是不開啟aof模式的罪既,默認(rèn)是使用rdb方式持久化的铸题,在大部分情況下,rdb完全夠用
appendfilename "appendonly.aof" #持久化的文件名
# appendfsync always #每次修改都會(huì) sync(同步)琢感,消耗性能
appendfsync everysec #每秒執(zhí)行一次 sync丢间,可能會(huì)丟失這一秒的數(shù)據(jù)
# appendfsync no #不執(zhí)行 sync,操作系統(tǒng)自己同步數(shù)據(jù)驹针,速度最快
Redis持久化
Redis是內(nèi)存數(shù)據(jù)庫烘挫,如果不將內(nèi)存中的數(shù)據(jù)庫狀態(tài)保存到磁盤,那么一旦服務(wù)器進(jìn)程退出柬甥,服務(wù)器中的數(shù)據(jù)庫狀態(tài)也會(huì)消失饮六,所以Redis提供了持久化功能。
RDB(Redis DataBase)
在指定的時(shí)間間隔內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤苛蒲,也就是行話講的Snapshot快照卤橄,它恢復(fù)時(shí)是將快照文件直接讀到內(nèi)存里。
Redis會(huì)單獨(dú)創(chuàng)建( fork )一個(gè)子進(jìn)程來進(jìn)行持久化臂外,會(huì)先將數(shù)據(jù)寫入到一個(gè)臨時(shí)文件中窟扑,待持久化過程都結(jié)束了,再用這個(gè)臨時(shí)文件替換上次持久化好的文件漏健。整個(gè)過程中嚎货,主進(jìn)程是不進(jìn)行任何IO操作的。這就確保了極高的性能漾肮。如果需要進(jìn)行大規(guī)模數(shù)據(jù)的恢復(fù)厂抖,且對于數(shù)據(jù)恢復(fù)的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效克懊。RDB的缺點(diǎn)是最后一次持久化后的數(shù)據(jù)可能丟失忱辅。我們默認(rèn)的就是RDB七蜘,一般情況下不需要修改這個(gè)配置!
==rdb保存的文件是dump.rdb==
自定義配置只要60s內(nèi)修改5次key,就會(huì)觸發(fā)rdb操作
觸發(fā)機(jī)制
1墙懂、save的規(guī)則滿足的情況下橡卤,會(huì)自動(dòng)觸發(fā)rdb規(guī)則
2、執(zhí)行flushall命令损搬,也會(huì)觸發(fā)rdb規(guī)則
3碧库、退出redis,也會(huì)產(chǎn)生rdb文件
備份就會(huì)自動(dòng)生成一個(gè)dump.rdb
恢復(fù)rdb文件
1巧勤、只需要將rdb文件放在redis啟動(dòng)目錄下就可以了嵌灰,redis啟動(dòng)的時(shí)候會(huì)自動(dòng)檢查dump.rdb恢復(fù)其中的數(shù)據(jù)
2、查看文件存在的位置
46.154.150.53:6379> config get dir
1) "dir"
2) "/usr/local/bin" #如果在這個(gè)目錄下存在dump.rdb文件颅悉,啟動(dòng)就會(huì)自動(dòng)恢復(fù)其中的數(shù)據(jù)
優(yōu)點(diǎn):
1沽瞭、適合大規(guī)模的數(shù)據(jù)恢復(fù)
2、對數(shù)據(jù)的完整性要求不高
缺點(diǎn):
1剩瓶、需要一定的時(shí)間進(jìn)行操作驹溃!如果redis意外宕機(jī)了,最后一次修改的數(shù)據(jù)就沒有了
2延曙、fork進(jìn)程的時(shí)候豌鹤,會(huì)占用一定的內(nèi)容空間
AOF(Append Only File)
將所有命令都記錄下來,history枝缔,恢復(fù)的時(shí)候就把這個(gè)文件全部在執(zhí)行一遍!
是什么
以日志的形式來記錄每個(gè)寫操作布疙,將Redis執(zhí)行過的所有指令記錄下來(讀操作不記錄),只許追加文件但不可以改寫文件魂仍,redis啟動(dòng)之初會(huì)讀取該文件重新構(gòu)建數(shù)據(jù)拐辽,換言之,redis重啟的話就根據(jù)日志文件的內(nèi)容將寫指令從前到后執(zhí)行一次以完成數(shù)據(jù)的恢復(fù)工作擦酌。
==Aof保存的是appendonly.aof文件==
默認(rèn)是不開啟的俱诸,我們需要手動(dòng)進(jìn)行配置 ! 我們只需要將appendorly改為yes就開啟了aof !
重啟,redis就可以生效了!
如果這個(gè)aof文件有錯(cuò)位赊舶,此時(shí)redis是無法啟動(dòng)的睁搭,需要修復(fù)aof文件
redis提供了一個(gè)修復(fù)工具==redis-check-aof==
修復(fù)之后,就能正常啟動(dòng)redis了
重寫規(guī)則
如果aof文件大于64Mb笼平,就會(huì)fork一個(gè)新的進(jìn)程來將文件進(jìn)行重寫
優(yōu)點(diǎn)和缺點(diǎn)
appendonly no #默認(rèn)是不開啟aof模式的园骆,默認(rèn)是使用rdb方式持久化的,在大部分情況下寓调,rdb完全夠用
appendfilename "appendonly.aof" #持久化的文件名
# appendfsync always #每次修改都會(huì) sync(同步)锌唾,消耗性能
appendfsync everysec #每秒執(zhí)行一次 sync,可能會(huì)丟失這一秒的數(shù)據(jù)
# appendfsync no #不執(zhí)行 sync,操作系統(tǒng)自己同步數(shù)據(jù)晌涕,速度最快
優(yōu)點(diǎn):
1滋捶、每一次修改都同步,文件的完整性會(huì)更好
2余黎、每秒同步一次重窟,可能會(huì)丟失一秒的數(shù)據(jù)
3、從不同步惧财,效率最高的
缺點(diǎn):
1巡扇、相對于數(shù)據(jù)文件來說,aof遠(yuǎn)遠(yuǎn)大于rdb垮衷,修復(fù)的速度也比rdb慢
2厅翔、Aof運(yùn)行效率也要比rdb慢。
Redis發(fā)布訂閱
Redis 發(fā)布訂閱(pub/sub)是一種消息通信模式︰發(fā)送者(pub)發(fā)送消息帘靡,訂閱者(sub)接收消息知给。
Redis 客戶端可以訂閱任意數(shù)量的頻道。
訂閱/發(fā)布消息圖:
下圖展示了頻道 channel1 描姚, 以及訂閱這個(gè)頻道的三個(gè)客戶端 —— client2 、 client5 和 client1 之間的關(guān)系:
當(dāng)有新消息通過 PUBLISH 命令發(fā)送給頻道 channel1 時(shí)戈次, 這個(gè)消息就會(huì)被發(fā)送給訂閱它的三個(gè)客戶端:
命令
1 | PSUBSCRIBE pattern [ pattern ...]訂閱一個(gè)或多個(gè)符合給定模式的頻道。 |
---|---|
2 | PUBSUB subcommand [argument [argument ...]]查看訂閱與發(fā)布系統(tǒng)狀態(tài)澄步。 |
3 | PUBLISH channel message將信息發(fā)送到指定的頻道。 |
4 | PUBLISH channel message將信息發(fā)送到指定的頻道冤议。 |
5 | SUBSCRIBE channel [channel ..]訂閱給定的一個(gè)或多個(gè)頻道的信息犯眠。 |
6 | UNSUBSCRIBE_[channel [channel ...]]指退訂給定的頻道缩滨。 |
測試
訂閱端:
46.154.150.53:6379> SUBSCRIBE Alibaba #訂閱一個(gè)頻道 Alibaba
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Alibaba"
3) (integer) 1
#等待讀取推送的信息
1) "message" #消息
2) "Alibaba" #頻道
3) "hello,boy" #消息的具體內(nèi)容
1) "message"
2) "Alibaba"
3) "life"
發(fā)送端:
46.154.150.53:6379> PUBLISH Alibaba "hello,boy" #發(fā)布者發(fā)布消息到頻道
(integer) 1
46.154.150.53:6379> PUBLISH Alibaba "life"
(integer) 1
原理
Redis是使用C實(shí)現(xiàn)的刃泡,通過分析Redis源碼里的pubsud.c文件铺峭,了解發(fā)布和訂閱機(jī)制的底層實(shí)現(xiàn)墓怀,籍此加深對Redis的理解。
Redis 通過PUBLISH卫键、SUBSCRIBE 和PSUBSCRIBE等命令實(shí)現(xiàn)發(fā)布和訂閱功能傀履。
通過SUBSCRIBE 命令訂閱某頻道后,redis-server里維護(hù)了一個(gè)字典莉炉,字典的鍵就是一個(gè)個(gè)channel(頻道)钓账,而字典的值則是一個(gè)鏈表,鏈表中保存了所有訂閱這個(gè)channel的客戶端絮宁。SUBSCRIBE命令的關(guān)鍵梆暮,就是將客戶端添加到給定channel的訂閱鏈表中。
通過PUBLISH命令向訂閱者發(fā)送消息绍昂,redis-server會(huì)使用給定的頻道作為鍵啦粹,在它所維護(hù)的channel字典中查找記錄了訂閱這個(gè)頻道的所有客戶端的鏈表,遍歷這個(gè)鏈表窘游,將消息發(fā)布給所有訂閱者唠椭。
Pub/Sub從字面上理解就是發(fā)布( Publish )與訂閱(Subscribe ),在Redis中张峰,你可以設(shè)定對某一個(gè)key值進(jìn)行消息發(fā)布及消息訂閱泪蔫,當(dāng)一個(gè)key值上進(jìn)行了消息發(fā)布后,所有訂閱它的客戶端都會(huì)收到相應(yīng)的消息喘批。這一功能最明顯的用法就是用作實(shí)時(shí)消息系統(tǒng),比如普通的即時(shí)聊天铣揉,群聊等功能饶深。
Redis主從復(fù)制
概念
主從復(fù)制,是指將一臺(tái)Redis服務(wù)器的數(shù)據(jù)逛拱,復(fù)制到其他的Redis服務(wù)器敌厘。前者稱為主節(jié)點(diǎn)(master/leader),后者稱為從節(jié)點(diǎn)(slave/follower);數(shù)據(jù)的復(fù)制是單向的朽合,只能由主節(jié)點(diǎn)到從節(jié)點(diǎn)俱两。Master以寫為主,Slave以讀為主曹步。
默認(rèn)情況下宪彩,每臺(tái)Redis服務(wù)器都是主節(jié)點(diǎn);且一個(gè)主節(jié)點(diǎn)可以有多個(gè)從節(jié)點(diǎn)(或沒有從節(jié)點(diǎn)),但一個(gè)從節(jié)點(diǎn)只能有一個(gè)主節(jié)點(diǎn)讲婚。
主從復(fù)制的作用主要包括:
1尿孔、數(shù)據(jù)冗余︰主從復(fù)制實(shí)現(xiàn)了數(shù)據(jù)的熱備份,是持久化之外的一種數(shù)據(jù)冗余方式。
2活合、故障恢復(fù)︰當(dāng)主節(jié)點(diǎn)出現(xiàn)問題時(shí)雏婶,可以由從節(jié)點(diǎn)提供服務(wù),實(shí)現(xiàn)快速的故障恢復(fù);實(shí)際上是一種服務(wù)的冗余白指。
3留晚、負(fù)載均衡︰在主從復(fù)制的基礎(chǔ)上,配合讀寫分離告嘲,可以由主節(jié)點(diǎn)提供寫服務(wù)错维,由從節(jié)點(diǎn)提供讀服務(wù)(即寫Redis數(shù)據(jù)時(shí)應(yīng)用連接主節(jié)點(diǎn),讀Redis數(shù)據(jù)時(shí)應(yīng)用連接從節(jié)點(diǎn))状蜗,分擔(dān)服務(wù)器負(fù)載;尤其是在寫少讀多的場景下需五,通過多個(gè)從節(jié)點(diǎn)分擔(dān)讀負(fù)載,可以大大提高Redis服務(wù)器的并發(fā)量轧坎。
4宏邮、高可用(集群)基石︰除了上述作用以外,主從復(fù)制還是哨兵和集群能夠?qū)嵤┑幕A(chǔ)缸血,因此說主從復(fù)制是Redis高可用的基礎(chǔ).
一般來說蜜氨,要將Redis運(yùn)用于工程項(xiàng)目中,只使用一臺(tái)Redis是萬萬不能的捎泻,原因如下:
1涡相、從結(jié)構(gòu)上,單個(gè)Redis服務(wù)器會(huì)發(fā)生單點(diǎn)故障坐梯,并且一臺(tái)服務(wù)器需要處理所有的請求負(fù)載价卤,壓力較大;
2、從容量上闯狱,單個(gè)Redis服務(wù)器內(nèi)存容量有限煞赢,就算一臺(tái)Redis服務(wù)器內(nèi)存容量為256G,也不能將所有內(nèi)存用作Redis存儲(chǔ)內(nèi)存哄孤,—般來說照筑,單臺(tái)Redis最大使用內(nèi)存不應(yīng)該超過20G。
電商網(wǎng)站上的商品瘦陈,一般都是一次上傳凝危,無數(shù)次瀏覽的,說專業(yè)點(diǎn)也就是"多讀少寫"晨逝。
主從復(fù)制蛾默,讀寫分離!80%的情況下都是在進(jìn)行讀操作!減緩服務(wù)器的壓力!架構(gòu)中經(jīng)常使用!一主二從!
只要在公司中,主從復(fù)制就是必須要使用的咏花,因?yàn)樵谡鎸?shí)的項(xiàng)目中不可能單機(jī)使用Redis !
環(huán)境配置
只配置從庫趴生,不用配置主庫
46.154.150.53:6379> info replication #查看當(dāng)前庫的信息
# Replication
role:master #角色 master
connected_slaves:0 #沒有從機(jī)
master_replid:8ff1c6abcfd4dd8bb6621f5fee5c5162ba4dd377
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
復(fù)制3個(gè)配置文件阀趴,修改對應(yīng)的信息
1、端口
2苍匆、pid名字
3刘急、log文件名字
4、dump.rdb文件名
修改完畢之后浸踩,啟動(dòng)3個(gè)服務(wù)器叔汁,可以通過進(jìn)程信息查詢
配置過密碼的需要在從機(jī)的配置文件中修改
masterauth 密碼
一主二從
==默認(rèn)情況下,每臺(tái)Redis服務(wù)器都是主節(jié)點(diǎn);== 一般情況下只用配置從機(jī)就好了
一主(79)二從(80检碗,81)
redis-cli -h 47.101.150.57 -p 6380
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 # SLAVEOF host 6379
OK
127.0.0.1:6380> info replication
# Replication
role:slave #當(dāng)前角色是從機(jī)
master_host:127.0.0.1 #可以看到主機(jī)的信息
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:1603424493
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:a882caf1c2e8657e1b7edd438c2d7f3b4d7e0e1a
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
127.0.0.1:6380>
#在主機(jī)中查看
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2 #多了從機(jī)的配置
slave0:ip=127.0.0.1,port=6381,state=online,offset=336,lag=1
slave1:ip=127.0.0.1,port=6380,state=online,offset=336,lag=1
master_replid:c18fcb2b0f21e0a0691a232c71bb11f48ed6017f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:336
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:336
真實(shí)的主從配置應(yīng)該是在配置文件中配置据块,能夠保證永久性,以上的為命令配置折剃,是暫時(shí)的另假!
注意
主機(jī)可以寫,從機(jī)不能寫只能讀怕犁!主機(jī)中的所有信息和數(shù)據(jù)边篮,都會(huì)被從機(jī)保存
主機(jī)寫:
主機(jī)只能讀取內(nèi)容:
測試:主機(jī)斷開連接,從機(jī)依舊連接到主機(jī)奏甫,但是沒有寫操作戈轿,如果主機(jī)恢復(fù)運(yùn)行,從機(jī)依舊可以直接獲得主機(jī)寫的信息
如果是使用命令行來配置主從阵子,從機(jī)如果重啟了思杯,就會(huì)變回主機(jī),只要再次設(shè)置為從機(jī)挠进,依舊可以從主機(jī)中獲取值
復(fù)制原理
Slave啟動(dòng)成功連接到master 后會(huì)發(fā)送一個(gè)sync同步命令
Master接到命令色乾,啟動(dòng)后臺(tái)的存盤進(jìn)程,同時(shí)收集所有接收到的用于修改數(shù)據(jù)集命令领突,在后臺(tái)進(jìn)程執(zhí)行完畢之后杈湾,master將傳送整個(gè)數(shù)據(jù)文件到slave ,并完成一次完全同步。
全量復(fù)制︰而slave服務(wù)在接收到數(shù)據(jù)庫文件數(shù)據(jù)后攘须,將其存盤并加載到內(nèi)存中。增量復(fù)制:Master繼續(xù)將新的所有收集到的修改命令依次傳給slave殴泰,完成同步但是只要是重新連接master 于宙,一次完全同步(全量復(fù)制)將被自動(dòng)執(zhí)行
層層鏈路(級聯(lián)復(fù)制)
也能完成主從復(fù)制!
如果沒有主機(jī)了悍汛,可以手動(dòng)選擇一個(gè)老大
如果主機(jī)斷開了連接捞魁,可以使用==SLAVEOF no one== 使自己變成主機(jī),其他的節(jié)點(diǎn)就可以手動(dòng)連接到最新的這個(gè)主節(jié)點(diǎn)
哨兵模式
概述
主從切換技術(shù)的方法是∶當(dāng)主服務(wù)器宕機(jī)后离咐,需要手動(dòng)把一臺(tái)從服務(wù)器切換為主服務(wù)器谱俭,這就需要人工干預(yù)奉件,費(fèi)事費(fèi)力,還會(huì)造成一段時(shí)間內(nèi)服務(wù)不可用昆著。這不是一種推薦的方式县貌,更多時(shí)候,我們優(yōu)先考慮哨兵模式凑懂。Redis從2.8開始正式提供了Sentinel (哨兵)架構(gòu)來解決這個(gè)問題煤痕。
謀權(quán)篡位的自動(dòng)版,能夠后臺(tái)監(jiān)控主機(jī)是否故障接谨,如果故障了根據(jù)投票數(shù)自動(dòng)將從庫轉(zhuǎn)換為主庫摆碉。
哨兵模式是一種特殊的模式,首先Redis提供了哨兵的命令脓豪,哨兵是一個(gè)獨(dú)立的進(jìn)程巷帝,作為進(jìn)程,它會(huì)獨(dú)立運(yùn)行扫夜。其原理是哨兵通過發(fā)送命令楞泼,等待Redis服務(wù)器響應(yīng),從而監(jiān)控運(yùn)行的多個(gè)Redis實(shí)例历谍。
這里的哨兵有兩個(gè)作用:
- 通過發(fā)送命令现拒,讓Redis服務(wù)器返回監(jiān)控其運(yùn)行狀態(tài),包括主服務(wù)器和從服務(wù)器望侈。
- 當(dāng)哨兵監(jiān)測到master宕機(jī)印蔬,會(huì)自動(dòng)將slave切換成master,然后通過發(fā)布訂閱模式通知其他的從服務(wù)器脱衙,修改配置文件侥猬,讓它們切換主機(jī)。
然而一個(gè)哨兵進(jìn)程對Redis服務(wù)器進(jìn)行監(jiān)控捐韩,可能會(huì)出現(xiàn)問題退唠,為此,我們可以使用多個(gè)哨兵進(jìn)行監(jiān)控荤胁。各個(gè)哨兵之間還會(huì)進(jìn)行監(jiān)控瞧预,這樣就形成了多哨兵模式。
假設(shè)主服務(wù)器宕機(jī)仅政,哨兵1先檢測到這個(gè)結(jié)果垢油,系統(tǒng)并不會(huì)馬上進(jìn)failover過程,僅僅是哨兵1主觀的認(rèn)為主服務(wù)器不可用圆丹,這個(gè)現(xiàn)象成為主觀線滩愁。當(dāng)后面的哨兵也檢測到主服務(wù)器不可用,并且數(shù)量達(dá)到一定值時(shí)辫封,那么哨兵之間就會(huì)進(jìn)行一次投票硝枉,投票的結(jié)果由一個(gè)哨兵發(fā)起廉丽,進(jìn)行failover[故障轉(zhuǎn)移]操作。切換成功后妻味,就會(huì)通過發(fā)布訂閱模式正压,讓各個(gè)哨兵把自己監(jiān)控的從服務(wù)器實(shí)現(xiàn)切換主機(jī),這個(gè)過程稱為客觀下線弧可。
測試
一主二從
1蔑匣、配置哨兵配置文件==sentinel.conf==
#sentinel monitor 被監(jiān)控的主機(jī)名稱 host port 1
sentinel monitor myredis 127.0.0.1 6379 1
如果主從服務(wù)器設(shè)置密碼,需主從服務(wù)器密碼保持一致棕诵,否則哨兵機(jī)制會(huì)失敳昧肌!
sentinel auth-pass myredis 12345678 -主密碼校套,不設(shè)置的話不能動(dòng)態(tài)切換
數(shù)字 1 代表主機(jī)掛了价脾,哨兵投票讓從機(jī)接替成為主機(jī),票數(shù)最多的就會(huì)成為主機(jī)
2笛匙、啟動(dòng)哨兵
[root@deng bin]# redis-sentinel dconfig/sentinel.conf
13323:X 23 Oct 2020 20:31:19.928 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
13323:X 23 Oct 2020 20:31:19.928 # Redis version=6.0.8, bits=64, commit=00000000, modified=0, pid=13323, just started
13323:X 23 Oct 2020 20:31:19.928 # Configuration loaded
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.0.8 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 13323
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
13323:X 23 Oct 2020 20:31:19.929 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
13323:X 23 Oct 2020 20:31:19.929 # Sentinel ID is 010f244aef16ebf4f40798f0176a27a86c4e8c9d
13323:X 23 Oct 2020 20:31:19.929 # +monitor master myredis 127.0.0.1 6379 quorum 1
13323:X 23 Oct 2020 20:31:32.384 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
13323:X 23 Oct 2020 20:31:42.393 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
如果Master節(jié)點(diǎn)斷開了侨把,這個(gè)時(shí)候就會(huì)從從機(jī)中隨機(jī)選擇一個(gè)服務(wù)器(這里面有一個(gè)投票算法)
哨兵日志
如果主機(jī)此時(shí)回來了,只能歸并到新的主機(jī)下妹孙,當(dāng)做從機(jī)秋柄,這就是哨兵模式的規(guī)則
哨兵模式
優(yōu)點(diǎn):
1、哨兵集群蠢正,基于主從復(fù)制骇笔,所有主從配置的優(yōu)點(diǎn)它都有
2、主從可以切換嚣崭,故障可以轉(zhuǎn)移笨触,系統(tǒng)的可用性就會(huì)更好
3、哨兵模式就是主從復(fù)制的升級雹舀,手動(dòng)到自動(dòng)芦劣,更加健壯
缺點(diǎn):
1、Redis不好在線擴(kuò)容说榆,集群容量一旦到達(dá)上限虚吟,在線擴(kuò)容就十分麻煩
2、實(shí)現(xiàn)哨兵模式的配置其實(shí)較為麻煩签财,里面有很多選擇
哨兵模式的全部配置
# 配置端口,默認(rèn)26379
port 26379
# 以守護(hù)進(jìn)程模式啟動(dòng)
daemonize yes
# 日志文件名
logfile "sentinel_26379.log"
# 存放備份文件以及日志等文件的目錄
dir "/opt/redis/data"
# sentinel monitor <master-name> <master-ip> <master-port> <quorum>
# master-name 監(jiān)控主節(jié)點(diǎn)的名稱稍味,只能由字母A-z、0-9荠卷、.、-烛愧、_ 這些符號(hào)組成
# quorum 配置多少個(gè)sentinel哨兵同意認(rèn)為Master主節(jié)點(diǎn)失聯(lián)油宜,那么這時(shí)客觀上認(rèn)為主節(jié)點(diǎn)失聯(lián)了
sentinel monitor mymaster 192.168.14.101 6379 2
# sentinel down-after-milliseconds <master-name> <milliseconds>
# 30秒ping不通主節(jié)點(diǎn)的信息掂碱,主觀認(rèn)為master宕機(jī)
sentinel down-after-milliseconds mymaster 30000
# sentinel auth-pass <master-name> <password>
# 當(dāng)在redis實(shí)例開啟了requirepass foobared 授權(quán)密碼,這樣所有連接redis的客戶端都要提供密碼慎冤,
# 設(shè)置哨兵連接主從的密碼疼燥,注意: 必須為主從設(shè)置一樣的密碼
sentinel auth-pass mymaster 123456
# sentinel parallel-syncs <master-name> <numslaves>
# 指定了在發(fā)生failover主備切換是最多可以有多少個(gè)slave同時(shí)對新的master進(jìn)行同步,
# 這個(gè)數(shù)字越小蚁堤,完成failover的時(shí)間越長醉者,但是數(shù)字越大,意味著越多的slave因?yàn)閞eplication不可用披诗,可以通過將這個(gè)值設(shè)為1撬即,來保證# # 每次只有一個(gè)slave處于不能處理命令請求的狀態(tài)
sentinel parallel-syncs mymaster 1
# 故障轉(zhuǎn)移開始,三分鐘內(nèi)沒有完成呈队,則認(rèn)為轉(zhuǎn)移失敗剥槐,默認(rèn)三分鐘
# sentinel failover-timeout <master-name> <milliseconds>
# 故障轉(zhuǎn)移的超時(shí)時(shí)間,可以用在以下方面
# 1宪摧、同一個(gè)sentinel對同一個(gè)master兩次failover之間的時(shí)間間隔粒竖;
# 2、當(dāng)一個(gè)slave從一個(gè)錯(cuò)誤的master那里同步數(shù)據(jù)開始計(jì)算時(shí)間几于,直到slave被糾正為向正確的master那里同步數(shù)據(jù)時(shí)間蕊苗;
# 3、當(dāng)想要取消一個(gè)正在進(jìn)行的failover所需要的時(shí)間沿彭;
# 4朽砰、當(dāng)failover時(shí),配置所有slaves指向新的master所需要的最大時(shí)間膝蜈,不過即使過了這個(gè)超時(shí)锅移,slaves依然會(huì)被正確配置為指向master,但是就不按parallel-syncs所配置的規(guī)則來了。
sentinel failover-timeout mymaster 180000
# sentinel notification-script <master-name> <script-path>
# 配置當(dāng)某一事件發(fā)生時(shí)所需要執(zhí)行的腳本饱搏,可以通過腳本來通知管理員非剃,例如當(dāng)系統(tǒng)運(yùn)行不正常時(shí),發(fā)送郵件通知相關(guān)人員
# 對于腳本的運(yùn)行結(jié)果有以下規(guī)則:
# 1推沸、若腳本返回1备绽,該腳本稍后將會(huì)被再次執(zhí)行,重復(fù)次數(shù)默認(rèn)為10鬓催;
# 2肺素、若腳本返回≥2,腳本將不會(huì)重復(fù)執(zhí)行宇驾;
# 3倍靡、如果腳本在執(zhí)行過程中收到系統(tǒng)終端信號(hào)被終止了蘸朋,則與返回值為1的行為相同邑茄;
# 4、一個(gè)腳本的最大執(zhí)行時(shí)間為60s,如果超過這個(gè)時(shí)間拷橘,腳本將會(huì)被一個(gè)叫SIGKILL信號(hào)終止途戒,之后重新執(zhí)行愈魏。
# 如通知型腳本:當(dāng)sentinel有任何告警幾倍的時(shí)間發(fā)生時(shí)(比如redis實(shí)例的主搭综、客觀失效等等),將會(huì)去調(diào)用這個(gè)腳本站辉,此時(shí)腳本將通過郵件等方式通知系統(tǒng)管理員呢撞,調(diào)用該腳本時(shí),將傳給腳本兩個(gè)參數(shù):一是時(shí)間類型饰剥,二是事件的描述殊霞。如果sentinel.conf配置文件配置了這個(gè)腳本路徑,那么必須保證這個(gè)腳本存在這個(gè)路徑捐川,并且是可執(zhí)行的脓鹃,否則sentinel無法正常啟動(dòng)成功。
sentinel notification-script mymaster /var/redis/notify.sh
# sentinel client-reconfig-script <master-name> <script-path>
# 客戶端重新配置主節(jié)點(diǎn)參數(shù)腳本古沥,當(dāng)一個(gè)master由于failover發(fā)生改變時(shí)瘸右,這個(gè)腳本將會(huì)被調(diào)用,通知相關(guān)的客戶端關(guān)于master地址已經(jīng)發(fā)生改變的信息
# 以下參數(shù)將會(huì)在調(diào)用腳本時(shí)傳給腳本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前state總是 failover
# role 是 leader 或者 observer 中的一個(gè)
# <from-ip> <from-port> <to-ip> <to-port> 是用來和舊的master 和新的 master(即舊的slave)通信的
# 具備通用信岩齿,多次調(diào)用
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
Redis緩存穿透和雪崩
Redis緩存的使用太颤,極大的提升了應(yīng)用程序的性能和效率,特別是數(shù)據(jù)查詢方面盹沈。但同時(shí)龄章,它也帶來了一些問題。其中乞封,最要害的問題做裙,就是數(shù)據(jù)的一致性問題,從嚴(yán)格意義上講肃晚,這個(gè)問題無解锚贱。如果對數(shù)據(jù)的一致性要求很高,那么就不能使用緩存关串。
另外的一些典型問題就是拧廊,緩存穿透、緩存雪崩和緩存擊穿晋修。目前吧碾,業(yè)界也都有比較流行的解決方案。
緩存穿透
概念
緩存穿透的概念很簡單墓卦,用戶想要查詢一個(gè)數(shù)據(jù)倦春,發(fā)現(xiàn)redis內(nèi)存數(shù)據(jù)庫沒有,也就是緩存沒有命中,于是向持久層數(shù)據(jù)庫查詢溅漾。發(fā)現(xiàn)也沒有山叮,于是本次查詢失敗。當(dāng)用戶很多的時(shí)候添履,緩存都沒有命中,于是都去請求了持久層數(shù)據(jù)庫脑又。這會(huì)給持久層數(shù)據(jù)庫造成很大的壓力,這時(shí)候就相當(dāng)于出現(xiàn)了緩存穿透席舍。
解決方案
緩存穿透
概念
緩存穿透的概念很簡單滑黔,用戶想要查詢一個(gè)數(shù)據(jù),發(fā)現(xiàn)redis內(nèi)存數(shù)據(jù)庫沒有,也就是緩存沒有命中『于是向持久層數(shù)據(jù)庫查詢璃氢。發(fā)現(xiàn)也沒有,于是本次查詢失敗层皱。當(dāng)用戶很多的時(shí)候草冈,緩存都沒有命中怎棱,于是都去請求了持久層數(shù)據(jù)庫。這會(huì)給持久層數(shù)據(jù)庫造成很大的壓力吩谦,這時(shí)候就相當(dāng)于出現(xiàn)了緩存穿透。
解決方案
布隆過濾器
布隆過濾器是一種數(shù)據(jù)結(jié)構(gòu)芭挽,對所有可能查詢的參數(shù)以hash形式存儲(chǔ)滑废,在控制層先進(jìn)行校驗(yàn),不符合則丟棄袜爪,從而避免了對底層存儲(chǔ)系統(tǒng)的查詢壓力
緩存空對象
當(dāng)存儲(chǔ)層不命中后蠕趁,即使返回的空對象也將其緩存起來,同時(shí)會(huì)設(shè)置一個(gè)過期時(shí)間辛馆,之后再訪問這個(gè)數(shù)據(jù)將會(huì)從緩存中獲取俺陋,保護(hù)了后端數(shù)據(jù)源;
但是這種方法會(huì)存在兩個(gè)問題:
1、如果空值能夠被緩存起來,這就意味著緩存需要更多的空間存儲(chǔ)更多的鍵腊状,因?yàn)檫@當(dāng)中可能會(huì)有很多的空值的鍵;
2诱咏、即使對空值設(shè)置了過期時(shí)間,還是會(huì)存在緩存層和存儲(chǔ)層的數(shù)據(jù)會(huì)有一段時(shí)間窗口的不一致缴挖,這對于需要保持一致性的業(yè)務(wù)會(huì)有影響袋狞。
緩存擊穿
概述
這里需要注意和緩存擊穿的區(qū)別,緩存擊穿映屋,是指一個(gè)key非常熱點(diǎn)硕并,在不停的扛著大并發(fā),大并發(fā)集中對這一個(gè)點(diǎn)進(jìn)行訪問秧荆,當(dāng)這個(gè)key在失效的瞬間,持續(xù)的大并發(fā)就穿破緩存埃仪,直接請求數(shù)據(jù)庫乙濒,就像在一個(gè)屏障上鑿開了一個(gè)洞。
當(dāng)某個(gè)key在過期的瞬間卵蛉,有大量的請求并發(fā)訪問颁股,這類數(shù)據(jù)一般是熱點(diǎn)數(shù)據(jù),由于緩存過期傻丝,會(huì)同時(shí)訪問數(shù)據(jù)庫來查詢最新數(shù)據(jù)甘有,并且回寫緩存,會(huì)導(dǎo)使數(shù)據(jù)庫瞬間壓力過大葡缰。
解決方案
設(shè)置熱點(diǎn)數(shù)據(jù)永不過期
從緩存層面來看亏掀,沒有設(shè)置過期時(shí)間,所以不會(huì)出現(xiàn)熱點(diǎn) key過期后產(chǎn)生的問題泛释。
加互斥鎖
分布式鎖∶使用分布式鎖滤愕,保證對于每個(gè)key同時(shí)只有一個(gè)線程去查詢后端服務(wù),其他線程沒有獲得分布式鎖的權(quán)限怜校,因此只需要等待即可间影。這種方式將高并發(fā)的壓力轉(zhuǎn)移到了分布式鎖,因此對分布式鎖的考驗(yàn)很大茄茁。
緩存雪崩
概念
緩存雪崩魂贬,是指在某一個(gè)時(shí)間段,緩存集中過期失效裙顽。Redis宕機(jī)!
產(chǎn)生雪崩的原因之一付燥,比如在寫本文的時(shí)候,馬上就要到雙十二零點(diǎn)锦庸,很快就會(huì)迎來一波搶購机蔗,這波商品時(shí)間比較集中的放入了緩存邑茄,假設(shè)緩存一個(gè)小時(shí)。那么到了凌晨一點(diǎn)鐘的時(shí)候慨蛙,這批商品的緩存就都過期了瘪贱。而對這批商品的訪問查詢,都落到了數(shù)據(jù)庫上牙言,對于數(shù)據(jù)庫而言酸钦,就會(huì)產(chǎn)生周期性的壓力波峰。于是所有的請求都會(huì)達(dá)到存儲(chǔ)層咱枉,存儲(chǔ)層的調(diào)用量會(huì)暴增卑硫,造成存儲(chǔ)層也會(huì)掛掉的情況。
其實(shí)集中過期蚕断,倒不是非常致命欢伏,比較致命的緩存雪崩,是緩存服務(wù)器某個(gè)節(jié)點(diǎn)宕機(jī)或斷網(wǎng)亿乳。因?yàn)樽匀恍纬傻木彺嫜┍溃欢ㄊ窃谀硞€(gè)時(shí)間段集中創(chuàng)建緩存葛假,這個(gè)時(shí)候障陶,數(shù)據(jù)庫也是可以頂住壓力的。無非就是對數(shù)據(jù)庫產(chǎn)生周期性的壓力而已侄刽。而緩存服務(wù)節(jié)點(diǎn)的宕機(jī)所计,對數(shù)據(jù)庫服務(wù)器造成的壓力是不可預(yù)知的,很有可能瞬間就把數(shù)據(jù)庫壓垮团秽。
解決方案
reids高可用
這個(gè)思想的含義是主胧,既然redis有可能掛掉叭首,那我多增設(shè)幾臺(tái)redis ,這樣一臺(tái)掛掉之后其他的還可以繼續(xù)工作踪栋,其實(shí)就是搭建的集群焙格。
限流降級
這個(gè)解決方案的思想是,在緩存失效后夷都,通過加鎖或者隊(duì)列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量眷唉。比如對某個(gè)key只允許一個(gè)線程查詢數(shù)據(jù)和寫緩存,其他線程等待囤官。
數(shù)據(jù)預(yù)熱
數(shù)據(jù)加熱的含義就是在正式部署之前冬阳,我先把可能的數(shù)據(jù)先預(yù)先訪問一遍,這樣部分可能大量訪問的數(shù)據(jù)就會(huì)加載到緩存中党饮。在即將發(fā)生大并發(fā)訪問前手動(dòng)觸發(fā)加載緩存不同的key肝陪,設(shè)置不同的過期時(shí)間,讓緩存失效的時(shí)間點(diǎn)盡量均勻刑顺。