日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis常见面试题详解

發布時間:2023/12/19 数据库 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis常见面试题详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1. Redis
        • 1.1 Redis可以用來做什么?
        • 1.2 Redis和傳統的關系型數據庫有什么不同?
        • 1.3 Redis有哪些數據類型?
        • 1.4 Redis是單線程的,為什么還能這么快?
        • 1.5 Redis在持久化時fork出一個子進程,這時已經有兩個進程了,怎么能說是單線程呢?
        • 1.6 set和zset有什么區別?
        • 1.7 說一下Redis中的watch命令
        • 1.8 說說Redis中List結構的相關操作
        • 1.9 你要如何設計Redis的過期時間?
        • 1.10 Redis中,sexnx命令的返回值是什么,如何使用該命令實現分布式鎖?
        • 1.11 說一說Redis的持久化策略
        • 1.12 如何實現Redis的高可用?
        • 1.13 Redis的主從同步是如何實現的?
        • 1.14 Redis為什么存的快,內存斷電數據怎么恢復?
        • 1.15 說一說Redis的緩存淘汰策略
        • 1.16 請介紹一下Redis的過期策略
        • 1.17 緩存穿透、緩存擊穿、緩存雪崩有什么區別,該如何解決?
        • 1.18 如何保證緩存與數據庫的雙寫一致性?
        • 1.19 請介紹Redis集群的實現方案
        • 1.20 說一說Redis集群的分片機制
        • 1.21 說一說Redis集群的應用和優劣勢
        • 1.22 說一說hash類型底層的數據結構
        • 1.23 介紹一下zset類型底層的數據結構
        • 1.24 如何利用Redis實現分布式Session?
        • 1.25 如何利用Redis實現一個分布式鎖?
        • 1.26 說一說你對布隆過濾器的理解
        • 1.27 多臺Redis抗高并發訪問該怎么設計?
        • 1.28 如果并發量超過30萬,怎么設計Redis架構?
        • 1.27 多臺Redis抗高并發訪問該怎么設計?
        • 1.28 如果并發量超過30萬,怎么設計Redis架構?

1. Redis

1.1 Redis可以用來做什么?

參考答案

  • Redis最常用來做緩存,是實現分布式緩存的首先中間件;
  • Redis可以作為數據庫,實現諸如點贊、關注、排行等對性能要求極高的互聯網需求;
  • Redis可以作為計算工具,能用很小的代價,統計諸如PV/UV、用戶在線天數等數據;
  • Redis還有很多其他的使用場景,例如:可以實現分布式鎖,可以作為消息隊列使用。
  • 1.2 Redis和傳統的關系型數據庫有什么不同?

    參考答案

    Redis是一種基于鍵值對的NoSQL數據庫,而鍵值對的值是由多種數據結構和算法組成的。Redis的數據都存儲于內存中,因此它的速度驚人,讀寫性能可達10萬/秒,遠超關系型數據庫。

    關系型數據庫是基于二維數據表來存儲數據的,它的數據格式更為嚴謹,并支持關系查詢。關系型數據庫的數據存儲于磁盤上,可以存放海量的數據,但性能遠不如Redis。

    1.3 Redis有哪些數據類型?

    參考答案

  • Redis支持5種核心的數據類型,分別是字符串、哈希、列表、集合、有序集合;
  • Redis還提供了Bitmap、HyperLogLog、Geo類型,但這些類型都是基于上述核心數據類型實現的;
  • Redis在5.0新增加了Streams數據類型,它是一個功能強大的、支持多播的、可持久化的消息隊列。
  • 1.4 Redis是單線程的,為什么還能這么快?

    參考答案

  • 對服務端程序來說,線程切換和鎖通常是性能殺手,而單線程避免了線程切換和競爭所產生的消耗;
  • Redis的大部分操作是在內存上完成的,這是它實現高性能的一個重要原因;
  • Redis采用了IO多路復用機制,使其在網絡IO操作中能并發處理大量的客戶端請求,實現高吞吐率。
  • 關于Redis的單線程架構實現,如下圖:

    1.5 Redis在持久化時fork出一個子進程,這時已經有兩個進程了,怎么能說是單線程呢?

    參考答案

    Redis是單線程的,主要是指Redis的網絡IO和鍵值對讀寫是由一個線程來完成的。而Redis的其他功能,如持久化、異步刪除、集群數據同步等,則是依賴其他線程來執行的。所以,說Redis是單線程的只是一種習慣的說法,事實上它的底層不是單線程的。

    1.6 set和zset有什么區別?

    參考答案

    set:

    • 集合中的元素是無序、不可重復的,一個集合最多能存儲232-1個元素;
    • 集合除了支持對元素的增刪改查之外,還支持對多個集合取交集、并集、差集。

    zset:

    • 有序集合保留了集合元素不能重復的特點;
    • 有序集合會給每個元素設置一個分數,并以此作為排序的依據;
    • 有序集合不能包含相同的元素,但是不同元素的分數可以相同。

    1.7 說一下Redis中的watch命令

    參考答案

    很多時候,要確保事務中的數據沒有被其他客戶端修改才執行該事務。Redis提供了watch命令來解決這類問題,這是一種樂觀鎖的機制。客戶端通過watch命令,要求服務器對一個或多個key進行監視,如果在客戶端執行事務之前,這些key發生了變化,則服務器將拒絕執行客戶端提交的事務,并向它返回一個空值。

    1.8 說說Redis中List結構的相關操作

    參考答案

    列表是線性有序的數據結構,它內部的元素是可以重復的,并且一個列表最多能存儲2^32-1個元素。列表包含如下的常用命令:

    • lpush/rpush:從列表的左側/右側添加數據;
    • lrange:指定索引范圍,并返回這個范圍內的數據;
    • lindex:返回指定索引處的數據;
    • lpop/rpop:從列表的左側/右側彈出一個數據;
    • blpop/brpop:從列表的左側/右側彈出一個數據,若列表為空則進入阻塞狀態。

    1.9 你要如何設計Redis的過期時間?

    參考答案

  • 熱點數據不設置過期時間,使其達到“物理”上的永不過期,可以避免緩存擊穿問題;
  • 在設置過期時間時,可以附加一個隨機數,避免大量的key同時過期,導致緩存雪崩。
  • 1.10 Redis中,sexnx命令的返回值是什么,如何使用該命令實現分布式鎖?

    參考答案

    setnx命令返回整數值,當返回1時表示設置值成果,當返回0時表示設置值失敗(key已存在)。

    一般我們不建議直接使用setnx命令來實現分布式鎖,因為為了避免出現死鎖,我們要給鎖設置一個自動過期時間。而setnx命令和設置過期時間的命令不是原子的,可能加鎖成果而設置過期時間失敗,依然存在死鎖的隱患。對于這種情況,Redis改進了set命令,給它增加了nx選項,啟用該選項時set命令的效果就會setnx一樣了。

    采用Redis實現分布式鎖,就是在Redis里存一份代表鎖的數據,通常用字符串即可。采用改進后的setnx命令(即set...nx...命令)實現分布式鎖的思路,以及優化的過程如下:

    加鎖:

    第一版,這種方式的缺點是容易產生死鎖,因為客戶端有可能忘記解鎖,或者解鎖失敗。

    setnx key value

    第二版,給鎖增加了過期時間,避免出現死鎖。但這兩個命令不是原子的,第二步可能會失敗,依然無法避免死鎖問題。

    setnx key value``expire key seconds

    第三版,通過“set…nx…”命令,將加鎖、過期命令編排到一起,它們是原子操作了,可以避免死鎖。

    set key value nx ex seconds

    解鎖:

    解鎖就是刪除代表鎖的那份數據。

    del key

    問題:

    看起來已經很完美了,但實際上還有隱患,如下圖。進程A在任務沒有執行完畢時,鎖已經到期被釋放了。等進程A的任務執行結束后,它依然會嘗試釋放鎖,因為它的代碼邏輯就是任務結束后釋放鎖。但是,它的鎖早已自動釋放過了,它此時釋放的可能是其他線程的鎖。

    想要解決這個問題,我們需要解決兩件事情:

  • 在加鎖時就要給鎖設置一個標識,進程要記住這個標識。當進程解鎖的時候,要進行判斷,是自己持有的鎖才能釋放,否則不能釋放。可以為key賦一個隨機值,來充當進程的標識。
  • 解鎖時要先判斷、再釋放,這兩步需要保證原子性,否則第二步失敗的話,就會出現死鎖。而獲取和刪除命令不是原子的,這就需要采用Lua腳本,通過Lua腳本將兩個命令編排在一起,而整個Lua腳本的執行是原子的。
  • 按照以上思路,優化后的命令如下:

    # 加鎖 set key random-value nx ex seconds # 解鎖 if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1]) elsereturn 0 end

    1.11 說一說Redis的持久化策略

    參考答案

    Redis支持RDB持久化、AOF持久化、RDB-AOF混合持久化這三種持久化方式。

    RDB:

    RDB(Redis Database)是Redis默認采用的持久化方式,它以快照的形式將進程數據持久化到硬盤中。RDB會創建一個經過壓縮的二進制文件,文件以“.rdb”結尾,內部存儲了各個數據庫的鍵值對數據等信息。RDB持久化的觸發方式有兩種:

    • 手動觸發:通過SAVE或BGSAVE命令觸發RDB持久化操作,創建“.rdb”文件;
    • 自動觸發:通過配置選項,讓服務器在滿足指定條件時自動執行BGSAVE命令。

    其中,SAVE命令執行期間,Redis服務器將阻塞,直到“.rdb”文件創建完畢為止。而BGSAVE命令是異步版本的SAVE命令,它會使用Redis服務器進程的子進程,創建“.rdb”文件。BGSAVE命令在創建子進程時會存在短暫的阻塞,之后服務器便可以繼續處理其他客戶端的請求。總之,BGSAVE命令是針對SAVE阻塞問題做的優化,Redis內部所有涉及RDB的操作都采用BGSAVE的方式,而SAVE命令已經廢棄!

    BGSAVE命令的執行流程,如下圖:

    BGSAVE命令的原理,如下圖:

    RDB持久化的優缺點如下:

    • 優點:RDB生成緊湊壓縮的二進制文件,體積小,使用該文件恢復數據的速度非常快;

    • 缺點:BGSAVE每次運行都要執行fork操作創建子進程,屬于重量級操作,不宜頻繁執行,

      所以RDB持久化沒辦法做到實時的持久化。

    AOF:

    AOF(Append Only File),解決了數據持久化的實時性,是目前Redis持久化的主流方式。AOF以獨立日志的方式,記錄了每次寫入命令,重啟時再重新執行AOF文件中的命令來恢復數據。AOF的工作流程包括:命令寫入(append)、文件同步(sync)、文件重寫(rewrite)、重啟加載(load),如下圖:

    AOF默認不開啟,需要修改配置項來啟用它:

    appendonly yes # 啟用AOF appendfilename "appendonly.aof" # 設置文件名

    AOF以文本協議格式寫入命令,如:

    *3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n

    文本協議格式具有如下的優點:

  • 文本協議具有很好的兼容性;
  • 直接采用文本協議格式,可以避免二次處理的開銷;
  • 文本協議具有可讀性,方便直接修改和處理。
  • AOF持久化的文件同步機制:

    為了提高程序的寫入性能,現代操作系統會把針對硬盤的多次寫操作優化為一次寫操作。

  • 當程序調用write對文件寫入時,系統不會直接把書記寫入硬盤,而是先將數據寫入內存的緩沖區中;
  • 當達到特定的時間周期或緩沖區寫滿時,系統才會執行flush操作,將緩沖區中的數據沖洗至硬盤中;
  • 這種優化機制雖然提高了性能,但也給程序的寫入操作帶來了不確定性。

  • 對于AOF這樣的持久化功能來說,沖洗機制將直接影響AOF持久化的安全性;
  • 為了消除上述機制的不確定性,Redis向用戶提供了appendfsync選項,來控制系統沖洗AOF的頻率;
  • Linux的glibc提供了fsync函數,可以將指定文件強制從緩沖區刷到硬盤,上述選項正是基于此函數。
  • appendfsync選項的取值和含義如下:

    AOF持久化的優缺點如下:

    • 優點:與RDB持久化可能丟失大量的數據相比,AOF持久化的安全性要高很多。通過使用everysec選項,用戶可以將數據丟失的時間窗口限制在1秒之內。
    • 缺點:AOF文件存儲的是協議文本,它的體積要比二進制格式的”.rdb”文件大很多。AOF需要通過執行AOF文件中的命令來恢復數據庫,其恢復速度比RDB慢很多。AOF在進行重寫時也需要創建子進程,在數據庫體積較大時將占用大量資源,會導致服務器的短暫阻塞。

    RDB-AOF混合持久化:

    Redis從4.0開始引入RDB-AOF混合持久化模式,這種模式是基于AOF持久化構建而來的。用戶可以通過配置文件中的“aof-use-rdb-preamble yes”配置項開啟AOF混合持久化。Redis服務器在執行AOF重寫操作時,會按照如下原則處理數據:

    • 像執行BGSAVE命令一樣,根據數據庫當前的狀態生成相應的RDB數據,并將其寫入AOF文件中;
    • 對于重寫之后執行的Redis命令,則以協議文本的方式追加到AOF文件的末尾,即RDB數據之后。

    通過使用RDB-AOF混合持久化,用戶可以同時獲得RDB持久化和AOF持久化的優點,服務器既可以通過AOF文件包含的RDB數據來實現快速的數據恢復操作,又可以通過AOF文件包含的AOF數據來將丟失數據的時間窗口限制在1s之內。

    1.12 如何實現Redis的高可用?

    參考答案

    實現Redis的高可用,主要有哨兵和集群兩種方式。

    哨兵:

    Redis Sentinel(哨兵)是一個分布式架構,它包含若干個哨兵節點和數據節點。每個哨兵節點會對數據節點和其余的哨兵節點進行監控,當發現節點不可達時,會對節點做下線標識。如果被標識的是主節點,它就會與其他的哨兵節點進行協商,當多數哨兵節點都認為主節點不可達時,它們便會選舉出一個哨兵節點來完成自動故障轉移的工作,同時還會將這個變化實時地通知給應用方。整個過程是自動的,不需要人工介入,有效地解決了Redis的高可用問題!

    一組哨兵可以監控一個主節點,也可以同時監控多個主節點,兩種情況的拓撲結構如下圖:

    哨兵節點包含如下的特征:

  • 哨兵節點會定期監控數據節點,其他哨兵節點是否可達;
  • 哨兵節點會將故障轉移的結果通知給應用方;
  • 哨兵節點可以將從節點晉升為主節點,并維護后續正確的主從關系;
  • 哨兵模式下,客戶端連接的是哨兵節點集合,從中獲取主節點信息;
  • 節點的故障判斷是由多個哨兵節點共同完成的,可有效地防止誤判;
  • 哨兵節點集合是由多個哨兵節點組成的,即使個別哨兵節點不可用,整個集合依然是健壯的;
  • 哨兵節點也是獨立的Redis節點,是特殊的Redis節點,它們不存儲數據,只支持部分命令。
  • 集群:

    Redis集群采用虛擬槽分區來實現數據分片,它把所有的鍵根據哈希函數映射到0-16383整數槽內,計算公式為slot=CRC16(key)&16383,每一個節點負責維護一部分槽以及槽所映射的鍵值數據。虛擬槽分區具有如下特點:

  • 解耦數據和節點之間的關系,簡化了節點擴容和收縮的難度;
  • 節點自身維護槽的映射關系,不需要客戶端或者代理服務維護槽分區元數據;
  • 支持節點、槽、鍵之間的映射查詢,用于數據路由,在線伸縮等場景。
  • Redis集群中數據的分片邏輯如下圖:

    1.13 Redis的主從同步是如何實現的?

    參考答案

    從2.8版本開始,Redis使用psync命令完成主從數據同步,同步過程分為全量復制和部分復制。全量復制一般用于初次復制的場景,部分復制則用于處理因網絡中斷等原因造成數據丟失的場景。psync命令需要以下參數的支持:

  • 復制偏移量:主節點處理寫命令后,會把命令長度做累加記錄,從節點在接收到寫命令后,也會做累加記錄;從節點會每秒鐘上報一次自身的復制偏移量給主節點,而主節點則會保存從節點的復制偏移量。
  • 積壓緩沖區:保存在主節點上的一個固定長度的隊列,默認大小為1M,當主節點有連接的從節點時被創建;主節點處理寫命令時,不但會把命令發送給從節點,還會寫入積壓緩沖區;緩沖區是先進先出的隊列,可以保存最近已復制的數據,用于部分復制和命令丟失的數據補救。
  • 主節點運行ID:每個Redis節點啟動后,都會動態分配一個40位的十六進制字符串作為運行ID;如果使用IP和端口的方式標識主節點,那么主節點重啟變更了數據集(RDB/AOF),從節點再基于復制偏移量復制數據將是不安全的,因此當主節點的運行ID變化后,從節點將做全量復制。
  • psync命令的執行過程以及返回結果,如下圖:

    全量復制的過程,如下圖:

    部分復制的過程,如下圖:

    1.14 Redis為什么存的快,內存斷電數據怎么恢復?

    參考答案

    Redis存的快是因為它的數據都存放在內存里,并且為了保證數據的安全性,Redis還提供了三種數據的持久化機制,即RDB持久化、AOF持久化、RDB-AOF混合持久化。若服務器斷電,那么我們可以利用持久化文件,對數據進行恢復。理論上來說,AOF/RDB-AOF持久化可以將丟失數據的窗口控制在1S之內。

    1.15 說一說Redis的緩存淘汰策略

    參考答案

    當寫入數據將導致超出maxmemory限制時,Redis會采用maxmemory-policy所指定的策略進行數據淘汰,該策略一共包含如下8種選項:

    策略描述版本
    noeviction直接返回錯誤;
    volatile-ttl從設置了過期時間的鍵中,選擇過期時間最小的鍵,進行淘汰;
    volatile-random從設置了過期時間的鍵中,隨機選擇鍵,進行淘汰;
    volatile-lru從設置了過期時間的鍵中,使用LRU算法選擇鍵,進行淘汰;
    volatile-lfu從設置了過期時間的鍵中,使用LFU算法選擇鍵,進行淘汰;4.0
    allleys-random從所有的鍵中,隨機選擇鍵,進行淘汰;
    allkeys-lru從所有的鍵中,使用LRU算法選擇鍵,進行淘汰;
    allkeys-lfu從所有的鍵中,使用LFU算法選擇鍵,進行淘汰;4.0

    其中,volatile前綴代表從設置了過期時間的鍵中淘汰數據,allkeys前綴代表從所有的鍵中淘汰數據。關于后綴,ttl代表選擇過期時間最小的鍵,random代表隨機選擇鍵,需要我們額外關注的是lru和lfu后綴,它們分別代表采用lru算法和lfu算法來淘汰數據。

    LRU(Least Recently Used)是按照最近最少使用原則來篩選數據,即最不常用的數據會被篩選出來!

    • 標準LRU:把所有的數據組成一個鏈表,表頭和表尾分別表示MRU和LRU端,即最常使用端和最少使用端。剛被訪問的數據會被移動到MRU端,而新增的數據也是剛被訪問的數據,也會被移動到MRU端。當鏈表的空間被占滿時,它會刪除LRU端的數據。
    • 近似LRU:Redis會記錄每個數據的最近一次訪問的時間戳(LRU)。Redis執行寫入操作時,若發現內存超出maxmemory,就會執行一次近似LRU淘汰算法。近似LRU會隨機采樣N個key,然后淘汰掉最舊的key,若淘汰后內存依然超出限制,則繼續采樣淘汰。可以通過maxmemory_samples配置項,設置近似LRU每次采樣的數據個數,該配置項的默認值為5。

    LRU算法的不足之處在于,若一個key很少被訪問,只是剛剛偶爾被訪問了一次,則它就被認為是熱點數據,短時間內不會被淘汰。

    LFU算法正式用于解決上述問題,LFU(Least Frequently Used)是Redis4新增的淘汰策略,它根據key的最近訪問頻率進行淘汰。LFU在LRU的基礎上,為每個數據增加了一個計數器,來統計這個數據的訪問次數。當使用LFU策略淘汰數據時,首先會根據數據的訪問次數進行篩選,把訪問次數最低的數據淘汰出內存。如果兩個數據的訪問次數相同,LFU再比較這兩個數據的訪問時間,把訪問時間更早的數據淘汰出內存。

    1.16 請介紹一下Redis的過期策略

    參考答案

    Redis支持如下兩種過期策略:

    惰性刪除:客戶端訪問一個key的時候,Redis會先檢查它的過期時間,如果發現過期就立刻刪除這個key。

    定期刪除:Redis會將設置了過期時間的key放到一個獨立的字典中,并對該字典進行每秒10次的過期掃描,

    過期掃描不會遍歷字典中所有的key,而是采用了一種簡單的貪心策略。該策略的刪除邏輯如下:

  • 從過期字典中隨機選擇20個key;
  • 刪除這20個key中已過期的key;
  • 如果已過期key的比例超過25%,則重復步驟1。
  • 1.17 緩存穿透、緩存擊穿、緩存雪崩有什么區別,該如何解決?

    參考答案

    緩存穿透:

    問題描述:

    客戶端查詢根本不存在的數據,使得請求直達存儲層,導致其負載過大,甚至宕機。出現這種情況的原因,可能是業務層誤將緩存和庫中的數據刪除了,也可能是有人惡意攻擊,專門訪問庫中不存在的數據。

    解決方案:

  • 緩存空對象:存儲層未命中后,仍然將空值存入緩存層,客戶端再次訪問數據時,緩存層會直接返回空值。
  • 布隆過濾器:將數據存入布隆過濾器,訪問緩存之前以過濾器攔截,若請求的數據不存在則直接返回空值。
  • 緩存擊穿:

    問題描述:

    一份熱點數據,它的訪問量非常大。在其緩存失效的瞬間,大量請求直達存儲層,導致服務崩潰。

    解決方案:

  • 永不過期:熱點數據不設置過期時間,所以不會出現上述問題,這是“物理”上的永不過期。或者為每個數據設置邏輯過期時間,當發現該數據邏輯過期時,使用單獨的線程重建緩存。
  • 加互斥鎖:對數據的訪問加互斥鎖,當一個線程訪問該數據時,其他線程只能等待。這個線程訪問過后,緩存中的數據將被重建,屆時其他線程就可以直接從緩存中取值。
  • 緩存雪崩:

    問題描述:

    在某一時刻,緩存層無法繼續提供服務,導致所有的請求直達存儲層,造成數據庫宕機。可能是緩存中有大量數據同時過期,也可能是Redis節點發生故障,導致大量請求無法得到處理。

    解決方案:

  • 避免數據同時過期:設置過期時間時,附加一個隨機數,避免大量的key同時過期。
  • 啟用降級和熔斷措施:在發生雪崩時,若應用訪問的不是核心數據,則直接返回預定義信息/空值/錯誤信息。或者在發生雪崩時,對于訪問緩存接口的請求,客戶端并不會把請求發給Redis,而是直接返回。
  • 構建高可用的Redis服務:采用哨兵或集群模式,部署多個Redis實例,個別節點宕機,依然可以保持服務的整體可用。
  • 1.18 如何保證緩存與數據庫的雙寫一致性?

    參考答案

    四種同步策略:

    想要保證緩存與數據庫的雙寫一致,一共有4種方式,即4種同步策略:

  • 先更新緩存,再更新數據庫;
  • 先更新數據庫,再更新緩存;
  • 先刪除緩存,再更新數據庫;
  • 先更新數據庫,再刪除緩存。
  • 從這4種同步策略中,我們需要作出比較的是:

  • 更新緩存與刪除緩存哪種方式更合適?
  • 應該先操作數據庫還是先操作緩存?
  • 更新緩存還是刪除緩存:

    下面,我們來分析一下,應該采用更新緩存還是刪除緩存的方式。

    • 更新緩存

      優點:每次數據變化都及時更新緩存,所以查詢時不容易出現未命中的情況。

      缺點:更新緩存的消耗比較大。如果數據需要經過復雜的計算再寫入緩存,那么頻繁的更新緩存,就會影響服務器的性能。如果是寫入數據頻繁的業務場景,那么可能頻繁的更新緩存時,卻沒有業務讀取該數據。

    • 刪除緩存

      優點:操作簡單,無論更新操作是否復雜,都是將緩存中的數據直接刪除。

      缺點:刪除緩存后,下一次查詢緩存會出現未命中,這時需要重新讀取一次數據庫。

    從上面的比較來看,一般情況下,刪除緩存是更優的方案。

    先操作數據庫還是緩存:

    下面,我們再來分析一下,應該先操作數據庫還是先操作緩存。

    首先,我們將先刪除緩存與先更新數據庫,在出現失敗時進行一個對比:

    如上圖,是先刪除緩存再更新數據庫,在出現失敗時可能出現的問題:

  • 進程A刪除緩存成功;
  • 進程A更新數據庫失敗;
  • 進程B從緩存中讀取數據;
  • 由于緩存被刪,進程B無法從緩存中得到數據,進而從數據庫讀取數據;
  • 進程B從數據庫成功獲取數據,然后將數據更新到了緩存。
  • 最終,緩存和數據庫的數據是一致的,但仍然是舊的數據。而我們的期望是二者數據一致,并且是新的數據。

    如上圖,是先更新數據庫再刪除緩存,在出現失敗時可能出現的問題:

  • 進程A更新數據庫成功;
  • 進程A刪除緩存失敗;
  • 進程B讀取緩存成功,由于緩存刪除失敗,所以進程B讀取到的是舊的數據。
  • 最終,緩存和數據庫的數據是不一致的。

    經過上面的比較,我們發現在出現失敗的時候,是無法明確分辨出先刪緩存和先更新數據庫哪個方式更好,以為它們都存在問題。后面我們會進一步對這兩種方式進行比較,但是在這里我們先探討一下,上述場景出現的問題,應該如何解決呢?

    實際上,無論上面我們采用哪種方式去同步緩存與數據庫,在第二步出現失敗的時候,都建議采用重試機制解決,因為最終我們是要解決掉這個錯誤的。而為了避免重試機制影響主要業務的執行,一般建議重試機制采用異步的方式執行,如下圖:

    這里我們按照先更新數據庫,再刪除緩存的方式,來說明重試機制的主要步驟:

  • 更新數據庫成功;
  • 刪除緩存失敗;
  • 將此數據加入消息隊列;
  • 業務代碼消費這條消息;
  • 業務代碼根據這條消息的內容,發起重試機制,即從緩存中刪除這條記錄。
  • 好了,下面我們再將先刪緩存與先更新數據庫,在沒有出現失敗時進行對比:

    如上圖,是先刪除緩存再更新數據庫,在沒有出現失敗時可能出現的問題:

  • 進程A刪除緩存成功;
  • 進程B讀取緩存失敗;
  • 進程B讀取數據庫成功,得到舊的數據;
  • 進程B將舊的數據成功地更新到了緩存;
  • 進程A將新的數據成功地更新到數據庫。
  • 可見,進程A的兩步操作均成功,但由于存在并發,在這兩步之間,進程B訪問了緩存。最終結果是,緩存中存儲了舊的數據,而數據庫中存儲了新的數據,二者數據不一致。

    如上圖,是先更新數據庫再刪除緩存,再沒有出現失敗時可能出現的問題:

  • 進程A更新數據庫成功;
  • 進程B讀取緩存成功;
  • 進程A更新數據庫成功。
  • 可見,最終緩存與數據庫的數據是一致的,并且都是最新的數據。但進程B在這個過程里讀到了舊的數據,可能還有其他進程也像進程B一樣,在這兩步之間讀到了緩存中舊的數據,但因為這兩步的執行速度會比較快,所以影響不大。對于這兩步之后,其他進程再讀取緩存數據的時候,就不會出現類似于進程B的問題了。

    最終結論:

    經過對比你會發現,先更新數據庫、再刪除緩存是影響更小的方案。如果第二步出現失敗的情況,則可以采用重試機制解決問題。

    擴展閱讀

    延時雙刪

    上面我們提到,如果是先刪緩存、再更新數據庫,在沒有出現失敗時可能會導致數據的不一致。如果在實際的應用中,出于某些考慮我們需要選擇這種方式,那有辦法解決這個問題嗎?答案是有的,那就是采用延時雙刪的策略,延時雙刪的基本思路如下:

  • 刪除緩存;
  • 更新數據庫;
  • sleep N毫秒;
  • 再次刪除緩存。
  • 阻塞一段時間之后,再次刪除緩存,就可以把這個過程中緩存中不一致的數據刪除掉。而具體的時間,要評估你這項業務的大致時間,按照這個時間來設定即可。

    采用讀寫分離的架構怎么辦?

    如果數據庫采用的是讀寫分離的架構,那么又會出現新的問題,如下圖:

    進程A先刪除緩存,再更新主數據庫,然后主庫將數據同步到從庫。而在主從數據庫同步之前,可能會有進程B訪問了緩存,發現數據不存在,進而它去訪問從庫獲取到舊的數據,然后同步到緩存。這樣,最終也會導致緩存與數據庫的數據不一致。這個問題的解決方案,依然是采用延時雙刪的策略,但是在評估延長時間的時候,要考慮到主從數據庫同步的時間。

    第二次刪除失敗了怎么辦?

    如果第二次刪除依然失敗,則可以增加重試的次數,但是這個次數要有限制,當超出一定的次數時,要采取報錯、記日志、發郵件提醒等措施。

    1.19 請介紹Redis集群的實現方案

    參考答案

    Redis集群的分區方案:

    Redis集群采用虛擬槽分區來實現數據分片,它把所有的鍵根據哈希函數映射到0-16383整數槽內,計算公式為slot=CRC16(key)&16383,每一個節點負責維護一部分槽以及槽所映射的鍵值數據。虛擬槽分區具有如下特點:

  • 解耦數據和節點之間的關系,簡化了節點擴容和收縮的難度;
  • 節點自身維護槽的映射關系,不需要客戶端或者代理服務維護槽分區元數據;
  • 支持節點、槽、鍵之間的映射查詢,用于數據路由,在線伸縮等場景。
  • Redis集群中數據的分片邏輯如下圖:

    Redis集群的功能限制:

    Redis集群方案在擴展了Redis處理能力的同時,也帶來了一些使用上的限制:

  • key批量操作支持有限。如mset、mget,目前只支持具有相同slot值的key執行批量操作。對于映射為不同slot值的key由于執行mset、mget等操作可能存在于多個節點上所以不被支持。
  • key事務操作支持有限。同理只支持多key在同一節點上的事務操作,當多個key分布在不同的節點上時無法使用事務功能。
  • key作為數據分區的最小粒度,因此不能將一個大的鍵值對象(如hash、list等)映射到不同的節點。
  • 不支持多數據庫空間。單機下的Redis可以支持16個數據庫,集群模式下只能使用一個數據庫空間,即DB0。
  • 復制結構只支持一層,從節點只能復制主節點,不支持嵌套樹狀復制結構。
  • Redis集群的通信方案:

    在分布式存儲中需要提供維護節點元數據信息的機制,所謂元數據是指:節點負責哪些數據,是否出現故障等狀態信息。常見的元數據維護方式分為:集中式和P2P方式。

    Redis集群采用P2P的Gossip(流言)協議,Gossip協議的工作原理就是節點彼此不斷通信交換信息,一段時間后所有的節點都會知道集群完整的信息,這種方式類似流言傳播。通信的大致過程如下:

  • 集群中每個節點都會單獨開辟一個TCP通道,用于節點之間彼此通信,通信端口號在基礎端口號上加10000;
  • 每個節點再固定周期內通過特定規則選擇幾個節點發送ping消息;
  • 接收ping消息的節點用pong消息作為響應。
  • 其中,Gossip協議的主要職責就是信息交換,而信息交換的載體就是節點彼此發送的Gossip消息,Gossip消息分為:meet消息、ping消息、pong消息、fail消息等。

    • meet消息:用于通知新節點加入,消息發送者通知接受者加入到當前集群。meet消息通信正常完成后,接收節點會加入到集群中并進行周期性的ping、pong消息交換。
    • ping消息:集群內交換最頻繁的消息,集群內每個節點每秒向多個其他節點發送ping消息,用于檢測節點是否在線和交換彼此狀態信息。ping消息封裝了自身節點和一部分其他節點的狀態數據。
    • pong消息:當接收到meet、ping消息時,作為響應消息回復給發送方確認消息正常通信。pong消息內封裝了自身狀態數據,節點也可以向集群內廣播自身的pong消息來通知整個集群對自身狀態進行更新。
    • fail消息:當節點判定集群內另一個節點下線時,會向集群內廣播一個fail消息,其他節點接收到fail消息之后把對應節點更新為下線狀態。

    雖然Gossip協議的信息交換機制具有天然的分布式特性,但它是有成本的。因為Redis集群內部需要頻繁地進行節點信息交換,而ping/pong消息會攜帶當前節點和部分其他節點的狀態數據,勢必會加重帶寬和計算的負擔。所以,Redis集群的Gossip協議需要兼顧信息交換的實時性和成本的開銷。

    • 集群里的每個節點默認每隔一秒鐘就會從已知節點列表中隨機選出五個節點,然后對這五個節點中最長時間沒有發送過PING消息的節點發送PING消息,以此來檢測被選中的節點是否在線。
    • 如果節點A最后一次收到節點B發送的PONG消息的時間,距離當前時間已經超過了節點A的超時選項設置時長的一半(cluster-node-timeout/2),那么節點A也會向節點B發送PING消息,這可以防止節點A因為長時間沒有隨機選中節點B作為PING消息的發送對象而導致對節點B的信息更新滯后。
    • 每個消息主要的數據占用:slots槽數組(2KB)和整個集群1/10的狀態數據(10個節點狀態數據約1KB)。

    1.20 說一說Redis集群的分片機制

    參考答案

    Redis集群采用虛擬槽分區來實現數據分片,它把所有的鍵根據哈希函數映射到0-16383整數槽內,計算公式為slot=CRC16(key)&16383,每一個節點負責維護一部分槽以及槽所映射的鍵值數據。虛擬槽分區具有如下特點:

  • 解耦數據和節點之間的關系,簡化了節點擴容和收縮的難度;
  • 節點自身維護槽的映射關系,不需要客戶端或者代理服務維護槽分區元數據;
  • 支持節點、槽、鍵之間的映射查詢,用于數據路由,在線伸縮等場景。
  • Redis集群中數據的分片邏輯如下圖:

    1.21 說一說Redis集群的應用和優劣勢

    參考答案

    優勢:

    Redis Cluster是Redis的分布式解決方案,在3.0版本正式推出,有效地解決了Redis分布式方面的需求。當遇到單機內存、并發、流量等瓶頸時,可以采用Cluster架構方案達到負載均衡的目的。

    劣勢:

    Redis集群方案在擴展了Redis處理能力的同時,也帶來了一些使用上的限制:

  • key批量操作支持有限。如mset、mget,目前只支持具有相同slot值的key執行批量操作。對于映射為不同slot值的key由于執行mset、mget等操作可能存在于多個節點上所以不被支持。
  • key事務操作支持有限。同理只支持多key在同一節點上的事務操作,當多個key分布在不同的節點上時無法使用事務功能。
  • key作為數據分區的最小粒度,因此不能將一個大的鍵值對象(如hash、list等)映射到不同的節點。
  • 不支持多數據庫空間。單機下的Redis可以支持16個數據庫,集群模式下只能使用一個數據庫空間,即DB0。
  • 復制結構只支持一層,從節點只能復制主節點,不支持嵌套樹狀復制結構。
  • 1.22 說一說hash類型底層的數據結構

    參考答案

    哈希對象有兩種編碼方案,當同時滿足以下條件時,哈希對象采用ziplist編碼,否則采用hashtable編碼:

    • 哈希對象保存的鍵值對數量小于512個;
    • 哈希對象保存的所有鍵值對中的鍵和值,其字符串長度都小于64字節。

    其中,ziplist編碼采用壓縮列表作為底層實現,而hashtable編碼采用字典作為底層實現。

    壓縮列表:

    壓縮列表(ziplist),是Redis為了節約內存而設計的一種線性數據結構,它是由一系列具有特殊編碼的連續內存塊構成的。一個壓縮列表可以包含任意多個節點,每個節點可以保存一個字節數組或一個整數值。

    壓縮列表的結構如下圖所示:

    該結構當中的字段含義如下表所示:

    屬性類型長度說明
    zlbytesuint32_t4字節壓縮列表占用的內存字節數;
    zltailuint32_t4字節壓縮列表表尾節點距離列表起始地址的偏移量(單位字節);
    zllenuint16_t2字節壓縮列表包含的節點數量,等于UINT16_MAX時,需遍歷列表計算真實數量;
    entryX列表節點不固定壓縮列表包含的節點,節點的長度由節點所保存的內容決定;
    zlenduint8_t1字節壓縮列表的結尾標識,是一個固定值0xFF;

    其中,壓縮列表的節點由以下字段構成:

    previous_entry_length(pel)屬性以字節為單位,記錄當前節點的前一節點的長度,其自身占據1字節或5字節:

  • 如果前一節點的長度小于254字節,則“pel”屬性的長度為1字節,前一節點的長度就保存在這一個字節內;
  • 如果前一節點的長度達到254字節,則“pel”屬性的長度為5字節,其中第一個字節被設置為0xFE,之后的四個字節用來保存前一節點的長度;
  • 基于“pel”屬性,程序便可以通過指針運算,根據當前節點的起始地址計算出前一節點的起始地址,從而實現從表尾向表頭的遍歷操作。

    content屬性負責保存節點的值(字節數組或整數),其類型和長度則由encoding屬性決定,它們的關系如下:

    encoding長度content
    00 xxxxxx1字節最大長度為26 -1的字節數組;
    01 xxxxxx bbbbbbbb2字節最大長度為214-1的字節數組;
    10 __ bbbbbbbb … … …5字節最大長度為232-1的字節數組;
    11 0000001字節int16_t類型的整數;
    11 0100001字節int32_t類型的整數;
    11 1000001字節int64_t類型的整數;
    11 1100001字節24位有符號整數;
    11 1111101字節8位有符號整數;
    11 11xxxx1字節沒有content屬性,xxxx直接存[0,12]范圍的整數值;

    字典:

    字典(dict)又稱為散列表,是一種用來存儲鍵值對的數據結構。C語言沒有內置這種數據結構,所以Redis構建了自己的字典實現。

    Redis字典的實現主要涉及三個結構體:字典、哈希表、哈希表節點。其中,每個哈希表節點保存一個鍵值對,每個哈希表由多個哈希表節點構成,而字典則是對哈希表的進一步封裝。這三個結構體的關系如下圖所示:

    其中,dict代表字典,dictht代表哈希表,dictEntry代表哈希表節點。可以看出,dictEntry是一個數組,這很好理解,因為一個哈希表里要包含多個哈希表節點。而dict里包含2個dictht,多出的哈希表用于REHASH。當哈希表保存的鍵值對數量過多或過少時,需要對哈希表的大小進行擴展或收縮操作,在Redis中,擴展和收縮哈希表是通過REHASH實現的,執行REHASH的大致步驟如下:

  • 為字典的ht[1]哈希表分配內存空間

    如果執行的是擴展操作,則ht[1]的大小為第1個大于等于ht[0].used*2的2n。如果執行的是收縮操作,則ht[1]的大小為第1個大于等于ht[0].used的2n。

  • 將存儲在ht[0]中的數據遷移到ht[1]上

    重新計算鍵的哈希值和索引值,然后將鍵值對放置到ht[1]哈希表的指定位置上。

  • 將字典的ht[1]哈希表晉升為默認哈希表

    遷移完成后,清空ht[0],再交換ht[0]和ht[1]的值,為下一次REHASH做準備。

  • 當滿足以下任何一個條件時,程序會自動開始對哈希表執行擴展操作:

  • 服務器目前沒有執行bgsave或bgrewriteof命令,并且哈希表的負載因子大于等于1;
  • 服務器目前正在執行bgsave或bgrewriteof命令,并且哈希表的負載因子大于等于5。
  • 為了避免REHASH對服務器性能造成影響,REHASH操作不是一次性地完成的,而是分多次、漸進式地完成的。漸進式REHASH的詳細過程如下:

  • 為ht[1]分配空間,讓字典同時持有ht[0]和ht[1]兩個哈希表;
  • 在字典中的索引計數器rehashidx設置為0,表示REHASH操作正式開始;
  • 在REHASH期間,每次對字典執行添加、刪除、修改、查找操作時,程序除了執行指定的操作外,還會順帶將ht[0]中位于rehashidx上的所有鍵值對遷移到ht[1]中,再將rehashidx的值加1;
  • 隨著字典不斷被訪問,最終在某個時刻,ht[0]上的所有鍵值對都被遷移到ht[1]上,此時程序將rehashidx屬性值設置為-1,標識REHASH操作完成。
  • REHSH期間,字典同時持有兩個哈希表,此時的訪問將按照如下原則處理:

  • 新添加的鍵值對,一律被保存到ht[1]中;
  • 刪除、修改、查找等其他操作,會在兩個哈希表上進行,即程序先嘗試去ht[0]中訪問要操作的數據,若不存在則到ht[1]中訪問,再對訪問到的數據做相應的處理。
  • 1.23 介紹一下zset類型底層的數據結構

    參考答案

    有序集合對象有2種編碼方案,當同時滿足以下條件時,集合對象采用ziplist編碼,否則采用skiplist編碼:

    • 有序集合保存的元素數量不超過128個;
    • 有序集合保存的所有元素的成員長度都小于64字節。

    其中,ziplist編碼的有序集合采用壓縮列表作為底層實現,skiplist編碼的有序集合采用zset結構作為底層實現。

    其中,zset是一個復合結構,它的內部采用字典和跳躍表來實現,其源碼如下。其中,dict保存了從成員到分支的映射關系,zsl則按分值由小到大保存了所有的集合元素。這樣,當按照成員來訪問有序集合時可以直接從dict中取值,當按照分值的范圍訪問有序集合時可以直接從zsl中取值,采用了空間換時間的策略以提高訪問效率。

    typedef struct zset {dict *dict; // 字典,保存了從成員到分值的映射關系;zskiplist *zsl; // 跳躍表,按分值由小到大保存所有集合元素; } zset;

    綜上,zset對象的底層數據結構包括:壓縮列表、字典、跳躍表。

    壓縮列表:

    壓縮列表(ziplist),是Redis為了節約內存而設計的一種線性數據結構,它是由一系列具有特殊編碼的連續內存塊構成的。一個壓縮列表可以包含任意多個節點,每個節點可以保存一個字節數組或一個整數值。

    壓縮列表的結構如下圖所示:

    該結構當中的字段含義如下表所示:

    屬性類型長度說明
    zlbytesuint32_t4字節壓縮列表占用的內存字節數;
    zltailuint32_t4字節壓縮列表表尾節點距離列表起始地址的偏移量(單位字節);
    zllenuint16_t2字節壓縮列表包含的節點數量,等于UINT16_MAX時,需遍歷列表計算真實數量;
    entryX列表節點不固定壓縮列表包含的節點,節點的長度由節點所保存的內容決定;
    zlenduint8_t1字節壓縮列表的結尾標識,是一個固定值0xFF;

    其中,壓縮列表的節點由以下字段構成:

    previous_entry_length(pel)屬性以字節為單位,記錄當前節點的前一節點的長度,其自身占據1字節或5字節:

  • 如果前一節點的長度小于254字節,則“pel”屬性的長度為1字節,前一節點的長度就保存在這一個字節內;
  • 如果前一節點的長度達到254字節,則“pel”屬性的長度為5字節,其中第一個字節被設置為0xFE,之后的四個字節用來保存前一節點的長度;
  • 基于“pel”屬性,程序便可以通過指針運算,根據當前節點的起始地址計算出前一節點的起始地址,從而實現從表尾向表頭的遍歷操作。

    content屬性負責保存節點的值(字節數組或整數),其類型和長度則由encoding屬性決定,它們的關系如下:

    encoding長度content
    00 xxxxxx1字節最大長度為26 -1的字節數組;
    01 xxxxxx bbbbbbbb2字節最大長度為214-1的字節數組;
    10 __ bbbbbbbb … … …5字節最大長度為232-1的字節數組;
    11 0000001字節int16_t類型的整數;
    11 0100001字節int32_t類型的整數;
    11 1000001字節int64_t類型的整數;
    11 1100001字節24位有符號整數;
    11 1111101字節8位有符號整數;
    11 11xxxx1字節沒有content屬性,xxxx直接存[0,12]范圍的整數值;

    字典:

    字典(dict)又稱為散列表,是一種用來存儲鍵值對的數據結構。C語言沒有內置這種數據結構,所以Redis構建了自己的字典實現。

    Redis字典的實現主要涉及三個結構體:字典、哈希表、哈希表節點。其中,每個哈希表節點保存一個鍵值對,每個哈希表由多個哈希表節點構成,而字典則是對哈希表的進一步封裝。這三個結構體的關系如下圖所示:

    其中,dict代表字典,dictht代表哈希表,dictEntry代表哈希表節點。可以看出,dictEntry是一個數組,這很好理解,因為一個哈希表里要包含多個哈希表節點。而dict里包含2個dictht,多出的哈希表用于REHASH。當哈希表保存的鍵值對數量過多或過少時,需要對哈希表的大小進行擴展或收縮操作,在Redis中,擴展和收縮哈希表是通過REHASH實現的,執行REHASH的大致步驟如下:

  • 為字典的ht[1]哈希表分配內存空間

    如果執行的是擴展操作,則ht[1]的大小為第1個大于等于ht[0].used*2的2n。如果執行的是收縮操作,則ht[1]的大小為第1個大于等于ht[0].used的2n。

  • 將存儲在ht[0]中的數據遷移到ht[1]上

    重新計算鍵的哈希值和索引值,然后將鍵值對放置到ht[1]哈希表的指定位置上。

  • 將字典的ht[1]哈希表晉升為默認哈希表

    遷移完成后,清空ht[0],再交換ht[0]和ht[1]的值,為下一次REHASH做準備。

  • 當滿足以下任何一個條件時,程序會自動開始對哈希表執行擴展操作:

  • 服務器目前沒有執行bgsave或bgrewriteof命令,并且哈希表的負載因子大于等于1;
  • 服務器目前正在執行bgsave或bgrewriteof命令,并且哈希表的負載因子大于等于5。
  • 為了避免REHASH對服務器性能造成影響,REHASH操作不是一次性地完成的,而是分多次、漸進式地完成的。漸進式REHASH的詳細過程如下:

  • 為ht[1]分配空間,讓字典同時持有ht[0]和ht[1]兩個哈希表;
  • 在字典中的索引計數器rehashidx設置為0,表示REHASH操作正式開始;
  • 在REHASH期間,每次對字典執行添加、刪除、修改、查找操作時,程序除了執行指定的操作外,還會順帶將ht[0]中位于rehashidx上的所有鍵值對遷移到ht[1]中,再將rehashidx的值加1;
  • 隨著字典不斷被訪問,最終在某個時刻,ht[0]上的所有鍵值對都被遷移到ht[1]上,此時程序將rehashidx屬性值設置為-1,標識REHASH操作完成。
  • REHSH期間,字典同時持有兩個哈希表,此時的訪問將按照如下原則處理:

  • 新添加的鍵值對,一律被保存到ht[1]中;
  • 刪除、修改、查找等其他操作,會在兩個哈希表上進行,即程序先嘗試去ht[0]中訪問要操作的數據,若不存在則到ht[1]中訪問,再對訪問到的數據做相應的處理。
  • 跳躍表:

    跳躍表的查找復雜度為平均O(logN),最壞O(N),效率堪比紅黑樹,卻遠比紅黑樹實現簡單。跳躍表是在鏈表的基礎上,通過增加索引來提高查找效率的。

    有序鏈表插入、刪除的復雜度為O(1),而查找的復雜度為O(N)。例:若要查找值為60的元素,需要從第1個元素依次向后比較,共需比較6次才行,如下圖:

    跳躍表是從有序鏈表中選取部分節點,組成一個新鏈表,并以此作為原始鏈表的一級索引。再從一級索引中選取部分節點,組成一個新鏈表,并以此作為原始鏈表的二級索引。以此類推,可以有多級索引,如下圖:

    跳躍表在查找時,優先從高層開始查找,若next節點值大于目標值,或next指針指向NULL,則從當前節點下降一層繼續向后查找,這樣便可以提高查找的效率了。

    跳躍表的實現主要涉及2個結構體:zskiplist、zskiplistNode,它們的關系如下圖所示:

    其中,藍色的表格代表zskiplist,紅色的表格代表zskiplistNode。zskiplist有指向頭尾節點的指針,以及列表的長度,列表中最高的層級。zskiplistNode的頭節點是空的,它不存儲任何真實的數據,它擁有最高的層級,但這個層級不記錄在zskiplist之內。

    1.24 如何利用Redis實現分布式Session?

    參考答案

    在web開發中,我們會把用戶的登錄信息存儲在session里。而session是依賴于cookie的,即服務器創建session時會給它分配一個唯一的ID,并且在響應時創建一個cookie用于存儲這個SESSIONID。當客戶端收到這個cookie之后,就會自動保存這個SESSIONID,并且在下次訪問時自動攜帶這個SESSIONID,屆時服務器就可以通過這個SESSIONID得到與之對應的session,從而識別用戶的身。如下圖:

    現在的互聯網應用,基本都是采用分布式部署方式,即將應用程序部署在多臺服務器上,并通過nginx做統一的請求分發。而服務器與服務器之間是隔離的,它們的session是不共享的,這就存在session同步的問題了,如下圖:

    如果客戶端第一次訪問服務器,請求被分發到了服務器A上,則服務器A會為該客戶端創建session。如果客戶端再次訪問服務器,請求被分發到服務器B上,則由于服務器B中沒有這個session,所以用戶的身份無法得到驗證,從而產生了不一致的問題。

    解決這個問題的辦法有很多,比如可以協調多個服務器,讓他們的session保持同步。也可以在分發請求時做綁定處理,即將某一個IP固定分配給同一個服務器。但這些方式都比較麻煩,而且性能上也有一定的消耗。更合理的方式就是采用類似于Redis這樣的高性能緩存服務器,來實現分布式session。

    從上面的敘述可知,我們使用session保存用戶的身份信息,本質上是要做兩件事情。第一是保存用戶的身份信息,第二是驗證用戶的身份信息。如果利用其它手段實現這兩個目標,那么就可以不用session,或者說我們使用的是廣義上的session了。

    具體實現的思路如下圖,我們在服務端增加兩段程序:

    第一是創建令牌的程序,就是在用戶初次訪問服務器時,給它創建一個唯一的身份標識,并且使用cookie封裝這個標識再發送給客戶端。那么當客戶端下次再訪問服務器時,就會自動攜帶這個身份標識了,這和SESSIONID的道理是一樣的,只是改由我們自己來實現了。另外,在返回令牌之前,我們需要將它存儲起來,以便于后續的驗證。而這個令牌是不能保存在服務器本地的,因為其他服務器無法訪問它。因此,我們可以將其存儲在服務器之外的一個地方,那么Redis便是一個理想的場所。

    第二是驗證令牌的程序,就是在用戶再次訪問服務器時,我們獲取到了它之前的身份標識,那么我們就要驗證一下這個標識是否存在了。驗證的過程很簡單,我們從Redis中嘗試獲取一下就可以知道結果。

    1.25 如何利用Redis實現一個分布式鎖?

    參考答案

    何時需要分布式鎖?

    在分布式的環境下,當多個server并發修改同一個資源時,為了避免競爭就需要使用分布式鎖。那為什么不能使用Java自帶的鎖呢?因為Java中的鎖是面向多線程設計的,它只局限于當前的JRE環境。而多個server實際上是多進程,是不同的JRE環境,所以Java自帶的鎖機制在這個場景下是無效的。

    如何實現分布式鎖?

    采用Redis實現分布式鎖,就是在Redis里存一份代表鎖的數據,通常用字符串即可。實現分布式鎖的思路,以及優化的過程如下:

  • 加鎖:

    第一版,這種方式的缺點是容易產生死鎖,因為客戶端有可能忘記解鎖,或者解鎖失敗。

    setnx key value

    第二版,給鎖增加了過期時間,避免出現死鎖。但這兩個命令不是原子的,第二步可能會失敗,依然無法避免死鎖問題。

    setnx key value expire key seconds

    第三版,通過“set…nx…”命令,將加鎖、過期命令編排到一起,它們是原子操作了,可以避免死鎖。

    set key value nx ex seconds
  • 解鎖:

    解鎖就是刪除代表鎖的那份數據。

    del key
  • 問題:

    看起來已經很完美了,但實際上還有隱患,如下圖。進程A在任務沒有執行完畢時,鎖已經到期被釋放了。等進程A的任務執行結束后,它依然會嘗試釋放鎖,因為它的代碼邏輯就是任務結束后釋放鎖。但是,它的鎖早已自動釋放過了,它此時釋放的可能是其他線程的鎖。

  • 想要解決這個問題,我們需要解決兩件事情:

  • 在加鎖時就要給鎖設置一個標識,進程要記住這個標識。當進程解鎖的時候,要進行判斷,是自己持有的鎖才能釋放,否則不能釋放。可以為key賦一個隨機值,來充當進程的標識。
  • 解鎖時要先判斷、再釋放,這兩步需要保證原子性,否則第二步失敗的話,就會出現死鎖。而獲取和刪除命令不是原子的,這就需要采用Lua腳本,通過Lua腳本將兩個命令編排在一起,而整個Lua腳本的執行是原子的。
  • 按照以上思路,優化后的命令如下:

    # 加鎖 set key random-value nx ex seconds # 解鎖 if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1]) elsereturn 0 end

    基于RedLock算法的分布式鎖:

    上述分布式鎖的實現方案,是建立在單個主節點之上的。它的潛在問題如下圖所示,如果進程A在主節點上加鎖成功,然后這個主節點宕機了,則從節點將會晉升為主節點。若此時進程B在新的主節點上加鎖成果,之后原主節點重啟,成為了從節點,系統中將同時出現兩把鎖,這是違背鎖的唯一性原則的。

    總之,就是在單個主節點的架構上實現分布式鎖,是無法保證高可用的。若要保證分布式鎖的高可用,則可以采用多個節點的實現方案。這種方案有很多,而Redis的官方給出的建議是采用RedLock算法的實現方案。該算法基于多個Redis節點,它的基本邏輯如下:

    • 這些節點相互獨立,不存在主從復制或者集群協調機制;
    • 加鎖:以相同的KEY向N個實例加鎖,只要超過一半節點成功,則認定加鎖成功;
    • 解鎖:向所有的實例發送DEL命令,進行解鎖;

    RedLock算法的示意圖如下,我們可以自己實現該算法,也可以直接使用Redisson框架。

    1.26 說一說你對布隆過濾器的理解

    參考答案

    布隆過濾器可以用很低的代價,估算出數據是否真實存在。例如:給用戶推薦新聞時,要去掉重復的新聞,就可以利用布隆過濾器,判斷該新聞是否已經推薦過。

    布隆過濾器的核心包括兩部分:

  • 一個大型的位數組;
  • 若干個不一樣的哈希函數,每個哈希函數都能將哈希值算的比較均勻。
  • 布隆過濾器的工作原理:

  • 添加key時,每個哈希函數都利用這個key計算出一個哈希值,再根據哈希值計算一個位置,并將位數組中這個位置的值設置為1。
  • 詢問key時,每個哈希函數都利用這個key計算出一個哈希值,再根據哈希值計算一個位置。然后對比這些哈希函數在位數組中對應位置的數值:
    • 如果這幾個位置中,有一個位置的值是0,就說明這個布隆過濾器中,不存在這個key。
    • 如果這幾個位置中,所有位置的值都是1,就說明這個布隆過濾器中,極有可能存在這個key。之所以不是百分之百確定,是因為也可能是其他的key運算導致該位置為1。
  • 1.27 多臺Redis抗高并發訪問該怎么設計?

    參考答案

    Redis Cluster是Redis的分布式解決方案,在3.0版本正式推出,有效地解決了Redis分布式方面的需求。當遇到單機內存、并發、流量等瓶頸時,可以采用Cluster架構方案達到負載均衡的目的。

    Redis集群采用虛擬槽分區來實現數據分片,它把所有的鍵根據哈希函數映射到0-16383整數槽內,計算公式為slot=CRC16(key)&16383,每一個節點負責維護一部分槽以及槽所映射的鍵值數據。虛擬槽分區具有如下特點:

  • 解耦數據和節點之間的關系,簡化了節點擴容和收縮的難度;
  • 節點自身維護槽的映射關系,不需要客戶端或者代理服務維護槽分區元數據;
  • 支持節點、槽、鍵之間的映射查詢,用于數據路由,在線伸縮等場景。
  • Redis集群中數據的分片邏輯如下圖:

    1.28 如果并發量超過30萬,怎么設計Redis架構?

    參考答案

    Redis Cluster是Redis的分布式解決方案,在3.0版本正式推出,有效地解決了Redis分布式方面的需求。當遇到單機內存、并發、流量等瓶頸時,可以采用Cluster架構方案達到負載均衡的目的。

    Redis集群采用虛擬槽分區來實現數據分片,它把所有的鍵根據哈希函數映射到0-16383整數槽內,計算公式為slot=CRC16(key)&16383,每一個節點負責維護一部分槽以及槽所映射的鍵值數據。虛擬槽分區具有如下特點:

  • 解耦數據和節點之間的關系,簡化了節點擴容和收縮的難度;
  • 節點自身維護槽的映射關系,不需要客戶端或者代理服務維護槽分區元數據;
  • 支持節點、槽、鍵之間的映射查詢,用于數據路由,在線伸縮等場景。
  • Redis集群中數據的分片邏輯如下圖:

    ,就說明這個布隆過濾器中,不存在這個key。

    • 如果這幾個位置中,所有位置的值都是1,就說明這個布隆過濾器中,極有可能存在這個key。之所以不是百分之百確定,是因為也可能是其他的key運算導致該位置為1。

    1.27 多臺Redis抗高并發訪問該怎么設計?

    參考答案

    Redis Cluster是Redis的分布式解決方案,在3.0版本正式推出,有效地解決了Redis分布式方面的需求。當遇到單機內存、并發、流量等瓶頸時,可以采用Cluster架構方案達到負載均衡的目的。

    Redis集群采用虛擬槽分區來實現數據分片,它把所有的鍵根據哈希函數映射到0-16383整數槽內,計算公式為slot=CRC16(key)&16383,每一個節點負責維護一部分槽以及槽所映射的鍵值數據。虛擬槽分區具有如下特點:

  • 解耦數據和節點之間的關系,簡化了節點擴容和收縮的難度;
  • 節點自身維護槽的映射關系,不需要客戶端或者代理服務維護槽分區元數據;
  • 支持節點、槽、鍵之間的映射查詢,用于數據路由,在線伸縮等場景。
  • Redis集群中數據的分片邏輯如下圖:

    [外鏈圖片轉存中…(img-W0cUgnt5-1641471982130)]

    1.28 如果并發量超過30萬,怎么設計Redis架構?

    參考答案

    Redis Cluster是Redis的分布式解決方案,在3.0版本正式推出,有效地解決了Redis分布式方面的需求。當遇到單機內存、并發、流量等瓶頸時,可以采用Cluster架構方案達到負載均衡的目的。

    Redis集群采用虛擬槽分區來實現數據分片,它把所有的鍵根據哈希函數映射到0-16383整數槽內,計算公式為slot=CRC16(key)&16383,每一個節點負責維護一部分槽以及槽所映射的鍵值數據。虛擬槽分區具有如下特點:

  • 解耦數據和節點之間的關系,簡化了節點擴容和收縮的難度;
  • 節點自身維護槽的映射關系,不需要客戶端或者代理服務維護槽分區元數據;
  • 支持節點、槽、鍵之間的映射查詢,用于數據路由,在線伸縮等場景。
  • Redis集群中數據的分片邏輯如下圖:

    總結

    以上是生活随笔為你收集整理的Redis常见面试题详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    日韩视频三区 | 免费看污黄网站 | 欧美老少交| 精品久久久免费视频 | 国产不卡精品视频 | 午夜精品福利一区二区 | 日日噜噜噜噜夜夜爽亚洲精品 | 五月婷婷欧美视频 | 欧美一区在线观看视频 | 亚洲免费色 | 国产九色91 | 欧美日韩免费一区二区 | av片无限看| 午夜国产福利在线观看 | 国产手机在线精品 | 日本论理电影 | 深爱婷婷| 操处女逼 | 国产精品久久久久婷婷二区次 | 日韩在线免费视频观看 | 亚洲最大成人网4388xx | 在线国产视频观看 | 8x成人免费视频 | 久久久久久久久久久久久久免费看 | 毛片网站在线观看 | 伊人久久婷婷 | 欧美日韩国产一区二区在线观看 | 91中文字幕永久在线 | 激情综合亚洲精品 | 久久久亚洲国产精品麻豆综合天堂 | 999久久 | 日韩av在线看 | 精品久久亚洲 | 国产精品123 | 天天色视频 | 一区二区三区高清在线观看 | 奇米先锋| 亚洲第一区在线播放 | www.大网伊人 | 狠狠色狠狠色合久久伊人 | 色九九视频 | 免费黄色a级毛片 | 欧美精品一区在线发布 | 国产精久久久久久久 | 国产精品 国产精品 | 超碰在线人 | 黄色精品一区二区 | 日韩免费看 | 久久久久久久久艹 | 97成人在线视频 | 国产二区av | 天天色天天干天天色 | 亚洲在线激情 | 在线观看国产www | 黄色免费网站大全 | 狠狠亚洲| 国产91学生粉嫩喷水 | 韩日av在线| 91视频在线免费下载 | 亚洲成av人片在线观看 | 激情久久网 | 国产一区二区影院 | 日韩欧美在线免费 | 精品国产自在精品国产精野外直播 | 国产 色| 久久综合婷婷国产二区高清 | 成人免费中文字幕 | 三级黄免费看 | 欧美精品成人在线 | 日韩欧美视频免费观看 | 五月综合激情网 | 免费在线色 | 中文字幕中文 | 99中文字幕视频 | 免费av一级电影 | 成人精品视频久久久久 | 综合成人在线 | 久久久午夜视频 | 国产一级大片在线观看 | 91精品久久香蕉国产线看观看 | 99日精品 | 婷婷视频在线观看 | 亚洲91中文字幕无线码三区 | 91九色精品女同系列 | 男女激情麻豆 | 九草视频在线 | 日本中文字幕久久 | 亚洲专区 国产精品 | a资源在线| 一区二精品 | 国产在线观看99 | 国产精品破处视频 | 最近最新mv字幕免费观看 | 狠狠伊人 | 亚洲精品在线免费播放 | 久久久久久久久福利 | 亚洲 成人 欧美 | 免费在线观看91 | 日韩精品中文字幕在线 | 亚洲欧洲精品视频 | 色天天中文 | 六月丁香色婷婷 | 欧美日产在线观看 | 色婷五月天 | 日韩av不卡在线播放 | 国产视频精品视频 | 91精品婷婷国产综合久久蝌蚪 | 日本精品在线看 | 美女免费电影 | 中文字幕 影院 | 99性视频 | 日韩成人免费电影 | www激情久久| 久久久久成人精品 | 国产成人av网址 | 欧美视频xxx | 国产综合在线视频 | 日韩精品视频免费在线观看 | 天天射天天干天天爽 | 在线观看视频你懂的 | 爱色av.com | 成人黄色免费在线观看 | 欧美孕交vivoestv另类 | 久久综合狠狠综合 | 91福利影院在线观看 | av在线免费在线 | 久久久久免费 | 色黄www小说 | 色欧美成人精品a∨在线观看 | 国产午夜精品一区二区三区欧美 | 国产99久久九九精品 | 99综合久久 | 97视频播放 | www.久久成人| 91网免费观看 | 国产中文在线观看 | 精品一区二区在线观看 | 精品国产1区2区3区 国产欧美精品在线观看 | 国产亚州精品视频 | 视频直播国产精品 | 欧美性色黄大片在线观看 | 国产午夜剧场 | 欧美精品久久久久久 | 在线精品视频免费观看 | 久草在线在线精品观看 | 国产精品久久久久久久久久久杏吧 | 欧美91精品久久久久国产性生爱 | 人人干人人草 | 亚洲精品乱码久久久久久按摩 | 久久久久女人精品毛片九一 | 91九色自拍 | 九九热免费在线视频 | 波多野结依在线观看 | 夜夜操天天干 | 国产高清视频免费在线观看 | 四虎小视频 | 91色综合| 久久久精品国产一区二区三区 | 黄网站色欧美视频 | 精品一区在线 | 久久五月天色综合 | 激情网在线视频 | 国产视频精品免费播放 | 毛片激情永久免费 | 精品久久免费看 | 五月婷婷丁香色 | 青青河边草免费 | 天天摸天天操天天爽 | 麻豆国产视频 | 人人插人人爱 | 欧美日韩国产综合一区二区 | 国产色a在线观看 | 亚洲精品免费视频 | 免费观看性生活大片 | 97色综合| 九九热免费视频在线观看 | 日韩av中文字幕在线免费观看 | av一区二区在线观看中文字幕 | 久久久久久久久久电影 | 97在线观看免费高清 | 久久久久免费观看 | 亚洲黄色影院 | 亚洲视频在线视频 | 亚洲成人频道 | 国产一区二区久久精品 | 久久久久久在线观看 | 天天想夜夜操 | 久草在线视频国产 | 久久69精品久久久久久久电影好 | 国产黄色成人 | 麻豆久久久久久久 | 国产精品久久久久久麻豆一区 | 国产拍在线 | 国产精品久久久久久一区二区 | 视频国产在线观看18 | 久久亚洲视频 | 亚洲狠狠丁香婷婷综合久久久 | 精品久久久成人 | 日韩久久久久久久久久久久 | 波多野结衣在线观看一区 | 欧美一级在线 | 国产精品6999成人免费视频 | 在线观看久久 | 精品视频999 | 爱爱av网| 日韩av成人 | 欧美日韩一区三区 | 免费试看一区 | 一区二区三区四区在线免费观看 | 久久精品美女视频 | 国产二区精品 | 91精品91 | 国产中文字幕在线免费观看 | 天天天干天天射天天天操 | 97视频免费看 | 色网站在线免费观看 | 日韩久久久久久久久 | 有码视频在线观看 | 亚洲码国产日韩欧美高潮在线播放 | 伊人视频| 亚洲成人黄色网址 | 波多野结衣一区二区 | 日韩免费在线视频观看 | a午夜电影 | 国产一级精品绿帽视频 | 丁香5月婷婷 | 福利视频导航网址 | 狠狠插狠狠干 | 午夜精品电影 | 天天综合网入口 | av免费在线网 | 久久免费视频在线观看30 | 久久这里只有精品视频99 | 国产黄色片一级 | www.色国产| 久久婷婷国产 | 88av网站 | 日韩乱理| 99在线热播 | 美女国内精品自产拍在线播放 | 免费在线观看的av网站 | 国产精品自产拍在线观看中文 | 超碰大片 | 色噜噜在线观看 | 五月天婷亚洲天综合网精品偷 | 免费热情视频 | 国产黄色片在线免费观看 | 婷婷综合视频 | 亚洲理论在线观看电影 | 国产精品美女久久久久久久网站 | 五月激情丁香图片 | 中文字幕 第二区 | 国产99re| 国产精品精品久久久久久 | 亚洲欧洲日韩在线观看 | av色综合| 久久伦理视频 | 亚洲精品女 | 欧美日韩国产网站 | 成人黄色小说网 | 免费午夜在线视频 | 综合网天天射 | 97人人超碰在线 | 国产精品爽爽久久久久久蜜臀 | 国产专区欧美专区 | 国产大片免费久久 | 国产精品久久久久久久久久新婚 | 久久久网页 | 天堂在线视频免费观看 | 久久最新网址 | 亚洲天堂网站视频 | 1000部18岁以下禁看视频 | 97视频人人 | 亚洲精品午夜久久久久久久久久久 | 免费看十八岁美女 | 奇米影视8888 | 奇米导航 | 欧洲亚洲国产视频 | 国产精品在线看 | 天天摸日日摸人人看 | 婷婷爱五月天 | 国产女v资源在线观看 | 国产精成人品免费观看 | 中文字幕资源网在线观看 | 免费涩涩网站 | av先锋中文字幕 | 日日爽 | 91黄视频在线 | 日韩三级视频在线观看 | 久久黄页| 亚洲精品一区中文字幕乱码 | 亚洲手机av| 香蕉影院在线观看 | 国产区在线 | 欧美日韩国语 | 免费日韩 精品中文字幕视频在线 | 国产黄色av影视 | 亚洲精品短视频 | 久久综合久久88 | 久久国产色 | 日韩一区精品 | 国产在线视频一区二区 | 麻豆免费视频 | 狠狠的干狠狠的操 | 国产香蕉久久精品综合网 | 欧美日韩视频免费看 | 国产99久久99热这里精品5 | 久久久久欧美精品999 | 天堂av在线免费观看 | 天天舔夜夜操 | 亚州五月| 最新免费av在线 | 日本久久久影视 | 日日爽天天爽 | 国产日韩中文在线 | 成人一级黄色片 | 伊人伊成久久人综合网站 | 91中文字幕在线 | 亚洲国产精品成人综合 | 热久精品 | 美女在线免费视频 | 成人va天堂| 免费一级特黄录像 | 中文字幕精品一区二区三区电影 | 日韩成人黄色 | 99热在线国产 | 97电影网站| 日本99干网 | 日本三级大片 | 操操操影院 | 最近更新的中文字幕 | 在线观看国产亚洲 | 久久国精品 | 4438全国亚洲精品在线观看视频 | 日韩三级中文字幕 | 精品高清美女精品国产区 | 少妇啪啪av入口 | 亚洲人成网站精品片在线观看 | 久久在线免费 | 免费日韩一区二区三区 | 免费在线中文字幕 | 日韩一区精品 | 五月婷香 | 91在线视频一区 | 国产午夜精品久久久久久久久久 | 久热香蕉视频 | 黄av在线| 最近中文字幕免费av | 欧美性性网 | 久久人人爽人人爽人人片 | 国产精品国产亚洲精品看不卡15 | 国产一区黄色 | 啪啪免费观看网站 | 亚洲欧美日韩一二三区 | 精品毛片久久久久久 | 久久视频国产精品免费视频在线 | av最新资源 | 精品国产亚洲一区二区麻豆 | 午夜av在线播放 | 亚洲va欧洲va国产va不卡 | 久久久麻豆精品一区二区 | 久久av中文字幕片 | 国产五十路毛片 | 久久国色夜色精品国产 | 久草在线视频免费资源观看 | 日韩精品中文字幕在线不卡尤物 | 久久深夜福利免费观看 | 国产精品99免费看 | 狠狠的日日 | 九九久久久久99精品 | 日韩中文在线字幕 | 日日天天狠狠 | 日韩精品不卡在线 | 欧美嫩草影院 | 97视频网址 | 大型av综合网站 | 国产在线观看91 | 久久久久久国产一区二区三区 | 国产精品大片免费观看 | 免费网站黄 | 日韩网站免费观看 | 国产破处在线播放 | 四虎国产精品成人免费影视 | 夜夜夜草| 玖玖玖国产精品 | 日韩福利在线观看 | 成人亚洲免费 | 天天操月月操 | 久久1区| 日韩aa视频| 九九久久久久久久久激情 | 特级西西444www高清大视频 | 天天曰天天| 麻豆一区二区 | 欧美日韩国产在线一区 | 四虎影视精品永久在线观看 | 国产精品一区二区三区久久久 | 国产精品国产自产拍高清av | 黄色片免费在线 | 国产91勾搭技师精品 | 精品国产a | 久久国产精品99久久久久久丝袜 | 在线 国产 亚洲 欧美 | 一区二区三区在线观看中文字幕 | 国产精品 欧美 日韩 | 国产精品资源 | 国产小视频在线看 | 国产综合精品一区二区三区 | 免费涩涩网站 | 欧美在线观看禁18 | 国产精品福利视频 | 97国产大学生情侣白嫩酒店 | 亚洲97在线| 日本久热| 国产精品va最新国产精品视频 | 久久久www成人免费毛片 | 免费视频区 | 欧美日韩亚洲在线观看 | 在线免费视频一区 | 超碰在线中文字幕 | 久久综合久久综合这里只有精品 | 久免费 | 午夜男人影院 | 婷婷深爱激情 | 激情av资源网| 亚洲闷骚少妇在线观看网站 | 99热亚洲精品| 免费高清在线观看成人 | 久久女同性恋中文字幕 | 日本中文字幕在线视频 | 日韩在线免费电影 | 夜夜夜精品| 国产剧情在线一区 | 久久久96| 亚洲综合视频在线 | 国产在线看一区 | 国产91综合一区在线观看 | 国产中文字幕国产 | 操操爽| 热re99久久精品国产66热 | 成人资源在线 | 狠狠色伊人亚洲综合网站野外 | 性色av免费观看 | 免费在线日韩 | 久久久久国产精品免费免费搜索 | 亚洲最新在线 | 日韩精品在线一区 | 日韩精品久久久久久久电影99爱 | 国际精品久久久久 | 在线观看aaa | 91亚洲国产成人久久精品网站 | 久久久久久国产精品美女 | 999视频在线播放 | 狠狠躁夜夜a产精品视频 | 日韩xxxbbb| 91大神电影 | 欧美精品在线视频观看 | 国产一区二区在线免费观看 | 黄色大片网 | 人人玩人人弄 | 丁香花在线视频观看免费 | 成人欧美亚洲 | 亚洲精品男人天堂 | 精品国产1区 | 毛片视频网址 | 日韩av午夜 | 九九热久久久 | 一级免费av | 久久久噜噜噜久久久 | 奇米影视在线99精品 | 欧美日韩国产精品一区二区三区 | 91麻豆国产 | 不卡在线一区 | 国产精品日韩久久久久 | 手机在线永久免费观看av片 | 成人国产一区二区 | 亚洲精品国产精品99久久 | 五月婷婷影视 | 99爱在线| 日韩av快播电影网 | 亚洲免费精品视频 | 亚洲天天在线日亚洲洲精 | 在线视频区 | 久久午夜网 | 日韩二区三区在线观看 | 欧美精品一级视频 | 久久综合色8888 | 色婷婷影视 | 狠狠激情中文字幕 | 91网站免费观看 | 日本精油按摩3 | 国内精品久久天天躁人人爽 | 欧美一级免费在线 | 日日操日日插 | 免费久久久 | 精品久久久久国产免费第一页 | 午夜色影院 | 在线免费黄色毛片 | 欧美极品少妇xbxb性爽爽视频 | 一区二区三区四区五区在线视频 | 婷婷丁香社区 | 黄色片网站av | 日韩久久精品一区二区 | 亚洲欧洲久久久 | 亚洲专区中文字幕 | 天天干天天干天天干天天干天天干天天干 | 久久精品视频免费观看 | 国产精品精品久久久久久 | 狠狠躁夜夜躁人人爽超碰91 | 国产免费视频一区二区裸体 | 国产精品淫 | 色噜噜日韩精品一区二区三区视频 | 国产 日韩 在线 亚洲 字幕 中文 | 91在线精品秘密一区二区 | 久久精品国产免费观看 | 国产美女在线观看 | 久草影视在线 | 亚洲精品国产第一综合99久久 | 91成年人在线观看 | 久久久一本精品99久久精品 | 成人黄色在线视频 | 狠狠色丁香久久婷婷综合五月 | 国产精品资源在线观看 | 91经典在线 | 日韩一区在线免费观看 | 黄色大片入口 | 天天躁天天躁天天躁婷 | 久久a视频| 久久久久久久久久久免费 | 久草网站 | 日韩精品不卡在线观看 | 不卡电影免费在线播放一区 | 久草在线费播放视频 | 亚洲国产成人久久综合 | 香蕉久草| 久久免费在线观看 | 一区精品久久 | 日韩一区二区三区高清免费看看 | 久久久免费 | 国产精品99蜜臀久久不卡二区 | 日韩大片在线 | 国产色网 | 色午夜 | 久久久久国产成人精品亚洲午夜 | 日韩久久网站 | 国产精品久久久久一区二区三区共 | 欧美精品资源 | 国产精品自产拍在线观看蜜 | 欧美日韩国产综合一区二区 | 国产v在线观看 | 亚洲精品xxxx| 少妇精品久久久一区二区免费 | 在线综合 亚洲 欧美在线视频 | 亚洲第一av在线播放 | 中文字幕一区二区在线观看 | 久久久精品一区二区三区 | 黄色大片日本免费大片 | 亚洲三级黄 | 久久8精品 | 天天干天天操人体 | 久久久在线 | 国产精品国产三级国产aⅴ9色 | 狠狠久久伊人 | 亚洲电影成人 | 亚洲美女视频在线 | www.久久久精品 | 亚洲高清色综合 | 国产成人一区二区三区 | 国产精品久久久久久久午夜 | 国产xxxx性hd极品 | 国产97超碰 | 国产精品一区二区 91 | 亚洲成人av影片 | 9在线观看免费 | 天堂中文在线视频 | 欧美一区二视频在线免费观看 | 九九九九精品九九九九 | 日韩网站视频 | 久久精品国产亚洲精品2020 | 亚洲高清免费在线 | 激情伊人五月天久久综合 | av中文在线观看 | www.天天草 | www.久久成人 | 中文字幕一区二区三区四区视频 | 99久e精品热线免费 99国产精品久久久久久久久久 | 亚洲精品五月天 | 国内小视频在线观看 | 欧美有色| 国产精品久久久久久模特 | 国产a国产a国产a | 成人黄色av免费在线观看 | a√天堂中文在线 | 亚洲毛片一区二区三区 | 国产免费作爱视频 | 久久久一本精品99久久精品66 | 亚洲丝袜中文 | 国产小视频在线播放 | 国产成人精品一区二区在线 | 久久免费精品国产 | 成人a大片 | 国产一区免费在线观看 | 国产一线天在线观看 | 国产综合久久 | 国产99久久 | 中文字幕精品www乱入免费视频 | 深爱激情综合 | 亚洲国产精品传媒在线观看 | 久草在线国产 | 日韩一级电影网站 | 亚洲午夜精品在线观看 | 一本一本久久a久久精品综合小说 | avwww在线观看 | 六月丁香六月婷婷 | 久久精品草 | 亚洲精品国产精品国产 | 欧美久久久久久久久中文字幕 | 国产精品福利在线观看 | 日韩有码专区 | 国产中文字幕久久 | 草久草久 | 一区二区三区在线播放 | 国产乱对白刺激视频不卡 | 国产麻豆精品久久一二三 | a√天堂中文在线 | 欧美专区国产专区 | 日韩在线视频不卡 | 日韩一区二区免费播放 | 成人网页在线免费观看 | 成人黄色小视频 | 伊人网综合在线观看 | 精品国产1区2区3区 国产欧美精品在线观看 | 国产精品久久久 | 五月天精品视频 | 色国产精品 | 成人免费在线观看电影 | av一级在线 | 日韩免费一区二区在线观看 | 中文字幕在线久一本久 | 不卡av在线免费观看 | 色网站黄| 亚洲欧美日韩精品一区二区 | 精品久久一区二区三区 | 国产色视频一区 | 黄色最新网址 | 狠狠狠干 | 欧美99精品 | 国产精品永久久久久久久www | av在线最新 | 欧美日韩一区二区视频在线观看 | 正在播放 国产精品 | 激情中文在线 | 久久国产精品免费看 | 91热爆视频 | 日韩最新在线 | 国产亚洲精品久久久久久 | 最近日本韩国中文字幕 | 久久国产精品影片 | www.亚洲视频 | 成人手机在线视频 | 日韩欧美69 | 四虎在线影视 | 亚洲久草网 | 99热最新精品| 日本视频久久久 | 久久久久女教师免费一区 | 免费看短 | 蜜臀91丨九色丨蝌蚪老版 | 成人亚洲免费 | 久久视 | 国内成人精品视频 | 色噜噜在线观看 | 99中文视频在线 | av高清影院| 黄色.com| 国产精品成人免费精品自在线观看 | 欧美一级性视频 | 在线看一区二区 | 在线观看日韩 | 免费合欢视频成人app | 免费精品在线观看 | 亚洲视频大全 | 天天操夜夜想 | 91免费版在线 | 精品国产91亚洲一区二区三区www | 一区二区三区高清在线观看 | 在线观看www视频 | 亚洲精品www久久久久久 | 久久看毛片 | 久久九九精品久久 | 欧美99精品 | 欧美巨大荫蒂茸毛毛人妖 | 国产精品欧美久久久久天天影视 | 日韩久久精品一区二区三区 | 成人午夜网 | 国产一区免费观看 | 午夜精品一区二区三区在线播放 | 夜夜骑首页| av在线播放网址 | .精品久久久麻豆国产精品 亚洲va欧美 | 国产黄色视| 三级av在线播放 | 国产精品久久麻豆 | 国产精品乱码久久久久 | 中文字幕在线影视资源 | 国产在线观看免费 | 欧美日韩免费观看一区=区三区 | av韩国在线 | 日韩欧美精品一区 | 国产精品涩涩屋www在线观看 | 天天综合网 天天综合色 | 国产精品完整版 | 日韩欧美视频免费看 | 色综合激情网 | av性在线| 欧美aa级| 成人精品一区二区三区中文字幕 | 在线综合 亚洲 欧美在线视频 | 又黄又爽又刺激视频 | 成人精品一区二区三区中文字幕 | 久久精品视频2 | aaa免费毛片 | 999成人网 | 91av在线视频免费观看 | 精品中文字幕视频 | 精品在线你懂的 | 精品在线免费视频 | 亚洲欧洲成人 | 亚洲三级黄 | 婷婷综合伊人 | 国产乱码精品一区二区蜜臀 | 五月婷婷在线视频观看 | 麻豆视频www | 欧美亚洲国产日韩 | 一区二区中文字幕在线观看 | 免费一级日韩欧美性大片 | 成人中文字幕av | 久在线 | 综合网欧美| 特级黄色视频毛片 | 久久99九九99精品 | 国产精品丝袜久久久久久久不卡 | 国产精品免费久久 | 热九九精品| 亚州av网站| 99国内精品久久久久久久 | 日韩精品一区二区三区高清免费 | 久久久久久久久爱 | 91麻豆免费看 | 337p西西人体大胆瓣开下部 | 亚洲精品字幕 | 国产精品久久久久久久久久三级 | 黄色免费大片 | 欧美日韩一区二区三区免费视频 | 草久在线播放 | 国产亚洲精品久久久久久移动网络 | 久久久久久久久久久影院 | 粉嫩av一区二区三区四区五区 | 欧美精品乱码久久久久 | 狠狠躁天天躁综合网 | 日韩精品资源 | 日韩高清三区 | 国产精品久久毛片 | 国产午夜精品免费一区二区三区视频 | 国产一级电影免费观看 | 国产精品 欧美 日韩 | 亚洲激情综合 | 六月激情网 | 91九色视频观看 | 国产原创91 | 在线看毛片网站 | 偷拍福利视频一区二区三区 | 精品国产视频在线 | 午夜精品电影一区二区在线 | 人人艹视频 | 久久九九免费 | 人交video另类hd | 中文字幕在线视频精品 | 日韩一区二区三免费高清在线观看 | 国产一级精品在线观看 | 一区二区欧美在线观看 | 亚洲激情中文 | 日韩一片| 日韩高清一二三区 | 国产亚洲免费观看 | 在线免费视频a | 一区二区三区韩国免费中文网站 | 在线观看一区二区视频 | 久草视频免费在线观看 | 香蕉影视| 欧美一级看片 | 天天干中文字幕 | 日韩在线免费不卡 | 久久99国产综合精品免费 | 美女视频黄免费网站 | 午夜久久久久久久 | 中文国产成人精品久久一 | 9在线观看免费高清完整 | 五月婷婷综合在线观看 | 欧美日韩精品久久久 | 午夜神马福利 | 国产精品久久在线 | 欧美激情精品一区 | 色噜噜在线观看视频 | 日本精品一区二区 | 99精品久久精品一区二区 | 国产免费激情久久 | 在线 日韩 av | 四虎国产精品永久在线国在线 | 国产美女视频网站 | 国产人免费人成免费视频 | 亚洲精选99 | 亚洲国产精品999 | 中文字幕日韩电影 | 91成熟丰满女人少妇 | 成人在线视频免费看 | 亚洲一区二区精品 | 成人精品久久久 | 国产高清在线精品 | 99精品在线观看 | 久久久在线视频 | 欧美激情第28页 | 国产精品一码二码三码在线 | 久久99日韩 | 欧美另类xxxxx | 国产黄色资源 | 亚洲一级二级 | 中文av在线播放 | 国产区精品区 | 成人在线免费观看视视频 | 亚洲夜夜网 | 网站免费黄色 | 草久在线观看视频 | 久久这里只有精品1 | 色婷av | 麻豆成人精品视频 | 91av中文字幕| 中文字幕免费观看 | 麻豆小视频在线观看 | 少妇bbbb搡bbbb桶 | 日日婷婷夜日日天干 | 亚洲精品久久久久久国 | 中文字幕超清在线免费 | 日本丶国产丶欧美色综合 | 久久超 | 2019免费中文字幕 | 六月激情网 | 国产在线观看免费观看 | 91精品视频免费看 | 久久久久99精品成人片三人毛片 | 亚洲影院国产 | 美女久久久久久 | 丝袜少妇在线 | 中文字幕在线观看网址 | 98超碰在线| 国产在线日本 | 国产精品久久久久久久免费 | wwwwww色 | 免费亚洲精品视频 | 91成人黄色 | 久久久精品欧美 | 免费成人黄色av | 福利在线看片 | 免费看污在线观看 | 久精品在线| 91精品久久久久久久99蜜桃 | 欧美色伊人 | 四虎伊人| 日韩高清一区 | 国产一级淫片在线观看 | 国产黄大片在线观看 | 国内精品久久影院 | 一级黄色片在线播放 | 日韩免费看片 | 中文av在线免费观看 | avav99| av女优中文字幕在线观看 | 三上悠亚一区二区在线观看 | av 一区二区三区四区 | 五月婷婷播播 | 国产97在线观看 | 久久视频精品在线观看 | 久久国产免费视频 | 日韩黄色一级电影 | 黄网站app在线观看免费视频 | 蜜桃av久久久亚洲精品 | 日韩一级电影网站 | 在线播放国产精品 | 亚洲成人精品久久久 | av在线一 | 99免在线观看免费视频高清 | 中文字幕影片免费在线观看 | 精品久久久久国产 | 精品国产一区二区三区四区vr | 97超碰在线免费观看 | 99国产视频 | 黄色三几片 | 国产97在线播放 | 国产永久网站 | 日韩在线视频播放 | 亚洲精品在线观看中文字幕 | 99爱在线观看 | 欧美极品少妇xbxb性爽爽视频 | 精品中文字幕在线观看 | 国产黄色片免费看 | 久久电影国产免费久久电影 | 日韩在线网址 | 成人a v视频 | 人人舔人人爽 | 黄色成人91 | 欧美亚洲国产精品久久高清浪潮 | 黄色三级网站在线观看 | 99视频在线精品免费观看2 | 精品自拍网 | 在线免费观看视频一区二区三区 | 日韩av影片在线观看 | 国产午夜精品久久久久久久久久 | 国产精品一级视频 | 免费a v在线 | 久久精品国产v日韩v亚洲 | 69av国产| 成人午夜影视 | 麻豆视频免费入口 | 亚洲不卡在线 | 99久久日韩精品视频免费在线观看 | 九九免费观看视频 | 国产精品麻 | 中文字幕在线观看视频网站 | 日韩专区在线播放 | 日韩有码在线播放 | 日韩美女黄色片 | 午夜影院一级 | 最近更新好看的中文字幕 | 91天堂素人约啪 | 美女网站视频免费黄 | 精品在线不卡 | 日日操操 | 一本之道乱码区 | 国产一区二区久久精品 | 免费黄色看片 | 东方av在线免费观看 | 天天爽夜夜爽人人爽一区二区 | 久久久国产精品电影 | 亚洲美女在线国产 | 97国产在线观看 | 99免费在线播放99久久免费 | 中文字幕文字幕一区二区 | 日本中文字幕视频 | 欧美成人aa | 国产999精品久久久久久 | 国产专区在线视频 | 国产高清在线免费视频 | 欧美一区,二区 | 国产精品美女免费视频 | 日韩电影在线观看中文字幕 | 瑞典xxxx性hd极品 | 天天干人人干 | 一区二区三区久久 | 伊人宗合网 | 国产精品成人一区二区三区吃奶 | 日韩av在线小说 | 在线免费观看国产精品 | 日韩视频一 | av中文天堂在线 | 天天摸天天舔天天操 | 久久99网 | 亚洲天堂免费视频 | 91久久精品一区二区三区 | 日日夜夜天天综合 | 精品久久一 | 久热只有精品 | 黄色免费网站大全 | 成人观看 | 少妇性色午夜淫片aaaze | 中文字幕在线观看1 | 亚洲激情视频 | 98精品国产自产在线观看 | 91精品在线观看入口 | 久草免费在线视频观看 | 亚洲免费视频在线观看 | 亚洲毛片一区二区三区 | 国产精品成人av电影 | 久久婷婷久久 | 欧美国产日韩一区 | 国产免费成人 | 国产99久久99热这里精品5 | 人人视频网站 | 欧美日韩在线观看不卡 | 亚洲视频一级 | 日韩欧美一区二区三区在线观看 | 韩日电影在线观看 | 在线视频手机国产 | 黄色大片日本免费大片 | 免费在线成人av电影 | 国产区精品在线 | 日韩精品久久久免费观看夜色 | 不卡视频在线 | 国产在线观看h |