1. 程式人生 > 其它 >680. 剪繩子//AcWing6//寒假打卡計劃

680. 剪繩子//AcWing6//寒假打卡計劃

Redis入門


歷史

  1. 優化資料結構和索引
  2. 檔案快取----通過IO流獲取比每次訪問資料庫效率略高,但是流量爆炸式增長時候,IO流也承受不了
  3. Memcached:通過再資料庫和資料訪問層之間再加上一層快取,第一次訪問時查詢資料庫,將結果儲存到快取,後續的查詢先檢查快取,若有直接拿去使用,效率顯著提高

早年MYISAM:表鎖(查詢一行資料將一張表鎖起來),十分影響效率

早些年Innodb:行鎖(每次查資料只鎖一行)

慢慢的就開始使用分庫表來解決寫的壓力

Nosql

Nosql=Not Only SQL

NOT Only Structured Query Language

關係型資料庫:行+列,同一個表下資料的結構是一樣的

非關係型資料庫:資料儲存沒有固定的格式,並且可以橫向擴充套件

Nosql泛指非關係型資料庫,

NoSQL特點

  1. 方便擴充套件(資料之間沒有關係,很好擴充套件)

  2. 大資料量高效能(Redis一秒可以寫8萬次,讀寫11萬次,NoSQL的快取記錄級,是一種細粒度的快取,效能會比較高)

  3. 資料型別是多樣性的(不需要先設計資料庫,隨去隨用)

  4. 傳統的RDBMS和NoSQL

    傳統的RDBMS(關係型資料庫)
    結構化組織
    SQL
    資料和關係都存再單獨的表中  row  col
    操作,資料定義語言
    嚴格的一致性
    基礎的食物
    
    NoSQL
    不僅僅是資料
    沒有固定的查詢語句
    鍵值對儲存,列儲存,文件儲存,圖形資料庫
    最終一致性
    CAP定理和BASE
    高效能,高可用,高擴充套件
    

    大資料時代的3v:主要描述問題的

    1. 海量的Velume
    2. 多樣Variety
    3. 實時Velocity

    大資料時代的3高:主要是對程式的要求

    1. 高併發
    2. 高可擴
    3. 高效能

    真正的公司中的實踐:NoSQL+RDBMS 一起使用才是最強的

    商品資訊
        一般存放在關係型資料庫:Mysql
    
    商品描述,評論
    文件型資料庫:MongoDB
    
    圖片
    分散式檔案系統:FastDFS
    淘寶:TFS
    Goolge:GFS
    Hadoop:HDFS
    阿里雲:oss
    
    
    # 商品關鍵字
        搜尋引擎:solr,elasticsearch
        阿里:Isearch,多隆
        
    #商品熱門的波段資訊
        記憶體資料庫:Redis,Memcache
        
    #商品交易,外部支付介面
        第三方應用
    
    

NoSQL的四大分類

  1. KV鍵值對

    1. 新浪:Redis
    2. 美團:Redis+Tair
    3. 阿里,百度:Redis+memcache
  2. 文件型資料庫(bson資料格式)

    1. MongoDB

      基於分散式檔案儲存的資料庫,c++編寫,用於處理大量的文件

      MongoDB是RDBMS和NoSQL的中間產品,MongoDB是非關係型資料庫中功能最豐富的

    2. ConthDB

  3. 列儲存資料庫

    1. Hbase
    2. 分散式檔案管理系統
  4. 圖關係資料庫

    用於廣告推廣,社交網路

    Neoconj,InfoGrid

Redis入門

概述

Redis(Remote Dictionary Server),遠端字典服務

是一個開源的使用ANSI C語言編寫,支援網路,可基於記憶體亦可持久化的日誌型,Key-Value資料庫,並提供多種語言的API

與memcached一樣,為了保證效率,資料都是快取在記憶體中,區別的是Redis會週期的把更新的資料寫入硬碟或者把修改操作寫入追加的記錄檔案,並且在此基礎上實現了master-slave(主從)同步

能幹什麼

  1. 記憶體儲存,持久化,記憶體是斷電即逝的,所以需要持久化
  2. 高效率,用於快取記憶體
  3. 釋出訂閱系統
  4. 地圖資訊分析
  5. 計時器,計數器
  6. 。。。。。

特性

  1. 多樣化的資料型別
  2. 持久化
  3. 叢集
  4. 事務

Redis具體

# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16

16個數據庫

預設使用第0個

使用 select n 切換資料庫

127.0.0.1:6379> config get database   #命令列檢視資料庫數量
(empty list or set)
127.0.0.1:6379> select 8  #切換資料庫
OK
127.0.0.1:6379[8]> dbsize   #檢視資料庫大小
(integer) 0
127.0.0.1:6379[8]> set name hou  #輸入資料
OK  
127.0.0.1:6379[8]> select 8
OK
127.0.0.1:6379[8]> get name   #不能獲取其他資料庫資料
"hou"
127.0.0.1:6379[8]> dbsize   #size與key相關
(integer) 1
127.0.0.1:6379[8]> select 0
OK
127.0.0.1:6379> flushall  #清空所有資料庫鍵值對
OK

Redis是單執行緒,基於記憶體操作

所以Redis的效能瓶頸不是cpu,而是機器記憶體和網路頻寬

Redis是基於C語言寫的

為什麼Redis這麼快

誤區:1. 高效能的伺服器都是多執行緒的

  1. 多執行緒一定比單執行緒效率高

核心:redis是將多有的資料放在記憶體中的,所以使用單執行緒操作效率是最高的

,不需要有多執行緒的上下文切換

對於記憶體系統來說,如果沒有上下文切換效率就是最高的,多次讀寫都在一個cpu上

1. 操作

基本資料型別

基本資料型別方法說明
Redis-key
EXISTS age判斷key是否存在
move age 1移除key到1號資料庫
expire name 10設定key過期時間
ttl name檢視key的過期剩餘時間
type age檢視key的型別
Stringget,set
append age age追加字串,如果key不存在就相當於set key
strlen age獲取字串的長度
incr views自增1
decr views自減1
incrby views 10設定步長,指定增量
decrby views 5
getrange key1 0 5擷取字串[0 ,5]
getrange key1 0 -1獲取所有字串
setrange key2 1 xs替換指定位置字串
setex key3 30 hello設定key3過期時間,30秒後失效
setnx mykey rredis不存在的話設定成功,存在設定失敗,分佈鎖中常用
mset k1 v1 k2 v2 k3 v3同時設定多個值
mget k1 k2 k3同時獲取多個值
msetnx k1 v1 k4 v4不存在設定失敗,證明msetnx為原子性操作
set user:1{name:houquanwei,age:3}#設定一個user:1 物件值為json字元來儲存一個物件

#這裡的key:user:{id}:{filed}
getset先get然後set
getset db mongodb如果存在,獲取原來的值(db),並設定新的值(mongodb)
Listlpush,rpush,lpop,rpop
lpush list one將一個值或者多個值放到頭部,左邊進到最右邊
lrange list 0 1左邊開始第0個第1個
rpush list right右邊進入
lpop list移除左邊的第一個
rpop list移除右邊的第一個
lindex list 1根據下標拿值 只能左邊
llen list列表長度
lrem list 1 222移除一個222值,精確匹配
lrem list 2 two移除2個two,批量操作
ltrim mylist 1 2擷取指定的長度,通過下標,截斷了就修改了列表
rpoplpush list mylist移除列表的最後一個元素,將他移動到新的列表中
lset將列表中指定下標的值替換成另一個
exists list判斷列表是否存在
lset list 0 item不存在就報錯,存在就替換
linsert list before world other把other插入world前面
linsert list after world object插入world後面
Setsadd,smembers,sismember
sadd myset hello向myset集合中新增hello
smembers myset檢視set值
sismember myset hello判斷myset中有沒有hello
scard myset獲取set值的個數
srem myset hou移除set集合中的指定元素
srandmember myset隨機抽取一個元素
srandmember myset 2隨機抽出指定個數的元素
spop myset隨機刪除
smove set2 set1 python將set2指定元素移到另一個set1,沒有就建立一個set1
sdiff set1 set2差集檢視不同的,左邊set
sinter set1 set2交集
sunion set1 set2並集
HashMap集合—key-
hset hash fields houset具體的key-value
hget hash fieldsget到hou
hmset hash field1 quan field1 qei f2 java設定多個key-value,qei頂掉quan,key不可重複
hmget hash field1 f2獲取多個
hmgetall hash獲取全部
hdel hash field1刪除對應的key-value通過key
hlen hash獲得hash的欄位數量(key-value)
hexists hash f2判斷指定欄位是否存在
hkeys hash只獲取keys
hvals hash只獲取values
hincrby hash f3 1指定增量
hsetnx hash f4 hello如果不存在則可以設定,存在則不能
Zsetscore
zadd set1 1 on新增
zadd set1 2 to 3 ok新增多個
zrange set1 0 -1輸出
zrangebyscore set1 -inf +inf排序從小到大,從負無窮到正無窮
zrangebyscore set1 -inf +inf withscores排序帶上score
zrangebyscore salary -inf 3800 withscores排序配合score
zrevrange set1 0 -1從大到小
zrem set1 ok移除有序集合中的指定元素
zcard set1獲取有序集合中的個數
zcount salary 100 5000判斷100 5000之間的值有多少

特殊資料型別

特殊資料型別方法說明
Geospatial地理位置,有序集合zset儲存
geoadd china:city 121.47 31.23 shanghai新增城市資訊,超過經緯度就會報錯
geopos china:city beijing獲得指定城市的經緯度
geodist china:city shanghai shenz km返回兩個地區的距離(千米單位),預設米單位
georadius china:city 120 25 1000 km withcoord withdist以(120 25)為範圍方圓1000km的城市的經度和維度
GEORADIUSBYMEMBER china:city shanghai 1000 km以上海為中心
geohash china:city shanghai返回一個或多個位置元素的Geohash表示。使用Geohash位置52點整數編碼
zrange china:city 0 -1檢視
zrem china:city shanghai刪除
Hyperloglog(基數統級)pfadd底層String
pfadd myp a b c d e f g新增
pfcount myp統計myp基數數量
pfmerge myp mypp將多個合併
Bitmaps資訊狀態只有 0 和 1 兩個狀態的結果就可以用他
setbit sign 0 1在sign上的第0為設定為1
setbit sign 1 1在sign上的第1為設定為1
setbit sign 2 0在sign上的第2為設定為0
getbit sign 2獲得sign第二個的值
bitcount sign獲得 sign為1的個數

高階

Redis 釋出訂閱(pub/sub)訊息通訊模式
subscribe hou訂閱一個頻道
publish hou hello釋出者釋出資訊到指定頻道
PUBSUB subcommand [argument[argument]]檢視訂閱與釋出系統狀況
PSUBSCRIBE pattern [pattern..]訂閱一個或多個符合給定模式的頻道
PUNSUBSCRIBE pattern [pattern..]退訂一個或多個符合給定模式的頻道。

2.Redis五大資料型別

Redis是一個開源(BSD許可),記憶體儲存的資料結構伺服器,可用作資料庫快取記憶體訊息佇列代理。它支援、、、、,,等資料型別。內建複製、、LRU收回、以及不同級別磁碟持久化功能,同時通過Redis Sentinel提供高可用,通過Redis Cluster提供自動。

1.Redis-key

在redis中無論什麼資料型別,在資料庫中都是以key-value形式儲存,通過進行對Redis-key的操作,來完成對資料庫中資料的操作。

127.0.0.1:6379> set age 1
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> EXISTS age  #判斷key是否存在
(integer) 1
127.0.0.1:6379> move age 1  #移除key到1號資料庫
(integer) 1
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "age"
127.0.0.1:6379[1]> move age 0
(integer) 1
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> exists name 10  
(integer) 1
127.0.0.1:6379> ttl name
(integer) -1
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> keys name
1) "name"
127.0.0.1:6379> expire name 10  #設定key過期時間
(integer) 1
127.0.0.1:6379> ttl name   #檢視key的過期剩餘時間
(integer) 6
127.0.0.1:6379> ttl name
(integer) 1
127.0.0.1:6379> ttl  name
(integer) -2  #表示過期
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> type age  #檢視key的型別
string

關於ttl命令

Redis的key,通過TTL命令返回key的過期時間,一般來說有3種:

  1. 當前key沒有設定過期時間,所以會返回-1.
  2. 當前key有設定過期時間,而且key已經過期,所以會返回-2.
  3. 當前key有設定過期時間,且key還沒有過期,故會返回key的正常剩餘時間.

關於重新命名RENAMERENAMENX

  • RENAME key newkey修改 key 的名稱
  • RENAMENX key newkey僅當 newkey 不存在時,將 key 改名為 newkey 。

2. String

get ,set 略過

127.0.0.1:6379> type age     #檢視型別
string
127.0.0.1:6379> append age age  #追加字串,如果key不存在就相當於set key
(integer) 4
127.0.0.1:6379> get age
"1age"
127.0.0.1:6379> strlen age   #獲取字串的長度
(integer) 4
127.0.0.1:6379> append age name
(integer) 8
127.0.0.1:6379> get age
"1agename"
127.0.0.1:6379>
#############################################
步長

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> get views
"1"
127.0.0.1:6379> incrby views 10   #設定步長,指定增量
(integer) 11
127.0.0.1:6379> decrby views 5    #與上邊相反
(integer) 6

#################################
#range 字串範圍
127.0.0.1:6379> set key1 hello,houquanwei
OK
127.0.0.1:6379> get key1
"hello,houquanwei"
127.0.0.1:6379> getrange key1 0 5   #擷取字串【】
"hello,"
127.0.0.1:6379> getrange key1 0 -1   #獲取所有的字串  和get key一樣
"hello,houquanwei"
127.0.0.1:6379> 

###############################
#替換
127.0.0.1:6379> set key2 abcdefg
OK
127.0.0.1:6379> get key2
"abcdefg"
127.0.0.1:6379> setrange key2 1 xs #替換指定位置的字串 
(integer) 7
127.0.0.1:6379> get key2
"axsdefg"
127.0.0.1:6379> 
#########################
#setex(set with expire) #設定過期時間
#setnx(set if not exist) #不存在設定  (分散式鎖中常常使用)
127.0.0.1:6379> setex key3 30 hello   #設定key3過期時間,30秒後失效
OK
127.0.0.1:6379> ttl key3
(integer) 26
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> setnx mykey rredis  #不存在的話設定成功
(integer) 1
127.0.0.1:6379> ttl key3
(integer) 3
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> keys *
1) "key2"
2) "mykey"
3) "key1"
127.0.0.1:6379> setnx mykey ok #存在了建立失敗
(integer) 0
127.0.0.1:6379> 
################################
#mset
#mget
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3  #同時設定多個值
OK
127.0.0.1:6379> mget k1 k2 k3  #同時獲取多個值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> keys *
1) "k2"
2) "k3"
3) "k1"
127.0.0.1:6379> msetnx k1 v1 k4 v4  #不存在設定失敗,證明msetnx為原子性操作,
(integer) 0
127.0.0.1:6379> get k4
(nil)
#物件
set user:1{name:houquanwei,age:3}  #設定一個user:1 物件值為json字元來儲存一個物件

#這裡的key:user:{id}:{filed}

127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "2"

###########################
#getset  先get然後set
127.0.0.1:6379> getset db redis  #如果不存在值,則返回nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb  #如果存在,獲取原來的值,並設定新的值
"redis"
127.0.0.1:6379> get db
"mongodb"

資料結構是相同的!

String 類似的使用場景:value除了是我們的字串還可以是我們的數字

  1. 計數器
  2. 統計多單位的數量
  3. 粉絲數

3.List

Redis列表是簡單的字串列表,按照插入順序排序。你可以新增一個元素到列表的頭部(左邊)或者尾部(右邊)

一個列表最多可以包含 232 - 1 個元素 (4294967295, 每個列表超過40億個元素)。

在redis裡面,我們可以把list玩成,棧,佇列,阻塞佇列

所有的list命令都是用l開頭的[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-VPvbIltc-1597890996518)(狂神說 Redis.assets/image-20200813114255459.png)]

正如圖Redis中List是可以進行雙端操作的,所以命令也就分為了LXXX和RLLL兩類,有時候L也表示List例如LLEN

127.0.0.1:6379> keys *
(empty list or set)
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> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1  #左邊開始第0個第1個
1) "three"
2) "two"
127.0.0.1:6379> rpush list right   #右邊進入
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
#########################33
#lpop
#rpop
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> lpop list  #移除左邊的第一個
"one"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379> rpop list  #移除右邊的第一個
"right"
127.0.0.1:6379> lrange list  0 -1
1) "two"
2) "one"
#################################
#lindex
127.0.0.1:6379> lindex list 1  #根據下標拿值 只能左邊
"one"
#######################
#llen  列表長度
127.0.0.1:6379> lpush list two
(integer) 1
127.0.0.1:6379> lpush list three
(integer) 2
127.0.0.1:6379> llen list
(integer) 2
###############################33
#lrem  移除指定的值

127.0.0.1:6379> lrange list 0 -1
1) "222"
2) "three"
3) "two"
127.0.0.1:6379> lrem list 1 222   #移除一個222值,精確匹配
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "two"
3) "three"
4) "two"
127.0.0.1:6379> lrem list 2 two  #移除2個two
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
#####################################
trim 修剪 : list  截斷
127.0.0.1:6379> rpush mylist hello0
(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> rpush mylist hello3
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1   
1) "hello0"
2) "hello1"
3) "hello2"
4) "hello3"
127.0.0.1:6379> ltrim mylist 1 2 #擷取指定的長度,通過下標,截斷了就修改了列表
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
########################################
#rpoplpush  移除列表的最後一個元素,將他移動到新的列表中
127.0.0.1:6379> rpush list one
(integer) 1
127.0.0.1:6379> rpush list two
(integer) 2
127.0.0.1:6379> rpush list three
(integer) 3
127.0.0.1:6379> rpoplpush list mylist
"three"
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "two"
127.0.0.1:6379> lrange mylist 0 -1
1) "three"
#############################################
#lset   將列表中指定下標的值替換成另一個
127.0.0.1:6379> exists list  #判斷列表是否存在
(integer) 0
127.0.0.1:6379> lset list 0 item  #不存在就報錯
(error) ERR no such key
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lrange list 0 0
1) "one"
127.0.0.1:6379> lset list 0 item  #存在就替換
OK
127.0.0.1:6379> lrange list 0 0
1) "item"
127.0.0.1:6379> lset list 1 other  #不存在這個值就報錯
(error) ERR index out of range
############################################
#linsert  將某個具體的value插入到列表中某個元素的前面或後面
127.0.0.1:6379> lpush list hello
(integer) 1
127.0.0.1:6379> lpush list world
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "world"
2) "hello"
127.0.0.1:6379> linsert list before world other  #插入world前面
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "other"
2) "world"
3) "hello"
127.0.0.1:6379> linsert list after world object  #插入world後面
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "other"
2) "world"
3) "object"
4) "hello"
  • list實際上是一個連結串列,before Node after , left, right 都可以插入值
  • 如果key不存在,則建立新的連結串列
  • 如果key存在,新增內容
  • 如果移除了所有值,空連結串列,也代表不存在
  • 在兩邊插入或者改動值,效率最高!修改中間元素,效率相對較低

應用:

訊息排隊!訊息佇列(Lpush Rpop),棧(Lpush Lpop)

4.Set

Redis的Set是string型別的無序集合。集合成員是唯一的,這就意味著集合中不能出現重複的資料。

Redis 中 集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度都是O(1)。

集合中最大的成員數為 232 - 1 (4294967295, 每個集合可儲存40多億個成員)。

Redis hash 是一個string型別的field和value的對映表,hash特別適合用於儲存物件。

Set就是一種簡化的Hash,只變動key,而value使用預設值填充。可以將一個Hash表作為一個物件進行儲存,表中存放物件的資訊

##############################3
#sadd  像集合中新增元素
#smembers 檢視指定set 的值
#sismember 判斷一個值在不在set中
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> sadd myset hello
(integer) 1
127.0.0.1:6379> sadd myset world
(integer) 1
127.0.0.1:6379> sadd myset hou
(integer) 1
127.0.0.1:6379> smembers myset 
1) "hello"
2) "world"
3) "hou"
127.0.0.1:6379> sismember myset hello  #判斷
(integer) 1
127.0.0.1:6379> sismember myset ll
(integer) 0
####################################
127.0.0.1:6379> scard myset #獲取set中的個數
(integer) 3
################################
127.0.0.1:6379> srem myset hou  #移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> scard myset
(integer) 2
127.0.0.1:6379> smembers myset
1) "hello"
2) "world"
################################
127.0.0.1:6379> smembers myset  #查詢所有的元素
1) "hello"
2) "springboot"
3) "world"
127.0.0.1:6379> srandmember myset  #隨機抽取一個元素
"hello"
127.0.0.1:6379> srandmember myset
"hello"
127.0.0.1:6379> srandmember myset 2  #隨機抽出指定個數的元素
1) "hello"
2) "world"
127.0.0.1:6379> srandmember myset
"world"
##########################################
# 隨機刪除key
127.0.0.1:6379> smembers myset
1) "hello"
2) "springboot"
3) "world"
127.0.0.1:6379> spop myset
"springboot"
127.0.0.1:6379> smembers myset
1) "hello"
2) "world"
127.0.0.1:6379> spop myset  #隨機刪除
"world"
127.0.0.1:6379> smembers myset
1) "hello"
##################################
#將指定的key移動到另一個set
127.0.0.1:6379> sadd set1 hello
(integer) 1
127.0.0.1:6379> sadd set1 world
(integer) 1
127.0.0.1:6379> sadd set1 one
(integer) 1
127.0.0.1:6379> sadd set2 java
(integer) 1
127.0.0.1:6379> sadd set2 python
(integer) 1
127.0.0.1:6379> smove set2 set java  #如果沒有set就建立一個
(integer) 1
127.0.0.1:6379> smembers set1
1) "hello"
2) "one"
3) "world"
127.0.0.1:6379> smembers set
1) "java"
127.0.0.1:6379> smove set2 set1 python  #將指定元素移到另一個set
(integer) 1
127.0.0.1:6379> smembers set1
1) "python"
2) "hello"
3) "one"
4) "world"
127.0.0.1:6379> smembers set2
(empty list or set)
#########################################3
#共同關注
#數字集合類:差集,交集,並集
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  #差集檢視不同的,左邊set
1) "a"
2) "b"
127.0.0.1:6379> sinter key1 key2 #交集
1) "c"
127.0.0.1:6379> sunion key1 key2 #並集
1) "c"
2) "b"
3) "a"
4) "e"
5) "d"

5.Hash

Map集合—key-

127.0.0.1:6379> hset hash field1 hou #set具體的key-value
(integer) 1
127.0.0.1:6379> hget hash field1
"hou"
127.0.0.1:6379> hset hash f2 quan
(integer) 1
127.0.0.1:6379> hmset hash field1 quan field1 qei f2 #設定多個key-value

java
OK
127.0.0.1:6379> hget hsah f2
(nil)
127.0.0.1:6379> hget hash f2
"java"
127.0.0.1:6379> hmget hash f2
1) "java"
127.0.0.1:6379> hmget hash field1 f2  #獲取多個
1) "qei"
2) "java"
127.0.0.1:6379> hgetall hash  #獲取全部
1) "field1"
2) "qei"
3) "f2"
4) "java"
#############################
127.0.0.1:6379> hgetall hash
1) "field1"
2) "qei"
3) "f2"
4) "java"
127.0.0.1:6379> hdel hash field1  #刪除對應的key-value通過key
(integer) 1
127.0.0.1:6379> hgetall hash
1) "f2"
2) "java"
###############################
127.0.0.1:6379> hgetall hash
1) "f2"
2) "java"
127.0.0.1:6379> hlen hash  #獲得hash的欄位數量
(integer) 1
127.0.0.1:6379> hset hash f1 hou
(integer) 1
127.0.0.1:6379> hlen hash
(integer) 2
127.0.0.1:6379> hgetall hash
1) "f2"
2) "java"
3) "f1"
4) "hou"
##########################
127.0.0.1:6379> hexists hash f1  #判斷指定欄位是否存在
(integer) 1
127.0.0.1:6379> hexists hash f3
(integer) 0
##################### 
127.0.0.1:6379> hkeys hash #只獲取fields
1) "f2"
2) "f1"
127.0.0.1:6379> hvals hash #只獲取values
1) "java"
2) "hou"
##############################
127.0.0.1:6379> hset hash f3 5
(integer) 1
127.0.0.1:6379> hincrby hash f3 1  #指定增量
(integer) 6
127.0.0.1:6379> hget hash f3
"6"
127.0.0.1:6379> hsetnx hash f4 hello  #如果不存在則可以設定,存在則不能
(integer) 1

Hash變更的資料user name age,尤其是使用者資訊之類的,經常變動的資訊!Hash更適合於物件的儲存,Sring更加適合字串儲存

6.Zset(有序集合)

不同的是每個元素都會關聯一個double型別的分數(score)。redis正是通過分數來為集合中的成員進行從小到大的排序。

score相同:按字典順序排序

有序集合的成員是唯一的,但分數(score)卻可以重複。

127.0.0.1:6379> zadd set1 1 one
(integer) 1
127.0.0.1:6379> zadd set1 2 two 3 three # 新增多個
(integer) 2

127.0.0.1:6379> zrange set1 0 -1
1) "one"
2) "two"
3) "three"
###############################3
排序
127.0.0.1:6379> zadd salary 5000 zhangsan 2500 xiahong 3600 ming
(integer) 3
127.0.0.1:6379> zrangebyscore salary -inf +inf  #排序從小到大,從負無窮到正無窮
1) "xiahong"
2) "ming"
3) "zhangsan"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores  #排序帶上score
1) "xiahong"
2) "2500"
3) "ming"
4) "3600"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> zrangebyscore salary -inf 3800 withscores  #排序配合score範圍
1) "xiahong"
2) "2500"
3) "ming"
4) "3600"
127.0.0.1:6379> zrevrange salary 0 -1  #從大到小
1) "zhangsan"
2) "xiahong"
################################
移除 rem
127.0.0.1:6379> zrange salary 0 -1
1) "xiahong"
2) "ming"
3) "zhangsan"
127.0.0.1:6379> zrem salary ming  #移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "xiahong"
2) "zhangsan"
127.0.0.1:6379> zcard salary  #獲取有序集合中的個數
(integer) 2
127.0.0.1:6379> zcount salary 100 5000  #判斷100 5000之間的值有多少
(integer) 2
###########################

應用案例:

  • set排序 儲存班級成績表 工資表排序!
  • 普通訊息,1.重要訊息 2.帶權重進行判斷
  • 排行榜應用實現,取Top N測試

3.三種特殊資料型別

1.geospatial地理位置

附件的人,打車距離

Redis在geo的Redis3.2就推出了!這個功能可以推算地理位置的資訊,兩個人之間的距離

使用經緯度定位地理座標並用一個有序集合zset儲存,所以zset命令也可以使用

  1. GEOADD
  2. GEODIST
  3. GEOHASH
  4. GEOPOS
  5. GEORADIUS
  6. GEOPADIUSBYMEMBER

有效經緯度

  • 有效的經度從-180度到180度。
  • 有效的緯度從-85.05112878度到85.05112878度

指定單位的引數 unit 必須是以下單位的其中一個:

  • m 表示單位為米。
  • km 表示單位為千米。
  • mi 表示單位為英里。
  • ft 表示單位為英尺。

關於GEORADIUS的引數

通過georadius就可以完成 附近的人功能

withcoord:帶上座標

withdist:帶上距離,單位與半徑單位相同

COUNT n : 只顯示前n個(按距離遞增排序)

#########################3
#兩級無法新增,一般直接通過Java程式直接匯入
# 引數 key 值(longitud(經度) latitude(緯度) member)
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 160.50 29.53 choongqin
(integer) 1
127.0.0.1:6379> geoadd china:city 114.05 22.5 shenz
(integer) 1
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzou 108.96 34.26 xian
(integer) 2
#超過有效的精度維度就會報錯
127.0.0.1:6379> geoadd china:city 39.90 116.4 hangzou
(error) ERR invalid longitude,latitude pair 39.900000,116.400000
##########################
#geopos  獲取集合中的一個/多個成員座標
127.0.0.1:6379> geopos china:city beijing #獲得指定城市的經度緯度
1) 1) "116.39999896287918"
   2) "39.900000091670925"
127.0.0.1:6379> geopos china:city shanghai shenz
1) 1) "121.47000163793564"
   2) "31.229999039757836"
2) 1) "114.04999762773514"
   2) "22.500001138003192"
##################################3
#geodist  返回兩個給定位置之間的距離。預設以米作為單位
127.0.0.1:6379> geodist china:city shanghai shenz  
"1217731.4459"
127.0.0.1:6379> geodist china:city shanghai shenz km #千米為單位
"1217.7314"
###########################
#georadius   以給定的經緯度為中心, 返回集合包含的位置元素當中, 與中心的距離不超過給定最大距離的所有位置元素
#GEORADIUSBYMEMBER  與上邊一樣只不過是城市
127.0.0.1:6379> georadius china:city 120 25 1000 km
1) "shenz"
2) "hangzou"
3) "shanghai"
127.0.0.1:6379> georadius china:city 120 25 1000 km withcoord withdist  #以(120 25)為範圍方圓1000km的城市的經度和維度
1) 1) "shenz"
   2) "666.4144"
   3) 1) "114.04999762773514"
      2) "22.500001138003192"
2) 1) "hangzou"
   2) "583.0388"
   3) 1) "120.16000002622604"
      2) "30.240000322949022"
3) 1) "shanghai"
   2) "707.7597"
   3) 1) "121.47000163793564"
      2) "31.229999039757836"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 1000 km   #以上海為中心
1) "hangzou"
2) "shanghai"
#############################
#geohash  返回一個或多個位置元素的Geohash表示。使用Geohash位置52點整數編碼。
127.0.0.1:6379> geohash china:city shanghai
1) "wtw3sj5zbj0"   #獲取成員經緯座標的geohash表示
################################
127.0.0.1:6379> zrange china:city 0 -1  #檢視
1) "xian"
2) "shenz"
3) "hangzou"
4) "shanghai"
5) "beijing"
6) "choongqin"
127.0.0.1:6379> zrem china:city shanghai #刪除
(integer) 1
127.0.0.1:6379> zrange china:city  0 -1
1) "xian"
2) "shenz"
3) "hangzou"
4) "beijing"
5) "choongqin"

2. Hyperloglog(基數統計)

Redis HyperLogLog 是用來做基數統計的演算法,HyperLogLog 的優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定的、並且是很小的。

花費 12 KB 記憶體,就可以計算接近 2^64 個不同元素的基數。

因為 HyperLogLog 只會根據輸入元素來計算基數,而不會儲存輸入元素本身,所以 HyperLogLog 不能像集合那樣,返回輸入的各個元素。

其底層使用string資料型別

基數:資料集中不重複的元素的個數

127.0.0.1:6379> pfadd myp a b c d e f g  # 新增 
(integer) 1
127.0.0.1:6379> type myp  #檢視型別
string
127.0.0.1:6379> pfcount myp  #統計myp基數數量
(integer) 7
127.0.0.1:6379> pfadd myp e f g k z  m f g
(integer) 1
127.0.0.1:6379> pfcount myp
(integer) 10
127.0.0.1:6379> pfadd mypp q e r y u i o
(integer) 1
127.0.0.1:6379> pfmerge myp mypp  #將多個合併
OK
127.0.0.1:6379> pfcount myp
(integer) 16

3. Bitmaps

使用位儲存,資訊狀態只有 0 和 1 兩個狀態的結果就可以用他

Bitmap是一串連續的2進位制數字(0或1),每一位所在的位置為偏移(offset),在bitmap上可執行AND,OR,XOR,NOT以及其它位操作。

127.0.0.1:6379> setbit sign 0 1  #在sign上的第0為設定為1
(integer) 0
127.0.0.1:6379> setbit sign 1 1  #第1個設定為1
(integer) 0
127.0.0.1:6379> setbit sign 2 0  #第2個為0
(integer) 0
127.0.0.1:6379> type sign  #sign型別
string
127.0.0.1:6379> getbit sign 2  #獲得sign第二個的值
(integer) 0
127.0.0.1:6379> bitcount sign  #獲得 sign為1的個數
(integer) 2

事務

Redis的單條命令是保證原子性的,但是redis事務不能保證原子性

Redis事務本質:一組命令的集合。

----------------- 佇列 set set set 執行 -------------------

事務中每條命令都會被序列化,執行過程中按順序執行,不允許其他命令進行干擾。

  • 一次性
  • 順序性
  • 排他性

  1. Redis事務沒有隔離級別的概念
  2. 所有命令都在事務中,並沒有被直接執行,只有發起執行命令的時候才會執行!EXec
  3. Redis單條命令是保證原子性的,但是事務不保證原子性!

Redis事務操作過程

  • 開啟事務(multi
  • 命令入隊
  • 執行事務(exec
127.0.0.1:6379> multi  #開啟事務
OK
127.0.0.1:6379> set k1 v1  #命令
QUEUED
127.0.0.1:6379> get k1   #命令
QUEUED
127.0.0.1:6379> set k2 v2   #命令
QUEUED
127.0.0.1:6379> set k3 v3   #命令
QUEUED
127.0.0.1:6379> exec   #執行命令
1) OK
2) "v1"
3) OK
4) OK
###########################3
#事務取消:discard
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 k4 v4
QUEUED
127.0.0.1:6379> discard  
OK
127.0.0.1:6379> get k4  #事務取消了所以k4沒有執行
(nil)

事務錯誤

程式碼語法錯誤(編譯時異常)所有的命令都不執行,命令使用錯誤

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> error k1 # 這是一條語法錯誤命令
(error) ERR unknown command `error`, with args beginning with: `k1`, # 會報錯但是不影響後續命令入隊 
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors. # 執行報錯
127.0.0.1:6379> get k1 
(nil) # 其他命令並沒有被執行

程式碼邏輯錯誤 (執行時異常) **其他命令可以正常執行 ** >>> 所以不保證事務原子性

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> INCR k1 # 這條命令邏輯錯誤(對字串進行增量)
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range # 執行時報錯
4) "v2" # 其他命令正常執行

# 雖然中間有一條命令報錯了,但是後面的指令依舊正常執行成功了。
# 所以說Redis單條指令保證原子性,但是Redis事務不能保證原子性。

監控

悲觀鎖:

  • 很悲觀,認為什麼時候都會出現問題,無論做什麼都會加鎖

樂觀鎖:

  • 很樂觀,認為什麼時候都不會出現問題,所以不會上鎖!更新資料的時候去判斷一下,在此期間是否有人修改過這個資料
  • 獲取version
  • 更新的時候比較version

使用watch key監控指定資料,相當於樂觀鎖加鎖。

正常執行

127.0.0.1:6379> set money 100 # 設定餘額:100
OK
127.0.0.1:6379> set use 0 # 支出使用:0
OK
127.0.0.1:6379> watch money # 監視money (上鎖)
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379> exec # 監視值沒有被中途修改,事務正常執行
1) (integer) 80
2) (integer) 20
123456789101112131415

測試多執行緒修改值,使用watch可以當做redis的樂觀鎖操作(相當於getversion)

我們啟動另外一個客戶端模擬插隊執行緒。

執行緒1:

127.0.0.1:6379> watch money # money上鎖
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379> 	# 此時事務並沒有執行
123456789

模擬執行緒插隊,執行緒2:

127.0.0.1:6379> INCRBY money 500 # 修改了執行緒一中監視的money
(integer) 600
12

回到執行緒1,執行事務:

127.0.0.1:6379> EXEC # 執行之前,另一個執行緒修改了我們的值,這個時候就會導致事務執行失敗
(nil) # 沒有結果,說明事務執行失敗

127.0.0.1:6379> get money # 執行緒2 修改生效
"600"
127.0.0.1:6379> get use # 執行緒1事務執行失敗,數值沒有被修改
"0"
1234567

解鎖獲取最新值,然後再加鎖進行事務。

unwatch進行解鎖。

注意:每次提交執行exec後都會自動釋放鎖,不管是否成功

4.Jedis

使用Java來操作Redis,Jedis是Redis官方推薦使用的Java連線redis的客戶端。

  1. 匯入依賴
<!--匯入jredis的包-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.2.0</version>
</dependency>
<!--fastjson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.70</version>
</dependency>
  1. 編碼測試

    public class testjedis {
        public static void main(String[] args) {
            //new一個jedis物件
            Jedis jedis = new Jedis("127.0.0.1",6379);
            //jedis命令就是之前學的
            System.out.println(jedis.ping());
        }
    }
    

    事務

    public static void main(String[] args) {
            Jedis jedis = new Jedis("127.0.0.1", 6379);
         JSONObject jsonObject = new JSONObject();
            jsonObject.put("hello","world");
            jsonObject.put("name","hou");
            jedis.flushDB();
            //開啟事務
            Transaction multi = jedis.multi();
            String s = jsonObject.toJSONString();
    
           try{
               multi.set("user1",s);
               multi.set("user2",s);
               int i=1/0; //程式碼丟擲異常執行失敗
               multi.exec();//執行事務
           }catch (Exception e){
               multi.discard();  //放棄事務
               e.printStackTrace();
           }
           finally {
               System.out.println(jedis.get("user1"));
               System.out.println(jedis.get("user2"));
               jedis.close();
           }
    
        }
    

5.SpringBoot

沒學

6.Redis配置

  1. 配置檔案 unit單位對大小寫不敏感

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-ASHodF5A-1610855287568)(C:\Users\18335\AppData\Roaming\Typora\typora-user-images\image-20201115113728926.png)]

  1. 包含(好比import)

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-LXae7xNO-1610855287570)(C:\Users\18335\AppData\Roaming\Typora\typora-user-images\image-20201115113818243.png)]

  2. 網路配置

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-pK4mTf3i-1610855287572)(C:\Users\18335\AppData\Roaming\Typora\typora-user-images\image-20201115113958197.png)]

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-2QuyYLNt-1610855287575)(C:\Users\18335\AppData\Roaming\Typora\typora-user-images\image-20201115114244136.png)]

  3. 日誌輸出級別

    # 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 ""  #日誌檔名
    
    
  4. 持久化:在規定的時間內,執行了多少次操作,則會持久化到檔案 .rdb .aof,redis是記憶體資料庫如果沒有持久化斷電即失怎麼辦

    #900秒內,至少有一個key進行了修改,進行持久化
    save 900 1
    #300秒 ,10個key
    save 300 10
    #60s,至少10000修改就持久化
    save 60 10000
    
    stop-writes-on-bgsave-error yes  #持久化出錯是否繼續工作,預設開啟繼續工作
    
    rdbcompression yes #是否壓縮rdb檔案,需要消耗cpu的資源
    
    rdbchecksum yes#儲存rdb檔案的時候進行錯誤的校驗
    
    dir ./  #rdb儲存的路徑
    
    
    REPLICATION  #主從複製
    

    密碼

    ################ SECURITY #####################
    requirepass  123456  #設定密碼123456,預設沒有密碼
    

    客戶端設定

    maxclients 10000  # 最大客戶端數量
    maxmemory <bytes> #最大記憶體限制
    maxmemory-policy noeviction # 記憶體達到限制值的處理策略
    

    redis預設的過期策略volatile-lru

    maxmemory-policy 六種方式

    **1、volatile-lru:**只對設定了過期時間的key進行LRU(預設值)

    2、allkeys-lru : 刪除lru演算法的key

    **3、volatile-random:**隨機刪除即將過期key

    **4、allkeys-random:**隨機刪除

    5、volatile-ttl : 刪除即將過期的

    6、noeviction : 永不過期,返回錯誤

  5. aof配置

appendonly no #預設不開啟AOF
appendfilename "appendonly.aof"  #持久化的檔名字

# appendfsync always   #每次修改都會同步
appendfsync everysec    #每秒執行一個sync,可能會丟失
# appendfsync no     #不執行sync,這個時候作業系統自己同步資料

7.持久化RDB

什麼是RDB


在指定時間間隔後,將記憶體中的資料集快照寫入資料庫 ;在恢復時候,直接讀取快照檔案,進行資料的恢復 ;

在這裡插入圖片描述

預設情況下, Redis 將資料庫快照儲存在名字為 dump.rdb的二進位制檔案中。檔名可以在配置檔案中進行自定義。

工作原理


在進行 RDB 的時候,redis 的主執行緒是不會做 io 操作的,主執行緒會 fork 一個子執行緒來完成該操作;

  1. Redis 呼叫forks。同時擁有父程序和子程序。
  2. 子程序將資料集寫入到一個臨時 RDB 檔案中。
  3. 當子程序完成對新 RDB 檔案的寫入時,Redis 用新 RDB 檔案替換原來的 RDB 檔案,並刪除舊的 RDB 檔案。

這種工作方式使得 Redis 可以從寫時複製(copy-on-write)機制中獲益(因為是使用子程序進行寫操作,而父程序依然可以接收來自客戶端的請求。)

bgsave

bgsave 是非同步進行,進行持久化的時候,redis 還可以將繼續響應客戶端請求

在這裡插入圖片描述

觸發機制


  1. save的規則滿足的情況下,會自動觸發rdb原則 (上邊的持久化配置檔案中)
  2. 執行flushall命令,也會觸發我們的rdb原則
  3. 退出redis,也會自動產生rdb檔案

save

使用 save 命令,會立刻對當前記憶體中的資料進行持久化 ,但是會阻塞,也就是不接受其他操作了;

由於 save 命令是同步命令,會佔用Redis的主程序。若Redis資料非常多時,save命令執行速度會非常慢,阻塞所有客戶端的請求。

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

bgsave和save對比

命令savebgsave
IO型別同步非同步
阻塞?是(阻塞發生在fock(),通常非常快)
複雜度O(n)O(n)
優點不會消耗額外的記憶體不阻塞客戶端命令
缺點阻塞客戶端命令需要fock子程序,消耗記憶體

優缺點

優點:

  1. 適合大規模的資料恢復
  2. 對資料的完整性要求不高

缺點:

  1. 需要一定的時間間隔進行操作,如果redis意外宕機了,這個最後一次修改的資料就沒有了。
  2. fork程序的時候,會佔用一定的內容空間。

8.持久化AOF

Append Only File

將我們所有的命令都記錄(只記讀)下來,history,恢復的時候就把這個檔案全部再執行一遍

以日誌的形式來記錄每個寫的操作,將Redis執行過的所有指令記錄下來(讀操作不記錄),只許追加檔案但不可以改寫檔案,redis啟動之初會讀取該檔案重新構建資料,換言之,redis重啟的話就根據日誌檔案的內容將寫指令從前到後執行一次以完成資料的恢復工作。

什麼是AOF

快照功能(RDB)並不是非常耐久(durable): 如果 Redis 因為某些原因而造成故障停機, 那麼伺服器將丟失最近寫入、以及未儲存到快照中的那些資料。 從 1.1 版本開始, Redis 增加了一種完全耐久的持久化方式: AOF 持久化。

如果要使用AOF,需要修改配置檔案: 上邊的aof配置檔案

appendonly no yes則表示啟用AOF

預設是不開啟的,我們需要手動配置,然後重啟redis,就可以生效了!

如果這個aof檔案有錯誤,這時候redis是啟動不起來的,我需要修改這個aof檔案

redis給我們提供了一個工具redis-check-aof --fix

優點

  1. 每一次修改都會同步,檔案的完整性會更加好
  2. 每秒同步一次,可能會丟失一秒的資料
  3. 從不同步,效率最高

缺點

  1. 相對於資料檔案來說,aof遠遠大於rdb,修復速度比rdb慢!
  2. aof執行效率也要比rdb慢,所以我們redis預設的配置就是rdb持久化

9.RDB和AOP選擇

RDB 和 AOF 對比

RDBAOF
啟動優先順序
體積
恢復速度
資料安全性丟資料根據策略決定

如何選擇使用哪種持久化方式?

一般來說, 如果想達到足以媲美 PostgreSQL 的資料安全性, 你應該同時使用兩種持久化功能。

如果你非常關心你的資料, 但仍然可以承受數分鐘以內的資料丟失, 那麼你可以只使用 RDB 持久化。

有很多使用者都只使用 AOF 持久化, 但並不推薦這種方式: 因為定時生成 RDB 快照(snapshot)非常便於進行資料庫備份, 並且 RDB 恢復資料集的速度也要比 AOF 恢復的速度要快。

10.Redis釋出訂閱

Redis 釋出訂閱(pub/sub)是一種訊息通訊模式:傳送者(pub)傳送訊息,訂閱者(sub)接收訊息。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-MunfX2Uq-1610855287577)(C:\Users\18335\AppData\Roaming\Typora\typora-user-images\image-20201115131533116.png)]

下圖展示了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 、 client5 和 client1 之間的關係:

在這裡插入圖片描述

當有新訊息通過 PUBLISH 命令傳送給頻道 channel1 時, 這個訊息就會被髮送給訂閱它的三個客戶端:

在這裡插入圖片描述

命令

命令描述
PSUBSCRIBE pattern [pattern..]訂閱一個或多個符合給定模式的頻道。
PUNSUBSCRIBE pattern [pattern..]退訂一個或多個符合給定模式的頻道。
PUBSUB subcommand [argument[argument]]檢視訂閱與釋出系統狀態。
PUBLISH channel message向指定頻道釋出訊息
SUBSCRIBE channel [channel..]訂閱給定的一個或多個頻道。
SUBSCRIBE channel [channel..]退訂一個或多個頻道
#訂閱端
127.0.0.1:6379> subscribe hou   #訂閱一個頻道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "hou"
3) (integer) 1
# 等待資訊推送
1) "message"  #訊息
2) "hou"   #訊息的頻道
3) "hello"  #訊息的內容
1) "message"
2) "hou"
3) "nihao"
#傳送端
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> publish hou hello  #釋出者釋出資訊到指定頻道
(integer) 1
127.0.0.1:6379> publish hou nihao
(integer) 1

原理

每個 Redis 伺服器程序都維持著一個表示伺服器狀態的 redis.h/redisServer 結構, 結構的 pubsub_channels 屬性是一個字典, 這個字典就用於儲存訂閱頻道的資訊,其中,字典的鍵為正在被訂閱的頻道, 而字典的值則是一個連結串列, 連結串列中儲存了所有訂閱這個頻道的客戶端。

在這裡插入圖片描述

客戶端訂閱,就被連結到對應頻道的連結串列的尾部,退訂則就是將客戶端節點從連結串列中移除。

缺點

  1. 如果一個客戶端訂閱了頻道,但自己讀取訊息的速度卻不夠快的話,那麼不斷積壓的訊息會使redis輸出緩衝區的體積變得越來越大,這可能使得redis本身的速度變慢,甚至直接崩潰。
  2. 這和資料傳輸可靠性有關,如果在訂閱方斷線,那麼他將會丟失所有在短線期間釋出者釋出的訊息。

應用

  1. 訊息訂閱:公眾號訂閱,微博關注等等(起始更多是使用訊息佇列來進行實現)
  2. 多人線上聊天室。

稍微複雜的場景,我們就會使用訊息中介軟體MQ處理

11.Redis的主從複製

1. 概念

主從複製,是指將一臺Redis伺服器的資料,複製到其他的Redis伺服器。前者稱為主節點(Master/Leader),後者稱為從節點(Slave/Follower), 資料的複製是單向的!只能由主節點複製到從節點(主節點以寫為主、從節點以讀為主)。

預設情況下,每臺Redis伺服器都是主節點,一個主節點可以有0個或者多個從節點,但每個從節點只能由一個主節點。

2. 作用

  1. 資料冗餘:主從複製實現了資料的熱備份,是持久化之外的一種資料冗餘的方式。
  2. 故障恢復:當主節點故障時,從節點可以暫時替代主節點提供服務,是一種服務冗餘的方式
  3. 負載均衡:在主從複製的基礎上,配合讀寫分離,由主節點進行寫操作,從節點進行讀操作,分擔伺服器的負載;尤其是在多讀少寫的場景下,通過多個從節點分擔負載,提高併發量。
  4. 高可用基石:主從複製還是哨兵和叢集能夠實施的基礎。

3. 為什麼使用叢集

  1. 單臺伺服器難以負載大量的請求
  2. 單臺伺服器故障率高,系統崩壞概率大
  3. 單臺伺服器記憶體容量有限。

4. 環境搭建(linux 過了再搞)

5.複製原理

Slave啟動成功連線到master後會傳送一個sync命令

Maser接受命令,啟動後臺的存檔程序,同時收集所有接受到的用於修改資料集命令,在後臺程序執行完畢之後, master將傳送整個資料檔案到slave,並完成一次完全同步

全量複製:而slave服務在接受到資料庫檔案資料後,將其存檔並載入到記憶體中

增量複製:Master繼續將新的所有收集到的修改命令依次傳給slave,完成同步

但是隻要是重新連線master,一次完全同步(全量複製)將自動執行,我們的資料一定可以在從機中看到

6. 使用規則

  1. 從機只能讀,不能寫,主機可讀可寫但是多用於寫。

     127.0.0.1:6381> set name sakura # 從機6381寫入失敗
    (error) READONLY You can't write against a read only replica.
    
    127.0.0.1:6380> set name sakura # 從機6380寫入失敗
    (error) READONLY You can't write against a read only replica.
    
    127.0.0.1:6379> set name sakura
    OK
    127.0.0.1:6379> get name
    "sakura"
    12345678910
    
  2. 當主機斷電宕機後,預設情況下從機的角色不會發生變化 ,叢集中只是失去了寫操作,當主機恢復以後,又會連線上從機恢復原狀。

  3. 當從機斷電宕機後,若不是使用配置檔案配置的從機,再次啟動後作為主機是無法獲取之前主機的資料的,若此時重新配置稱為從機,又可以獲取到主機的所有資料。這裡就要提到一個同步原理。

  4. 第二條中提到,預設情況下,主機故障後,不會出現新的主機,有兩種方式可以產生新的主機:

    • 從機手動執行命令slaveof no one,這樣執行以後從機會獨立出來成為一個主機
    • 使用哨兵模式(自動選舉)

如果沒有老大了,這個時候能不能選擇出來一個老大呢?手動!

如果主機斷開了連線,我們可以使用SLAVEOF no one讓自己變成主機!其他的節點就可以手動連線到最新的主節點(手動)!如果這個時候老大修復了,那麼久重新連線!

12.哨兵模式(謀權篡位自動版)

主從切換技術的方法是:當主伺服器宕機後,需要手動把一臺從伺服器切換為主伺服器,這就需要人工干預,費事費力,還會造成一段時間內服務不可用。這不是一種推薦的方式,更多時候,我們優先考慮哨兵模式

哨兵模式是一種特殊的模式,首先Redis提供了哨兵的命令,哨兵是一個獨立的程序,作為程序,它會獨立進行,哨兵通過傳送命令,等待Redis伺服器響應,從而監控執行多個Redis例項

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-20YxWaFQ-1610855287579)(C:\Users\18335\AppData\Roaming\Typora\typora-user-images\image-20201115151147262.png)]

哨兵的作用:

  • 通過傳送命令,讓Redis伺服器返回監控其執行狀態,包括主伺服器和從伺服器。
  • 當哨兵監測到master宕機,會自動將slave切換成master,然後通過釋出訂閱模式通知其他的從伺服器,修改配置檔案,讓它們切換主機。

然而一個哨兵程序對Redis伺服器進行監控可能出現問題(也死了怎麼辦),為此,我們可以使用多個哨兵進行監控,各個哨兵之間還會進行監控,這樣就形成了多哨兵模式

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-0hemke1b-1610855287580)(C:\Users\18335\AppData\Roaming\Typora\typora-user-images\image-20201115151516839.png)]

用文字描述一下故障切換(failover)的過程。假設主伺服器宕機,哨兵1先檢測到這個結果,系統並不會馬上進行failover過程,僅僅是哨兵1主觀的認為主伺服器不可用,這個現象成為主觀下線。當後面的哨兵也檢測到主伺服器不可用,並且數量達到一定值時,那麼哨兵之間就會進行一次投票,投票的結果由一個哨兵發起,進行failover操作。切換成功後,就會通過釋出訂閱模式,讓各個哨兵把自己監控的從伺服器實現切換主機,這個過程稱為客觀下線。這樣對於客戶端而言,一切都是透明的。

更多資訊參考部落格:https://www.jianshu.com/p/06ab9daf921d

哨兵模式優缺點

優點:

  1. 哨兵叢集,基於主從複製模式,所有主從複製的優點,它都有
  2. 主從可以切換,故障可以轉移,系統的可用性更好
  3. 哨兵模式是主從模式的升級,手動到自動,更加健壯

缺點:

  1. Redis不好線上擴容,叢集容量一旦達到上限,線上擴容就十分麻煩
  2. 實現哨兵模式的配置其實是很麻煩的,裡面有很多配置項

13.Redis快取穿透和雪崩

快取穿透(查不到)

概念

在預設情況下,使用者請求資料時,會先在快取(Redis)中查詢,若沒找到即快取未命中,再在資料庫中進行查詢,數量少可能問題不大,可是一旦大量的請求資料(例如秒殺場景)快取都沒有命中的話,就會全部轉移到資料庫上,造成資料庫極大的壓力,就有可能導致資料庫崩潰。網路安全中也有人惡意使用這種手段進行攻擊被稱為洪水攻擊。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-F4EzwqDT-1610855287581)(C:\Users\18335\AppData\Roaming\Typora\typora-user-images\image-20201115155508550.png)]

1.布隆過濾器

是一種資料結構,對所有可能查詢的引數以Hash的形式儲存,以便快速確定是否存在這個值,在控制層先進行攔截校驗,校驗不通過直接打回,減輕了儲存系統的壓力

img

2.快取空物件

當儲存層不命中後(沒找到),即使返回的是空物件也將其快取起來。同時設定一個過期時間,之後再訪問這個資料將會從快取中獲取這個空的物件,保護後端資料來源。

一次請求若在快取和資料庫中都沒找到,就在快取中方一個空物件用於處理後續這個請求。

在這裡插入圖片描述

這樣做有一個缺陷

  1. 儲存 空物件也需要空間,大量的空物件會耗費一定的空間,儲存效率並不高。解決這個缺陷的方式就是設定較短過期時間

  2. 即使對空值設定了過期時間,還是會存在快取層和儲存層的資料會有一段時間視窗的不一致,這對於需要保持一致性的業務會有影響。

快取擊穿(訪問數量太大,快取過期)

概述

相較於快取穿透,快取擊穿的目的性更強,一個存在的key,在快取過期的一刻,同時有大量的請求,這些請求都會擊穿到DB,造成瞬時DB請求量大、壓力驟增。這就是快取被擊穿,只是針對其中某個key的快取不可用而導致擊穿,但是其他的key依然可以使用快取響應。

比如熱搜排行上,一個熱點新聞被同時大量訪問就可能導致快取擊穿。

解決方法

  1. 設定熱點資料永不過期

    這樣就不會出現熱點資料過期的情況,但是當Redis記憶體空間滿的時候也會清理部分資料,而且此種方案會佔用空間,一旦熱點資料多了起來,就會佔用部分空間。

  2. 加互斥鎖(分散式鎖)

    在訪問key之前,採用SETNX(set if not exists)來設定另一個短期key來鎖住當前key的訪問,訪問結束再刪除該短期key。保證同時刻只有一個執行緒訪問。這樣對鎖的要求就十分高。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-57zGGuAN-1610855287582)(C:\Users\18335\Desktop\整理\java\ssm 重啟\Redis\image-20201115161318252.png)]

快取雪崩

大量的key設定了相同的過期時間,導致在快取在同一時刻全部失效,造成瞬時DB請求量大、壓力驟增,引起雪崩。

在這裡插入圖片描述

解決方案

  • redis高可用

    這個思想的含義是,既然redis有可能掛掉,那我多增設幾臺redis,這樣一臺掛掉之後其他的還可以繼續工作,其實就是搭建的叢集

  • 限流降級

    這個解決方案的思想是,在快取失效後,通過加鎖或者佇列來控制讀資料庫寫快取的執行緒數量。比如對某個key只允許一個執行緒查詢資料和寫快取,其他執行緒等待。

  • 資料預熱

    資料加熱的含義就是在正式部署之前,我先把可能的資料先預先訪問一遍,這樣部分可能大量訪問的資料就會載入到快取中。在即將發生大併發訪問前手動觸發載入快取不同的key,設定不同的過期時間,讓快取失效的時間點儘量均勻。

DT-1610855287581)]

1.布隆過濾器

是一種資料結構,對所有可能查詢的引數以Hash的形式儲存,以便快速確定是否存在這個值,在控制層先進行攔截校驗,校驗不通過直接打回,減輕了儲存系統的壓力

img

2.快取空物件

當儲存層不命中後(沒找到),即使返回的是空物件也將其快取起來。同時設定一個過期時間,之後再訪問這個資料將會從快取中獲取這個空的物件,保護後端資料來源。

一次請求若在快取和資料庫中都沒找到,就在快取中方一個空物件用於處理後續這個請求。

在這裡插入圖片描述

這樣做有一個缺陷

  1. 儲存 空物件也需要空間,大量的空物件會耗費一定的空間,儲存效率並不高。解決這個缺陷的方式就是設定較短過期時間

  2. 即使對空值設定了過期時間,還是會存在快取層和儲存層的資料會有一段時間視窗的不一致,這對於需要保持一致性的業務會有影響。

快取擊穿(訪問數量太大,快取過期)

概述

相較於快取穿透,快取擊穿的目的性更強,一個存在的key,在快取過期的一刻,同時有大量的請求,這些請求都會擊穿到DB,造成瞬時DB請求量大、壓力驟增。這就是快取被擊穿,只是針對其中某個key的快取不可用而導致擊穿,但是其他的key依然可以使用快取響應。

比如熱搜排行上,一個熱點新聞被同時大量訪問就可能導致快取擊穿。

解決方法

  1. 設定熱點資料永不過期

    這樣就不會出現熱點資料過期的情況,但是當Redis記憶體空間滿的時候也會清理部分資料,而且此種方案會佔用空間,一旦熱點資料多了起來,就會佔用部分空間。

  2. 加互斥鎖(分散式鎖)

    在訪問key之前,採用SETNX(set if not exists)來設定另一個短期key來鎖住當前key的訪問,訪問結束再刪除該短期key。保證同時刻只有一個執行緒訪問。這樣對鎖的要求就十分高。

[外鏈圖片轉存中…(img-57zGGuAN-1610855287582)]

快取雪崩

大量的key設定了相同的過期時間,導致在快取在同一時刻全部失效,造成瞬時DB請求量大、壓力驟增,引起雪崩。

在這裡插入圖片描述

解決方案

  • redis高可用

    這個思想的含義是,既然redis有可能掛掉,那我多增設幾臺redis,這樣一臺掛掉之後其他的還可以繼續工作,其實就是搭建的叢集

  • 限流降級

    這個解決方案的思想是,在快取失效後,通過加鎖或者佇列來控制讀資料庫寫快取的執行緒數量。比如對某個key只允許一個執行緒查詢資料和寫快取,其他執行緒等待。

  • 資料預熱

    資料加熱的含義就是在正式部署之前,我先把可能的資料先預先訪問一遍,這樣部分可能大量訪問的資料就會載入到快取中。在即將發生大併發訪問前手動觸發載入快取不同的key,設定不同的過期時間,讓快取失效的時間點儘量均勻。