精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

天堂网一区二区三区| 在线不卡a资源高清| 欧美中文字幕一二三区视频| 国产自摸综合网| av无码一区二区三区| 久久综合之合合综合久久| 香蕉人人精品| 成人精品视频一区二区三区| 精品国产一区久久久| 精品视频无码一区二区三区| 亚洲国产精品二区| 99视频精品视频高清免费| 91高清视频免费看| 免费国产一区二区| av资源免费观看| 日韩动漫一区| 欧美酷刑日本凌虐凌虐| 久久久久久久久久久99| 丰满肥臀噗嗤啊x99av| 亚洲区综合中文字幕日日| 欧美日韩一区在线| 日韩中文一区| 中文字幕+乱码+中文乱码www| 蜜桃精品噜噜噜成人av| 欧美日韩国产中字| 久久亚洲精品欧美| 国产乱淫av免费| 在线成人直播| 一本色道久久88亚洲综合88| 精品少妇无遮挡毛片| 国产黄色片在线观看| 日韩av在线播放中文字幕| 日韩精品中文字幕视频在线| 日本不卡在线观看视频| 精品av中文字幕在线毛片| 久久精品网址| 中文字幕一区二区三区电影| 男生操女生视频在线观看| 国产最新在线| 国产性天天综合网| 国产免费成人av| 精品国产精品国产精品| xxxxxhd亚洲人hd| 精品国产老师黑色丝袜高跟鞋| 久久99精品久久久久久水蜜桃| 日本少妇激情舌吻| 日韩免费电影在线观看| 欧美一卡二卡在线观看| av日韩一区二区三区| 天堂a中文在线| 日韩vs国产vs欧美| 日韩欧美国产综合| 欧美日韩dvd| 日韩毛片在线一区二区毛片| 日本免费新一区视频| 欧美专区在线观看| 日本二区三区视频| 久久超级碰碰| 精品视频一区 二区 三区| 欧美视频在线免费播放| 国产调教在线| 中文字幕av不卡| 国产精品福利视频| 九九热精彩视频| 国产精品欧美日韩一区| 欧美一区二区久久久| 日本中文字幕二区| 电影91久久久| 日韩欧美在线观看视频| 国产精品av免费| 凸凹人妻人人澡人人添| 久久99热狠狠色一区二区| 国内揄拍国内精品| 林心如三级全黄裸体| 动漫3d精品一区二区三区乱码| 色8久久人人97超碰香蕉987| 欧美日韩一区二区在线免费观看 | 中文字幕久热精品在线视频| 久久久精品成人| 亚洲精品福利| 欧美视频在线一区二区三区| 欧美一级视频在线播放| av电影在线观看一区二区三区| 高清在线成人网| 国产精品久久久久免费a∨大胸| 精品无码av在线| 久久一区二区中文字幕| 亚洲精品久久久久久下一站| 亚洲制服在线观看| av成人免费看| 日韩欧美在线第一页| 九九九在线观看视频| 爱啪啪综合导航| 在线观看欧美日本| 欧美精品 - 色网| 吞精囗交69激情欧美| 亚洲高清视频的网址| 在线观看18视频网站| 成人高潮成人免费观看| 久久在线免费观看| 精品久久蜜桃| 蜜桃视频污在线观看| 国产一区二区成人久久免费影院| 国产精品久久久久99| 欧产日产国产69| 一区二区日韩免费看| 午夜精品久久久久久久男人的天堂| 深夜福利影院在线观看| 亚洲成av人电影| 精品久久久av| 伊人手机在线视频| 亚洲永久免费精品| 欧美专区第一页| 国产黄色片免费| 国产成人日日夜夜| 亚洲va欧美va国产综合久久| 亚洲午夜精品久久久| 美女脱光内衣内裤视频久久网站 | 精品视频久久久久久| 天天鲁一鲁摸一摸爽一爽| 日韩国产欧美| 中文字幕精品在线视频| 中文字幕一区二区三区手机版 | 日韩免费不卡av| 久久中文字幕免费| 国产成人免费视频网站| 亚洲精品美女久久7777777| 永久免费av在线| 自拍偷拍亚洲激情| 欧美一级黄色录像片| 成人性教育av免费网址| 在线观看日韩电影| 国产精品久久久久久久无码| 亚洲伊人春色| 亚洲午夜色婷婷在线| 99久久99久久精品免费看小说. | 国产无遮挡裸体免费久久| 久久久精品美女| 中文字幕av无码一区二区三区| 99久久伊人网影院| 欧美精品一区二区三区久久| 男人天堂亚洲二区| 国产精品天美传媒| 日本熟妇人妻xxxxx| 卡通动漫精品一区二区三区| 欧美国产日韩二区| 国产精品老女人| 国产成人精品三级麻豆| 日本xxx免费| 激情综合五月| 国产视频精品免费播放| 国产午夜视频在线播放| 福利一区二区在线观看| 黄黄视频在线观看| 亚洲私拍视频| 欧美日韩精品欧美日韩精品一综合| 国产农村妇女精品久久| 天天超碰亚洲| 成人国产亚洲精品a区天堂华泰 | 亚洲精品电影久久久| 国产在线免费视频| 首页亚洲欧美制服丝腿| 91国产在线免费观看| 欧美91精品久久久久国产性生爱| 亚洲成在线观看| 在线免费观看污视频| 国产精品综合色区在线观看| 91久久精品在线| 深夜福利视频一区| 色综合久久88色综合天天| 人人爽人人爽av| 外国成人在线视频| 日本道色综合久久影院| eeuss影院www在线观看| 欧美久久久久久久久中文字幕| 国产成人av免费在线观看| 国产美女视频91| 少妇高潮喷水在线观看| 久久综合色占| 国产在线精品一区免费香蕉| 少女频道在线观看高清 | 久久91亚洲精品中文字幕| 69亚洲精品久久久蜜桃小说| 国产福利精品一区二区| 日韩日韩日韩日韩日韩| 狠狠色丁香婷婷综合影院| 成人精品网站在线观看| 国内在线免费视频| 91精品婷婷国产综合久久竹菊| 成年人一级黄色片| 97精品视频在线观看自产线路二| 亚洲区成人777777精品| 久久动漫网址| 成人免费大片黄在线播放| 国内老司机av在线| 色99之美女主播在线视频| 中文字幕xxxx| 亚洲品质自拍视频网站| 亚洲欧美自拍另类日韩| 欧美天天综合| 国产成人avxxxxx在线看| 午夜视频免费看| 欧美视频在线一区二区三区 | 欧美性xxxx极品hd满灌| 久久精品在线观看视频| 日本vs亚洲vs韩国一区三区| 国内精品国产三级国产99| 欧美女优在线视频| 成人3d动漫一区二区三区91| 在线网址91| 欧美成人一区二区| 永久免费看片直接| 91丨porny丨国产入口| www国产精品内射老熟女| 日本在线中文字幕一区| 欧美中文在线视频| 宅男在线观看免费高清网站| 中文字幕不卡在线视频极品| 四虎永久在线精品免费网址| 精品国产31久久久久久| 在线日韩国产网站| 欧美激情在线看| jizz欧美性11| 亚洲在线观看| 国产av人人夜夜澡人人爽麻豆| 国产精品久久天天影视| 日韩不卡av| 伊人久久大香线蕉综合网站 | 激情 小说 亚洲 图片: 伦| 国产一区二区三区不卡视频网站| 99re视频在线播放| 中文字幕日本一区| 欧美国产在线电影| 黄页视频在线播放| 日韩亚洲成人av在线| 国产女人爽到高潮a毛片| 亚洲免费色视频| 性高潮免费视频| 欧美亚洲专区| 久久亚洲中文字幕无码| 伊人精品在线| 日韩中文字幕一区| 欧美人与拘性视交免费看| 精品无人区一区二区三区| 国产精品美女在线观看直播| 国产福利久久精品| 97se亚洲国产一区二区三区| 亚洲一区亚洲二区| 日韩精品中文字幕吗一区二区| 69久久夜色精品国产69| 91亚洲欧美| 在线精品高清中文字幕| 国产高清一区在线观看| 一区二区三区四区在线观看视频| 蜜桃视频在线观看网站| 亚洲欧美在线免费观看| 国产三级精品在线观看| 91麻豆精品国产91久久久使用方法| 中文字幕乱码视频| 欧美精品丝袜久久久中文字幕| 国产精品一区二区免费视频| 日韩一卡二卡三卡国产欧美| 在线观看 亚洲| 在线区一区二视频| 中文字幕一区二区三区四区视频 | 亚洲乱码国产乱码精品精98午夜 | 在线免费观看黄色| 久久天堂av综合合色| 日韩美女一级视频| 国产亚洲一区精品| 免费网站看v片在线a| 亚洲女人天堂色在线7777| 国产又粗又大又黄| 色综合久久88色综合天天| 亚洲午夜在线播放| 欧美日韩国产精品专区| 天天射天天干天天| 亚洲va国产va欧美va观看| 黄色精品视频在线观看| 亚洲精品写真福利| 国产微拍精品一区| 精品婷婷伊人一区三区三| 国产ts人妖调教重口男| 亚洲精品国产美女| 日本激情视频在线观看| 欧美高清一级大片| 欧美中文字幕精在线不卡| 国产日韩在线亚洲字幕中文| 99精品中文字幕在线不卡| 欧美日韩国产精品一卡| 五月天激情综合网| 国产精品999视频| 麻豆成人久久精品二区三区小说| 成熟老妇女视频| 国精产品一区一区三区mba桃花| 亚洲色图38p| 国产一区二区电影| 一级黄色性视频| 久久婷婷色综合| 国产三级aaa| 精品久久久久久久久国产字幕| 中文字幕精品无码亚| 亚洲成年人在线| 日本激情视频网站| 色哟哟网站入口亚洲精品| 福利在线导航136| 国产伦精品一区二区三区精品视频| 成人中文字幕视频| 影音先锋欧美资源| 中文字幕在线观看欧美| 第四色在线一区二区| 国产精品永久免费| 精品自拍偷拍| 国产精品夜夜夜爽张柏芝| 一本色道久久综合亚洲精品不卡| 日韩在线一区视频| 久久久久9999亚洲精品| 久久久久国产精品无码免费看| 亚洲国产成人一区二区三区| 中文字幕一区二区三区手机版 | 久久在线免费视频| 偷拍视频一区二区三区| 国产精品免费一区二区| 四季av在线一区二区三区| 国产成人亚洲精品无码h在线| 亚洲欧美高清| 美女网站视频在线观看| 日韩美女啊v在线免费观看| 国产91精品看黄网站在线观看| 欧美精品一区二区久久婷婷| 欧美特黄一级视频| 久久天天躁狠狠躁夜夜爽蜜月| 电影亚洲精品噜噜在线观看| 国产精品一区二区久久国产| 四虎884aa成人精品最新| av在线观看地址| 国产盗摄一区二区三区| 欧美一级特黄高清视频| 欧美日韩精品系列| 色哟哟免费在线观看| 国产精品大片wwwwww| 女厕嘘嘘一区二区在线播放 | 成av人片在线观看www| av资源站久久亚洲| 午夜日韩在线| 女人和拘做爰正片视频| 成人性生交大片免费| 亚洲av无码一区二区二三区| 国产欧美在线观看一区| 无码人妻久久一区二区三区| 亚洲女人被黑人巨大进入| 日韩欧美精品一区二区三区| 国产精品一区二区电影| 久久精品播放| 久久久久久久久久久久久久久国产| 中文字幕乱码一区二区免费| 中文字幕免费播放| 日韩中文字幕免费| 天天综合91| 精品国产一区二区三区日日嗨 | 亚洲人亚洲人色久| 男人日女人bb视频| 国产亚洲精品免费| 中文字幕在线播放不卡| 日韩在线视频观看| 麻豆国产一区| 老太脱裤让老头玩ⅹxxxx| 91看片淫黄大片一级在线观看| 国产午夜麻豆影院在线观看| 亚洲视频国产视频| 欧美激情不卡| 久久亚洲国产精品日日av夜夜| 久久婷婷av| 99久久精品久久亚洲精品| 日韩女优电影在线观看| 亚洲性图自拍| 精品国产乱码久久久久久88av | 欧美在线xxx| 视频二区欧美| 成人一区二区免费视频| wwwwww.欧美系列| 伊人免费在线观看| 欧美裸体xxxx极品少妇| 啪啪激情综合网| 亚洲一级片免费| 亚洲一区二区四区蜜桃| 国产美女主播在线观看| 久久久久久69| 日本精品视频| 凹凸国产熟女精品视频| 国产精品久久久久久久岛一牛影视| 国产又大又黄视频| 一区二区在线视频| 亚洲开心激情| 欧美日韩在线成人| 一区二区日韩av| 国产又粗又猛又爽又黄视频 | 日韩电影毛片| 亚洲 欧洲 日韩| 91丨porny丨国产入口|