主頁 > 後端開發 > Redis學習筆記(理論+面試)

Redis學習筆記(理論+面試)

2020-10-12 11:14:40 後端開發

Redis

1 簡介

  • 是開源免費、遵守BSD協議、高性能的NOSQLkey-value 資料庫,
  • 是簡單的、高效的、分布式的、基于記憶體單執行緒快取工具,
  • 簡單是Redis的突出特色,簡單可以保證核心功能的穩定和優異,
  • 支持網路、可以持久化、支持資料備份、集群、訊息佇列等高可用功能,
  • 在企業開發中,Redis常被作為分布式快取
    • 熱點資料:常被查詢、不常被修改、洗掉的資料
    • 臨時資料:不必持久化、有失效時間的資料(驗證碼等)

2 特點

2.1 關系型資料庫 VS 非關系型資料庫

(1)關系型

  • 優點
    • 容易理解,二維表比較符合邏輯世界的展示
    • 單一關系模型,可以進行結構化存盤,有完整的約束
    • 正是因為是結構化存盤的,所以可以很方便的使用結構化語言SQL,實作復雜的操作(表關聯JOIN等)
  • 缺點
    • SQL需要決議,效率低
    • 要保證資料一致性,就需要加鎖,并發性能低
    • 結構化存盤雖然方便,但是不靈活,擴展困難,

(2)非關系型

  • 優點
    • 并發性能高,可以處理大量的資料
    • 效率高、速度快
  • 缺點
    • 不能支持復雜的操作(比如JOIN),每次只能執行簡單的命令
    • 沒有完整的約束,事務處理能力差(比如Redis的事務就是偽事務)

2.2 Redis優點

  • 性能高速度快,簡單高效,可以處理萬級的QPS,使用集群后可達百萬級,遠超MySQL(幾千算好的),
  • 資料型別豐富(5個基本資料型別,還有其他的擴展型別,下面介紹)
  • 因為是單執行緒的,所以操作都是原子性的,天生執行緒安全,
  • 雖然是基于記憶體的,但是提供持久化功能,(兩種持久化方式,下面介紹)

2.3 Redis缺點

  • 基于記憶體,所以對記憶體的要求高

3 安裝

還是去搜詳細教程吧,這部分之后有時間再整理,

  • 使用docker安裝
docker pull redis
docker run -d --name redis -p 6379:6379 redis
docker exec -it redis redis-cli
  • 直接壓縮包安裝
1、確保Linux已經安裝gcc
    若沒安裝,則需要在root登錄下、可連接外網狀態下執行下面命令
    yum -y install gcc automake autoconf libtool make
     
2、下載Redis
    wget http://download.redis.io/releases/redis-5.0.5.tar.gz
    
3、解壓
    tar -zxvf redis-5.0.5.tar.gz -C /opt (解壓到此目錄)
    
4、進入到解壓的目錄后編譯
    cd redis-5.0.5
    make MALLOC=libc
    
5、同樣在解壓后的目錄內進行安裝(安裝后會有一個bin目錄,目錄內就有redis-server檔案)
    make PREFIX=/usr/local/redis install
    
6、啟動redis服務(默認不是后臺行程,所以當前視窗就會被占用,不能進行其他操作)
    cd /usr/local/redis/bin
    ./redis-server
   
7、啟動redis客戶端(可以在xshell重開一個對話,啟動客戶端)
    cd /usr/local/redis/bin
    ./redis-cli
        啟動客戶端后檢測redis服務是否開啟:
        redis 127.0.0.1:6379> PING(回傳PONG則表示ping通即服務已開啟)

4 配置

1、進入解壓的Redis目錄
    cd /opt/redis-5.0.5

2、將redis.conf復制到安裝檔案的目錄下(與bin同級)
    cp redis.conf /usr/local/redis

3、注意:要想讓組態檔生效,需要在啟動redis時帶上組態檔!!!
    cd /usr/local/redis/bin
    ./bin/redis-server ./redis.conf

?

部分配置項

配置項說明
bind指定可以訪問Redis的主機IP,默認只能本機訪問Redis,如果要遠程訪問Redis,則需要將 bind 其注釋掉,或者系結地址==# bind 127.0.0.1==
daemonize指定是否為守護行程(后臺運行), 默認為no,即不是后臺運行,一運行就占用視窗,無法啟動其他程式,一般修改為 yes
port指定埠號,默認為 6379
requirepass設定密碼,默認沒有密碼
pidfile指定piffile檔案位置,當Redis以守護行程運行時,會產生pid檔案默認把pid寫入到==/var/run/redis-6379.pid==檔案中,
timeout客戶端閑置多少(秒)后關閉連接, 默認為0,表示關閉此功能
loglevel指定日志記錄級別,共四級:debug、verbose、notice、warning ,默認為notice
database設定資料庫數量,默認是16個,編號 0-15
save 指定多少時間、有多少次更新操作,就將資料持久化到dump.rdb檔案,這是RDB持久化方式的配置,redis默認配置有三個,滿足其中一個就會進行持久化: save 900 1 (900秒有1個更改) save 300 10 (300秒有10個更改) save 60 10000 (60秒有10000更改)
rdbcompression指定持久化是否壓縮資料,默認是yes,
dbfilename指定持久化后生成的檔案,默認是dump.rdb
dir指定上面檔案存放的路徑,默認是 ./ (當前目錄,與bin同級)
slaveof 當本機設定為slave服務時,設定相應master服務的IP地址和埠號 ,redis啟動時,自動從master進行資料同步
masterauth 當master服務設定了密碼保護時,slave服務連接master的密碼
maxclients 設定同一時間最大客戶端連接數, 默認不限制,
maxmemory 指定最大記憶體限制,(建議不要超過1G,在256-512M) 默認不限制, 單位是M

5 啟動和關閉

5.1 啟動

(一)服務端啟動

[root@localhost ~]# cd /usr/local/redis
[root@localhost redis]# ./bin/redis-server ./redis.conf

(二)客戶端啟動

? ①本地訪問客戶端

[root@localhost redis]# ./bin/redis-cli -a [密碼(123456)]
默認: -h 127.0.0.1 -p 6379

? ②遠程訪問客戶端

[root@localhost redis]# ./bin/redis-cli -h [服務端IP] -p [埠號] -a [密碼]

5.2 關閉

  • 正常關閉(通過客戶端,關閉服務端)
127.0.0.1:6379> shutdown [NOSAVE|SAVE]
  • 非正常關閉(直接殺掉redis服務端行程)
查看當前與redis有關的行程:				 ps –ef | grep –i redis  

得到redis-server的PID后,殺死相應的行程:	kill -9 PID

6 基本資料型別和常用命令

key的命名建議

? 使用統一的命名模式,用冒號隔開,通過統一模式,來體現資料之間的關系

? 如:user:123:name 不用我解釋,也能看懂大概是什么

注意:

? 1、key區分大小寫,命令不區分大小寫

? 2、基本資料型別針對的是value,key的保存都是通過字串形式保存的

6.1 公用命令

? 對所有型別資料都可以操作,或者說是資料庫層面的命令,

  • SELECT numb:切換資料庫(默認是0)

  • DEL key:洗掉key(回傳受影響的key數量)

  • DUMP key:序列化給定key(回傳被序列化的值)

  • EXISTS key:檢查key是否存在(存在回傳1,不存在回傳0)

  • EXPIRE key seconds:為key設定過期時間(秒)

    • 沒有設定則默認為永久存在,除非delete掉
    • 驗證碼等有時效性的資料中常用
    • PEXPIRE key milliseconds:功能相同,毫秒為單位
  • TTL key:回傳key剩余時間(秒)

    • -1 表示永久有效
    • -2表示已無效 / 不存在
    • PTTL key:功能相同,毫秒為單位
  • PERSIST key:移除key的過期時間,key將持久保存

  • KEYS pattern:查詢所有符號給定模式的key

    • pattern表示通配符:* 代表任意數量任意字符,? 代表任意一個字符
      • KEYS * : 查詢所有的key
      • KEYS *a: 查詢以a為結尾的key
      • KEYS ?a*:查詢以任意一個字符拼接上a作為開頭的key
      • KEYS ??: 查詢出兩個字符的key
  • RANDOMKEY:隨機回傳一個key

  • RANAME key newkey:修改key的名稱

  • MOVE key numb:移動key至numb序號所指定資料庫中(第一個庫為0)

  • FLUSHDB:清除當前資料庫的所有key

  • FLUSHALL:清除整個Redis所有資料庫的所有key

  • DBSIZE:查看當前資料庫的key的數量

  • TYPE key:回傳key所儲存的值的型別

6.2 基本資料型別

? Redis有5中基本資料型別:StringHashListSetZSet

6.2.1 String

? String型別是二進制安全的,redis的String可以包含任何資料,如字串、影像音頻視頻的序列化物件(二進制),但是一個value最多能存盤512MB

二進制安全是指,在傳輸資料的時候,能保證二進制資料的不會被篡改、破譯;如果被攻擊,能夠及時檢測出來

注意:String可以覆寫其他型別的資料,但是其他型別不能覆寫String型別的資料

? 如果一個key已經存在且是String型別,那么就不可以有其他型別使用賦值陳述句覆寫原來的key

? 如果一個key是其他型別的,可以用String型別的賦值陳述句將其覆寫成String型別的資料

使用場景

  • 通常用于保存單個資料(字串或者數值)
  • 也可用于保密要求高的圖片檔案,內容作為字串(二進制序列化)來存盤

命令

  • SET key value [EX seconds|PX milliseconds] [ NX |XX ]:設定key-value
    • EX表示過期時間設為幾秒,PX則是毫秒級
    • NX → key不存在時才設定成功
    • XX → key存在時才能設定成功(覆寫)
    • 如果都不申明:不過期、key存不存在都可以設定
  • SETNX key value:設定key-value
    • 如果key已經存在,則不會執行任何操作(也就是只有不存在才設定
  • GET key:獲得key對應的value
  • GETRANGE key start end:獲取key中字串的子字串,
    • 下標從start開始,end結束(左閉右閉)(第一個字符的下標位0
  • SETRANGE key offset value:**置換(不是拼接)**指定的字串
    • 從 key 對應的 value 中 第offset位 開始置換
  • APPEND key value:字串拼接
    • 追加至末尾,如果不存在,為其賦值
  • MGET key1 [key2 …]:獲取多個key
    • 每個key之間用空格隔開
  • GETSET key value:設定key的值,并回傳key的舊值
    • 當key不存在,回傳nil
    • 當key存在時,回傳舊值
  • STRLEN key:回傳key所存盤的字串的長度
  • INCR key: key對應的value(整數)的數字 +1
    • 如果不存在key,則key中的值話先被初始化為 0 再加 1
  • INCRBY key 增量:key 對應的value(整數)增加增量的大小
    • 增量只能是整數
  • INCRBYFLOAT key 增量:key 對應的value增加增量的大小
    • value可以是整數,也可以是小數,增量同理
  • DECR key: key中的值自 -1
  • DECRBY key 減量:key 對應的value減少指定量的大小
  • DECRBYFLOAT key 減量:同理

6.2.2 Hash

? 是一個String型別的 field-value 的映射表,hash特別適用于存盤物件(類似于Java中的bean,field為屬性,value為屬性值),每個Hash可以存盤 (2^32 - 1) 鍵值對,可以看成存放key-value對的HashMap容器,相比于JSON,hash占用很少的磁盤空間,

?

使用場景

? 常用來存盤一個物件的資料,Hash是最接近關系資料庫的資料型別,可以將資料庫的一條記錄或程式中的一個物件轉換成HashMap存放在Redis中,

為什么不用String存盤一個物件?/ 為什么Hash更適合存盤多屬性的物件?

如果使用String來存盤,主要有下面兩種方式:

1、將物件的主鍵作為key,其他的所有屬性封裝成JSON串,保存成value,

? 這種方式有下面幾個缺點:JSON串修改單個屬性需要將整個值取出來;增加了序列化/反序列化的開銷

? 2、物件的所有屬性都保存成一個個key-value,在key中進行物件的唯一性標識不同物件的不同屬性

? (物件型別:實體物件主鍵:屬性名) 這種方式的缺點很明顯:浪費記憶體

常用命令

  • HSET key field value:為設定的key設定 field 及其 value

  • HSETNX key field value:只有field不存在,才設定此欄位

  • HMSET key field value [field2 value2]:批量設定(空格隔開即可)

  • HGET key field:獲取Hash型別的key中對應的field的value

  • HMGET key field [field2]:批量獲取

  • HGETALL key:回傳hash表中key的所有field和value

  • HKEYS key:獲取hash表中key的所有field

  • HLEN key:獲取hash表中key對應的field的數量

  • HDEL key field [field2]:洗掉一個或多個hash表中key的field

    • 注意:洗掉時,如果key中所有的field都被洗掉,redis會自動清除這個key (redis不能存在沒有value的key,對于hash型別,其value就是field)
  • HESISTS key field:查看field是否存在

  • HINCRBY key field 增量:指定field(整數)增加增量

  • HINCRBYFLOAT key field 增量:指定field(整數或小數)增加增量

    • 上面計數器的功能和效果,同String型別

6.2.3 List

? 類似于Java中LlinkedList,是簡單的字串串列,按照插入的順序排序,串列中的每個字串稱為元素(element),一個串列最多可以存盤2^32 -1個元素,

使用場景

  • 保存 資料量大、型別一致的基本資料型別的集合
    • 粉絲串列、留言評價、熱點新聞等
  • Redis的 lpush + brpop 命令組合即可實作阻塞佇列(不過一般不用redis作為訊息佇列)

常用命令

  • LPUSH key value1 [value2 …]:按順序從左側插入值

  • RPUSH key value1 [value2 …]:按順序從右側插入值

  • LPUSHX key value:從左側插入值,如果list不存在,則不操作

  • RPUSHX key value:從右側插入值,如果list不存在,則不操作

  • LLEN key:獲取串列長度(元素的個數)

  • LINDEX key index:獲取指定索引的元素

  • LRANGE key start stop:獲取串列指定范圍的元素

    • LRANGE key 0 -1:表示獲取list的全部元素
    • LRANGE key 1 1:表示獲取下標為1的一個元素
    • -1 表示最后一個元素 -2 表示倒數第二個元素 以此類推
  • LPOP key:從左側移除一個元素

  • RPOP key:從右側移除一個元素

    • 同樣的,list內一定要有元素,如果所有元素都移除,則redis也會自動洗掉這個key
  • BLPOP key [key2 …] timeout:從左側移除并獲取key串列一個元素

    • 可以多個串列一起
    • 如果串列不存在,會阻塞到等待超時(秒)或發現可彈出元素為止
  • BRPOP key [key2 …] timeout:功能同上,只不過是從右側

  • LTRIM key start stop :對串列進行修剪,保留指定區間的元素

  • LSET key index value : 修改指定索引的值

  • LINSERT key before|after element value:在串列key的element元素前或則后插入元素value

6.2.4 Set

? 是String型別的無序、不可重復的集合,(唯一性、無序性是與List型別的區別)

使用場景

? 常用于兩個集合之間資料進行交、差、并運算

  • 方便實作如共同關注、共同喜好等功能
  • 利用唯一性,可以統計訪問網站的所有獨立 IP等

常見命令

  • SADD key value1 [value2]:向key添加元素
  • SCARD key:回傳集合元素數
  • SMEMBERS key:回傳集合中所有元素
  • SISMEMBER key element:判斷element元素是否在集合 key 中
  • SRANDMEMBER key [count]:回傳集合中一個或多個(count)亂數
  • SREM key element1 [element2]:移除集合中一個或多個元素
  • SPOP key:移除并回傳集合中隨機一個元素
  • SMOVE source destination element:將element元素從source集合移動到destination集合
  • SINTER key [key …] : 回傳多個集合求交集
  • SUNION key [key …] :回傳多個集合求并集(不會出現重復資料)
  • SDIFF key [key …]: 回傳多個集合的差集
  • SINTERSTORE destination key [key …]: 回傳給定所有key集合的交集并存盤在destination中
  • SUNIONSTORE destination key [key …]: 回傳給定所有key集合的并集并存盤在destination中
  • SDIFFSTORE destination key [key …]: 回傳給定所有key集合的差集并存盤在destination中

6.2.5 ZSet

? 不能重復、可以排序的String型別元素的集合,每個元素都會關聯一個double型別分數(score),Redis通過分數進行從小到大的排序,(元素唯一,但是分數可以重復)

使用場景

? 排行榜

常用命令

  • ZADD key score1 memeber1 [score2 member2 ]:設定分數和元素

  • ZCARD key :獲取集合中的元素數量

  • ZCOUNT key min max: 計算指定區間分數的元素數量

  • ZRANK key member:回傳指定元素的索引(下標值)

  • ZSCORE key element:獲得指定元素的分數

  • ZINCRBY key 增量 element:為指定元素的分數增加增量

  • ZRANGE key start stop:回傳索引區間內的元素(分數從低到高排序)

  • ZREVRANGE key start stop :回傳索引區間內的元素(分數從高到低排序)

  • ZRANGEBYSCORE key min max:回傳分數區間內的元素

  • ZREM key member [member …]: 移除有序集合中的指定元素

  • ZREMRANGEBYRANK key start stop: 移除索引區間的所有元素

  • ZREMRANGEBYSCORE key min max: 移除分數區間的所有元素

7 持久化

? Redis有兩種持久化的機制:RDBAOF,默認是RDB
? 若兩種方式同時開啟,則重啟時以AOF的檔案恢復原始的Redis資料庫,(建議兩種都開啟)

7.1 RDB

? 這是Redis默認的持久化機制,是 定時快照 的方式,保存的是一種狀態,這種方式就是將記憶體中的資料以快照的方式寫入到二進制檔案中,默認檔案名為dump.rdb,(幾十G的資料 → 幾KB的快照檔案)

? 優點

  • 保存速度、還原速度極快,適合大規模的資料恢復

  • 適用于災難備份

    缺點

  • 小記憶體的機器不符合使用(快照的時候,redis消耗增加了一倍的記憶體)

  • 有可能丟失最后一份修改

    配置

    配置是在組態檔中的save部分進行相關的配置(何時進行快照)

#redis默認配置有三個,滿足其中一個即可 
save 900 1 #900秒有1個更改
save 300 10 #300秒有10個更改
save 60 10000 #60秒有10000更改

# 如果資料很重要,又不能等到時間到讓redis自動持久化,則可以使用命令來保存
save:只管保存,其他不管,全部阻塞
bgsave:后臺異步保存,快照的同時還可以相應客戶端請求

# (可以通過lastsave查看最后一次成功執行快照的時間)	

7.2 AOF

? 由于RDB方式是在一定時間間隔做的,如果在間隔期間,修改了資料而redis斷電等意外導致服務端shutdown,就會丟失最后一次修改,如果要求不能丟失任何修改的話,就可以采用AOF的方式,

? 使用AOF,Redis會將每一個收到的寫命令都通過write函式 追加到檔案 中,默認是appendonly.aof,當redis重啟時會通過重新執行檔案中命令,重建整個資料庫內容,(保存的不是狀態,而是命令)

? 優點

  • 可以保證修改資料不丟失

    缺點

  • 持久化檔案會越來越大,且檔案中可能存在許多多余的命令

  • 比如不斷對統一資料的修改命令都會加入到檔案中,所有命令都會執行,才能得到最后真正有用的一個資料,

  • aof檔案遠大于rdb檔案,恢復速度較慢

? 配置

# 首先要開啟AOF:
appendonly yes

# 可以指定檔案:
appendfilename "appendonly.aof"

# 有三種AOF方式:
appendfsync always   # 收到寫命令就立即寫入磁盤
appendfsync everysec # 每秒寫入磁盤一次(默認)
appendfsync no		 # 完全依賴OS,redis不主動進行AOF

重寫機制(REWRITE)

? 由于AOF采取檔案追加的方式,檔案會越來越大,為避免檔案過大的情況,新增了重寫機制:當檔案大小超過一定閾值的時候,Redis會啟動AOF檔案的內容壓縮,只保留可以恢復資料的最小指令集

? 重寫原理和快照相似,是 fork一個新行程將整個記憶體中的資料用命令的方式重寫了一個新的AOF檔案,并洗掉舊的AOF檔案,注意沒有讀取舊的AOF檔案

? 觸發情景:Redis會記錄上次重寫時AOF的大小,默認配置是當AOF檔案大小是上次rewrite后大小的一倍且檔案大于64M時觸發,

auto-aof-rewrite-percenttage 100  # 重寫資料所占的百分比
auto-aof-rewrite-min-size 64mb	  # 重寫檔案最小大小
no-appendfsync-on-rewrite no      # 重寫時是否可以運用Appendfsync,默認為no,保證資料安全

8 事務

? Redis事務可以一次執行多個命令,且有下面兩個保證:

  • Redis會將一個事務中的所有命令序列化,然后按順序執行(串行執行)
  • 執行中不會被其他命令插入,即不允許加塞行為

相關命令

  • MULTI:標記一個事務塊的開始
  • EXEC:執行上面事務塊內的命令
  • DISCARD:取消事務,放棄事務塊中的命令
  • WATCH key1 [ key2 ]:監視key,如果在事務執行前,這些key被其他命令改動,則打斷此事務,
    • 有點像樂觀鎖機制,watch相當于確定版本號,只要前后兩次所監視的key不同,則打斷
    • 一旦執行了EXEC或DISCARD或UNWATCH,之前所加的watch監控鎖都會取消掉
  • UNWATCH:取消對所有key的監視

正常事務命令塊例子

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> GET account:a
QUEUED
127.0.0.1:6379> GET account:b
QUEUED
127.0.0.1:6379> DECRBY account:a 50
QUEUED
127.0.0.1:6379> INCRBY account:b 50
QUEUED
127.0.0.1:6379> EXEC
1) "80"
2) "10"
3) (integer) 30
4) (integer) 60

事務的錯誤處理

  • 如果是邏輯上的錯誤(比如:對一個字串“hello”執行加一命令),那么此事務塊中,只有這個報錯的命令不執行,其他的命令,無論在其前或后,只要是邏輯正確的,都會執行,

  • 如果是語法上的錯誤(即命令語法不正確,也稱報告錯誤),那么此事務塊到最后執行exec時,會自動取消此事務,

    • 在事務塊中,提交了一個錯誤的命令,會立即列印出報告錯誤,但是如果無視,依舊可以繼續在事務塊中添加其他命令,但是到最后執行exec也會取消此事務,所以一旦出現報告錯誤,則可以使用discard中斷事務,

所以說:Redis的事務,并不是真正的事務,是偽事務

9 主從復制 & 哨兵模式 & 集群

單臺Redis服務機有下面幾個缺點:

  • 容易發生單點故障,不具備容錯性
  • 一臺服務器負責所有的請求負載,壓力較大
  • 且單臺服務器容量有限,一臺Redis服務器記憶體最好不要超過20G

如果使用多臺機器來保存Redis資料,不就可以解決上面問題,保證系統的高可用性了嗎?而這種方式,就需要進行機器之間的資料同步問題,這就是接下來要討論的,

9.1 主從復制

? 一個Redis服務可以有多個該服務的復制,這個Redis服務稱為Master,其他復制稱為Slave,因為大部分的應用都是“讀多寫少”,故:

  • Master負責處理寫請求可寫可讀
  • Slave負責處理讀請求只讀不可寫

主機(Master)資料更新后,根據配置和策略,自動同步到備機(Slave)中

  • Slave啟動成功連接到Master后,會發送一個sync命令,進行全量復制(Master所有的資料進行同步)
  • 之后的Master寫資料,slave進行的是增量復制(新增的部分進行同步)

? 要點:

  • 一個Slave只能有一個Master
  • 一個Redis可以既是Master,也是Slave
  • 主從復制的時候不會阻塞Master,是異步的

? 配置實作

? 配從不配主,有下面三種方式可以變成從機

  • 在服務端登錄時,帶上 -slaveof-p 兩個屬性
注意是服務端登錄帶上上面兩個屬性,客戶端登錄只需帶上6380埠
(因為這里假設6379為主機的埠,從機的埠為6380)

//服務端登錄
./bin/redis-server ./redis6380-conf -p 6380 -slaveof MasterIP Master埠

//客戶端登錄
./bin/redis-cli -p 6380 -a 123456
  • 在客戶端已經登錄的情況下,使用slaveof命令,可以變成從機
127.0.0.1:6380> slaveof MasterIP Master埠

上面兩種在與Master斷開連接(即從機shutdown等)時,需要重新進行salve連接,如果想要永久連接,則需要下面這種方式,將相關屬性配置在組態檔中

  • 復制一份conf檔案作為從機的組態檔,從機配置開啟下面配置
# 在redis-6380.conf中配置
slaveof 127.0.0.1 6379
port 6380
# 還有其他類似 log檔案名、pid檔案名、dump檔案名 都進行修改

# 注意:這里修改是為了在一臺機器上模擬成多臺機器運行,實際上都是在不同機器上的redis中實作主從的,所以一般只需要配置slaveof即可,

# 服務端啟動
./bin/redis-server ./redis6380-conf

? 若要將從機變為獨立的主機Master,則執行命令slaveof on one即可,

9.2 哨兵模式(Sentinel)

? 主從復制弊端就是不具備高可用性,一旦Master掛了,Redis就不能提供寫操作處理,所以就出現了哨兵模式(若Master掛了自動從slave機中選出新的Master機)

? 哨兵模式是Redis高可用的一種解決方案,當Master主機遇到例外中斷服務后,Redis能夠在后臺自動檢測到主機是否故障,如果故障了就根據投票數自動將一個Slave從機轉換成新Master機,并發送訊息通知管理員,無需人工操作,實作高可用性,

? 每秒每個哨兵會向整個集群:Master主庫、Slave從庫、其他Sentinel(哨兵)行程,發送一次ping命令做一次心跳檢測,這個就是哨兵用來判斷節點是否正常的重要依據,涉及兩概念:

  • 主觀下線:一個哨兵節點判定主機掛掉了
  • 客觀下線:多個哨兵交換主觀判定結果,只有半數以上的哨兵都認為主機掛了,才會判定主機下線

? 基本上哪個哨兵節點最先判斷出這個主機客觀下線,就會在各個哨兵節點中發起投票機制Raft演算法(選舉演算法),最終被投為新Master機的哨兵節點完成主從自動化切換,

? 注意

? 如果原來掛了的Master在哨兵決定了新Master后,又恢復正常回來了,則這個舊Master機則會作為Slave機,掛在新的Master后

? 和主從復制中手動修改新Master機不同,主從復制中手動修改,舊Master回來后,和其他的主從關系機無關系,是一個新的獨立的Master主機,

9.3 集群(Redis Cluster)

? 哨兵模式基本可以滿足一般生產的需求,具備高可用性,但是當資料量過大到一臺服務器存放不下的情況時,主從模式或sentinel模式就不能滿足需求了,這個時候需要對存盤的資料進行分片,將資料存盤到多個Redis實體中,

? 集群可以說是哨兵和主從模式的結合體,集群模式就是為了解決單機Redis容量有限的問題,將Redis的資料根據一定的規則分配到多臺機器

cluster集群特點

  • 所有redis節點網路互聯,資料共享

  • 都是 一主N從,其中從庫不提供服務,僅作為備用

  • 集群至少要三個主庫才能正常運行,即一個集群最少需要有6個Redis節點(三組,每組一主一從)

  • 不支持同時處理多個key,redis把key均勻分布在各個節點上,分開保存

  • 客戶端可以連接任何一個主節點進行讀寫,支持在線增加、洗掉節點

  • 集群是去中心化的,每個Master節點都是平等的,都可以獲取和設定資料,而slave節點不提供服務,只是對應Master的一個備份,

  • 在其中一個Master服務器上登錄的客戶端,寫入的資料會根據一定的演算法,將其存入到計算得到的 槽(slot) 中,由于槽平均分配給不同的Master,所以有可能在AMaster設定的資料,會保存在其他的BMaster服務器中,同時BSlave服務器也會同步備份,(這也是去中心化的部分體現)

真集群:每個Redis服務端都在不同的機器上(IP不同,port可能同)

假集群:在同一個機器上,通過不同的埠,模擬多個Redis服務端(IP同,port不同)

開啟集群(這部分留坑,之后深入使用后再填坑)

  • 需要將redis組態檔中的cluster-enable配置打開,配置為yes
  • 在redis沒有設定密碼并且bind IP注釋掉的情況下,redis會處于安全保護模式,禁止公網訪問redis cache,為了正常訪問需要取消保護模式:protected-mode no

10 快取常見問題 & 解決

10.1 快取穿透

? 當查詢一個資料庫不存在的資料,這個資料默認也不會寫入到快取Redis中,所以在高并發下,大量的用戶查詢此不存在的資料,會導致每次查詢都是直接訪問資料庫,穿透快取中間件,導致資料庫壓力極大,這就是快取穿透,

解決

  • 當查詢一個不存在的資料時,在Redis快取中間件中寫于相對應key的空字串,表示空結果,這樣子之后繼續查詢此資料時,就會在快取中找到對應的key回傳空字串,避免再次穿透到資料庫,
  • 進行引數過濾(對于大區間可以進行過濾,但是對于區間內的部分小無效的間隙,無法細致地過濾)
    • 比如引數在1-100中有效,其中的3、8號無效,區間過濾只能過濾掉<1 、>100部分無效引數,
  • 布隆過濾器(判斷所查資料是否存在資料庫中)

10.2 快取擊穿

? 在高并發下,對一個特定的值進行查詢,但是這個時候快取正好過期了,導致大量請求直接落到資料庫上

解決

  • 重要的快取資料不設定過期,若資料需要更新,就再同步更新快取,
  • 或者限流,減低直接查詢資料庫的請求數量,查詢到后會快取在redis中,之后的請求就不會直接查詢資料庫了,因為在redis的時候就命中了,
  • 甚至直接使用鎖(簡單利用redis的setnx以及Java的sleep即可),鎖住一小段時間,保證只有一個請求達到資料庫拿到資料后快取到redis中,sleep時間過后,大量的請求再去redis中拿,就不會打到資料庫上了,(如果請求量很多,個人感覺比限流好)

區分:

? 快取穿透:查詢的資料是資料庫不存在的資料

? 快取擊穿:查詢的資料是資料庫存在的資料,只是在快取中剛好過期了

10.3 快取雪崩

? 大量快取在同一時間內失效,導致大量的查詢直接訪問資料庫,即發生大量的快取擊穿,造成快取雪崩,

解決

  • 讓快取的失效時間分布均勻(加上個隨機值,不要同一時間失效),然后就是處理快取擊穿的操作了,

10.4 快取預熱

? 在專案產品上線前,快取暫時為空,如果直接上線,同樣會導致大量的查詢直接訪問資料庫,所以在上線之前,可以先進行主要的查詢,將熱點資料加入到快取中在上線,這就是快取預熱,

11 其他面試題

部分參考敖丙大佬的:https://blog.csdn.net/qq_35190492/article/details/103041932

1、快取型別

  • 本地快取:保存在本地,只有本地可以訪問,服務重啟就沒了,

  • 分布式快取:保存在快取中間件,各服務都可以訪問,可以持久化,

2、redis的淘汰策略

  • noeviction: 不洗掉策略, 達到最大記憶體限制時, 直接回傳錯誤資訊,
  • allkeys-lru: 所有key通用, 優先洗掉最近最少使用的 key,
  • volatile-lru: 只限于設定了 expire 的部分, 優先洗掉最近最少使用的 key,
  • allkeys-random: 所有key通用;,隨機洗掉一部分 key,
  • volatile-random: 只限于設定了 expire 的部分,隨機洗掉一部分 key,
  • volatile-ttl: 只限于設定了 expire 的部分,優先洗掉剩余時間短的key,

3、Redis為什么是單執行緒的?單執行緒還那么快?

? Redis 核心就是 如果我的資料全都在記憶體里,我單執行緒的去操作 就是效率最高的,為什么呢,因為多執行緒的本質就是 CPU 模擬出來多個執行緒的情況,這種模擬出來的情況就有一個代價,就是背景關系的切換,對于一個記憶體的系統來說,它沒有背景關系的切換就是效率最高的,**redis 用 單個CPU 系結一塊記憶體的資料,然后次讀寫的時候針對這塊記憶體的資料進行多,都是在一個CPU上完成的,所以它是單執行緒處理這個事,**在記憶體的情況下,這個方案就是最佳方案 —— 阿里 沈詢

4、Redis為什么很快

  • 是基于記憶體的單執行緒
  • IO多路復用
  • 底層的設計結構

5、RDB的原理是什么?

? 首先redis通過fork子行程來進行RDB檔案的寫入操作,然后進行copy on write,子行程創建后,父子行程共享資料段,父行程繼續提供讀寫服務子行程負責寫RDB,隨者子行程的RDB寫入,父子行程共享的資料段會不斷減少,即寫臟的頁面資料會逐漸和子行程分離開來,(和拉鏈一樣,還沒拉開的就共享,拉開了的就分離了,如果父行程在已分離的區域有寫了資料,是不會寫入到本次RDB檔案中的)

6、除了5種基本資料型別,還有沒有其他的?

? pipeline、pub/sub、HyperLogLog等

Redis的同步機制了解么?

? slave 剛啟動時,會發送一個psync命令給master ,如果是這個slave第一次連接到master,他會觸發一個**全量復制:master就會啟動一個執行緒,生成RDB快照,還會把新的寫請求都快取在記憶體中,RDB檔案生成后,master會將這個RDB發送給slave的,slave拿到之后做的第一件事情就是寫進本地的磁盤,然后加載進記憶體,而在RDB快照之后新增的資料,則是通過AOF進行增量復制**保證同步的,

7、多個系統同時操作(并發)Redis帶來的資料問題?

? Redis是單執行緒的,并沒有鎖機制,各個客戶端之間的事務并不隔離,所以需要同步的話,要么就利用外部的分布式鎖來保證,要么就采取非鎖的方式來保證,

  • 加上分布式鎖,可以利用zookeeper實作分布式鎖,
  • 非鎖方式,那就是CAS,成本低、非阻塞、并發性能高,這個也是Redis提供的一個機制:watch,在redis事務中可以監聽某些key,如果key在這個事務未提交期間被其他客戶端修改過了,則本事務不會提交,會執行回滾,

8、如何保證快取與資料庫的雙寫一致性?

? 如果是嚴格要求資料一致性的,那么就是只能將讀請求和寫請求串行化了,資料絕對一致性,但是并發性嚴重拉跨,

? 如果不是嚴格要求資料一致性的,那么最經典的快取+資料庫讀寫的模式,就是 Cache Aside Pattern,

  • 讀的時候,先讀快取,快取沒有的話,就讀資料庫,然后取出資料后放入快取,同時回傳回應,

  • 更新的時候,先更新資料庫,然后再洗掉快取/更新快取

    • 洗掉快取還是更新快取,主要看是不是熱點資料,如果不是熱點資料,那么直接洗掉掉,等到之后有人訪問再存入快取即可,不然直接更新快取的話,這個資料又是經常修改但是不被經常用到,也就白白更新了,
    • 注意這樣同樣也會出現資料不一致的情況,就比如更新資料庫后洗掉快取失敗,那么資料庫是新資料,快取是舊資料,不一致了,一般可以采用延時雙刪,也就是多次洗掉確保洗掉成功,

9、為什么要用redis做快取

? 首先,為什么要快取?

  • 如果沒有快取,請求會頻繁地達到資料庫上,對資料庫的壓力較大,使用了快取,相當于在資料庫前加了一層屏障,不僅可以降低資料庫的壓力,還可以較快地回傳擊中快取的資料,

? 其次,為什么要用redis做快取?

  • redis是分布式快取,可以獨立部署,可以持久化,
  • redis是基于記憶體、單執行緒、多路復用的快取,速度快,效率高,

10、redis實作分布式鎖

? 先拿setnx來爭搶鎖,搶到之后,再用expire給鎖加一個過期時間防止鎖忘記了釋放,最后如果是手動釋放鎖的話,使用delete

? 而為了避免出現setnx之后執行expire之前行程意外crash或者要重啟維護了,導致鎖釋放不遼,可以直接在setnx命名后面帶上過期時間(即將setnx和expire結合成一條命令

11、如果redis正在給線上的業務提供服務,那使用keys指令模糊查找會有什么問題?

? Redis的單執行緒的,keys指令會導致執行緒阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復,

? 這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key串列,但是會有一定的重復概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長,不過在scan程序中,鍵可能會被修改, 所以增量式迭代命令只能對被回傳的元素提供有限的保證 ,

12、使用過Redis做異步佇列么,你是怎么用的?

一般使用list結構作為佇列,rpush生產訊息,lpop消費訊息(或者相反也可以),當lpop沒有訊息的時候,要適當sleep一會再重試,

如果不用sleep,可以使用的是blpop,在沒有訊息的時候,它會阻塞住直到訊息到來,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/168884.html

標籤:python

上一篇:從歷史資料看阿里云和華為云?

下一篇:Linux系列之下載安裝jdk-8u261-linux-i586.tar.gz

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more