欧美1区2区3区激情无套,两个女人互添下身视频在线观看,久久av无码精品人妻系列,久久精品噜噜噜成人,末发育娇小性色xxxx

Redis面試題

更多技術(shù)干貨、面試筆記、自學(xué)Java經(jīng)歷,可以關(guān)注微信公眾號:【程序員大彬】

本文目錄:

  • Redis是什么?
  • Redis優(yōu)缺點?
  • Redis為什么這么快?
  • Redis為何選擇單線程
  • Redis6.0為何引入多線程?
  • Redis應(yīng)用場景有哪些?
  • Memcached和Redis的區(qū)別?
  • Redis 數(shù)據(jù)類型有哪些?
  • keys命令存在的問題?
  • SortedSet和List異同點?
  • Redis事務(wù)
  • 持久化機制
    • RDB方式
    • AOF方式
  • RDB和AOF如何選擇?
  • Redis常見的部署方式有哪些?
  • 主從復(fù)制
  • 哨兵Sentinel
  • Redis cluster
    • 哈希分區(qū)算法有哪些?
  • 過期鍵的刪除策略?
  • 內(nèi)存淘汰策略有哪些?
  • 如何保證緩存與數(shù)據(jù)庫雙寫時的數(shù)據(jù)一致性?
  • 緩存穿透
  • 緩存雪崩
  • 緩存擊穿
  • Redis 怎么實現(xiàn)消息隊列?
  • pipeline的作用?
  • LUA腳本
  • 什么是RedLock?

Redis是什么?

Redis(Remote Dictionary Server)是一個使用 C 語言編寫的,高性能非關(guān)系型的鍵值對數(shù)據(jù)庫。與傳統(tǒng)數(shù)據(jù)庫不同的是,Redis 的數(shù)據(jù)是存在內(nèi)存中的,所以讀寫速度非???,被廣泛應(yīng)用于緩存方向。Redis可以將數(shù)據(jù)寫入磁盤中,保證了數(shù)據(jù)的安全不丟失,而且Redis的操作是原子性的。

Redis優(yōu)缺點?

優(yōu)點

  1. 基于內(nèi)存操作,內(nèi)存讀寫速度快。
  2. Redis是單線程的,避免線程切換開銷及多線程的競爭問題。單線程是指網(wǎng)絡(luò)請求使用一個線程來處理,即一個線程處理所有網(wǎng)絡(luò)請求,Redis 運行時不止有一個線程,比如數(shù)據(jù)持久化的過程會另起線程。
  3. 支持多種數(shù)據(jù)類型,包括String、Hash、List、Set、ZSet等。
  4. 支持持久化。Redis支持RDB和AOF兩種持久化機制,持久化功能可以有效地避免數(shù)據(jù)丟失問題。
  5. 支持事務(wù)。Redis的所有操作都是原子性的,同時Redis還支持對幾個操作合并后的原子性執(zhí)行。
  6. 支持主從復(fù)制。主節(jié)點會自動將數(shù)據(jù)同步到從節(jié)點,可以進(jìn)行讀寫分離。

缺點

  1. 對結(jié)構(gòu)化查詢的支持比較差。
  2. 數(shù)據(jù)庫容量受到物理內(nèi)存的限制,不適合用作海量數(shù)據(jù)的高性能讀寫,因此Redis適合的場景主要局限在較小數(shù)據(jù)量的操作。
  3. Redis 較難支持在線擴容,在集群容量達(dá)到上限時在線擴容會變得很復(fù)雜。

Redis為什么這么快?

  • 基于內(nèi)存:Redis是使用內(nèi)存存儲,沒有磁盤IO上的開銷。數(shù)據(jù)存在內(nèi)存中,讀寫速度快。

  • 單線程實現(xiàn)( Redis 6.0以前):Redis使用單個線程處理請求,避免了多個線程之間線程切換和鎖資源爭用的開銷。

  • IO多路復(fù)用模型:Redis 采用 IO 多路復(fù)用技術(shù)。Redis 使用單線程來輪詢描述符,將數(shù)據(jù)庫的操作都轉(zhuǎn)換成了事件,不在網(wǎng)絡(luò)I/O上浪費過多的時間。

  • 高效的數(shù)據(jù)結(jié)構(gòu):Redis 每種數(shù)據(jù)類型底層都做了優(yōu)化,目的就是為了追求更快的速度。

Redis為何選擇單線程

  • 避免過多的上下文切換開銷。程序始終運行在進(jìn)程中單個線程內(nèi),沒有多線程切換的場景。
  • 避免同步機制的開銷:如果 Redis選擇多線程模型,需要考慮數(shù)據(jù)同步的問題,則必然會引入某些同步機制,會導(dǎo)致在操作數(shù)據(jù)過程中帶來更多的開銷,增加程序復(fù)雜度的同時還會降低性能。
  • 實現(xiàn)簡單,方便維護:如果 Redis使用多線程模式,那么所有的底層數(shù)據(jù)結(jié)構(gòu)的設(shè)計都必須考慮線程安全問題,那么 Redis 的實現(xiàn)將會變得更加復(fù)雜。

Redis6.0為何引入多線程?

Redis支持多線程主要有兩個原因:

  • 可以充分利用服務(wù)器 CPU 資源,單線程模型的主線程只能利用一個cpu;

  • 多線程任務(wù)可以分?jǐn)?Redis 同步 IO 讀寫的負(fù)荷。

Redis應(yīng)用場景有哪些?

  1. 緩存熱點數(shù)據(jù),緩解數(shù)據(jù)庫的壓力。
  2. 利用 Redis 原子性的自增操作,可以實現(xiàn)計數(shù)器的功能,比如統(tǒng)計用戶點贊數(shù)、用戶訪問數(shù)等。
  3. 簡單的消息隊列,可以使用Redis自身的發(fā)布/訂閱模式或者List來實現(xiàn)簡單的消息隊列,實現(xiàn)異步操作。
  4. 限速器,可用于限制某個用戶訪問某個接口的頻率,比如秒殺場景用于防止用戶快速點擊帶來不必要的壓力。
  5. 好友關(guān)系,利用集合的一些命令,比如交集、并集、差集等,實現(xiàn)共同好友、共同愛好之類的功能。

Memcached和Redis的區(qū)別?

  1. Redis 只使用單核,而 Memcached 可以使用多核。
  2. MemCached 數(shù)據(jù)結(jié)構(gòu)單一,僅用來緩存數(shù)據(jù),而 Redis 支持多種數(shù)據(jù)類型
  3. MemCached 不支持?jǐn)?shù)據(jù)持久化,重啟后數(shù)據(jù)會消失。Redis 支持?jǐn)?shù)據(jù)持久化
  4. Redis 提供主從同步機制和 cluster 集群部署能力,能夠提供高可用服務(wù)。Memcached 沒有提供原生的集群模式,需要依靠客戶端實現(xiàn)往集群中分片寫入數(shù)據(jù)。
  5. Redis 的速度比 Memcached 快很多。
  6. Redis 使用單線程的多路 IO 復(fù)用模型,Memcached使用多線程的非阻塞 IO 模型。

Redis 數(shù)據(jù)類型有哪些?

基本數(shù)據(jù)類型

1、String:最常用的一種數(shù)據(jù)類型,String類型的值可以是字符串、數(shù)字或者二進(jìn)制,但值最大不能超過512MB。

2、Hash:Hash 是一個鍵值對集合。

3、Set:無序去重的集合。Set 提供了交集、并集等方法,對于實現(xiàn)共同好友、共同關(guān)注等功能特別方便。

4、List:有序可重復(fù)的集合,底層是依賴雙向鏈表實現(xiàn)的。

5、SortedSet:有序Set。內(nèi)部維護了一個score的參數(shù)來實現(xiàn)。適用于排行榜和帶權(quán)重的消息隊列等場景。

特殊的數(shù)據(jù)類型

1、Bitmap:位圖,可以認(rèn)為是一個以位為單位數(shù)組,數(shù)組中的每個單元只能存0或者1,數(shù)組的下標(biāo)在 Bitmap 中叫做偏移量。Bitmap的長度與集合中元素個數(shù)無關(guān),而是與基數(shù)的上限有關(guān)。

2、Hyperloglog。HyperLogLog 是用來做基數(shù)統(tǒng)計的算法,其優(yōu)點是,在輸入元素的數(shù)量或者體積非常非常大時,計算基數(shù)所需的空間總是固定的、并且是很小的。典型的使用場景是統(tǒng)計獨立訪客。

3、Geospatial :主要用于存儲地理位置信息,并對存儲的信息進(jìn)行操作,適用場景如定位、附近的人等。

keys命令存在的問題?

redis的單線程的。keys指令會導(dǎo)致線程阻塞一段時間,直到執(zhí)行完畢,服務(wù)才能恢復(fù)。scan采用漸進(jìn)式遍歷的方式來解決keys命令可能帶來的阻塞問題,每次scan命令的時間復(fù)雜度是O(1),但是要真正實現(xiàn)keys的功能,需要執(zhí)行多次scan。

scan的缺點:在scan的過程中如果有鍵的變化(增加、刪除、修改),遍歷過程可能會有以下問題:新增的鍵可能沒有遍歷到,遍歷出了重復(fù)的鍵等情況,也就是說scan并不能保證完整的遍歷出來所有的鍵。

SortedSet和List異同點?

相同點

  1. 都是有序的;
  2. 都可以獲得某個范圍內(nèi)的元素。

不同點:

  1. 列表基于鏈表實現(xiàn),獲取兩端元素速度快,訪問中間元素速度慢;
  2. 有序集合基于散列表和跳躍表實現(xiàn),訪問中間元素時間復(fù)雜度是OlogN;
  3. 列表不能簡單的調(diào)整某個元素的位置,有序列表可以(更改元素的分?jǐn)?shù));
  4. 有序集合更耗內(nèi)存。

Redis事務(wù)

事務(wù)的原理是將一個事務(wù)范圍內(nèi)的若干命令發(fā)送給Redis,然后再讓Redis依次執(zhí)行這些命令。

事務(wù)的生命周期:

  1. 使用MULTI開啟一個事務(wù)

  2. 在開啟事務(wù)的時候,每次操作的命令將會被插入到一個隊列中,同時這個命令并不會被真的執(zhí)行

  3. EXEC命令進(jìn)行提交事務(wù)

一個事務(wù)范圍內(nèi)某個命令出錯不會影響其他命令的執(zhí)行,不保證原子性:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> set b 1 2
QUEUED
127.0.0.1:6379> set c 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR syntax error
3) OK

WATCH命令

WATCH命令可以監(jiān)控一個或多個鍵,一旦其中有一個鍵被修改,之后的事務(wù)就不會執(zhí)行(類似于樂觀鎖)。執(zhí)行EXEC命令之后,就會自動取消監(jiān)控。

127.0.0.1:6379> watch name
OK
127.0.0.1:6379> set name 1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name 2
QUEUED
127.0.0.1:6379> set gender 1
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get gender
(nil)

比如上面的代碼中:

  1. watch name開啟了對name這個key的監(jiān)控
  2. 修改name的值
  3. 開啟事務(wù)a
  4. 在事務(wù)a中設(shè)置了namegender的值
  5. 使用EXEC命令進(jìn)提交事務(wù)
  6. 使用命令get gender發(fā)現(xiàn)不存在,即事務(wù)a沒有執(zhí)行

使用UNWATCH可以取消WATCH命令對key的監(jiān)控,所有監(jiān)控鎖將會被取消。

持久化機制

持久化就是把內(nèi)存的數(shù)據(jù)寫到磁盤中,防止服務(wù)宕機導(dǎo)致內(nèi)存數(shù)據(jù)丟失。

Redis支持兩種方式的持久化,一種是RDB的方式,一種是AOF的方式。前者會根據(jù)指定的規(guī)則定時將內(nèi)存中的數(shù)據(jù)存儲在硬盤上,而后者在每次執(zhí)行完命令后將命令記錄下來。一般將兩者結(jié)合使用。

RDB方式

RDB是 Redis 默認(rèn)的持久化方案。RDB持久化時會將內(nèi)存中的數(shù)據(jù)寫入到磁盤中,在指定目錄下生成一個dump.rdb文件。Redis 重啟會加載dump.rdb文件恢復(fù)數(shù)據(jù)。

bgsave是主流的觸發(fā) RDB 持久化的方式,執(zhí)行過程如下:

  • 執(zhí)行BGSAVE命令
  • Redis 父進(jìn)程判斷當(dāng)前是否存在正在執(zhí)行的子進(jìn)程,如果存在,BGSAVE命令直接返回。
  • 父進(jìn)程執(zhí)行fork操作創(chuàng)建子進(jìn)程,fork操作過程中父進(jìn)程會阻塞。
  • 父進(jìn)程fork完成后,父進(jìn)程繼續(xù)接收并處理客戶端的請求,而子進(jìn)程開始將內(nèi)存中的數(shù)據(jù)寫進(jìn)硬盤的臨時文件
  • 當(dāng)子進(jìn)程寫完所有數(shù)據(jù)后會用該臨時文件替換舊的 RDB 文件。

Redis啟動時會讀取RDB快照文件,將數(shù)據(jù)從硬盤載入內(nèi)存。通過 RDB 方式的持久化,一旦Redis異常退出,就會丟失最近一次持久化以后更改的數(shù)據(jù)。

觸發(fā) RDB 持久化的方式:

  1. 手動觸發(fā):用戶執(zhí)行SAVEBGSAVE命令。SAVE命令執(zhí)行快照的過程會阻塞所有客戶端的請求,應(yīng)避免在生產(chǎn)環(huán)境使用此命令。BGSAVE命令可以在后臺異步進(jìn)行快照操作,快照的同時服務(wù)器還可以繼續(xù)響應(yīng)客戶端的請求,因此需要手動執(zhí)行快照時推薦使用BGSAVE命令。

  2. 被動觸發(fā)

    • 根據(jù)配置規(guī)則進(jìn)行自動快照,如SAVE 100 10,100秒內(nèi)至少有10個鍵被修改則進(jìn)行快照。
    • 如果從節(jié)點執(zhí)行全量復(fù)制操作,主節(jié)點會自動執(zhí)行BGSAVE生成 RDB 文件并發(fā)送給從節(jié)點。
    • 默認(rèn)情況下執(zhí)行shutdown命令時,如果沒有開啟 AOF 持久化功能則自動執(zhí)行·BGSAVE·。

優(yōu)點

  1. Redis 加載 RDB 恢復(fù)數(shù)據(jù)遠(yuǎn)遠(yuǎn)快于 AOF 的方式。
  2. 使用單獨子進(jìn)程來進(jìn)行持久化,主進(jìn)程不會進(jìn)行任何 IO 操作,保證了 Redis 的高性能。

缺點

  1. RDB方式數(shù)據(jù)無法做到實時持久化。因為BGSAVE每次運行都要執(zhí)行fork操作創(chuàng)建子進(jìn)程,屬于重量級操作,頻繁執(zhí)行成本比較高。
  2. RDB 文件使用特定二進(jìn)制格式保存,Redis 版本升級過程中有多個格式的 RDB 版本,存在老版本 Redis 無法兼容新版 RDB 格式的問題。

AOF方式

AOF(append only file)持久化:以獨立日志的方式記錄每次寫命令,Redis重啟時會重新執(zhí)行AOF文件中的命令達(dá)到恢復(fù)數(shù)據(jù)的目的。AOF的主要作用是解決了數(shù)據(jù)持久化的實時性,AOF 是Redis持久化的主流方式。

默認(rèn)情況下Redis沒有開啟AOF方式的持久化,可以通過appendonly參數(shù)啟用:appendonly yes。開啟AOF方式持久化后每執(zhí)行一條寫命令,Redis就會將該命令寫進(jìn)aof_buf緩沖區(qū),AOF緩沖區(qū)根據(jù)對應(yīng)的策略向硬盤做同步操作。

默認(rèn)情況下系統(tǒng)每30秒會執(zhí)行一次同步操作。為了防止緩沖區(qū)數(shù)據(jù)丟失,可以在Redis寫入AOF文件后主動要求系統(tǒng)將緩沖區(qū)數(shù)據(jù)同步到硬盤上??梢酝ㄟ^appendfsync參數(shù)設(shè)置同步的時機。

appendfsync always //每次寫入aof文件都會執(zhí)行同步,最安全最慢,不建議配置
appendfsync everysec  //既保證性能也保證安全,建議配置
appendfsync no //由操作系統(tǒng)決定何時進(jìn)行同步操作

接下來看一下 AOF 持久化執(zhí)行流程:

  1. 所有的寫入命令會追加到 AOP 緩沖區(qū)中。
  2. AOF 緩沖區(qū)根據(jù)對應(yīng)的策略向硬盤同步。
  3. 隨著 AOF 文件越來越大,需要定期對 AOF 文件進(jìn)行重寫,達(dá)到壓縮文件體積的目的。AOF文件重寫是把Redis進(jìn)程內(nèi)的數(shù)據(jù)轉(zhuǎn)化為寫命令同步到新AOF文件的過程。
  4. 當(dāng) Redis 服務(wù)器重啟時,可以加載 AOF 文件進(jìn)行數(shù)據(jù)恢復(fù)。

優(yōu)點

  1. AOF可以更好的保護數(shù)據(jù)不丟失,可以配置 AOF 每秒執(zhí)行一次fsync操作,如果Redis進(jìn)程掛掉,最多丟失1秒的數(shù)據(jù)。
  2. AOF以append-only的模式寫入,所以沒有磁盤尋址的開銷,寫入性能非常高。

缺點

  1. 對于同一份文件AOF文件比RDB數(shù)據(jù)快照要大。
  2. 數(shù)據(jù)恢復(fù)比較慢。

RDB和AOF如何選擇?

通常來說,應(yīng)該同時使用兩種持久化方案,以保證數(shù)據(jù)安全。

  • 如果數(shù)據(jù)不敏感,且可以從其他地方重新生成,可以關(guān)閉持久化。
  • 如果數(shù)據(jù)比較重要,且能夠承受幾分鐘的數(shù)據(jù)丟失,比如緩存等,只需要使用RDB即可。
  • 如果是用做內(nèi)存數(shù)據(jù),要使用Redis的持久化,建議是RDB和AOF都開啟。
  • 如果只用AOF,優(yōu)先使用everysec的配置選擇,因為它在可靠性和性能之間取了一個平衡。

當(dāng)RDB與AOF兩種方式都開啟時,Redis會優(yōu)先使用AOF恢復(fù)數(shù)據(jù),因為AOF保存的文件比RDB文件更完整。

Redis常見的部署方式有哪些?

Redis的幾種常見使用方式包括:

  • 單機版
  • Redis主從
  • Redis Sentinel(哨兵)
  • Redis Cluster

使用場景

單機版:很少使用。存在的問題:1、內(nèi)存容量有限 2、處理能力有限 3、無法高可用。

主從模式:master 節(jié)點掛掉后,需要手動指定新的 master,可用性不高,基本不用。

哨兵模式:master 節(jié)點掛掉后,哨兵進(jìn)程會主動選舉新的 master,可用性高,但是每個節(jié)點存儲的數(shù)據(jù)是一樣的,浪費內(nèi)存空間。數(shù)據(jù)量不是很多,集群規(guī)模不是很大,需要自動容錯容災(zāi)的時候使用。

Redis cluster:主要是針對海量數(shù)據(jù)+高并發(fā)+高可用的場景,如果是海量數(shù)據(jù),如果你的數(shù)據(jù)量很大,那么建議就用Redis cluster,所有主節(jié)點的容量總和就是Redis cluster可緩存的數(shù)據(jù)容量。

主從復(fù)制

Redis的復(fù)制功能是支持多個數(shù)據(jù)庫之間的數(shù)據(jù)同步。主數(shù)據(jù)庫可以進(jìn)行讀寫操作,當(dāng)主數(shù)據(jù)庫的數(shù)據(jù)發(fā)生變化時會自動將數(shù)據(jù)同步到從數(shù)據(jù)庫。從數(shù)據(jù)庫一般是只讀的,它會接收主數(shù)據(jù)庫同步過來的數(shù)據(jù)。一個主數(shù)據(jù)庫可以有多個從數(shù)據(jù)庫,而一個從數(shù)據(jù)庫只能有一個主數(shù)據(jù)庫。

redis-server //啟動Redis實例作為主數(shù)據(jù)庫 
redis-server --port 6380 --slaveof  127.0.0.1 6379  //啟動另一個實例作為從數(shù)據(jù)庫 
slaveof 127.0.0.1 6379
SLAVEOF NO ONE //停止接收其他數(shù)據(jù)庫的同步并轉(zhuǎn)化為主數(shù)據(jù)庫。

主從復(fù)制的原理?

  1. 當(dāng)啟動一個從節(jié)點時,它會發(fā)送一個 PSYNC 命令給主節(jié)點;
  2. 如果是從節(jié)點初次連接到主節(jié)點,那么會觸發(fā)一次全量復(fù)制。此時主節(jié)點會啟動一個后臺線程,開始生成一份 RDB 快照文件;
  3. 同時還會將從客戶端 client 新收到的所有寫命令緩存在內(nèi)存中。RDB 文件生成完畢后, 主節(jié)點會將RDB文件發(fā)送給從節(jié)點,從節(jié)點會先將RDB文件寫入本地磁盤,然后再從本地磁盤加載到內(nèi)存中;
  4. 接著主節(jié)點會將內(nèi)存中緩存的寫命令發(fā)送到從節(jié)點,從節(jié)點同步這些數(shù)據(jù);
  5. 如果從節(jié)點跟主節(jié)點之間網(wǎng)絡(luò)出現(xiàn)故障,連接斷開了,會自動重連,連接之后主節(jié)點僅會將部分缺失的數(shù)據(jù)同步給從節(jié)點。

哨兵Sentinel

主從復(fù)制存在不能自動故障轉(zhuǎn)移、達(dá)不到高可用的問題。哨兵模式解決了這些問題。通過哨兵機制可以自動切換主從節(jié)點。

客戶端連接Redis的時候,先連接哨兵,哨兵會告訴客戶端Redis主節(jié)點的地址,然后客戶端連接上Redis并進(jìn)行后續(xù)的操作。當(dāng)主節(jié)點宕機的時候,哨兵監(jiān)測到主節(jié)點宕機,會重新推選出某個表現(xiàn)良好的從節(jié)點成為新的主節(jié)點,然后通過發(fā)布訂閱模式通知其他的從服務(wù)器,讓它們切換主機。

工作原理

  • 每個Sentinel以每秒鐘一次的頻率向它所知道的Master,Slave以及其他 Sentinel實例發(fā)送一個 PING命令。
  • 如果一個實例距離最后一次有效回復(fù) PING 命令的時間超過指定值, 則這個實例會被 Sentine 標(biāo)記為主觀下線。
  • 如果一個Master被標(biāo)記為主觀下線,則正在監(jiān)視這個Master的所有 Sentinel要以每秒一次的頻率確認(rèn)Master是否真正進(jìn)入主觀下線狀態(tài)。
  • 當(dāng)有足夠數(shù)量的 Sentinel(大于等于配置文件指定值)在指定的時間范圍內(nèi)確認(rèn)Master的確進(jìn)入了主觀下線狀態(tài), 則Master會被標(biāo)記為客觀下線 。若沒有足夠數(shù)量的 Sentinel同意 Master 已經(jīng)下線, Master 的客觀下線狀態(tài)就會被解除。 若 Master重新向 SentinelPING 命令返回有效回復(fù), Master 的主觀下線狀態(tài)就會被移除。
  • 哨兵節(jié)點會選舉出哨兵 leader,負(fù)責(zé)故障轉(zhuǎn)移的工作。
  • 哨兵 leader 會推選出某個表現(xiàn)良好的從節(jié)點成為新的主節(jié)點,然后通知其他從節(jié)點更新主節(jié)點信息。

Redis cluster

哨兵模式解決了主從復(fù)制不能自動故障轉(zhuǎn)移、達(dá)不到高可用的問題,但還是存在主節(jié)點的寫能力、容量受限于單機配置的問題。而cluster模式實現(xiàn)了Redis的分布式存儲,每個節(jié)點存儲不同的內(nèi)容,解決主節(jié)點的寫能力、容量受限于單機配置的問題。

Redis cluster集群節(jié)點最小配置6個節(jié)點以上(3主3從),其中主節(jié)點提供讀寫操作,從節(jié)點作為備用節(jié)點,不提供請求,只作為故障轉(zhuǎn)移使用。

Redis cluster采用虛擬槽分區(qū),所有的鍵根據(jù)哈希函數(shù)映射到0~16383個整數(shù)槽內(nèi),每個節(jié)點負(fù)責(zé)維護一部分槽以及槽所映射的鍵值數(shù)據(jù)。

哈希槽是如何映射到 Redis 實例上的?

  1. 對鍵值對的key使用 crc16 算法計算一個結(jié)果
  2. 將結(jié)果對 16384 取余,得到的值表示 key 對應(yīng)的哈希槽
  3. 根據(jù)該槽信息定位到對應(yīng)的實例

優(yōu)點:

  • 無中心架構(gòu),支持動態(tài)擴容;
  • 數(shù)據(jù)按照slot存儲分布在多個節(jié)點,節(jié)點間數(shù)據(jù)共享,可動態(tài)調(diào)整數(shù)據(jù)分布;
  • 高可用性。部分節(jié)點不可用時,集群仍可用。集群模式能夠?qū)崿F(xiàn)自動故障轉(zhuǎn)移(failover),節(jié)點之間通過gossip協(xié)議交換狀態(tài)信息,用投票機制完成SlaveMaster的角色轉(zhuǎn)換。

缺點:

  • 不支持批量操作(pipeline)。
  • 數(shù)據(jù)通過異步復(fù)制,不保證數(shù)據(jù)的強一致性
  • 事務(wù)操作支持有限,只支持多key在同一節(jié)點上的事務(wù)操作,當(dāng)多個key分布于不同的節(jié)點上時無法使用事務(wù)功能。
  • key作為數(shù)據(jù)分區(qū)的最小粒度,不能將一個很大的鍵值對象如hash、list等映射到不同的節(jié)點。
  • 不支持多數(shù)據(jù)庫空間,單機下的Redis可以支持到16個數(shù)據(jù)庫,集群模式下只能使用1個數(shù)據(jù)庫空間。

哈希分區(qū)算法有哪些?

節(jié)點取余分區(qū)。使用特定的數(shù)據(jù),如Redis的鍵或用戶ID,對節(jié)點數(shù)量N取余:hash(key)%N計算出哈希值,用來決定數(shù)據(jù)映射到哪一個節(jié)點上。
優(yōu)點是簡單性。擴容時通常采用翻倍擴容,避免數(shù)據(jù)映射全部被打亂導(dǎo)致全量遷移的情況。

一致性哈希分區(qū)。為系統(tǒng)中每個節(jié)點分配一個token,范圍一般在0~232,這些token構(gòu)成一個哈希環(huán)。數(shù)據(jù)讀寫執(zhí)行節(jié)點查找操作時,先根據(jù)key計算hash值,然后順時針找到第一個大于等于該哈希值的token節(jié)點。
這種方式相比節(jié)點取余最大的好處在于加入和刪除節(jié)點只影響哈希環(huán)中相鄰的節(jié)點,對其他節(jié)點無影響。

虛擬槽分區(qū),所有的鍵根據(jù)哈希函數(shù)映射到0~16383整數(shù)槽內(nèi),計算公式:slot=CRC16(key)&16383。每一個節(jié)點負(fù)責(zé)維護一部分槽以及槽所映射的鍵值數(shù)據(jù)。Redis Cluser采用虛擬槽分區(qū)算法。

過期鍵的刪除策略?

1、被動刪除。在訪問key時,如果發(fā)現(xiàn)key已經(jīng)過期,那么會將key刪除。

2、主動刪除。定時清理key,每次清理會依次遍歷所有DB,從db隨機取出20個key,如果過期就刪除,如果其中有5個key過期,那么就繼續(xù)對這個db進(jìn)行清理,否則開始清理下一個db。

3、內(nèi)存不夠時清理。Redis有最大內(nèi)存的限制,通過maxmemory參數(shù)可以設(shè)置最大內(nèi)存,當(dāng)使用的內(nèi)存超過了設(shè)置的最大內(nèi)存,就要進(jìn)行內(nèi)存釋放, 在進(jìn)行內(nèi)存釋放的時候,會按照配置的淘汰策略清理內(nèi)存。

內(nèi)存淘汰策略有哪些?

當(dāng)Redis的內(nèi)存超過最大允許的內(nèi)存之后,Redis 會觸發(fā)內(nèi)存淘汰策略,刪除一些不常用的數(shù)據(jù),以保證Redis服務(wù)器正常運行。

Redisv4.0前提供 6 種數(shù)據(jù)淘汰策略

  • volatile-lru:LRU(Least Recently Used),最近使用。利用LRU算法移除設(shè)置了過期時間的key
  • allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,從數(shù)據(jù)集中移除最近最少使用的key
  • volatile-ttl:從已設(shè)置過期時間的數(shù)據(jù)集中挑選將要過期的數(shù)據(jù)淘汰
  • volatile-random:從已設(shè)置過期時間的數(shù)據(jù)集中任意選擇數(shù)據(jù)淘汰
  • allkeys-random:從數(shù)據(jù)集中任意選擇數(shù)據(jù)淘汰
  • no-eviction:禁止刪除數(shù)據(jù),當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,新寫入操作會報錯

Redisv4.0后增加以下兩種

  • volatile-lfu:LFU,Least Frequently Used,最少使用,從已設(shè)置過期時間的數(shù)據(jù)集中挑選最不經(jīng)常使用的數(shù)據(jù)淘汰。
  • allkeys-lfu:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,從數(shù)據(jù)集中移除最不經(jīng)常使用的key。

內(nèi)存淘汰策略可以通過配置文件來修改,相應(yīng)的配置項是maxmemory-policy,默認(rèn)配置是noeviction。

如何保證緩存與數(shù)據(jù)庫雙寫時的數(shù)據(jù)一致性?

1、先刪除緩存再更新數(shù)據(jù)庫

進(jìn)行更新操作時,先刪除緩存,然后更新數(shù)據(jù)庫,后續(xù)的請求再次讀取時,會從數(shù)據(jù)庫讀取后再將新數(shù)據(jù)更新到緩存。

存在的問題:刪除緩存數(shù)據(jù)之后,更新數(shù)據(jù)庫完成之前,這個時間段內(nèi)如果有新的讀請求過來,就會從數(shù)據(jù)庫讀取舊數(shù)據(jù)重新寫到緩存中,再次造成不一致,并且后續(xù)讀的都是舊數(shù)據(jù)。

2、先更新數(shù)據(jù)庫再刪除緩存

進(jìn)行更新操作時,先更新MySQL,成功之后,刪除緩存,后續(xù)讀取請求時再將新數(shù)據(jù)回寫緩存。

存在的問題:更新MySQL和刪除緩存這段時間內(nèi),請求讀取的還是緩存的舊數(shù)據(jù),不過等數(shù)據(jù)庫更新完成,就會恢復(fù)一致,影響相對比較小。

3、異步更新緩存

數(shù)據(jù)庫的更新操作完成后不直接操作緩存,而是把這個操作命令封裝成消息扔到消息隊列中,然后由Redis自己去消費更新數(shù)據(jù),消息隊列可以保證數(shù)據(jù)操作順序一致性,確保緩存系統(tǒng)的數(shù)據(jù)正常。

緩存穿透

緩存穿透是指查詢一個不存在的數(shù)據(jù),由于緩存是不命中時被動寫的,如果從DB查不到數(shù)據(jù)則不寫入緩存,這將導(dǎo)致這個不存在的數(shù)據(jù)每次請求都要到DB去查詢,失去了緩存的意義。在流量大時,可能DB就掛掉了。

  1. 緩存空值,不會查數(shù)據(jù)庫。
  2. 采用布隆過濾器,將所有可能存在的數(shù)據(jù)哈希到一個足夠大的bitmap中,查詢不存在的數(shù)據(jù)會被這個bitmap攔截掉,從而避免了對DB的查詢壓力。

布隆過濾器的原理:當(dāng)一個元素被加入集合時,通過K個散列函數(shù)將這個元素映射成一個位數(shù)組中的K個點,把它們置為1。查詢時,將元素通過散列函數(shù)映射之后會得到k個點,如果這些點有任何一個0,則被檢元素一定不在,直接返回;如果都是1,則查詢元素很可能存在,就會去查詢Redis和數(shù)據(jù)庫。

緩存雪崩

緩存雪崩是指在我們設(shè)置緩存時采用了相同的過期時間,導(dǎo)致緩存在某一時刻同時失效,請求全部轉(zhuǎn)發(fā)到DB,DB瞬時壓力過重掛掉。

解決方法:在原有的失效時間基礎(chǔ)上增加一個隨機值,使得過期時間分散一些。

緩存擊穿

緩存擊穿:大量的請求同時查詢一個 key 時,此時這個 key 正好失效了,就會導(dǎo)致大量的請求都落到數(shù)據(jù)庫。緩存擊穿是查詢緩存中失效的 key,而緩存穿透是查詢不存在的 key。

解決方法:加分布式鎖,第一個請求的線程可以拿到鎖,拿到鎖的線程查詢到了數(shù)據(jù)之后設(shè)置緩存,其他的線程獲取鎖失敗會等待50ms然后重新到緩存取數(shù)據(jù),這樣便可以避免大量的請求落到數(shù)據(jù)庫。

public String get(String key) {
    String value = redis.get(key);
    if (value == null) { //緩存值過期
        String unique_key = systemId + ":" + key;
        //設(shè)置30s的超時
        if (redis.set(unique_key, 1, 'NX', 'PX', 30000) == 1) {  //設(shè)置成功
            value = db.get(key);
            redis.set(key, value, expire_secs);
            redis.del(unique_key);
        } else {  //其他線程已經(jīng)到數(shù)據(jù)庫取值并回寫到緩存了,可以重試獲取緩存值
            sleep(50);
            get(key);  //重試
        }
    } else {
        return value;
    }
}

Redis 怎么實現(xiàn)消息隊列?

使用一個列表,讓生產(chǎn)者將任務(wù)使用LPUSH命令放進(jìn)列表,消費者不斷用RPOP從列表取出任務(wù)。

BRPOP和RPOP命令相似,唯一的區(qū)別就是當(dāng)列表沒有元素時BRPOP命令會一直阻塞連接,直到有新元素加入。

BRPOP queue 0  //0表示不限制等待時間

優(yōu)先級隊列

如果多個鍵都有元素,則按照從左到右的順序取元素。

BLPOP queue:1 queue:2 queue:3 0

發(fā)布/訂閱模式

PSUBSCRIBE channel?* 按照規(guī)則訂閱。
PUNSUBSCRIBE channel?* 退訂通過PSUBSCRIBE命令按照某種規(guī)則訂閱的頻道。其中訂閱規(guī)則要進(jìn)行嚴(yán)格的字符串匹配,PUNSUBSCRIBE *無法退訂channel?*規(guī)則。

PUBLISH channel1 hi
SUBSCRIBE channel1
UNSUBSCRIBE channel1 //退訂通過SUBSCRIBE命令訂閱的頻道。

缺點:在消費者下線的情況下,生產(chǎn)的消息會丟失。

延時隊列

使用sortedset,拿時間戳作為score,消息內(nèi)容作為key,調(diào)用zadd來生產(chǎn)消息,消費者用zrangebyscore指令獲取N秒之前的數(shù)據(jù)輪詢進(jìn)行處理。

pipeline的作用?

redis客戶端執(zhí)行一條命令分4個過程: 發(fā)送命令、命令排隊、命令執(zhí)行、返回結(jié)果。使用pipeline可以批量請求,批量返回結(jié)果,執(zhí)行速度比逐條執(zhí)行要快。

使用pipeline組裝的命令個數(shù)不能太多,不然數(shù)據(jù)量過大,增加客戶端的等待時間,還可能造成網(wǎng)絡(luò)阻塞,可以將大量命令的拆分多個小的pipeline命令完成。

原生批命令(mset和mget)與pipeline對比:

  1. 原生批命令是原子性,pipeline非原子性。pipeline命令中途異常退出,之前執(zhí)行成功的命令不會回滾。

  2. 原生批命令只有一個命令,但pipeline支持多命令。

LUA腳本

Redis 通過 LUA 腳本創(chuàng)建具有原子性的命令: 當(dāng)lua腳本命令正在運行的時候,不會有其他腳本或 Redis 命令被執(zhí)行,實現(xiàn)組合命令的原子操作。

在Redis中執(zhí)行Lua腳本有兩種方法:evalevalsha。eval命令使用內(nèi)置的 Lua 解釋器,對 Lua 腳本進(jìn)行求值。

//第一個參數(shù)是lua腳本,第二個參數(shù)是鍵名參數(shù)個數(shù),剩下的是鍵名參數(shù)和附加參數(shù)
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

lua腳本作用

1、Lua腳本在Redis中是原子執(zhí)行的,執(zhí)行過程中間不會插入其他命令。

2、Lua腳本可以將多條命令一次性打包,有效地減少網(wǎng)絡(luò)開銷。

應(yīng)用場景

舉例:限制接口訪問頻率。

在Redis維護一個接口訪問次數(shù)的鍵值對,key是接口名稱,value是訪問次數(shù)。每次訪問接口時,會執(zhí)行以下操作:

  • 通過aop攔截接口的請求,對接口請求進(jìn)行計數(shù),每次進(jìn)來一個請求,相應(yīng)的接口訪問次數(shù)count加1,存入redis。
  • 如果是第一次請求,則會設(shè)置count=1,并設(shè)置過期時間。因為這里set()expire()組合操作不是原子操作,所以引入lua腳本,實現(xiàn)原子操作,避免并發(fā)訪問問題。
  • 如果給定時間范圍內(nèi)超過最大訪問次數(shù),則會拋出異常。
private String buildLuaScript() {
    return "local c" +
        "\nc = redis.call('get',KEYS[1])" +
        "\nif c and tonumber(c) > tonumber(ARGV[1]) then" +
        "\nreturn c;" +
        "\nend" +
        "\nc = redis.call('incr',KEYS[1])" +
        "\nif tonumber(c) == 1 then" +
        "\nredis.call('expire',KEYS[1],ARGV[2])" +
        "\nend" +
        "\nreturn c;";
}

String luaScript = buildLuaScript();
RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period());

PS:這種接口限流的實現(xiàn)方式比較簡單,問題也比較多,一般不會使用,接口限流用的比較多的是令牌桶算法和漏桶算法。

什么是RedLock?

Redis 官方站提出了一種權(quán)威的基于 Redis 實現(xiàn)分布式鎖的方式名叫 Redlock,此種方式比原先的單節(jié)點的方法更安全。它可以保證以下特性:

  1. 安全特性:互斥訪問,即永遠(yuǎn)只有一個 client 能拿到鎖
  2. 避免死鎖:最終 client 都可能拿到鎖,不會出現(xiàn)死鎖的情況,即使原本鎖住某資源的 client 掛掉了
  3. 容錯性:只要大部分 Redis 節(jié)點存活就可以正常提供服務(wù)

更多技術(shù)干貨、面試筆記、自學(xué)Java經(jīng)歷,可以關(guān)注微信公眾號:【程序員大彬】

#Java求職##Redis##Java##面經(jīng)#
全部評論
點個贊
3 回復(fù) 分享
發(fā)布于 2022-02-16 00:48
假設(shè)使用sentinel,一主三從部署,怎么避免鎖丟失的問題,RedLock簡單概況只是在集群模式下,N/2+1個master節(jié)點獲取鎖成功才算真正成功,可以大大降低丟失的概率,但是主從下怎么避免
點贊 回復(fù) 分享
發(fā)布于 2022-02-19 22:28
可以?收藏了
點贊 回復(fù) 分享
發(fā)布于 2022-02-16 19:00

相關(guān)推薦

1.&nbsp;常見的行內(nèi)元素2.&nbsp;網(wǎng)頁中head部分的meta標(biāo)簽有什么作用3.&nbsp;子元素如何對父元素實現(xiàn)水平垂直居中4.&nbsp;有用過grid嗎?(了解過,但是沒在項目中使用過)5.&nbsp;css中rem和em的區(qū)別6.&nbsp;列舉position的值7.&nbsp;偽類和偽元素有什么區(qū)別8.&nbsp;canvas和svg有什么區(qū)別(回答了不熟悉canvas,svg使用一般是直接在網(wǎng)站中選取svg圖標(biāo)插入)9.&nbsp;了解過svg的圖標(biāo)是如何實現(xiàn)的嗎10.&nbsp;列舉數(shù)組有哪些方法11.&nbsp;針對回答的方法,問了push、splice、shift、slice、fliter哪些是改變原數(shù)組,哪些不改變12.&nbsp;講一下防抖和節(jié)流的使用場景13.&nbsp;字符串反轉(zhuǎn)&nbsp;說一下思路14.&nbsp;數(shù)組隨機打亂&nbsp;說一下思路15.&nbsp;vue3中的hooks方法用過嗎?&nbsp;(我只說了生命周期,好像不是16.&nbsp;computed和watch的區(qū)別17.&nbsp;列表渲染時,key有什么作用18.&nbsp;圖片懶加載具體是什么實現(xiàn)的(使用了vueuse中的useIntersectionObserver)19.&nbsp;知道vueuse的作者嗎(這個真不知道)20.&nbsp;之前自己有寫過一個hooks嗎21.&nbsp;有哪些常見的持久化方案22.&nbsp;cookies、sessionstorage、localstorage常見的使用場景23.&nbsp;了解或者使用過ts嗎(只看了一點ts,面試官還是比較看重這里的,表示ts一定要會24.&nbsp;package.json文件是干什么用的?25.&nbsp;package-lock.json文件和package.json有什么區(qū)別?26.&nbsp;列舉發(fā)起網(wǎng)絡(luò)請求常見的http的header27.&nbsp;說一下http的狀態(tài)碼28.&nbsp;有哪些途徑學(xué)習(xí)前端的知識29.&nbsp;有提到過在掘金看帖子,還問我有沒有固定關(guān)注的人或者訂閱的專欄30.&nbsp;未來對于前端學(xué)習(xí)有什么規(guī)劃嗎31.&nbsp;怎么看別人都說ai會取代前端?說說看法反問:針對面試表現(xiàn)能不能提一些未來學(xué)習(xí)的建議?一定要會ts,前端工程化方面雖然使用不多,但是也要掌握滴滴流程很快,十分鐘內(nèi)就知道結(jié)果了。#??虯I配圖神器#
點贊 評論 收藏
分享
05-10 16:15
已編輯
廣東第二師范學(xué)院 Java
醫(yī)療類的公司,主要業(yè)務(wù)也是和醫(yī)療有關(guān)的。不過我項目一個是代駕,一個是IM,沒啥相關(guān)的,全程聊天,沒啥八股更沒手撕,只能整理出一些問題。1.自我介紹————xxx學(xué)校,java學(xué)習(xí)路線,學(xué)習(xí)接觸了什么項目,算法刷多少,八股背多少,我上來就說我java路線走完了,八股背很多。2.介紹一下項目———b站學(xué)的,跟著做的,做過大街類項目外賣點評,音視頻和簡歷上的代駕和IM等3.項目是怎么開發(fā)的,怎么學(xué)的———跟著視頻學(xué)的4.前端是自己開發(fā)的嗎———不是,項目給的模板。我說我前端就學(xué)幾個月,更習(xí)慣用trae,面試官笑了5.IM是怎么實現(xiàn)的———netty或者spring都有websocket,用websocket實現(xiàn)。沒多問了6.項目中支付是怎么做的———我說支付一般是微信,微信支付個人開不了,一般是模擬支付。7.談?wù)勀銓uture的理解———異步獲取任務(wù)結(jié)果,runnable任務(wù)callable的get8.如果支付鏈路很長怎么辦———completefuture處理支付前的業(yè)務(wù)邏輯,然后支付用mq異步9.redis和mongodb在項目中怎么用的———redis保存臨時的經(jīng)緯度,mongodb持久化用10.哪些地方用的回調(diào)———忘記怎么答了11.項目怎么部署的,本機還是虛擬機———虛擬機,Linux,用docker搜一下很快12.有做過多節(jié)點部署嗎?———nginx嗎,有了解,但我沒真做過13.jvm了解嗎———我說我八股賊熟,隨便問,然后面試官讓說下gc,我說那就說下cms和g1,然后就是關(guān)于這2個垃圾回收器的特點14.你對我們公司了解嗎———了解(看過ssob主頁)15.學(xué)校在哪里,實習(xí)怎么辦———我說租房...下面開始我的反問1.實習(xí)生主要干什么———完成我們分配拆解的項目需求...2.部門業(yè)務(wù)是什么,有多少開發(fā)員工———醫(yī)療....,大概有30多個開發(fā)3.實習(xí)福利待遇———面試官自己不清楚4.公司加班調(diào)休策略———加班算調(diào)休,可以替換第二天的時間整個面試不到半小時,感覺可能項目不匹配沒啥好問題#面試問題記錄# #java# #26實習(xí)#
查看34道真題和解析 面試問題記錄
點贊 評論 收藏
分享
評論
88
688
分享

創(chuàng)作者周榜

更多
??途W(wǎng)
??推髽I(yè)服務(wù)