精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Redis 核心知識點深度剖析:原理、機制與應用

開發 Redis
本文將深入探討Redis的核心知識點,帶你領略其內部的奧秘,助力你在數據處理的領域中如魚得水。

在當今數字化浪潮洶涌澎湃的時代,數據如同企業的生命線,高效的數據存儲、處理與管理成為眾多應用程序成功的關鍵。在這數據管理的競技場上,Redis作為一款開源的內存數據結構存儲系統,憑借其卓越的性能、豐富的數據結構和強大的功能,脫穎而出,成為開發者手中的得力利器。無論是高并發場景下的數據緩存,還是實時數據分析、消息隊列等應用,Redis都展現出無可替代的價值。

本文將深入探討Redis的核心知識點,帶你領略其內部的奧秘,助力你在數據處理的領域中如魚得水。

一、詳解Redis基礎知識點

1. 為什么Redis被設計成是單線程的

redis本質上都是在內存操作,性能瓶頸不在CPU,通過單線程處理客戶端指令可以避免線程上下文切換開銷。

此時如果使用多線程進行操作,勢必要保護臨界資源并發安全而采用較粗力度的鎖,由此導致的大量線程阻塞爭搶臨界資源而導致操作各種大耗時操作顯然是得不償失的,并且多線程操作一般會引入各種同步原語,對于我們這種動輒十幾萬的內存數據庫問題的定位和排查的難度都會大大增加。

2. 為什么Redis單線程也能這么快

  • 通過IO多路復用保證單線程處理多連接
  • 數據結構做了極致的優化
  • 活躍于內存即純粹的內存操作,性能表現出色
  • 單線程處理所有指令,避免線程上下文切換和同步原語的使用的開銷

3. 說說Redis6.0中的多線程

Redis6.0的多線程是用多線程來處理數據的讀寫和協議解析,但是Redis執行命令還是單線程的:

4. Redis管道pipeline的概念

特定場景下我們某些業務需要針對redis執行多條指令,按照傳統做法我們需要逐條發送指令,這樣的做法使得一個業務針對redis的操作存在多次網絡往返即多次RTT:

于是就有了pipeline的概念,通過管道一次性將要執行的多條命令發送給服務端,其作用是為了降低 RTT(Round Trip Time) 對性能的影響,redis收到這些指令之后會依次執行并響應給客戶端:

Redis 服務端接收到管道發送過來的多條命令后,會一直執命令,并將命令的執行結果進行緩存,直到最后一條命令執行完成,再所有命令的執行結果一次性返回給客戶端 。

在性能方面, pipeline 有下面兩個優勢:

  • 節省了RTT:將多條命令打包一次性發送給服務端,減少了客戶端與服務端之間的網絡調用次數。
  • 減少了上下文切換:當客戶端/服務端需要從網絡中讀寫數據時,都會產生一次系統調用,系統調用是非常耗時的操作,其中設計到程序由用戶態切換到內核態,再從內核態切換回用戶態的過程。當我們執行 10 條 redis 命令的時候,就會發生 10 次用戶態到內核態的上下文切換,但如果我們使用 pipeline將多條命令打包成一條一次性發送給服務端,就只會產生一次上下文切換。

唯一需要注意的是redis的pipeline不保證原子性,即使我們通過pipeline處理多條指令,它也是逐條執行的,這一點我們還是需要注意一下。

5. Redis如何保證命令原子性

使用原子命令:

  • Redis 提供了 INCR/DECR/SETNX 命令,把RMW三個操作轉變為一個原子操作
  • Redis 是使用單線程串行處理客戶端的請求來操作命令,所以當 Redis 執行某個命令操作時,其他命令是無法執行的,這相當于命令操作是互斥執行的.

加鎖:

加鎖主要是考慮多個客戶端對相同業務方法進行修改操作,我們可以使用加鎖的方式保證原子性,大致的方式為:

  • 使用setnx上鎖
  • 上鎖成功后,執行業務修改操作
  • 使用del釋放鎖

這期間你可能會遇到兩個問題:

  • 假如在操作期間出現了業務異常(或者服務器宕機了),就會導致key未能及時釋放,進而導致鎖無法釋放,我們必須對這個鎖設置時效,并且在操作期間定時監測和續命。
SET key value [EX seconds | PX milliseconds] [NX]

誤刪除,比如用戶1持有鎖,用戶2拿不到鎖,用del命令把這個鎖刪除,對此我們可以使用setnx的value比對看看上鎖和用戶和解鎖的用戶是不是同一個進行進一步的操作。

SET lock_key unique_value NX PX 10000

使用lua腳本:多個操作寫到一個 Lua 腳本中(Redis 會把整個 Lua 腳本作為一個整體執行,在執行的過程中不會被其他命令打斷,從而保證了 Lua 腳本中操作的原子性)

local current current = redis.call("incr",KEYS[1]) 
if tonumber(current) == 1 
then redis.call("expire",KEYS[1],60) 
end

6. Redis 使用什么協議進行通信

是resp自己設計的RESP協議,該協議的特點為:

  • 簡單
  • 高效
  • 易于解析
  • 保證二進制安全

7. Redis 與 Memcached 有什么區別

  • 數據結構層面:redis支持多種數據結構例如字符串、列表、集合、有序集合、哈希,Memcached 僅僅支持簡單的鍵值對存儲。
  • 持久化層面:Redis 支持RDB或者AOF的方式進行持久化,后者不支持持久化。
  • 數據分片層面:redis通過hash slot實現自動分片和負載均衡,而后者只能手動進行分片。
  • 處理數據的方式:redis通過單線程處理所有的指令,并且支持事務、lua腳本等高級功能,而后者使用多線程處理請求,且僅僅支持get、set操作。
  • 協議:redis使用自定義的resp協議、同時支持多個數據庫并且支持密碼認證,而后者僅僅支持文本協議且只有一個默認的數據庫。
  • 內存管理:redis內存層面各種緩存置換、數據持久化等策略相比后者更加健壯和復雜。

8. Redis為什么這么快

  • 操作數據活躍于內存:通過內存進行數據操作速度遠快于硬盤訪問速度。
  • 單線程:通過單線程處理所有客戶端請求,避免線程上下文切換開銷,大大提高的redis的運行效率和響應速度。
  • IO多路復用:以Linux系統為例,redis通過epoll模型實現單線程處理大量客戶端并發請求,提升了redis的并發性能。
  • 數據結構:redis提供了各種各樣的數據結構,并且針對這些數據類型都進行了各種極致的優化,例如哈希對象,在數據大小較小的情況下使用壓縮列表,一旦數據大小達到閾值后就會轉為哈希集。
  • 6.0引入多線程:在高并發場景下,性能的瓶頸往往處于網絡連接上,為了進一步提升IO性能,redis通過多線程來充分利用CPU核心處理盡可能多個客戶端連接。

9. Redis 支持哪幾種數據類型

比較常見的有:

  • 字符串
  • 列表
  • 集合
  • 有序集合
  • 字典

需要補充的是redis還有一些高級的數據結構例如:

  • stream
  • bitmap
  • Geo
  • HyperLogLog

10. Redis為什么要自己定義SDS

這里我們直接引用《redis設計與實現》一書中的說法:

  • C語言的字符串用\0收尾,在redis的使用場景下,很可能因為這個結束符導致數據被截斷。
  • C語言字符串獲取長度需要進行遍歷,即O(n)級別的時間復雜度。
  • C語言進行字符串拼接總是需要預先做好分配,否則很容易出現緩沖區溢出的問題。對于不斷擴大的字符串還需要反復創建新的字符數組解決問題。

11. Redis中的Zset是怎么實現的

這個問題我們可以針對不同的版本進行回答:

  • 在5.0之前:有序集合在數據體積不是很大的情況下,通過ziplist或dict+skiplist的方式實現有序集合。
  • 7.0 之后:完全取消了壓縮列表,改為dict+listpack/skiplist。

以我們最常用的版本,即5.0左右的版本,本質上redis的有序集合是通過dict保證O(1)級別的直接映射定位,通過跳表實現O(logN)級別的范圍有序查詢。

12. 什么是GEO,有什么用

用于表示地理坐標信息,從而實現經緯度數據檢索,它主要支持的命令有: Redis 的 GEO 模塊提供了一系列用于處理地理位置數據的命令。以下是一些常見的 GEO 命令:

  • GEOADD:將一個或多個地理空間元素(經度、緯度和成員)添加到指定的鍵中。
GEOADD key longitude latitude member [longitude latitude member ...]
  • GEODIST:返回兩個給定成員之間的距離。如果其中任何一個成員不存在,則返回空值。
GEODIST key member1 member2 [unit]
  • GEOHASH:返回一個或多個成員的 Geohash 表示形式。
GEOHASH key member [member ...]
  • GEOPOS:返回一個或多個成員的位置(經度和緯度)。如果成員不存在,則返回空值。
GEOPOS key member [member ...]
  • GEORADIUS:以給定的經緯度為中心,返回指定半徑范圍內的所有成員。
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
  • GEORADIUSBYMEMBER:以給定成員的位置為中心,返回指定半徑范圍內的所有成員。
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

這些命令可以幫助您在 Redis 中高效地存儲和查詢地理位置信息。

13. 為什么Redis 6.0引入了多線程

redis處理能力即qps大約在8w-10w之間,對于某些高并發存在大量客戶端連接的請求,本質上可以通過增加實例解決,但是這種做法在資源消耗和成本無疑是非常大的:

經過分析,這些場景大概率性能瓶頸在連接處理上,雖說redis采用epoll等多路復用技術,但epoll本質還是一個同步阻塞IO模型,所以redis增加多個線程,充分利用CPU核心,從而減少網絡等待的影響,提升程序執行性能。

14. 為什么Lua腳本可以保證原子性

redis針對lua腳本的處理上,會一次性將lua腳本封裝成一個單獨的事務,從而保證操作的指令執行的原子性但不保證發生錯誤后的回滾兜底。

15. Redis中的setnx命令為什么是原子性的

以下兩個原因保證了setnx的原子性:

  • 該指令只有在key不存在時才會插入/
  • 單機情況下redis是單線程執行,所以保證執行執行的有序性,間接保證臨界資源操作的線程安全。

16. Redis 5.0中的 Stream是什么

5.0版本新增的數據結構,主要用于處理有序且可追朔的消息流,每個消息都有唯一的id,按照添加順序進行排序,并且開發人員可以從中添加、讀取和刪除消息,同時它還是支持讓多個消費者并發的處理消息流。 在5.0之前redis通過使用發布訂閱模型實現消息隊列,但缺點是不支持持久化,如果出現網絡斷開、redis宕機等情況,就會造成消息丟失。 而stream提供了消息持久化和主從復制功能保證消息不丟失,保證客戶端可以訪問任何時刻的數據,并且還能記住訪問位置。

總的來說,stream有幾個幾個優點:

  • 有序性
  • 多消費者支持
  • 持久化
  • 支持消息分組

17. Redis的虛擬內存機制是什么

2.4 之前的版本,redis提供了一種虛擬內存的機制,當內存空間不足時,將部分數據持久化到磁盤上,避免redis進程占用過多的內存。

18. Redis的持久化機制是怎樣的

  • rdb:按照協議規范定期生成持久化二進制數據,文件小,恢復速度快,適合做備份和災難恢復,當然缺點也很明顯,定期更新可能導致丟失某一部分的數據。
  • aof:實時完成指令持久化,有著更高的數據可靠性和更細粒度的數據恢復,缺點是文件可能占用空間更多,每次寫操作都需要寫磁盤導致負載過高。

19. Redis 的事務機制是怎樣的

掌握 Redis 事務,提升數據處理效率的必備秘籍

20. Redis的定期內存淘汰策略是怎么樣的

redis通過定期刪除和惰性刪除處理過期key:

  • 定期刪除:redis的serverCron函數會每個100ms隨機抽檢一些key查看是否過期,如果過期則將這些key刪除,通過隨機抽檢保證單線程執行不會阻塞。
  • 惰性刪除:當用戶查詢某個key的時候,redis函數會檢查該key是否會過期,如果過期則將其刪除并返回nil。

Redis 的內存淘汰策略用于在內存不足時決定如何移除數據,以確保 Redis 可以繼續正常運行。以下是 Redis 支持的主要內存淘汰策略:

  • noeviction:默認策略,當達到最大內存限制時,任何寫入操作都會返回錯誤(讀取操作仍然可以進行)。
  • allkeys-lru:從所有鍵中使用最近最少使用的算法來驅逐鍵。
  • volatile-lru:僅從設置了過期時間的鍵中使用最近最少使用的算法來驅逐鍵。
  • allkeys-random:從所有鍵中隨機選擇鍵來驅逐。
  • volatile-random:僅從設置了過期時間的鍵中隨機選擇鍵來驅逐。
  • volatile-ttl:優先根據剩余生存時間(TTL)來驅逐鍵,即 TTL 較短的鍵會被優先驅逐。

這些策略可以在 Redis 配置文件 redis.conf 中通過 maxmemory-policy 參數設置。選擇合適的淘汰策略取決于具體的應用場景和需求。例如,如果希望盡可能保留熱點數據,可以選擇 allkeys-lru 或 volatile-lru;如果希望更公平地處理所有數據,則可以選擇 allkeys-random 或 volatile-random。

21. Redis如何實現發布/訂閱

redis發布訂閱是通過pub和sub指令實現的,如果客戶端對某個事件感興趣可以通過sub訂閱,這些客戶端就會存儲到主題的channel中的鏈表,一旦有發送者用pub消息,channel就會遍歷訂閱者通知消息。

當然隨著stream的出現,可能更多的企業會考慮使用更可靠的stream實現發布訂閱。

22. 除了做緩存,Redis還能用來干什么

  • 消息隊列
  • 延遲隊列
  • 排行版
  • 分布式id
  • 分布式鎖
  • 地理位置運用
  • 分布式限流
  • 分布式session
  • 布隆過濾器
  • 狀態統計
  • 共同關注
  • 推薦關注
  • 數據庫

23. 為什么ZSet 既能支持高效的范圍查詢,還能以 O(1) 復雜度獲取元素權重值?

底層數據結構由字典和調表構成,兩者共同維護持有元素指針,當進行鍵定位時通過字典的哈希算法完成O(1)級別的定位,當需要有序的范圍查詢時,又可以通過跳表完成O(logN)級別的范圍檢索定位。

24. 什么是Redis的漸進式rehash

redis底層字典本質上是通過數組+哈希算法和拉鏈法解決沖突,隨著時間推移可能會重現大量的鏈表導致查詢性能下降,又因為redis是單線程,為避免哈希表擴容耗時長導致性能下降,redis采用漸進式哈希逐步遷移數據到新表。

對于源碼感興趣的讀者可以參考這篇文章:《聊聊redis中的字典設計與實現

25. Redis中key過期了一定會立即刪除嗎

不一定,serverCron的定時函數會批量抽取一批key進行檢查然后刪除。

26. Redis中有一批key瞬間過期,為什么其它key的讀寫效率會降低

出現讀寫效率低,大體是因為主動過期即用戶手動提交一批刪除過期key的任務,因為redis的單線程的原因,對于瞬時的過期key操作勢必出現大量指令需要處理,這時候就會對其他客戶端的讀寫請求造成一定的阻塞,對此我們的解決策略大體有:

  • 設置時間為隨機過期
  • 采用被動過期設置key,即通過redis ex指令完成

27. 什么是Redis的Pipeline,和事務有什么區別

redis的pipeline主要為了解決網絡延遲的技術,客戶端可以一次性批量提交請求,且無需等待每個命令的響應,redis收到這些請求后會依次執行并返回,需要注意的是該操作與事務不同的是它不保證操作處理的原子性,唯一與事務的相同點都是一條指令失敗后,后續的指令都還會執行且不會回滾操作。

28. Redis的事務和Lua之間有哪些區別

事務和lua之間的相同點是兩者都可以保證操作的原子性,不同點是前者一條指令失敗不影響后續指令的執行,而后者反之。

29. 為什么Redis不支持回滾

本質來說redis支持組隊時事務異常回滾,但是不支持執行時異常回滾,設計者針對這種情況也給出相應的原因:

  • redis的設計初衷就是為了簡單、高效,過于復雜的事務實現會讓系統復雜并影響性能
  • 從使用場景來說,redis本質上就是一個緩存工具,不需要復雜的事務支持
  • redis中出錯的問題基本上都是指令不正確,這些問題一般都需要預先解決,而不是依靠事務

30. 關于redis中的布隆過濾器

布隆過濾器是一種概率性的數據結構,用戶快速判斷一個元素是否存在于某個集合中,它的特點是:

  • 通過盡可能少的物理空間維護盡可能多的數據的存在情況
  • 允許誤判(這一點后續會補充)
  • 無法進行元素刪除

對于redis而言實現布隆過濾器的方式有兩種:

  • 基于bitmap結合多個哈希函數模擬布隆過濾器
  • 引入redis官方的redisBloom模塊,對應的操作指令示例如下:
BF.ADD myfilter "user123"  # 添加元素
BF.EXISTS myfilter "user123"  # 檢查是否存在

我們來一個實際的場景,例如我們要統計系統中千萬用戶是否在線,我們就布隆過濾器進行記錄和維護,整體的流程比較簡單:

  • 通過多次哈希運算定位當前用戶id對應的布隆過濾器中的位置。
  • 定位到bit array將索引i位置標記為1。

需要了解的是布隆過濾器在進行哈希的時候是可能存在碰撞的,例如id為1和id為13232的用戶可能因為哈希算法導致的bitmap索引位是一樣的,所以我們可以得出以下結論:

  • 當布隆過濾器認為數據不存在的時候,它100%不存在。
  • 當布隆過濾器認為數據存在的時候,它不一定存在。

二、詳解Redis持久化機制

1. Redis持久化方式有哪些?有什么區別?

持久化分為rdb和aof兩種。

RDB持久化是把當前進程數據生成快照保存到硬盤的過程,觸發RDB持久化過程分為手動觸發和自動觸發。分別使用命令save或者bgsave。 同時rdb是一個二進制的壓縮文件,

以下幾個場景會自動觸發rdb持久化:

  • 使用save相關配置,如“save m n”。表示m秒內數據集存在n次修改時,自動觸發bgsave。
  • 如果從節點執行全量復制操作,主節點自動執行bgsave生成RDB文件并發送給從節點
  • 執行debug reload命令重新加載Redis時,也會自動觸發save操作
  • 默認情況下執行shutdown命令時,如果沒有開啟AOF持久化功能則自動執行bgsave。

而AOF則是以獨立日志的方式記錄每次寫命令, 重啟時再重新執行AOF文件中的命令達到恢復數據的目的,整體工作過程為:

  • 所有的寫入命令會追加到aof_buf(緩沖區)中。
  • AOF緩沖區根據對應的策略向硬盤做同步操作。
  • 隨著AOF文件越來越大,需要定期對AOF文件進行重寫,達到壓縮 的目的。
  • 當Redis服務器重啟時,可以加載AOF文件進行數據恢復。

2. rdb和aof各自有什么優缺點?

rdb優點:

  • 只有一個緊湊的二進制文件 dump.rdb,非常適合備份、全量復制的場景。
  • 容災性好,可以把RDB文件拷貝道遠程機器或者文件系統張,用于容災恢復。
  • 恢復速度快,RDB恢復數據的速度遠遠快于AOF的方式

rdb的缺點:

  • 實時性低,RDB 是間隔一段時間進行持久化,沒法做到實時持久化/秒級持久化。如果在這一間隔事件發生故障,數據會丟失。
  • 存在兼容問題,Redis演進過程存在多個格式的RDB版本,存在老版本Redis無法兼容新版本RDB的問題。

aof優點:

  • 實時性好,aof 持久化可以配置 appendfsync 屬性,有 always,每進行一次命令操作就記錄到 aof 文件中一次。
  • 通過 append 模式寫文件,即使中途服務器宕機,可以通過 redis-check-aof 工具解決數據一致性問題。

aof缺點:

  • AOF文件比RDB 文件大,且 恢復速度慢。
  • 數據集大的時候,比RDB 啟動效率低。

3. rdb和aof如何選擇

如果想達到足以媲美數據庫的 數據安全性,應該 同時使用兩種持久化功能。在這種情況下,當 Redis 重啟的時候會優先載入 AOF 文件來恢復原始的數據,因為在通常情況下 AOF 文件保存的數據集要比 RDB 文件保存的數據集要完整。

如果 可以接受數分鐘以內的數據丟失,那么可以 只使用 RDB 持久化。

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

如果只需要數據在服務器運行的時候存在,也可以不使用任何持久化方式。

當然如果既要保證同步和故障恢復效率,又要盡可能減少數據丟失的概率,也可以考慮混合持久化機制。

4. Redis的數據恢復如何做到的?

  • AOF持久化開啟且存在AOF文件時,優先加載AOF文件。
  • AOF關閉或者AOF文件不存在時,加載RDB文件。
  • 加載AOF/RDB文件成功后,Redis啟動成功。
  • AOF/RDB文件存在錯誤時,Redis啟動失敗并打印錯誤信息。

5. Redis4.0的混合持久化持久化

將 rdb 文件的內容和增量的 AOF 日志文件存在一起。這里的 AOF 日志不再是全量的日志,而是 自持久化開始到持久化結束 的這段時間發生的增量 AOF 日志,通常這部分 AOF 日志很小。

三、Redis場景架構設計

1. 緩存擊穿、緩存穿透、緩存雪崩問題以及應對策略

緩存擊穿:要查詢的某一個緩存數據剛剛好過期,導致大量查詢的請求直接打到數據庫上,讓數據庫處于高負載狀態。

解決策略:

  • 加個互斥鎖保證單位時間內只有一個請求處理SQL查詢并緩存數據。
  • 設置熱點數據永不過期。

緩存穿透:盡管我們將數據庫中某些數據換到到內存中,但是若有些攻擊者使用一些數據庫中不存在的key進行惡意攻擊,這時候,所有的查詢請求就像穿透了緩存中間件一樣直接在數據庫中進行查詢操作,在高并發場景,這樣的攻擊就會使得數據壓力過大,從而導致數據庫被打死

針對緩存穿透問題,對此我們的應對策略有:

  • 使用過濾器,我們可以使用布隆過濾器來減少對數據庫的請求,布隆過濾器的原理是將數據庫的數據哈希到 bitmap 中(在initialBean階段將數據緩存到內存中),每次查詢之前,借用布隆過濾器的特性(不能保證數據一定存在,但一定能保證數據不存在),過濾掉一定不存在的無效請求,從而避免了無效請求給數據庫帶來的查詢壓力。
  • 緩存空結果,我們可以把每次從數據庫查詢的數據都保存到緩存中,為了提高前臺用戶的使用體驗 (解決長時間內查詢不到任何信息的情況),我們可以將空結果的緩存時間設置得短一些,例如 3~5 分鐘,但是有可能導致數據一致性問題,所以我們建議查詢或者更新的時候要對這個類型的緩存上個鎖進行進一步的操作。
  • 緩存雪崩:大量定時緩存失效或緩存服務器宕機,導致數據庫服務器被打死。

解決策略:

  • 加鎖排隊,示例代碼如下所示,如果數據庫中沒有值的話直接上鎖到數據庫查在放到緩存中,有點類似于單例模式的雙重鎖校驗,但是并發場景性能表現會差一些:
// 緩存 key
String cacheKey = "userlist";
// 查詢緩存
String data = jedis.get(cacheKey);
if (StringUtils.isNotBlank(data)) {
    // 查詢到數據,直接返回結果
    return data;
} else {
    // 先排隊查詢數據庫,再放入緩存
    synchronized (cacheKey) {
        data = jedis.get(cacheKey);
        if (!StringUtils.isNotBlank(data)) { // 雙重判斷
            // 查詢數據庫
            data = findUserInfo();
            // 放入緩存
            jedis.set(cacheKey, data);
        }
        return data;
    }
}
  • 設計緩存時,對緩存設置隨機時間:
// 緩存原本的失效時間
int exTime = 10 * 60;
// 隨機數生成類
Random random = new Random();
// 緩存設置
jedis.setex(cacheKey, exTime + random.nextInt(1000) , value);

2. 緩存污染(緩存空間全滿)

某些數據查詢一次就被緩存在數據庫中,隨著時間推移,緩存空間已經滿了,這時候redis就要根據緩存策略進行緩存置換。這就造成沒意義的數據需要通過緩存置換策略來淘汰數據,而且還可能出現淘汰熱點數據的情況。

解決方案:選定合適的緩存置換策略,而redis緩存策略主要分三類。

  • noeviction (v4.0后默認的):不會淘汰任何過期鍵,滿了就報錯,對設置了過期時間的數據中進行淘汰
  • volatile-random:隨機刪除過期key
  • volatile-ttl:根據過期時間進行排序,越早過期的數據就優先被淘汰。
  • volatile-lru:即最近最少使用算法(推薦),redis的lru緩存置換算法相比傳統的算法做了一定優化,根據 maxmemory-samples從緩存中隨機取出幾個key值,然后進行比較在進行淘汰,這樣就避免了緩存置換時需要操作一個大鏈表進行key值淘汰了。
  • volatile-lfu:lru只知曉用戶最近使用次數,而不知道該數據使用頻率,所以lfu就是基于lru進一步的優化,進行淘汰時隨機取出訪問次數最少的數據,如果最少的數據有多個,按按照lru算法進行淘汰。但是redis只用8bit記錄訪問次數,超過255就無法進行自增了,所以我們可以使用lfu-log-factor 和lfu-decay-time來用戶訪問次數增加的頻率。
  • lfu-decay-time:控制訪問次數衰減。LFU 策略會計算當前時間和數據最近一次訪問時間的差值,并把這個差值換算成以分鐘為單位。然后,LFU 策略再把這個差值除以 lfu_decay_time 值,所得的結果就是數據 counter 要衰減的值。若設置為0,則意味著每次掃描訪問次數都會扣減。
  • lfu-log-factor:用計數器當前的值乘以配置項 lfu_log_factor 再加 1,再取其倒數,得到一個 p 值;然后,把這個 p 值和一個取值范圍在(0,1)間的隨機數 r 值比大小,只有 p 值大于 r 值時,計數器才加 1。  全部數據進行淘汰
  • allkeys-random:從所有鍵值對中使用lru淘汰
  • allkeys-lru:從所有鍵值對中隨機刪除
  • allkeys-lfu:從所有鍵值對中使用lfu隨機淘汰

3. 基于Redis定位億級數據

假如Redis里面有1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,如何將它們全部找出來?

我們可以使用 keys 指令可以掃出指定模式的 key 列表。但是要注意 keys 指令會導致線程阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這個時候可以使用 scan 指令,scan 指令可以無阻塞的提取出指定模式的 key 列表,但是會有一定的重復概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用 keys 指令長。

4. 什么情況下會出現數據庫和緩存不一致的問題?

大體有以下兩種情況: 我們先來說說更新數據庫,然后更新緩存的情況,如下圖所示,線程1和線程2都是先更新數據再更新緩存,由于線程1因為網絡波動或者線程調度順序原因導致后更新緩存,最終導致數據庫和緩存不一致,而先更新緩存再更新數據庫同理這里就不多贅述:

還有一種情況是針對讀場景的,如下所示:

  • 線程2查詢緩存發現沒有數據,到數據庫讀取到值10。
  • 此時,線程1更新緩存值為20,準備寫數據庫。
  • 線程2將數據庫讀取到的10寫入緩存。
  • 線程1將數據庫更新為20。

自此,緩存不一致問題又出現:

5. 如何解決Redis和數據庫的一致性問題?

  • 延時雙刪
  • 先更新數據庫再刪除緩存
  • 更新數據庫,并基于用binlog監聽數據庫變化進行緩存刪除。

6. 為什么需要延遲雙刪,兩次刪除的原因是什么?

第一次刪除避免讀請求讀到臟數據 第二次刪除避免讀請求將臟數據寫入緩存.

7. Redis如何實現延遲消息

通過配置notify-keyspace-events Ex開啟過期key事件,再通過程序繼承KeyExpirationEventMessageListener監聽過期的事件,這種做法的缺點也很明顯,即過期的key不一定會立即刪除,且該消息沒有持久化可能出現丟失。 關于過期key不一定會立即刪除的這一點。

通過zset將過期時間作為score,然后key作為member,程序通過計算過期時間差值進行休眠,到期后刪除這個key,當然我們需要保證的就是如果有時效更短的key進來注意更新時間。

通過redission內存輪子提交一個任務,原理和方法2差不多,只不過對于并發消費等問題有了較好的優化,且使用更加簡單。

8. 如何基于Redis實現滑動窗口限流?

滑動窗口本質上就是通過有序集合的方式保證單位時間內保持一定流量數據,避免突然流量突刺的問題,假設我們現在有個接口,希望每秒對應請求控制在2000,對應的落地方案為:

  • 將請求接口作為key。
  • 當某個請求到來時,生成唯一id作為member,時間戳作為value。
  • 基于當前時間戳減去60s看看60s以內的請求數。
  • 查看當前有序集合中元素是否小于2000,如果是則允許新的請求到來。反之不允許。

當然我們也可以直接用redisson中的RRateLimiter,它底層本質就是用一個令牌桶算法。

9. 怎么處理熱key

什么是熱Key? 所謂的熱key,就是訪問頻率比較的key。 比如,熱門新聞事件或商品,這類key通常有大流量的訪問,對存儲這類信息的 Redis來說,是不小的壓力。 假如Redis集群部署,熱key可能會造成整體流量的不均衡,個別節點出現OPS過大的情況,極端情況下熱點key甚至會超過 Redis本身能夠承受的OPS。

怎么處理熱key?

熱key處理 對熱key的處理,最關鍵的是對熱點key的監控,可以從這些端來監控熱點key: 客戶端 客戶端其實是距離key“最近”的地方,因為Redis命令就是從客戶端發出的,例如在客戶端設置全局字典(key和調用次數),每次調用Redis命令時,使用這個字典進行記錄。 代理端 像Twemproxy、Codis這些基于代理的Redis分布式架構,所有客戶端的請求都是通過代理端完成的,可以在代理端進行收集統計。

Redis服務端 使用monitor命令統計熱點key是很多開發和運維人員首先想到,monitor命令可以監控到Redis執行的所有命令。

只要監控到了熱key,對熱key的處理就簡單了: 把熱key打散到不同的服務器,降低壓? 加??級緩存,提前加載熱key數據到內存中,如果redis宕機,?內存查詢

10. 緩存預熱怎么做?

所謂緩存預熱,就是提前把數據庫里的數據刷到緩存里,通常有這些方法:

  • 直接寫個緩存刷新頁面或者接口,上線時手動操作
  • 數據量不大,可以在項目啟動的時候自動進行加載(我們目前就是執行這種操作,通過繼承InitializingBean實現)
  • 定時任務刷新緩存.

11. 熱點key重建問題了解過?你是如何解決的呢?

開發的時候一般使用“緩存+過期時間”的策略,既可以加速數據讀寫,又保證數據的定期更新,這種模式基本能夠滿足絕大部分需求。

但是有兩個問題如果同時出現,可能就會出現比較大的問題:

  • 當前key是一個熱點key(例如一個熱門的娛樂新聞),并發量非常大。
  • 重建緩存不能在短時間完成,可能是一個復雜計算,例如復雜的 SQL、多次IO、多個依賴等。 在緩存失效的瞬間,有大量線程來重建緩存,造成后端負載加大,甚至可能會讓應用崩潰。

要解決這個問題也不是很復雜,解決問題的要點在于:

  • 減少重建緩存的次數。
  • 數據盡可能一致。
  • 較少的潛在危險。 所以一般采用如下方式:
  • 互斥鎖(mutex key) 這種方法只允許一個線程重建緩存,其他線程等待重建緩存的線程執行完,重新從緩存獲取數據即可。
  • 永遠不過期 “永遠不過期”包含兩層意思:
  • 從緩存層面來看,確實沒有設置過期時間,所以不會出現熱點key過期后產生的問題,也就是“物理”不過期,注意數據更新后要實時加鎖更新。

從功能層面來看,為每個value設置一個邏輯過期時間,當發現超過邏輯過期時間后,會使用單獨的線程去構建緩存。

四、詳解Redis日常運維

1. Redis阻塞問題如何解決

(1) API或數據結構使用不合理:通常Redis執行命令速度非常快,但是不合理地使用命令,可能會導致執行速度很慢,導致阻塞,對于高并發的場景,應該盡量避免在大對象上執行算法復雜 度超過O(n)的命令。

對慢查詢的處理分為兩步:

  • 發現慢查詢: slowlog get{n}命令可以獲取最近 的n條慢查詢命令;
  • 發現慢查詢后,可以從兩個方向去優化慢查詢: 1)修改為低算法復雜度的命令,如hgetall改為hmget等,禁用keys、sort等命 令 2)調整大對象:縮減大對象數據或把大對象拆分為多個小對象,防止一次命令操作過多的數據。

(2) CPU飽和的問題:單線程的Redis處理命令時只能使用一個CPU,而CPU飽和是指Redis單核CPU使用率跑到接近100%。

針對這種情況,處理步驟一般如下:

  • 判斷當前Redis并發量是否已經達到極限,可以使用統計命令`redis-cli-h{ip}-p{port}--stat`獲取當前 Redis使用情況
  • 如果Redis的請求幾萬+,那么大概就是Redis的OPS已經到了極限,應該做集群化水品擴展來分攤OPS壓力
  • 如果只有幾百幾千,那么就得排查命令和內存的使用

(3) 持久化相關的阻塞:對于開啟了持久化功能的Redis節點,需要排查是否是持久化導致的阻塞。

fork阻塞 fork操作發生在RDB和AOF重寫時,Redis主線程調用fork操作產生共享 內存的子進程,由子進程完成持久化文件重寫工作。如果fork操作本身耗時過長,必然會導致主線程的阻塞。

AOF刷盤阻塞 當我們開啟AOF持久化功能時,文件刷盤的方式一般采用每秒一次,后臺線程每秒對AOF文件做fsync操作。當硬盤壓力過大時,fsync操作需要等待,直到寫入完成。如果主線程發現距離上一次的fsync成功超過2秒,為了 數據安全性它會阻塞直到后臺線程執行fsync操作完成。

HugePage寫操作阻塞 對于開啟Transparent HugePages的 操作系統,每次寫命令引起的復制內存頁單位由4K變為2MB,放大了512 倍,會拖慢寫操作的執行時間,導致大量寫操作慢查詢。

2. Redis大key問題

Redis使用過程中,有時候會出現大key的情況, 比如:

(1) 單個簡單的key存儲的value很大,size超過10KBhash, set,zset,list 中存儲過多的元素(以萬為單位) 大key會造成什么問題呢?

  • 客戶端耗時增加,甚至超時
  • 對大key進行IO操作時,會嚴重占用帶寬和CPU
  • 造成Redis集群中數據傾斜
  • 主動刪除、被動刪等,可能會導致阻塞

(2) 如何找到大key?

  • bigkeys命令:使用bigkeys命令以遍歷的方式分析Redis實例中的所有Key,并返回整體統計信息與每個數據類型中Top1的大Key
  • redis-rdb-tools:redis-rdb-tools是由Python寫的用來分析Redis的rdb快照文件用的工具,它可以把rdb快照文件生成json文件或者生成報表用來分析Redis的使用詳情。

(3) 如何處理大key?

  • 刪除大key:當Redis版本大于4.0時,可使用UNLINK命令安全地刪除大Key,該命令能夠以非阻塞的方式,逐步地清理傳入的Key。 當Redis版本小于4.0時,避免使用阻塞式命令KEYS,而是建議通過SCAN命令執行增量迭代掃描key,然后判斷進行刪除。
  • 壓縮和拆分key:當vaule是string時,比較難拆分,則使用序列化、壓縮算法將key的大小控制在合理范圍內,但是序列化和反序列化都會帶來更多時間上的消耗。 當value是string,壓縮之后仍然是大key,則需要進行拆分,一個大key分為不同的部分,記錄每個部分的key,使用multiget等操作實現事務讀取。 當value是list/set等集合類型時,根據預估的數據規模來進行分片,不同的元素計算后分到不同的片。

3. Redis常見的性能問題和解決方案了解嘛?

Master 最好不要做任何持久化工作,包括內存快照和 AOF 日志文件,特別是不要啟用內存快照做持久化。

如果數據比較關鍵,某個 Slave 開啟 AOF 備份數據,策略為每秒同步一次。

為了主從復制的速度和連接的穩定性,Slave 和 Master 最好在同一個局域網內。 盡量避免在壓力較大的主庫上增加從庫。

Master 調用 BGREWRITEAOF 重寫 AOF 文件,AOF 在重寫的時候會占大量的 CPU 和內存資源,導致服務 load 過高,出現短暫服務暫停現象。

為了 Master 的穩定性,主從復制不要用圖狀結構,用單向鏈表結構更穩定,即主從關為:Master<–Slave1<–Slave2<–Slave3…,這樣的結構也方便解決單點故障問題,實現 Slave 對 Master 的替換,也即,如果 Master 掛了,可以立馬啟用 Slave1 做 Master,其他不變。

4. 什么是熱Key問題,如何解決熱key問題

即同一個時間點上,redis中同一個key被大量訪問,導致流量過于集中,進而導致服務器資源無法支撐嚴重情況下可能導致服務癱瘓。 所以,對應的處理策略要針對不同的情況,如果是事前,我們可以根據往年經驗識別出對應的熱點key,提前擴充實例,做預熱緩存。 如果是事中,需要考慮線上進行熱點key拆分,多級緩存、增加實例甚至通過限流等策略來解決問題。

5. 如何用Redis實現樂觀鎖、可重入鎖

大體可以通過以下幾個指令:

  • watch監視一個或者多個鍵
  • get查詢數據
  • multi開始事務
  • set執行命令
  • exec提交事務

而可重入鎖通過setnx+incr和decr指令完成上鎖和解鎖邏輯。

6. Redis實現分布鎖的時候,哪些問題需要考慮

  • 互斥
  • 性能
  • 誤解鎖
  • 鎖超時
  • 鎖續命
  • 單點故障
  • 鎖重入
  • 網絡分區
  • 時間漂移

7. Redis如何高效安全的遍歷所有key

加入redis中存在大量的key,使用常規的keys * 請求會導致其他的客戶端請求阻塞,所以針對遍歷key的需求,我們更建議使用scan,它會以游標的方式分批次迭代鍵集合,這個概念和游標查詢是優點類似的:

這里筆者簡單制造了百萬級別的熱key進行演示:

StringRedisTemplate redisTemplate = SpringUtil.getBean(StringRedisTemplate.class);
        IntStream.rangeClosed(0, 100_0000).parallel()
                .forEach(n -> {
                    redisTemplate.opsForValue().set("key-" + n, "value-" + n);
                });

這里筆者用了keys * 嘗試了一下遍歷,可以看到耗時約70s,這也就意味則在這70s之間其他的請求是阻塞的:

而使用scan就可以很好的解決問題,通過scan指令從0開始,每次基于上一次的游標進行數據檢索獲取,通過逐批次的檢索和遍歷很好的解決keys *的阻塞問題:

雖說scan很好的解決的遍歷阻塞問題,但它對于數據實時性的把控不是很好,從上面我可以知道scan指令本質上就是漸進式的遍歷,這意味著在掃描過的區間上進行的任何修改操作我們都是無法感知的。

責任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關推薦

2025-01-07 14:10:46

SpringBoot開發Java

2021-12-30 08:17:27

Springboot數據訪問DataSourceB

2021-01-15 08:35:49

Zookeeper

2020-11-06 00:50:16

JavaClassLoaderJVM

2020-10-26 10:40:31

Axios前端攔截器

2021-01-06 13:52:19

zookeeper開源分布式

2024-11-04 09:00:00

Java開發

2021-04-13 08:25:12

測試開發Java注解Spring

2025-02-12 00:29:58

2024-04-23 14:25:16

Python備忘清單

2025-05-13 08:10:00

MySQL二進制日志binlog

2020-05-19 14:40:08

Linux互聯網核心

2022-10-29 08:55:19

頁面react

2025-06-30 02:22:00

2022-04-08 07:51:31

JavaJVM垃圾回收

2020-10-14 10:50:50

SpringSessiJavaweb

2024-03-12 12:57:07

Redis主從架構

2024-07-11 08:17:00

2024-06-04 14:07:00

2023-08-07 14:44:56

Socket文件描述符
點贊
收藏

51CTO技術棧公眾號

成年人网站在线观看视频| www.超碰com| 天天干天天爱天天操| 久久都是精品| 操日韩av在线电影| 少妇户外露出[11p]| 欧美xxxx网站| 精品久久久久久久久久久久| 神马影院我不卡| 刘亦菲久久免费一区二区| 视频在线在亚洲| 欧美久久精品午夜青青大伊人| 精品无码人妻一区| 免费一区二区三区在线视频| 91国模大尺度私拍在线视频| 日韩一级特黄毛片| 大地资源中文在线观看免费版| 国产 日韩 欧美大片| 国产精品久久一区| 国产无套丰满白嫩对白| 欧美.日韩.国产.一区.二区| 亚洲性视频网站| 久久久久久久人妻无码中文字幕爆| 中文字幕系列一区| 欧美视频二区36p| 日韩激情视频一区二区| 毛片在线看网站| 国产清纯白嫩初高生在线观看91| 国产高清精品一区二区| 国产免费不卡av| 喷水一区二区三区| 热re91久久精品国99热蜜臀| 日韩欧美一区二区一幕| 欧美va天堂在线| 欧美成人精品在线视频| 成人信息集中地| 国产最新精品| 国产一区二区三区日韩欧美| 人妻精品久久久久中文字幕| 国产精品久久久久久久久久白浆| 日韩一级高清毛片| 亚洲天堂网站在线| 欧美性www| 欧美色图在线观看| www.com操| 老司机精品视频网| 欧美色成人综合| 一本色道久久亚洲综合精品蜜桃 | 亚洲精品国产无套在线观| 亚洲国产精品久久久久婷婷老年| 国产二区在线播放| 日本一区二区三区国色天香 | 欧美理论片在线| 国产xxxxx视频| 色成人免费网站| 色老综合老女人久久久| 欧美丰满少妇xxxxx高潮对白| 水蜜桃亚洲一二三四在线| 日本一区高清| 久久精品欧美一区二区三区麻豆 | 日韩中文字幕免费在线观看| 成人精品国产福利| 麻豆av一区二区三区| 日本在线视频1区| 国产欧美一区二区精品久导航| 日产精品一线二线三线芒果| 成年人在线视频免费观看| 中文字幕av一区 二区| 亚洲一区三区视频在线观看| www在线观看播放免费视频日本| 亚洲精品欧美二区三区中文字幕| 午夜在线视频免费观看| 欧美大胆的人体xxxx| 天天综合天天做天天综合| 国产黄色特级片| 日韩久久一区| 亚洲第一页在线| 精品欧美一区二区久久久| 日韩欧美字幕| 久久久久女教师免费一区| 亚洲免费在线视频观看| 麻豆视频观看网址久久| www久久99| 国产视频二区在线观看| 日韩毛片高清在线播放| a天堂资源在线观看| 日韩不卡免费高清视频| 欧美日本一区二区| 天天躁日日躁狠狠躁av麻豆男男| 国产精品密蕾丝视频下载| 久久色精品视频| 97人人澡人人爽人人模亚洲| 另类的小说在线视频另类成人小视频在线| 91青青草免费观看| 涩视频在线观看| 日韩中文字幕视频网| 日韩乱码在线视频| 免费成人美女女在线观看| 日韩视频在线一区二区三区 | 日韩精品中文字幕吗一区二区| 亚洲精品www久久久| 国产一二三四视频| 亚洲伦伦在线| 91免费视频网站| 国产色a在线| 午夜av电影一区| 伊人色在线视频| 偷窥自拍亚洲色图精选| 另类图片亚洲另类| 精品国产青草久久久久96| 波多野结衣视频一区| 亚洲资源在线网| 在线观看网站免费入口在线观看国内 | 在线色欧美三级视频| 久久久国产精华液| 美腿丝袜在线亚洲一区| 免费不卡亚洲欧美| 免费在线观看av电影| 欧美男同性恋视频网站| 在线免费观看麻豆| 91久久综合| av免费观看久久| 婷婷成人激情| 欧美网站一区二区| 91成年人网站| 国产精品亚洲综合色区韩国| 国产精品初高中精品久久| 国产欧美黑人| 欧美一区二区三区在线看| 日韩av片在线免费观看| 日韩精彩视频在线观看| 蜜桃精品久久久久久久免费影院| av2020不卡| 欧美mv日韩mv| 青青草成人免费| 国产精品自在欧美一区| 致1999电视剧免费观看策驰影院| 69堂免费精品视频在线播放| 亚洲日本成人网| 亚洲欧美一区二区三区在线观看| av中文字幕不卡| 日韩精品 欧美| 国产福利一区二区精品秒拍| 欧美激情图片区| 亚洲精品字幕在线| 亚洲激情综合网| 亚洲美女精品视频| 黄色成人精品网站| 国产原创精品| 亚洲一二三四| 国产亚洲成av人片在线观看桃| www.com亚洲| 国产精品久久久久久久蜜臀| 嫩草影院国产精品| 久久精品国内一区二区三区水蜜桃| 91精品综合视频| av网址在线看| 亚洲第一福利视频| 东京热一区二区三区四区| 久久无码av三级| 亚洲国产精品三区| 亚洲国产日韩欧美在线| 99精彩视频在线观看免费| 青青草原av在线| 亚洲国产精品久久久久| 国内自拍视频在线播放| 国产精品青草综合久久久久99| av中文字幕网址| 欧美日韩亚洲一区| 免费成人在线观看av| 国产精品成人国产| 欧美黑人性猛交| 日本人妖在线| 欧美日韩在线三级| 久草福利资源在线观看| 99精品久久99久久久久| 搡女人真爽免费午夜网站| 91超碰成人| 精品免费日产一区一区三区免费| 欧美××××黑人××性爽 | 日韩精品一区在线观看| 中文字幕亚洲精品在线| 日本一区二区三区dvd视频在线| 91在线第一页| 国产精品久久久久毛片大屁完整版| 日韩影院一区| 国产精品成人**免费视频| 777午夜精品福利在线观看| 在线国产情侣| 亚洲精品国产综合区久久久久久久 | 女人被狂躁c到高潮| 麻豆久久久久久| 91免费黄视频| 91欧美日韩| 久久精品国产精品青草色艺 | 麻豆天美蜜桃91| 精品国产一区二区三区久久久蜜臀 | 日韩欧美三级一区二区| 婷婷视频一区二区三区| 国产精品免费网站| 97超碰在线免费| 美女视频久久黄| 国产人成在线视频| 亚洲精品久久久久久久久久久久| 91精品国自产| 日韩欧美在线一区| 精品小视频在线观看| 1区2区3区国产精品| 国产精品毛片一区二区| 国产成人av一区二区三区在线观看| 日韩欧美在线免费观看视频| 最新亚洲视频| 成年丰满熟妇午夜免费视频| 日本欧美视频| 欧美亚洲国产免费| youjizz欧美| 92国产精品久久久久首页| 久久三级毛片| 国产成人极品视频| 色多多在线观看| 性色av一区二区三区免费| 国产精品va在线观看视色| 中文字幕最新精品| 懂色av中文在线| 亚洲性生活视频| 男人的天堂在线免费视频| 日韩女同互慰一区二区| 国产免费高清视频| 91麻豆精品国产91久久久资源速度| 国产主播第一页| 色婷婷精品久久二区二区蜜臀av| 五月婷婷中文字幕| 欧美日韩国产精品专区| 日韩三级小视频| 性久久久久久久| 日本在线观看中文字幕| 亚洲高清不卡在线| 激情视频在线播放| 亚洲综合色区另类av| 久久综合久久鬼| 亚洲国产成人tv| 日韩av片在线播放| 精品免费在线观看| 成人免费区一区二区三区| 精品久久久久久亚洲国产300| 日韩免费在线视频观看| 午夜不卡av免费| 久久精品视频5| 欧美三级在线播放| 国产精品一区二区黑人巨大| 日韩一区二区电影在线| www.超碰在线.com| 亚洲成色777777在线观看影院| 熟妇人妻系列aⅴ无码专区友真希| 亚洲高清色综合| 免费在线观看污视频| 亚洲日韩欧美视频一区| 在线免费看黄网站| 久久久91精品国产| 最新黄网在线观看| 91精品国产91久久久久久久久 | 一区免费在线| 国产美女无遮挡网站| 久久精品中文| 日本三级黄色网址| 国产精品亚洲一区二区三区妖精| 免费不卡的av| 久久先锋资源网| 国产天堂av在线| 亚洲成av人片在www色猫咪| 99超碰在线观看| 9191成人精品久久| 香蕉视频成人在线| 在线观看国产欧美| 色呦呦在线看| 国产aⅴ夜夜欢一区二区三区| 中文字幕日本一区| 国产精品一区二区av| 欧美伦理在线视频| 精品91一区二区三区| 国产精品婷婷| 中文字幕第66页| 91女人视频在线观看| 永久免费看片视频教学| 亚洲va国产天堂va久久en| 国产精品传媒在线观看| 日韩午夜电影在线观看| 九色蝌蚪在线| 色综合视频网站| www.国产精品| 好吊色欧美一区二区三区 | 无人在线观看的免费高清视频| 国产乱理伦片在线观看夜一区 | 亚洲三级在线免费观看| 国产午夜在线播放| 欧美一级理论性理论a| 免费毛片在线| 久久久久久九九九| 国产毛片精品久久| 久久久精彩视频| 欧美精品一卡| 在线观看av网页| 99久久免费视频.com| 亚洲xxxx3d动漫| 一本大道久久精品懂色aⅴ| 草草视频在线播放| 色天天综合狠狠色| 婷婷午夜社区一区| 精品综合久久久| 午夜激情一区| 182午夜在线观看| 久久精品无码一区二区三区| 日韩欧美a级片| 欧美一区二区成人6969| 日本成人网址| 国产成人一区二区三区| 日韩三级毛片| 蜜臀av无码一区二区三区| 国产福利一区在线| 朝桐光av在线| 欧美日韩精品一区二区三区| 免费成人av电影| 欧洲日韩成人av| 日韩美女国产精品| 国产日韩av网站| 成人av在线一区二区| 欧产日产国产v| 欧美一区二区三区人| 午夜毛片在线| 国产精品一区二区久久| 日韩久久精品网| 午夜dv内射一区二区| 久久久精品黄色| 亚洲欧美偷拍视频| 日韩精品在线观看一区| 黄色激情在线播放| 久久久久国产精品视频| 亚洲中字在线| 最新中文字幕视频| 色呦呦日韩精品| 成人免费视频| 国产日韩欧美视频在线| 亚洲成人二区| 国产探花一区二区三区| 亚洲一卡二卡三卡四卡| 日批视频免费播放| 97超碰蝌蚪网人人做人人爽| 欧美精品国产白浆久久久久| heyzo国产| 国产欧美一区二区精品秋霞影院| www.五月婷婷.com| 色婷婷综合成人| 国产精品高清一区二区| 欧美乱做爰xxxⅹ久久久| 丁香五精品蜜臀久久久久99网站| 天堂资源在线播放| 亚洲老板91色精品久久| 色猫猫成人app| 老司机午夜网站| 成人性生交大片| 手机看片久久久| 精品国产一区二区在线| 精品视频在线观看免费观看| 国产一线二线三线女| 91影院在线免费观看| 久久久久久无码精品大片| 久久五月天色综合| 风间由美性色一区二区三区四区 | 国产精品自拍一区| 在线看成人av| 中文字幕九色91在线| 亚洲成人黄色| 国产99久久九九精品无码| 欧美极品美女视频| 成人av无码一区二区三区| 97成人在线视频| 久久精品99久久无色码中文字幕| 黄色片子免费看| 色婷婷av一区| 在线xxxx| 欧美一级日本a级v片| 国产伦精品一区二区三区视频青涩 | 怡红院一区二区三区| 91精品久久久久久久99蜜桃| 激情国产在线| 国产a级片免费看| 26uuu另类欧美| 国产v在线观看| 欧亚精品中文字幕| 一个色综合网| 中文字幕网站在线观看| 欧美大片一区二区三区| 日本精品网站| av高清在线免费观看| 成人免费在线视频| 国产在线黄色| 国产亚洲精品自在久久| 精品一区二区三区在线观看| 免费日韩一级片| 欧美成人精品h版在线观看|