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

歡迎訪問 生活随笔!

生活随笔

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

数据库

【大厂面试】面试官看了赞不绝口的Redis笔记

發布時間:2023/12/8 数据库 61 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【大厂面试】面试官看了赞不绝口的Redis笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 一、Redis簡介
  • 二、Redis API的使用和理解
    • (一)通用命令
    • (二)單線程架構
    • (三)數據結構和內部編碼
    • (四)字符串
    • (五)hash (字典)
    • (六)列表
    • (七)Set集合
    • (八)zset (有序集合)
  • 三、Redis 客戶端操作
  • 說明

一、Redis簡介

Redis(Remote Dictionary Server)是一個使用ANSI C編寫的開源、支持網絡、基于內存、可選持久性的鍵值對存儲數據庫,也是于開發或者運維都是必須要掌握的非關系型數據庫。

Redis可作為高性能 Key-Value服務器,擁有多種數據結構,并提供豐富的功能以及對高可用分布式的支持。

Redis的具有以下:1. 速度快;2. 功能豐富;3. 可持久化;4. 簡單;5. 多種數據結構;6. 主從復制;7. 支持多種編輯語言;8. 高可用、分布式等。

二、Redis API的使用和理解

下面我們會依次詳細探討常用的Redis 的API。

再說之前,給大家推薦一個網站,用于查閱Redis API: http://redisdoc.com/index.html

文中的部分演示結果來源于該網站

傳送門:Redis 命令參考

(一)通用命令

因為通用命令會涉及Redis的數據結構操作,而Redis的數據結構操作也會涉及到通用命令,所以這兩部分要結合著看。

不同數據結構的操作內容在下面

1. KEYS

通用命令KEYS pattern(pattern 為正則表達式)
功能描述查找所有符合給定模式 pattern 的 key
時間復雜度O(N), N 為數據庫中 key 的數量。

功能描述方面舉例:

  • KEYS * 匹配數據庫中所有 key 。
  • KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
  • KEYS h*llo 匹配 hllo 和 heeeeello 等。
  • KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。

特殊符號用 \ 隔開。

示例

redis> MSET one 1 two 2 three 3 four 4 # 一次設置 4 個 key OKredis> KEYS *o* 1) "four" 2) "two" 3) "one"redis> KEYS t?? 1) "two"redis> KEYS t[w]* 1) "two"redis> KEYS * # 匹配數據庫內所有 key 1) "four" 2) "three" 3) "two" 4) "one"

在生產環境中,使用keys命令取出所有key并沒有什么意義,而且Redis是單線程應用,如果Redis中存的key很多,使用keys命令會阻塞其他命令執行,所以keys命令一般不在生產環境中使用

2. DBSIZE

通用命令DBSIZE
功能描述返回當前數據庫的 key 的數量。
時間復雜度時間復雜度: O(1)

示例

redis> DBSIZE (integer) 5redis> SET new_key "hello_moto" # 增加一個 key 試試 OKredis> DBSIZE (integer) 6

Redis內置一個計數器,可以實時更新Redis中key的總數,因此dbsize的時間復雜度為O(1),可以在線上使用。

3. EXISTS

通用命令EXISTS key
功能描述檢查給定 key 是否存在。若 key 存在,返回 1 ,否則返回 0 。
時間復雜度O(1)
redis> SET db "redis" OKredis> EXISTS db (integer) 1redis> DEL db (integer) 1redis> EXISTS db (integer) 0

4. DEL

通用命令DEL key [key …]
功能描述刪除給定的一個或多個 key 。不存在的 key 會被忽略。返回值是被刪除 key 的數量。
時間復雜度O(N), N 為被刪除的 key 的數量,其中刪除單個字符串類型的 key ,時間復雜度為O(1);刪除單個列表、集合、有序集合或哈希表類型的 key ,時間復雜度為O(M), M 為以上數據結構內的元素數量。
# 刪除單個 key redis> SET name huangz OKredis> DEL name (integer) 1# 刪除一個不存在的 key redis> EXISTS phone (integer) 0redis> DEL phone # 失敗,沒有 key 被刪除 (integer) 0# 同時刪除多個 key redis> SET name "redis" OKredis> SET type "key-value store" OKredis> SET website "redis.com" OKredis> DEL name type website (integer) 3

5. EXPIRE

通用命令EXPIRE key seconds
功能描述為給定 key 設置生存時間,當 key 過期時(生存時間為 0 ),它會被自動刪除。設置成功返回 1 。 當 key 不存在或者不能為 key 設置生存時間時(比如在低于 2.1.3 版本的 Redis 中你嘗試更新 key 的生存時間),返回 0 。
時間復雜度O(1)
redis> SET cache_page "www.google.com" OKredis> EXPIRE cache_page 30 # 設置過期時間為 30 秒 (integer) 1redis> TTL cache_page # 查看剩余生存時間 (integer) 23redis> EXPIRE cache_page 30000 # 更新過期時間 (integer) 1redis> TTL cache_page (integer) 29996

6. TTL

通用命令TTL key
功能描述以秒為單位,返回給定 key 的剩余生存時間(TTL, time to live)。
時間復雜度O(1)
redis> FLUSHDB OKredis> TTL key (integer) -2# key 存在,但沒有設置剩余生存時間 redis> SET key value OKredis> TTL key (integer) -1# 有剩余生存時間的 key redis> EXPIRE key 10086 (integer) 1redis> TTL key (integer) 10084

7. PERSIST

通用命令PERSIST key
功能描述移除給定 key 的生存時間,將這個 key 從“易失的”(帶生存時間 key )轉換成“持久的”(一個不帶生存時間、永不過期的 key )。當生存時間移除成功時,返回 1 . 如果 key 不存在或 key 沒有設置生存時間,返回 0 。
時間復雜度O(1)
redis> SET mykey "Hello" OKredis> EXPIRE mykey 10 # 為 key 設置生存時間 (integer) 1redis> TTL mykey (integer) 10redis> PERSIST mykey # 移除 key 的生存時間 (integer) 1redis> TTL mykey (integer) -1

8. TYPE

通用命令TYPE key
功能描述返回 key 所儲存的值的類型。
時間復雜度O(1)

值的類型有:

  • none (key不存在)
  • string (字符串)
  • list (列表)
  • set (集合)
  • zset (有序集)
  • hash (哈希表)
  • stream (流)
# 字符串 redis> SET weather "sunny" OKredis> TYPE weather string# 列表 redis> LPUSH book_list "programming in scala" (integer) 1redis> TYPE book_list list# 集合 redis> SADD pat "dog" (integer) 1redis> TYPE pat set

8. del

通用命令DEL key [key …]
功能描述刪除給定的一個或多個 key 。不存在的 key 會被忽略。返回值是被刪除 key 的數量。
時間復雜度O(N), N 為被刪除的 key 的數量,其中刪除單個字符串類型的 key ,時間復雜度為O(1);刪除單個列表、集合、有序集合或哈希表類型的 key ,時間復雜度為O(M), M 為以上數據結構內的元素數量。
# 刪除單個 key redis> SET name huangz OKredis> DEL name (integer) 1# 刪除一個不存在的 key redis> EXISTS phone (integer) 0redis> DEL phone # 失敗,沒有 key 被刪除 (integer) 0# 同時刪除多個 key redis> SET name "redis" OKredis> SET type "key-value store" OKredis> SET website "redis.com" OKredis> DEL name type website (integer) 3

9. scan

通用命令SCAN cursor [MATCH pattern] [LIMT count]
功能描述查找(limit個)(符合給定模式 pattern )的 key ,返回值是符合 條件的key
時間復雜度增量式迭代命令每次執行的復雜度為 O(1) , 對數據集進行一次完整迭代的復雜度為 O(N) , 其中 N 為數據集中的元素數量。

剛開始我們已經介紹過了keys,它的缺點非常明顯:

  • ?次性查出所有滿?條件的 key,萬?Redis中有?百 w 個 key 滿?條件,滿屏都是輸出的結果,眼花繚亂。
  • keys由于走的是遍歷算法,復雜度是 O(n),如果Redis中有千萬級以上的 key,這個指令就會導致 Redis 服務卡頓,所有讀寫Redis 的其它的指令都會被延后甚?會超時報錯,因為 Redis是單線程程序,順序執?所有指令,其它指令必須等到當前的keys 指令執?完了才可以繼續。
  • 為了解決這個問題, 2.8 版本中的Redis加?了scan。

    scan 相? keys 具備有以下特點:

  • 復雜度雖然也是 O(n),但是它是通過游標(cursor,相當于位置)分步進?的,不會阻塞線程;
  • 提供 limit 參數(可選),可以控制每次返回結果的最?條數(實際上是遍歷的key的數量);
  • 它也提供模式匹配功能;
  • 服務器不需要為游標(cursor)保存狀態,游標(cursor)的唯?狀態就是 scan 返回給客戶端的游標整數;
  • 返回的結果可能會有重復,需要客戶端去重復(重要);
  • 遍歷的過程中如果有數據修改,改動后的數據能不能遍歷到是不確定的;
  • 單次返回的結果是空的并不意味著遍歷結束,?要看返回的游標值是否為零;
  • scan 參數提供了三個參數,第?個是 cursor 整數值,第?個是key 的正則模式,第三個是遍歷的limit。第?次遍歷時,cursor 值為 0,然后將返回結果中第?個整數值作為下?次遍歷的cursor。?直遍歷到返回的 cursor 值為 0 時結束。

    127.0.0.1:6379> scan 0 match key99* count 1000 1) "13976" 2) 1) "key9911"2) "key9974"3) "key9994"4) "key9910"5) "key9907"6) "key9989"7) "key9971"8) "key99" 127.0.0.1:6379> scan 13976 match key99* count 1000 1) "1996" 2) 1) "key9982"2) "key9997" 3) "key9963"4) "key996"5) "key9912"6) "key9999"7) "key9921"8) "key994"9) "key9956"10) "key9919" 127.0.0.1:6379> scan 1996 match key99* count 1000 1) "12594" 2) 1) "key9939"2) "key9941"3) "key9967"4) "key9938"5) "key9906"6) "key999"7) "key9909"... 127.0.0.1:6379> scan 11687 match key99* count 1000 1) "0" 2) 1) "key9969"2) "key998"3) "key9986"4) "key9968"5) "key9965"6) "key9990"7) "key9915"8) "key9928"9) "key9908"

    剛才也強調了,limit是遍歷的key的個數,從上?的過程可以看到雖然提供的 limit 是 1000,但是返回的結果,有的只有 10 個。

    scan 指令返回的游標就是第?維數組的位置索引,我們將這個位置索引稱為槽 (slot)。如果不考慮字典的擴容縮容,直接按數組下標挨個遍歷就?了。limit 參數就表示需要遍歷的槽位數,之所以返回的結果可能多可能少,是因為不是所有的槽位上都會掛接鏈表,有些槽位可能是空的,還有些槽位上掛接的鏈表上的元素可能會有多個。每?次遍歷都會將 limit 數量的槽位上掛接的所有鏈表元素進?模式匹配過濾后,?次性返回給客戶端。

    scan除了可以遍歷所有的 key 之外,還可以對指定的容器集合進?遍歷。
    ?如 zscan 遍歷 zset 集合元素,hscan遍歷 hash 字典的元素、sscan 遍歷 set 集合的元素。

    常用的通用命令已經介紹完了,下面我們探討一個Redis總要面臨的問題:

    在 Redis 中有可能會形成很?的對象,?如?個?個很?的 zset。

    這樣的對象對 Redis 的集群數據遷移帶來了挑戰,在集群環境下,如果某個 key 太?,可能會導致數據遷移卡頓。另外在內存分配上,如果 key 太?,那么當它需要擴容時,會?次性申請更?的?塊內存,這也可能會導致卡頓。如果這個? key 被刪除,內存會?次性回收,卡頓現象也有可能再產?。

    在平時的開發中,盡量避免? key 的產?。如果遇到 Redis 的內存?起?落的現象,有可能是因為? key 導致的,這時候你就需要定位這個大 key,進?步定位出具體的業務來源,然后再改進相關業務代碼設計。

    關于大key的尋找,可以通過 scan 指令,對于掃描出來的每?個 key,使? type 指令獲得 key 的類型,然后使?相應數據結構的 size 或者 len ?法來得到它的??,對于每?種類型,保留??的前 N 名作為掃描結果展示出來。(需要編寫腳本)

    除此之外, Redis 官?在redis-cli 指令中提供了這樣的掃描功能

    redis-cli -h 127.0.0.1 -p 7001 –-bigkeys

    如果你擔?這個指令會?幅抬升 Redis 的 ops ,還可以增加?個休眠參數。

    redis-cli -h 127.0.0.1 -p 7001 –-bigkeys -i 0.1 # 每隔 100 條 scan 指令就會休眠 0.1s,ops 就不會劇烈抬升,但是掃描的時間會變?。

    redis中的OPS 即operation per second 每秒操作次數。意味著每秒對Redis的持久化操作

    (二)單線程架構

    Redis內部使用單線程架構。Redis一個瞬間只能執行一條命令,不能執行兩條命令

    Redis單線程速度這么快的原因可大致歸結三個:
    1.純內存

    Redis把所有的數據都保存在內存中,而內存的響應速度是非常快的

    2.非阻塞IO

    Redis使用epoll異步非阻塞模型 ,Redis自身實現了事件處理

    3.避免線程切換和競態消耗

    在使用多線程編程中,線程之間的切換也會消耗一部分CPU資源,如果不合理的實現多線程編程,可能比單線程還要慢

    主要原因是 純內存。

    不過第二條和第三條倒是面試中經常會問到,尤其是第二條。為了便于大家,理解更深刻,我們這里探討一下操作系統的IO

    用戶程序進行IO的讀寫,依賴于底層的IO讀寫,基本上會用到底層的read&write兩大系統調用。

    read系統調用,并不是直接從物理設備把數據讀取到內存中;write系統調用,也不是直接把數據寫入到物理設備。上層應用無論是調用操作系統的read,還是調用操作系統的write,都會涉及緩沖區。具體來說,調用操作系統的read,是把數據從內核緩沖區復制到進程緩沖區;而write系統調用,是把數據從進程緩沖區復制到內核緩沖區。

    緩沖區的目的,是為了減少頻繁地與設備之間的物理交換。外部設備的直接讀寫,涉及操作系統的中斷。發生系統中斷時,需要保存之前的進程數據和狀態等信息,而結束中斷之后,還需要恢復之前的進程數據和狀態等信息。為了減少這種底層系統的時間損耗、性能損耗,于是出現了內存緩沖區。

    有了內存緩沖區,上層應用使用read系統調用時,僅僅把數據從內核緩沖區復制到上層應用的緩沖區(進程緩沖區);上層應用使用write系統調用時,僅僅把數據從進程緩沖區復制到內核緩沖區中。底層操作會對內核緩沖區進行監控,等待緩沖區達到一定數量的時候,再進行IO設備的中斷處理,集中執行物理設備的實際IO操作,這種機制提升了系統的性能。至于什么時候中斷(讀中斷、寫中斷),由操作系統的內核來決定,用戶程序則不需要關心。

    從數量上來說,在Linux系統中,操作系統內核只有一個內核緩沖區。而每個用戶程序(進程),有自己獨立的緩沖區,叫作進程緩沖區。所以,用戶程序的IO讀寫程序,在大多數情況下,并沒有進行實際的IO操作,而是在進程緩沖區和內核緩沖區之間直接進行數據的交換。

    有了對操作系統IO的基本認識之后,還要提一下操作系統四種主要的IO模型

  • 同步阻塞IO(Blocking IO)
  • 同步非阻塞IO(Non-blocking IO)
  • IO多路復用(IO Multiplexing)
  • 異步IO(Asynchronous IO)
  • Redis的IO模型是IO多路復用,有意思的是Java 的NIO模型,也是IO多路復用(不是同步非阻塞IO)

    有興趣的可以查閱相關的資料,或者不著急的 可以等我接下來的博文(過段日子會有網絡編程詳解的博文,對IO作深入探究)

    這里還要強調一下,由于Redis單線程一次只運行一條命令,我們要拒絕長(慢)命令

    keys flushallflushdbslow lua scriptmutil/execoperate

    (三)數據結構和內部編碼

    Redis每種數據結構及對應的內部編碼如下圖所示

    你會發現 數據結構 內部編碼方式有不同的方式,其實這是時間換空間 空間換時間的做法,選擇何種內部編碼要結合實際情況。

    (四)字符串

    字符串 string 是 Redis 最簡單的數據結構。Redis 所有的數據結構都是以唯?的 key 字符串作為名稱,然后通過這個唯? key 值來獲取相應的 value 數據。不同類型的數據結構的差異就在于 value 的結構不?樣。

    字符串的value值類型有三種:1. 字符串;2. 整型;3.二進制。

    Redis 的字符串是動態字符串,是可以修改的字符串,內部結構實現上類似于 Java 的 ArrayList,采?預分配冗余空間的?式來減少內存的頻繁分配,當字符串?度?于 1M時,擴容都是加倍現有的空間,如果超過 1M,擴容時?次只會多擴1M 的空間。需要注意的是字符串最??度為 512M。

    我們看一下它的常用API

    1. GET

    命令GET key
    功能描述返回與鍵 key 相關聯的字符串值。 如果鍵 key 不存在, 那么返回特殊值 nil ; 否則, 返回鍵 key 的值。如果鍵 key 的值并非字符串類型, 那么返回一個錯誤, 因為 GET 命令只能用于字符串值。
    時間復雜度O(1)

    示例
    對不存在的鍵 key 或是字符串類型的鍵 key 執行 GET 命令:

    redis> GET db (nil)redis> SET db redis OKredis> GET db "redis"

    對不是字符串類型的鍵 key 執行 GET 命令:

    redis> DEL db (integer) 1redis> LPUSH db redis mongodb mysql (integer) 3redis> GET db (error) ERR Operation against a key holding the wrong kind of value

    2. set

    命令SET key value [EX seconds] [PX milliseconds] [NX|XX]
    功能描述將字符串值 value 關聯到 key 。如果 key 已經持有其他值, SET 就覆寫舊值, 無視類型。當 SET 命令對一個帶有生存時間(TTL)的鍵進行設置之后, 該鍵原有的 TTL 將被清除。
    時間復雜度O(1)

    可選參數
    從 Redis 2.6.12 版本開始, SET 命令的行為可以通過一系列參數來修改:

    • EX seconds : 將鍵的過期時間設置為 seconds 秒。 執行 SET key value EX seconds 的效果等同于執行 SETEX key seconds value 。
    • PX milliseconds : 將鍵的過期時間設置為 milliseconds 毫秒。 執行 SET key value PX milliseconds 的效果等同于執行 PSETEX key milliseconds value 。
    • NX : 只在鍵不存在時, 才對鍵進行設置操作。 執行 SET key value NX 的效果等同于執行 SETNX key value 。
    • XX : 只在鍵已經存在時, 才對鍵進行設置操作。

    關于返回值

    • 在 Redis 2.6.12 版本以前, SET 命令總是返回 OK 。
    • 從 Redis 2.6.12 版本開始, SET 命令只在設置操作成功完成時才返回 OK ; 如果命令使用了 NX 或者 XX 選項, 但是因為條件沒達到而造成設置操作未執行, 那么命令將返回空批量回復(NULL Bulk Reply)。

    3. INCR

    命令INCR key
    功能描述為鍵 key 儲存的數字值加上一。如果鍵 key 不存在, 那么它的值會先被初始化為 0 , 然后再執行 INCR 命令。如果鍵 key 儲存的值不能被解釋為數字, 那么 INCR 命令將返回一個錯誤。本操作的值限制在 64 位(bit)有符號數字表示之內。返回值是DECR 命令會返回鍵 key 在執行減一操作之后的值。
    時間復雜度O(1)

    對儲存數字值的鍵 key 執行 DECR 命令:

    redis> SET page_view 20 OKredis> INCR page_view (integer) 21redis> GET page_view # 數字值在 Redis 中以字符串的形式保存 "21"

    對不存在的鍵執行 DECR 命令:

    redis> EXISTS count (integer) 0redis> DECR count (integer) -1

    4. DECR

    命令DECRBY key decrement
    功能描述將鍵 key 儲存的整數值減去減量 decrement 。如果鍵 key 不存在, 那么鍵 key 的值會先被初始化為 0 , 然后再執行 DECRBY 命令。如果鍵 key 儲存的值不能被解釋為數字, 那么 DECRBY 命令將返回一個錯誤。本操作的值限制在 64 位(bit)有符號數字表示之內。返回值是DECRBY 命令會返回鍵在執行減法操作之后的值。
    時間復雜度O(1)

    對已經存在的鍵執行 DECRBY 命令:

    redis> SET count 100 OKredis> DECRBY count 20 (integer) 80

    對不存在的鍵執行 DECRBY 命令:

    redis> EXISTS pages (integer) 0redis> DECRBY pages 10 (integer) -10

    5. INCRBY

    命令INCRBY key increment
    功能描述為鍵 key 儲存的數字值加上增量 increment 。如果鍵 key 不存在, 那么鍵 key 的值會先被初始化為 0 , 然后再執行 INCRBY 命令。如果鍵 key 儲存的值不能被解釋為數字, 那么 INCRBY 命令將返回一個錯誤。本操作的值限制在 64 位(bit)有符號數字表示之內。返回值為在加上增量 increment 之后, 鍵 key 當前的值。
    時間復雜度O(1)

    示例演示與上面類似

    6. DECRBY

    命令DECRBY key decrement
    功能描述將鍵 key 儲存的整數值減去減量 decrement 。如果鍵 key 不存在, 那么鍵 key 的值會先被初始化為 0 , 然后再執行 DECRBY 命令。如果鍵 key 儲存的值不能被解釋為數字, 那么 DECRBY 命令將返回一個錯誤。本操作的值限制在 64 位(bit)有符號數字表示之內。 返回值DECRBY 命令會返回鍵在執行減法操作之后的值。
    時間復雜度O(1)

    示例演示與上面類似

    使用上面這一些命令,其實我們就可以做一些事情了。

    應用

    1.比如說記錄每個用戶博文的訪問量

    incr userid:pageview(單線程:無競爭)


    2.緩存用戶的基本信息(數據源在 MySQL中),信息被序列化存放在value中。

    一般而言,需要通過我們自定義規則的key,從Redis獲取value,如果key存在的話,則直接獲取value使用;如果不存在的話,從Mysql中讀取使用,然后存在Redis中。
    主要的命令是 get 和 set

    3.分布式id生成器
    如果集群規模和運算不太復雜的話,可以用Redis生成分布式id,因為Redis單線程的特點,一次只執行一條指令,保證了id值的唯一。
    主要的命令還是incr

    7. SETNX

    命令SETNX key value
    功能描述只在鍵 key 不存在的情況下, 將鍵 key 的值設置為 value 。若鍵 key 已經存在, 則 SETNX 命令不做任何動作。命令在設置成功時返回 1 , 設置失敗時返回 0 。
    時間復雜度O(1)
    redis> EXISTS job # job 不存在 (integer) 0redis> SETNX job "programmer" # job 設置成功 (integer) 1redis> SETNX job "code-farmer" # 嘗試覆蓋 job ,失敗 (integer) 0redis> GET job # 沒有被覆蓋 "programmer"

    8. SETEX

    命令SETNX key value
    功能描述將鍵 key 的值設置為 value , 并將鍵 key 的生存時間設置為 seconds 秒鐘。如果鍵 key 已經存在, 那么 SETEX 命令將覆蓋已有的值。命令在設置成功時返回 OK 。 當 seconds 參數不合法時, 命令將返回一個錯誤。
    時間復雜度O(1)

    SETEX 命令的效果和以下兩個命令的效果類似:

    SET key value EXPIRE key seconds # 設置生存時間

    SETEX 和這兩個命令的不同之處在于 SETEX 是一個原子(atomic)操作, 它可以在同一時間內完成設置值和設置過期時間這兩個操作, 因此 SETEX 命令在儲存緩存的時候非常實用。

    這兩個命令的典型應用就是分布式鎖了。

    ?如?個操作要修改?戶的狀態,修改狀態需要先讀出?戶的狀態,在內存?進?修改,改完了再存回去。如果這樣的操作同時進?了,就會出現并發問題。這個時候就要使?到分布式鎖來限制程序的并發執?。Redis 分布式鎖使??常?泛,必須要掌握。

    分布式鎖本質上要實現的?標就是在 Redis ??占?個“位置”,當別的進程也要來占時,發現“位置”被占了,就只好放棄或者稍后
    再試。

    占位置?般是使? setnx(set if not exists) 指令,只允許被?個客戶端占據。先來先占,?完了,再調? del 指令釋放位置。

    如果邏輯執?到中間出現異常了,可能會導致 del指令沒有被調?,這樣就會陷?死鎖,鎖永遠得不到釋放。于是我們在拿到鎖之后,再給鎖加上?個過期時間。

    但如果在 setnx 和 expire 之間服務器進程突然掛掉了,可能是因為機器掉電或者是被?為殺掉的,就會導致expire 得不到執?,也會造成死鎖。

    原因是 setnx 和 expire 是兩條指令?不能保證都一定成功執行。如果這兩條指令可以?起執?就不會出現問題(要么成功,要么失敗)。所以說setex是最佳的方案

    上面就是分布式鎖的基本思想。但是在真正投入使用的時候,還會面臨一個常見的問題:超時問題

    Redis 的分布式鎖不能解決超時問題,如果在加鎖和釋放鎖之間的邏輯執?的時間太?,超出了鎖的超時限制,就會出現問題。這時候第?個線程持有的鎖過期了,臨界區的邏輯沒有執?完,而第?個線程就提前重新持有了這把鎖,導致臨界區代碼不能嚴格地串?執?。

    為了避免這個問題,Redis 分布式鎖不要?于較?時間的任務。

    我們會在下篇文章,也就是分布式章節繼續探討分布式鎖。

    9. MSET

    命令MSET key value [key value …]
    功能描述同時為多個鍵設置值。如果某個給定鍵已經存在, 那么 MSET 將使用新值去覆蓋舊值, 如果這不是你所希望的效果, 請考慮使用 MSETNX 命令, 這個命令只會在所有給定鍵都不存在的情況下進行設置。MSET 是一個原子性(atomic)操作, 所有給定鍵都會在同一時間內被設置, 不會出現某些鍵被設置了但是另一些鍵沒有被設置的情況。MSET 命令總是返回 OK 。
    時間復雜度O(N),其中 N 為被設置的鍵數量。

    同時對多個鍵進行設置:

    redis> MSET date "2012.3.30" time "11:00 a.m." weather "sunny" OKredis> MGET date time weather 1) "2012.3.30" 2) "11:00 a.m." 3) "sunny"

    覆蓋已有的值:

    redis> MGET k1 k2 1) "hello" 2) "world"redis> MSET k1 "good" k2 "bye" OKredis> MGET k1 k2 1) "good" 2) "bye"

    10 . MGET

    命令MGET key [key …]
    功能描述返回給定的一個或多個字符串鍵的值。如果給定的字符串鍵里面, 有某個鍵不存在, 那么這個鍵的值將以特殊值 nil 表示。MGET 命令將返回一個列表, 列表中包含了所有給定鍵的值。
    時間復雜度O(N),其中 N 為被設置的鍵數量。
    redis> SET redis redis.com OKredis> SET mongodb mongodb.org OKredis> MGET redis mongodb 1) "redis.com" 2) "mongodb.org"redis> MGET redis mongodb mysql # 不存在的 mysql 返回 nil 1) "redis.com" 2) "mongodb.org" 3) (nil)

    下面說說mset和mget的好處
    不使用mget和mset::

    客戶端和服務器端可能不在同一個地方
    n次get/set=n次網絡時間+n次命令時間

    一次mget/mset:

    1次mget/mset=1次網絡時間+n次命令時間

    隨著n的增大,差距一下子就體現出來了。

    下面的命令不太常用,大體過一下:

    10. GETSET
    GETSET key value
    將鍵 key 的值設為 value , 并返回鍵 key 在被設置之前的舊值。

    11. STRLEN
    STRLEN key
    返回鍵 key 儲存的字符串值的長度

    12. APPEND
    APPEND key value
    如果鍵 key 已經存在并且它的值是一個字符串, APPEND 命令將把 value 追加到鍵 key 現有值的末尾。

    13. INCRBYFLOAT
    INCRBYFLOAT key increment
    為鍵 key 儲存的值加上浮點數增量 increment 。
    如果鍵 key 不存在, 那么 INCRBYFLOAT 會先將鍵 key 的值設為 0 , 然后再執行加法操作。
    如果命令執行成功, 那么鍵 key 的值會被更新為執行加法計算之后的新值, 并且新值會以字符串的形式返回給調用者。
    無論是鍵 key 的值還是增量 increment , 都可以使用像 2.0e7 、 3e5 、 90e-2 那樣的指數符號(exponential notation)來表示, 但是, 執行 INCRBYFLOAT 命令之后的值總是以同樣的形式儲存, 也即是, 它們總是由一個數字, 一個(可選的)小數點和一個任意長度的小數部分組成(比如 3.14 、 69.768 ,諸如此類), 小數部分尾隨的 0 會被移除, 如果可能的話, 命令還會將浮點數轉換為整數(比如 3.0 會被保存成 3 )。
    此外, 無論加法計算所得的浮點數的實際精度有多長, INCRBYFLOAT 命令的計算結果最多只保留小數點的后十七位。
    當以下任意一個條件發生時, 命令返回一個錯誤:

    • 鍵 key 的值不是字符串類型(因為 Redis 中的數字和浮點數都以字符串的形式保存,所以它們都屬于字符串類型);
    • 鍵 key 當前的值或者給定的增量 increment 不能被解釋(parse)為雙精度浮點數。

    14. GETRANGE
    GETRANGE key start end
    返回鍵 key 儲存的字符串值的指定部分, 字符串的截取范圍由 start 和 end 兩個偏移量決定 (包括 start 和 end 在內)。
    負數偏移量表示從字符串的末尾開始計數, -1 表示最后一個字符, -2 表示倒數第二個字符, 以此類推。
    GETRANGE 通過保證子字符串的值域(range)不超過實際字符串的值域來處理超出范圍的值域請求。

    15. SETRANGE
    SETRANGE key offset value
    從偏移量 offset 開始, 用 value 參數覆寫(overwrite)鍵 key 儲存的字符串值。
    不存在的鍵 key 當作空白字符串處理。
    SETRANGE 命令會確保字符串足夠長以便將 value 設置到指定的偏移量上, 如果鍵 key 原來儲存的字符串長度比偏移量小(比如字符串只有 5 個字符長,但你設置的 offset 是 10 ), 那么原字符和偏移量之間的空白將用零字節(zerobytes, “\x00” )進行填充。
    因為 Redis 字符串的大小被限制在 512 兆(megabytes)以內, 所以用戶能夠使用的最大偏移量為 2^29-1(536870911) , 如果你需要使用比這更大的空間, 請使用多個 key 。


    在對字符串類型有了整體的了解之后,我們看看它具體的結構

    Redis 的字符串名字是SDS(Simple Dynamic String)。它的結構是?個帶?度信息的字節數組。

    struct SDS<T> {T capacity; // 數組容量T len; // 數組?度byte flags; // 特殊標識位,不理睬它byte[] content; // 數組內容 }

    capacity 表示所分配數組的?度,len 表示字符串的實際?度。前?API提到?持 append操作(字符串是可修改的)。如果數組沒有冗余空間,那么追加操作必然涉及到分配新數組,然后將舊內容復制過來,再 append 新內容。如果字符串的?
    度?常?,這樣的內存分配和復制開銷就會?常?。

    /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the * end of the specified sds string 's' . * * After the call, the passed sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. */ sds sdscatlen(sds s, const void *t, size_t len) {size_t curlen = sdslen(s); // 原字符串?度// 按需調整空間,如果 capacity 不夠容納追加的內容,就會重新分配字節數組并復制原字符串的內容到新數組中s = sdsMakeRoomFor(s,len);if (s == NULL) return NULL; // 內存不?memcpy(s+curlen, t, len); // 追加?標字符串的內容到字節數組中sdssetlen(s, curlen+len); // 設置追加后的?度值s[curlen+len] ='\0'; // 讓字符串以\0 結尾,便于調試打印,還可以直接使? glibc 的字符串函數進?操作return s; }

    上?的 SDS 結構使?了范型 T,這是Redis 對內存做出的優化,不同?度的字符串使?不同的結構體來表示,字符串?較短時,len 和 capacity 可以使? byte 和 short來表示。

    Redis 規定字符串的?度不得超過 512M 字節。創建字符串時 len和 capacity ?樣?,不會多分配冗余空間,這是因為絕?多數場景下我們不會使? append 操作來修改字符串。

    (五)hash (字典)

    Redis 的字典結構為數組 +鏈表?維結構。第?維 hash 的數組位置碰撞時,就會將碰撞的元素使?鏈表串接起來。Redis 的字典的值只能是字符串。當字典很大的時候,會進行rehash,Redis 為了?性能,不能堵塞服務,采?了漸進式 rehash 策略。

    漸進式 rehash 保留新舊兩個 hash 結構,查詢時會同時查詢兩個 hash 結構,然后在后續的定時任務中以及hash 操作指令中,循序漸進地將舊 hash 的內容?點點遷移到新的hash 結構中。當搬遷完成了,就會使?新的hash結構取?代之。當 hash 移除了最后?個元素之后,該數據結構?動被刪除,內存被回收。


    下面我們看一下它的API, 所有hash的命令都是h開頭
    1. HSET hash field value

    時間復雜度: O(1)

    將哈希表 hash 中域 field 的值設置為 value 。如果給定的哈希表并不存在, 那么一個新的哈希表將被創建并執行 HSET 操作。如果域 field 已經存在于哈希表中, 那么它的舊值將被新值 value 覆蓋。當 HSET 命令在哈希表中新創建 field 域并成功為它設置值時, 命令返回 1 ; 如果域 field 已經存在于哈希表, 并且 HSET 命令成功使用新值覆蓋了它的舊值, 那么命令返回 0 。

    設置一個新域:

    redis> HSET website google "www.g.cn" (integer) 1redis> HGET website google "www.g.cn"

    對一個已存在的域進行更新:

    redis> HSET website google "www.google.com" (integer) 0redis> HGET website google "www.google.com"

    2.HGET hash field
    時間復雜度: O(1)

    返回哈希表中給定域的值。HGET 命令在默認情況下返回給定域的值。如果給定域不存在于哈希表中, 又或者給定的哈希表并不存在, 那么命令返回 nil 。

    域存在的情況:

    redis> HSET homepage redis redis.com (integer) 1redis> HGET homepage redis "redis.com"

    域不存在的情況:

    redis> HGET site mysql (nil)

    3.HDEL
    HDEL key field [field …]
    O(N), N 為要刪除的域的數量。
    刪除哈希表 key 中的一個或多個指定域,不存在的域將被忽略。返回值為被成功移除的域的數量,不包括被忽略的域。

    # 測試數據 redis> HGETALL abbr 1) "a" 2) "apple" 3) "b" 4) "banana" 5) "c" 6) "cat" 7) "d" 8) "dog"# 刪除單個域 redis> HDEL abbr a (integer) 1# 刪除不存在的域 redis> HDEL abbr not-exists-field (integer) 0# 刪除多個域 redis> HDEL abbr b c (integer) 2redis> HGETALL abbr 1) "d" 2) "dog"

    4. HSETNX hash field value
    時間復雜度: O(1)
    當且僅當域 field 尚未存在于哈希表的情況下, 將它的值設置為 value 。如果給定域已經存在于哈希表當中, 那么命令將放棄執行設置操作。如果哈希表 hash 不存在, 那么一個新的哈希表將被創建并執行 HSETNX 命令。HSETNX 命令在設置成功時返回 1 , 在給定域已經存在而放棄執行設置操作時返回 0 。

    域尚未存在, 設置成功:

    redis> HSETNX database key-value-store Redis (integer) 1redis> HGET database key-value-store "Redis"

    域已經存在, 設置未成功, 域原有的值未被改變:

    redis> HSETNX database key-value-store Riak (integer) 0redis> HGET database key-value-store "Redis"

    5. HLEN
    時間復雜度:O(1)

    返回哈希表 key 中域的數量。當 key 不存在時,返回 0 。

    redis> HSET db redis redis.com (integer) 1redis> HSET db mysql mysql.com (integer) 1redis> HLEN db (integer) 2redis> HSET db mongodb mongodb.org (integer) 1redis> HLEN db (integer) 3

    6.HMSET
    HMSET key field value [field value …]
    時間復雜度:O(N), N 為 field-value 對的數量。

    同時將多個 field-value (域-值)對設置到哈希表 key 中。此命令會覆蓋哈希表中已存在的域。如果 key 不存在,一個空哈希表被創建并執行 HMSET 操作。如果命令執行成功,返回 OK 。當 key 不是哈希表(hash)類型時,返回一個錯誤。

    redis> HMSET website google www.google.com yahoo www.yahoo.com OKredis> HGET website google "www.google.com"redis> HGET website yahoo "www.yahoo.com"

    7. HMGET
    HMGET key field [field …]
    時間復雜度:O(N), N 為給定域的數量。

    返回哈希表 key 中,一個或多個給定域的值。
    如果給定的域不存在于哈希表,那么返回一個 nil 值。因為不存在的 key 被當作一個空哈希表來處理,所以對一個不存在的 key 進行 HMGET 操作將返回一個只帶有 nil 值的表。具體返回一個包含多個給定域的關聯值的表,表值的排列順序和給定域參數的請求順序一樣。

    redis> HMSET pet dog "doudou" cat "nounou" # 一次設置多個域 OKredis> HMGET pet dog cat fake_pet # 返回值的順序和傳入參數的順序一樣 1) "doudou" 2) "nounou" 3) (nil) # 不存在的域返回nil值

    8.HINCRBY
    HINCRBY key field increment
    時間復雜度:O(1)
    為哈希表 key 中的域 field 的值加上增量 increment 。增量也可以為負數,相當于對給定域進行減法操作。如果 key 不存在,一個新的哈希表被創建并執行 HINCRBY 命令。如果域 field 不存在,那么在執行命令前,域的值被初始化為 0 。對一個儲存字符串值的域 field 執行 HINCRBY 命令將造成一個錯誤。本操作的值被限制在 64 位(bit)有符號數字表示之內。執行 HINCRBY 命令之后,返回值哈希表 key 中域 field 的值。

    # increment 為正數 redis> HEXISTS counter page_view # 對空域進行設置 (integer) 0redis> HINCRBY counter page_view 200 (integer) 200redis> HGET counter page_view "200"# increment 為負數 redis> HGET counter page_view "200"redis> HINCRBY counter page_view -50 (integer) 150redis> HGET counter page_view "150"# 嘗試對字符串值的域執行HINCRBY命令 redis> HSET myhash string hello,world # 設定一個字符串值 (integer) 1redis> HGET myhash string "hello,world"redis> HINCRBY myhash string 1 # 命令執行失敗,錯誤。 (error) ERR hash value is not an integerredis> HGET myhash string # 原值不變 "hello,world"

    9. HKEYS
    時間復雜度:O(N), N 為哈希表的大小。

    返回哈希表 key 中的所有域。即一個包含哈希表中所有域的表。當 key 不存在時,返回一個空表。

    # 哈希表非空 redis> HMSET website google www.google.com yahoo www.yahoo.com OKredis> HKEYS website 1) "google" 2) "yahoo"# 空哈希表/key不存在 redis> EXISTS fake_key (integer) 0redis> HKEYS fake_key (empty list or set)

    10.HVALS
    HVALS key
    時間復雜度:O(N), N 為哈希表的大小。
    返回哈希表 key 中所有域的值。即一個包含哈希表中所有值的表。當 key 不存在時,返回一個空表。

    # 非空哈希表 redis> HMSET website google www.google.com yahoo www.yahoo.com OKredis> HVALS website 1) "www.google.com" 2) "www.yahoo.com"# 空哈希表/不存在的key redis> EXISTS not_exists (integer) 0redis> HVALS not_exists (empty list or set)

    11.HGETALL
    時間復雜度:O(N), N 為哈希表的大小。
    HGETALL key
    返回哈希表 key 中,所有的域和值。在返回值里,緊跟每個域名(field name)之后是域的值(value),所以返回值的長度是哈希表大小的兩倍。返回值以列表形式返回哈希表的域和域的值。若 key 不存在,返回空列表。

    redis> HSET people jack "Jack Sparrow" (integer) 1redis> HSET people gump "Forrest Gump" (integer) 1redis> HGETALL people 1) "jack" # 域 2) "Jack Sparrow" # 值 3) "gump" 4) "Forrest Gump"

    小心單線程 數據量大的話 會比較慢

    12. hsetnx
    時間復雜度: O(1)

    當且僅當域 field 尚未存在于哈希表的情況下, 將它的值設置為 value 。如果給定域已經存在于哈希表當中, 那么命令將放棄執行設置操作。如果哈希表 hash 不存在, 那么一個新的哈希表將被創建并執行 HSETNX 命令。HSETNX 命令在設置成功時返回 1 , 在給定域已經存在而放棄執行設置操作時返回 0 。

    域尚未存在, 設置成功:

    redis> HSETNX database key-value-store Redis (integer) 1redis> HGET database key-value-store "Redis"

    域已經存在, 設置未成功, 域原有的值未被改變:

    redis> HSETNX database key-value-store Riak (integer) 0redis> HGET database key-value-store "Redis"

    13. hincrbyfloat
    HINCRBYFLOAT key field increment
    時間復雜度:O(1)
    為哈希表 key 中的域 field 加上浮點數增量 increment 。如果哈希表中沒有域 field ,那么 HINCRBYFLOAT 會先將域 field 的值設為 0 ,然后再執行加法操作。如果鍵 key 不存在,那么 HINCRBYFLOAT 會先創建一個哈希表,再創建域 field ,最后再執行加法操作。當以下任意一個條件發生時,返回一個錯誤:

    • 域 field 的值不是字符串類型(因為 redis 中的數字和浮點數都以字符串的形式保存,所以它們都屬于字符串類型)
    • 域 field 當前的值或給定的增量 increment 不能解釋(parse)為雙精度浮點數(double precision floating point number)
      返回值為返回值:執行加法操作之后 field 域的值。
    # 值和增量都是普通小數 redis> HSET mykey field 10.50 (integer) 1 redis> HINCRBYFLOAT mykey field 0.1 "10.6"# 值和增量都是指數符號 redis> HSET mykey field 5.0e3 (integer) 0 redis> HINCRBYFLOAT mykey field 2.0e2 "5200"# 對不存在的鍵執行 HINCRBYFLOAT redis> EXISTS price (integer) 0 redis> HINCRBYFLOAT price milk 3.5 "3.5" redis> HGETALL price 1) "milk" 2) "3.5"# 對不存在的域進行 HINCRBYFLOAT redis> HGETALL price 1) "milk" 2) "3.5" redis> HINCRBYFLOAT price coffee 4.5 # 新增 coffee 域 "4.5" redis> HGETALL price 1) "milk" 2) "3.5" 3) "coffee" 4) "4.5"

    知道上面的命令后,就可以做一些事情了。

    應用
    類似于字符串,我們可以記錄網站每個用戶個人主頁的訪問量

    hincrby user chenxiao pageviewCount

    當然還有緩存用戶信息。

    對于記錄個人主頁的訪問量,自然字符串要比hash更好點。

    但是對于緩存用戶新信息這種邏輯要好好斟酌一下

    字符串Key:Value的結構:(第一種方案 String-v1)

    key: 'user:userId' value: {"name": "chenxiao","age":100,"pageview": 8000000 }

    value是序列化的結果

    字符串Key:Value的結構:(第二種方案 String-v2)

    key: user:userId:name value: chenxiaokey: user:userId:age value: 100key: user:userId:pageView value: 800000

    相比上面的方案更新屬性更方便 只需要一條

    再看看hash形式的方案(hash)

    key:user:userIdfield:name value:chenxiaofield:age value:100field:pageView vale:80000

    3種方案比較:

    方案優點缺點
    String v1編程簡單,可能節約內存1.序列化開銷 2.設置屬性要操作整個數據。
    String v2直觀,可以部分更新1.內存占用較大 2.key較為分散
    hash直觀 節省空間 可以部分更新1.編程稍微復雜 2.ttl不好控制

    (六)列表

    Redis 的列表相當于 Java 語???的 LinkedList,數據結構形式為鏈表,插?和刪除操作?常快,時間復雜度為O(1),但是索引定位很慢,時間復雜度為 O(n)。

    當列表彈出了最后?個元素之后,該數據結構?動被刪除,內存被回收。

    插入元素后,各元素的相對位置確定,遍歷的結果也與之保持一致。鏈表元素可以重復。下面我們看看它的API

    1.LPUSH
    LPUSH key value [value …]
    時間復雜度: O(1)

    將一個或多個值 value 插入到列表 key 的表頭。如果有多個 value 值,那么各個 value 值按從左到右的順序依次插入到表頭: 比如說,對空列表 mylist 執行命令 LPUSH mylist a b c ,列表的值將是 c b a ,這等同于原子性地執行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三個命令。如果 key 不存在,一個空列表會被創建并執行 LPUSH 操作。當 key 存在但不是列表類型時,返回一個錯誤。正常返回列表的長度。

    # 加入單個元素 redis> LPUSH languages python (integer) 1# 加入重復元素 redis> LPUSH languages python (integer) 2redis> LRANGE languages 0 -1 # 列表允許重復元素 1) "python" 2) "python"# 加入多個元素 redis> LPUSH mylist a b c (integer) 3redis> LRANGE mylist 0 -1 1) "c" 2) "b" 3) "a"

    2.RPUSH
    RPUSH key value [value …]
    時間復雜度: O(1)

    將一個或多個值 value 插入到列表 key 的表尾(最右邊)。如果有多個 value 值,那么各個 value 值按從左到右的順序依次插入到表尾:比如對一個空列表 mylist 執行 RPUSH mylist a b c ,得出的結果列表為 a b c ,等同于執行命令 RPUSH mylist a 、 RPUSH mylist b 、 RPUSH mylist c 。如果 key 不存在,一個空列表會被創建并執行 RPUSH 操作。當 key 存在但不是列表類型時,返回一個錯誤。正常返回列表的長度。

    # 添加單個元素 redis> RPUSH languages c (integer) 1# 添加重復元素 redis> RPUSH languages c (integer) 2redis> LRANGE languages 0 -1 # 列表允許重復元素 1) "c" 2) "c"# 添加多個元素 redis> RPUSH mylist a b c (integer) 3redis> LRANGE mylist 0 -1 1) "a" 2) "b" 3) "c"

    3.LINSERT
    LINSERT key BEFORE|AFTER pivot value
    時間復雜度: O(N), N 為尋找 pivot 過程中經過的元素數量。

    將值 value 插入到列表 key 當中,位于值 pivot 之前或之后。當 pivot 不存在于列表 key 時,不執行任何操作。當 key 不存在時, key 被視為空列表,不執行任何操作。如果 key 不是列表類型,返回一個錯誤。如果命令執行成功,返回插入操作完成之后,列表的長度。 如果沒有找到 pivot ,返回 -1 。 如果 key 不存在或為空列表,返回 0 。

    redis> RPUSH mylist "Hello" (integer) 1redis> RPUSH mylist "World" (integer) 2redis> LINSERT mylist BEFORE "World" "There" (integer) 3redis> LRANGE mylist 0 -1 1) "Hello" 2) "There" 3) "World"# 對一個非空列表插入,查找一個不存在的 pivot redis> LINSERT mylist BEFORE "go" "let's" (integer) -1 # 失敗# 對一個空列表執行 LINSERT 命令 redis> EXISTS fake_list (integer) 0redis> LINSERT fake_list BEFORE "nono" "gogogog" (integer) 0 # 失敗

    4.LPOP
    LPOP key
    時間復雜度: O(1)

    移除并返回列表 key 的頭元素。返回列表的頭元素。 當 key 不存在時,返回 nil 。

    redis> LLEN course (integer) 0redis> RPUSH course algorithm001 (integer) 1redis> RPUSH course c++101 (integer) 2redis> LPOP course # 移除頭元素 "algorithm001"

    5.RPOP
    RPOP key
    時間復雜度: O(1)

    移除并返回列表 key 的尾元素。返回列表的尾元素。 當 key 不存在時,返回 nil 。

    redis> RPUSH mylist "one" (integer) 1redis> RPUSH mylist "two" (integer) 2redis> RPUSH mylist "three" (integer) 3redis> RPOP mylist # 返回被彈出的元素 "three"redis> LRANGE mylist 0 -1 # 列表剩下的元素 1) "one" 2) "two"

    6.LREM
    LREM key count value

    時間復雜度: O(N), N 為列表的長度。
    根據參數 count 的值,移除列表中與參數 value 相等的元素。

    count 的值可以是以下幾種:

    • count > 0 : 從表頭開始向表尾搜索,移除與 value 相等的元素,數量為 count 。
    • count < 0 : 從表尾開始向表頭搜索,移除與 value 相等的元素,數量為 count 的絕對值。
    • count = 0 : 移除表中所有與 value 相等的值。

    返回值被移除元素的數量。 因為不存在的 key 被視作空表(empty list),所以當 key 不存在時, LREM 命令總是返回 0 。

    # 先創建一個表,內容排列是 # morning hello morning helllo morningredis> LPUSH greet "morning" (integer) 1 redis> LPUSH greet "hello" (integer) 2 redis> LPUSH greet "morning" (integer) 3 redis> LPUSH greet "hello" (integer) 4 redis> LPUSH greet "morning" (integer) 5redis> LRANGE greet 0 4 # 查看所有元素 1) "morning" 2) "hello" 3) "morning" 4) "hello" 5) "morning"redis> LREM greet 2 morning # 移除從表頭到表尾,最先發現的兩個 morning (integer) 2 # 兩個元素被移除redis> LLEN greet # 還剩 3 個元素 (integer) 3redis> LRANGE greet 0 2 1) "hello" 2) "hello" 3) "morning"redis> LREM greet -1 morning # 移除從表尾到表頭,第一個 morning (integer) 1redis> LLEN greet # 剩下兩個元素 (integer) 2redis> LRANGE greet 0 1 1) "hello" 2) "hello"redis> LREM greet 0 hello # 移除表中所有 hello (integer) 2 # 兩個 hello 被移除redis> LLEN greet (integer) 0

    7.LTRIM
    LTRIM key start stop
    時間復雜度: O(N), N 為被移除的元素的數量。
    對一個列表進行修剪(trim),就是說,讓列表只保留指定區間內的元素,不在指定區間之內的元素都將被刪除。

    這個從字面意思不容易理解 ,我們圖解一下:


    運行命令

    Itrim listkey 1 4

    結果

    上面的圖能一下子讓你看懂,它是做什么的了,下面演示一下

    # 情況 1: 常見情況, start 和 stop 都在列表的索引范圍之內 redis> LRANGE alpha 0 -1 # alpha 是一個包含 5 個字符串的列表 1) "h" 2) "e" 3) "l" 4) "l" 5) "o"redis> LTRIM alpha 1 -1 # 刪除 alpha 列表索引為 0 的元素 OKredis> LRANGE alpha 0 -1 # "h" 被刪除了 1) "e" 2) "l" 3) "l" 4) "o"# 情況 2: stop 比列表的最大下標還要大 redis> LTRIM alpha 1 10086 # 保留 alpha 列表索引 1 至索引 10086 上的元素 OKredis> LRANGE alpha 0 -1 # 只有索引 0 上的元素 "e" 被刪除了,其他元素還在 1) "l" 2) "l" 3) "o"# 情況 3: start 和 stop 都比列表的最大下標要大,并且 start < stop redis> LTRIM alpha 10086 123321 OKredis> LRANGE alpha 0 -1 # 列表被清空 (empty list or set)# 情況 4: start 和 stop 都比列表的最大下標要大,并且 start > stop redis> RPUSH new-alpha "h" "e" "l" "l" "o" # 重新建立一個新列表 (integer) 5redis> LRANGE new-alpha 0 -1 1) "h" 2) "e" 3) "l" 4) "l" 5) "o"redis> LTRIM new-alpha 123321 10086 # 執行 LTRIM OKredis> LRANGE new-alpha 0 -1 # 同樣被清空 (empty list or set)

    8.LINDEX
    LINDEX key index
    時間復雜度:O(N), N 為到達下標 index 過程中經過的元素數量。因此,對列表的頭元素和尾元素執行 LINDEX 命令,復雜度為O(1)。

    返回列表 key 中,下標為 index 的元素。下標(index)參數 start 和 stop 都以 0 為底,也就是說,以 0 表示列表的第一個元素,以 1 表示列表的第二個元素,以此類推。你也可以使用負數下標,以 -1 表示列表的最后一個元素, -2 表示列表的倒數第二個元素,以此類推。如果 key 不是列表類型,返回一個錯誤。返回列表中下標為 index 的元素。 如果 index 參數的值不在列表的區間范圍內(out of range),返回 nil 。

    redis> LPUSH mylist "World" (integer) 1redis> LPUSH mylist "Hello" (integer) 2redis> LINDEX mylist 0 "Hello"redis> LINDEX mylist -1 "World"redis> LINDEX mylist 3 # index不在 mylist 的區間范圍內 (nil)

    9.LLEN
    LLEN key
    時間復雜度: O(1)

    返回列表 key 的長度。如果 key 不存在,則 key 被解釋為一個空列表,返回 0 .如果 key 不是列表類型,返回一個錯誤。

    # 空列表 redis> LLEN job (integer) 0# 非空列表 redis> LPUSH job "cook food" (integer) 1redis> LPUSH job "have lunch" (integer) 2redis> LLEN job (integer) 2

    10.LSET
    LSET key index value
    時間復雜度:對頭元素或尾元素進行 LSET 操作,復雜度為 O(1)。其他情況下,為 O(N), N 為列表的長度。

    將列表 key 下標為 index 的元素的值設置為 value 。當 index 參數超出范圍,或對一個空列表( key 不存在)進行 LSET 時,返回一個錯誤。操作成功返回 ok ,否則返回錯誤信息。

    # 對空列表(key 不存在)進行 LSETredis> EXISTS list (integer) 0redis> LSET list 0 item (error) ERR no such key# 對非空列表進行 LSETredis> LPUSH job "cook food" (integer) 1redis> LRANGE job 0 0 1) "cook food"redis> LSET job 0 "play game" OKredis> LRANGE job 0 0 1) "play game"# index 超出范圍redis> LLEN list # 列表長度為 1 (integer) 1redis> LSET list 3 'out of range' (error) ERR index out of range

    下面我們看一下它的應用:

    最典型的就是時間軸

    以CSDN APP的bink推薦為例,

    當有一個bink發表的時候,要將這個blink插入時間軸第一個位置。可以是LPUSH的操作。這樣子每次你就能從列表中,看到最新的blink,越往下時間越遠。

    11. BLPOP
    BLPOP key [key …] timeout
    時間復雜度: O(1)

    BLPOP 是列表的阻塞式(blocking)彈出原語。它是 LPOP key 命令的阻塞版本,當給定列表內沒有任何元素可供彈出的時候,連接將被 BLPOP 命令阻塞,直到等待超時或發現可彈出元素為止。當給定多個 key 參數時,按參數 key 的先后順序依次檢查各個列表,彈出第一個非空列表的頭元素。

    12.BRPOP
    BRPOP key [key …] timeout
    時間復雜度: O(1)

    BRPOP 是列表的阻塞式(blocking)彈出原語。它是 RPOP key 命令的阻塞版本,當給定列表內沒有任何元素可供彈出的時候,連接將被 BRPOP 命令阻塞,直到等待超時或發現可彈出元素為止。當給定多個 key 參數時,按參數 key 的先后順序依次檢查各個列表,彈出第一個非空列表的尾部元素。

    下面再看一個Redis的應用:簡易的消息隊列

    Redis 的簡易的消息隊列不是專業的消息隊列,它沒有?常多的?級特性,沒有 ack 保證,如果對消息的可靠性有著極致的追求,那么它就不適合使?。但是對于那些只有?組消費者的消息隊列,使? Redis 就可以?常輕松的搞定。

    后面會談到Redis的高級特性–發布訂閱。這個也是消息隊列

    異步消息隊列
    Redis 的 list(列表) 數據結構常?來作為異步消息隊列使?,使?rpush/lpush操作?隊列,使?lpop 和 rpop來出隊列。

    客戶端是通過隊列的 pop 操作來獲取消息,然后進?處理。處理完了再接著獲取消息,再進?處理。如此循環往復。

    但是如果隊列空了,客戶端就會陷? pop 的死循環,不停地 pop。這樣不但拉?了客戶端的 CPU,redis 的 QPS(每秒執行次數) 也會被拉?,如果這樣不斷輪詢的客戶端有??來個,資源被大量無效占用。通常我們使? sleep 來解決這個問題,讓線程睡?會,睡個 1s 鐘就可以了。不但客戶端的 CPU 能降下來,Redis 的 QPS 也降下來了。不過這樣也并不是很好的手段,

    延時隊列
    運用blpop/brpop構建延時隊列是一個非常好的方式。這兩個指令的前綴字符b代表的是blocking,也就是阻塞讀。

    阻塞讀在隊列沒有數據的時候,會?即進?休眠狀態,?旦數據到來,則?刻醒過來。消息的延遲?乎為零。不過這也帶來一個問題:空閑連接。如果線程?直阻塞在哪?,Redis 的客戶端連接就成了閑置連接,閑置過久,服務器?般會主動斷開連接,減少閑置資源占?。這個時候blpop/brpop會拋出異常來。編寫客戶端消費者的時候要??,注意捕獲異常,還要重試。

    在上面簡單說明分布式鎖的時候,沒有提到客戶端在處理請求時加鎖沒加成功怎么辦。
    對于這種情況,有 3 種策略來處理加鎖失敗:

  • 直接拋出異常,通知?戶稍后重試;
  • sleep ?會再重試;
  • 將請求轉移?延時隊列,過?會再試;
  • (七)Set集合

    Redis 的集合相當于 Java 語???的 HashSet,它內部的鍵值對是?序的唯?的。它的內部實現相當于?個特殊的字典,字典中所有的value 都是?個值NULL。當集合中最后?個元素移除之后,數據結構?動刪除,內存被回收。

    插入之后,元素的先后位置是不固定的,遍歷的時候無序。

    下面我們看一下它的API

    1.SADD
    SADD key member [member …]
    時間復雜度: O(N), N 是被添加的元素的數量。

    將一個或多個 member 元素加入到集合 key 當中,已經存在于集合的 member 元素將被忽略。假如 key 不存在,則創建一個只包含 member 元素作成員的集合。當 key 不是集合類型時,返回一個錯誤。正常返回被添加到集合中的新元素的數量,不包括被忽略的元素。

    # 添加單個元素 redis> SADD bbs "discuz.net" (integer) 1# 添加重復元素 redis> SADD bbs "discuz.net" (integer) 0# 添加多個元素 redis> SADD bbs "tianya.cn" "groups.google.com" (integer) 2redis> SMEMBERS bbs 1) "discuz.net" 2) "groups.google.com" 3) "tianya.cn"

    2.SCARD
    SCARD key
    時間復雜度: O(1)
    返回集合 key 的基數(集合中元素的數量)。當 key 不存在時,返回 0 。

    redis> SADD tool pc printer phone (integer) 3redis> SCARD tool # 非空集合 (integer) 3redis> DEL tool (integer) 1redis> SCARD tool # 空集合 (integer) 0

    3.SMEMBERS
    SMEMBERS key
    時間復雜度: O(N), N 為集合的基數。返回集合 key 中的所有成員。不存在的 key 被視為空集合。

    # key 不存在或集合為空redis> EXISTS not_exists_key (integer) 0redis> SMEMBERS not_exists_key (empty list or set)# 非空集合redis> SADD language Ruby Python Clojure (integer) 3redis> SMEMBERS language 1) "Python" 2) "Ruby" 3) "Clojure"

    可以看出來,插入順序,與返回順序不同。要小心使用

    4.srandmember和spop
    SRANDMEMBER key [count]
    時間復雜度: 只提供 key 參數時為 O(1) 。如果提供了 count 參數,那么為 O(N) ,N 為返回數組的元素個數。

    如果命令執行時,只提供了 key 參數,那么返回集合中的一個隨機元素。
    從 Redis 2.6 版本開始, SRANDMEMBER 命令接受可選的 count 參數:

    • 如果 count 為正數,且小于集合基數,那么命令返回一個包含 count 個元素的數組,數組中的元素各不相同。如果 count 大于等于集合基數,那么返回整個集合。
    • 如果 count 為負數,那么命令返回一個數組,數組中的元素可能會重復出現多次,而數組的長度為 count 的絕對值。
    # 添加元素 redis> SADD fruit apple banana cherry (integer) 3# 只給定 key 參數,返回一個隨機元素 redis> SRANDMEMBER fruit "cherry"redis> SRANDMEMBER fruit "apple"# 給定 3 為 count 參數,返回 3 個隨機元素 # 每個隨機元素都不相同 redis> SRANDMEMBER fruit 3 1) "apple" 2) "banana" 3) "cherry"# 給定 -3 為 count 參數,返回 3 個隨機元素 # 元素可能會重復出現多次 redis> SRANDMEMBER fruit -3 1) "banana" 2) "cherry" 3) "apple"redis> SRANDMEMBER fruit -3 1) "apple" 2) "apple" 3) "cherry"# 如果 count 是整數,且大于等于集合基數,那么返回整個集合 redis> SRANDMEMBER fruit 10 1) "apple" 2) "banana" 3) "cherry"# 如果 count 是負數,且 count 的絕對值大于集合的基數 # 那么返回的數組的長度為 count 的絕對值 redis> SRANDMEMBER fruit -10 1) "banana" 2) "apple" 3) "banana" 4) "cherry" 5) "apple" 6) "apple" 7) "cherry" 8) "apple" 9) "apple" 10) "banana"# SRANDMEMBER 并不會修改集合內容 redis> SMEMBERS fruit 1) "apple" 2) "cherry" 3) "banana"# 集合為空時返回 nil 或者空數組 redis> SRANDMEMBER not-exists (nil)redis> SRANDMEMBER not-eixsts 10 (empty list or set) 討論

    SPOP key
    時間復雜度: O(1)
    移除并返回集合中的一個隨機元素。如果只想獲取一個隨機元素,但不想該元素從集合中被移除的話,可以使用 SRANDMEMBER key [count] 命令。

    redis> SMEMBERS db 1) "MySQL" 2) "MongoDB" 3) "Redis"redis> SPOP db "Redis"redis> SMEMBERS db 1) "MySQL" 2) "MongoDB"redis> SPOP db "MySQL"redis> SMEMBERS db 1) "MongoDB"

    spop從集合彈出 srandmembe不會破壞集合

    下面我們看看應用:
    CSDN的點贊狀態就可以用set集合進行處理。類似于點贊、踩這種場景,狀態不重復,就可以set集合。


    還有CSDN發表博文的時候,給文章打標簽,標簽就可以存儲在set集合中

    除此之外,set 結構可以?來存儲活動中獎的?戶 ID,因為有去重功能,可以保證同?個?戶不會中獎兩次。

    下面看看集合間的API

    5.sdiff sinter sunion

    分別是差集 交集 并集。

    這個玩法就比較多了。

    比如微信上,將每個人擁有的群id都存儲在每個人的set集合中

    我們只要對兩個人的集合取交集,就可以得出 我和他的公共群聊的個數。

    (八)zset (有序集合)

    zset 類似于 Java 的 SortedSet 和HashMap的結合體,

    ???它是?個 set,保證了內部 value 的唯?性,另???它可以給每個 value 賦予?個 score,代表這個 value 的排序權重。它的內部實現?的是?種叫做「跳躍列表」的數據結構。zset 中最后?個 value 被移除后,數據結構?動刪除,內存被回收。

    有序集合結構:
    key: user

    scorevalue
    1tom
    91jerry
    102jeffery

    集合 VS 有序集合
    集合:無重復元素、無序、element
    有序集合 : 無重復元素、有序、element + score
    有序集合相比集合時間復雜度較高

    1.ZADD
    ZADD key score member [[score member] [score member] …]
    時間復雜度: O(M*log(N)), N 是有序集的基數, M 為成功添加的新成員的數量。

    將一個或多個 member 元素及其 score 值加入到有序集 key 當中。如果某個 member 已經是有序集的成員,那么更新這個 member 的 score 值,并通過重新插入這個 member 元素,來保證該 member 在正確的位置上。score 值可以是整數值或雙精度浮點數。如果 key 不存在,則創建一個空的有序集并執行 ZADD 操作。當 key 存在但不是有序集類型時,返回一個錯誤。正常返回被成功添加的新成員的數量,不包括那些被更新的、已經存在的成員。

    # 添加單個元素 redis> ZADD page_rank 10 google.com (integer) 1# 添加多個元素 redis> ZADD page_rank 9 baidu.com 8 bing.com (integer) 2redis> ZRANGE page_rank 0 -1 WITHSCORES 1) "bing.com" 2) "8" 3) "baidu.com" 4) "9" 5) "google.com" 6) "10"# 添加已存在元素,且 score 值不變 redis> ZADD page_rank 10 google.com (integer) 0redis> ZRANGE page_rank 0 -1 WITHSCORES # 沒有改變 1) "bing.com" 2) "8" 3) "baidu.com" 4) "9" 5) "google.com" 6) "10"# 添加已存在元素,但是改變 score 值 redis> ZADD page_rank 6 bing.com (integer) 0redis> ZRANGE page_rank 0 -1 WITHSCORES # bing.com 元素的 score 值被改變 1) "bing.com" 2) "6" 3) "baidu.com" 4) "9" 5) "google.com" 6) "10"

    2.ZREM
    ZREM key member [member …]
    時間復雜度: O(M*log(N)), N 為有序集的基數, M 為被成功移除的成員的數量。

    移除有序集 key 中的一個或多個成員,不存在的成員將被忽略。當 key 存在但不是有序集類型時,返回一個錯誤。正常返回值
    被成功移除的成員的數量,不包括被忽略的成員。

    # 測試數據 redis> ZRANGE page_rank 0 -1 WITHSCORES 1) "bing.com" 2) "8" 3) "baidu.com" 4) "9" 5) "google.com" 6) "10"# 移除單個元素 redis> ZREM page_rank google.com (integer) 1redis> ZRANGE page_rank 0 -1 WITHSCORES 1) "bing.com" 2) "8" 3) "baidu.com" 4) "9"# 移除多個元素 redis> ZREM page_rank baidu.com bing.com (integer) 2redis> ZRANGE page_rank 0 -1 WITHSCORES (empty list or set)# 移除不存在元素 redis> ZREM page_rank non-exists-element (integer) 0

    3.zscore
    ZSCORE key member
    時間復雜度: O(1)

    返回有序集 key 中,成員 member 的 score 值(member 成員的 score 值,以字符串形式表示)。如果 member 元素不是有序集 key 的成員,或 key 不存在,返回 nil 。

    1) "tom" 2) "2000" 3) "peter" 4) "3500" 5) "jack" 6) "5000"redis> ZSCORE salary peter # 注意返回值是字符串 "3500"

    4.ZINCRBY
    ZINCRBY key increment member
    時間復雜度: O(log(N))

    為有序集 key 的成員 member 的 score 值加上增量 increment 。可以通過傳遞一個負數值 increment ,讓 score 減去相應的值,比如 ZINCRBY key -5 member ,就是讓 member 的 score 值減去 5 。當 key 不存在,或 member 不是 key 的成員時, ZINCRBY key increment member 等同于 ZADD key increment member 。當 key 不是有序集類型時,返回一個錯誤。score 值可以是整數值或雙精度浮點數。正常返回值為member 成員的新 score 值,以字符串形式表示。

    redis> ZSCORE salary tom "2000"redis> ZINCRBY salary 2000 tom # tom 加薪啦! "4000"

    5.zcard
    ZCARD key
    時間復雜度: O(1)
    當 key 存在且是有序集類型時,返回有序集的基數。 當 key 不存在時,返回 0 。

    redis > ZADD salary 2000 tom # 添加一個成員 (integer) 1redis > ZCARD salary (integer) 1redis > ZADD salary 5000 jack # 再添加一個成員 (integer) 1redis > ZCARD salary (integer) 2redis > EXISTS non_exists_key # 對不存在的 key 進行 ZCARD 操作 (integer) 0redis > ZCARD non_exists_key (integer) 0

    6.ZRANGE
    ZRANGE key start stop [WITHSCORES]
    時間復雜度: O(log(N)+M), N 為有序集的基數,而 M 為結果集的基數。

    返回有序集 key 中,指定區間內的成員。其中成員的位置按 score 值遞增(從小到大)來排序。具有相同 score 值的成員按字典序(lexicographical order )來排列。如果你需要成員按 score 值遞減(從大到小)來排列,請使用 ZREVRANGE key start stop [WITHSCORES] 命令。下標參數 start 和 stop 都以 0 為底,也就是說,以 0 表示有序集第一個成員,以 1 表示有序集第二個成員,以此類推。 你也可以使用負數下標,以 -1 表示最后一個成員, -2 表示倒數第二個成員,以此類推。超出范圍的下標并不會引起錯誤。 比如說,當 start 的值比有序集的最大下標還要大,或是 start > stop 時, ZRANGE 命令只是簡單地返回一個空列表。 另一方面,假如 stop 參數的值比有序集的最大下標還要大,那么 Redis 將 stop 當作最大下標來處理。可以通過使用 WITHSCORES 選項,來讓成員和它的 score 值一并返回,返回列表以 value1,score1, …, valueN,scoreN 的格式表示。 客戶端庫可能會返回一些更復雜的數據類型,比如數組、元組等。返回為指定區間內,帶有 score 值(可選)的有序集成員的列表。

    redis > ZRANGE salary 0 -1 WITHSCORES # 顯示整個有序集成員 1) "jack" 2) "3500" 3) "tom" 4) "5000" 5) "boss" 6) "10086"redis > ZRANGE salary 1 2 WITHSCORES # 顯示有序集下標區間 1 至 2 的成員 1) "tom" 2) "5000" 3) "boss" 4) "10086"redis > ZRANGE salary 0 200000 WITHSCORES # 測試 end 下標超出最大下標時的情況 1) "jack" 2) "3500" 3) "tom" 4) "5000" 5) "boss" 6) "10086"redis > ZRANGE salary 200000 3000000 WITHSCORES # 測試當給定區間不存在于有序集時的情況 (empty list or set)

    7.ZRANGEBYSCOR
    ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

    時間復雜度: O(log(N)+M), N 為有序集的基數, M 為被結果集的基數。

    返回有序集 key 中,所有 score 值介于 min 和 max 之間(包括等于 min 或 max )的成員。有序集成員按 score 值遞增(從小到大)次序排列。具有相同 score 值的成員按字典序(lexicographical order)來排列(該屬性是有序集提供的,不需要額外的計算)。

    可選的 LIMIT 參數指定返回結果的數量及區間(就像SQL中的 SELECT LIMIT offset, count ),注意當 offset 很大時,定位 offset 的操作可能需要遍歷整個有序集,此過程最壞復雜度為 O(N) 時間。

    可選的 WITHSCORES 參數決定結果集是單單返回有序集的成員,還是將有序集成員及其 score 值一起返回。 該選項自 Redis 2.0 版本起可用。

    區間及無限
    min 和 max 可以是 -inf 和 +inf ,這樣一來,你就可以在不知道有序集的最低和最高 score 值的情況下,使用ZRANGEBYSCORE 這類命令。默認情況下,區間的取值使用閉區間 (小于等于或大于等于),你也可以通過給參數前增加 ( 符號來使用可選的開區間 (小于或大于)。

    (integer) 0 redis> ZADD salary 5000 tom (integer) 0 redis> ZADD salary 12000 peter (integer) 0redis> ZRANGEBYSCORE salary -inf +inf # 顯示整個有序集 1) "jack" 2) "tom" 3) "peter"redis> ZRANGEBYSCORE salary -inf +inf WITHSCORES # 顯示整個有序集及成員的 score 值 1) "jack" 2) "2500" 3) "tom" 4) "5000" 5) "peter" 6) "12000"redis> ZRANGEBYSCORE salary -inf 5000 WITHSCORES # 顯示工資 <=5000 的所有成員 1) "jack" 2) "2500" 3) "tom" 4) "5000"redis> ZRANGEBYSCORE salary (5000 400000 # 顯示工資大于 5000 小于等于 400000 的成員 1) "peter"

    8.zcount
    ZCOUNT key min max

    時間復雜度: O(log(N)), N 為有序集的基數。
    返回有序集 key 中, score 值在 min 和 max 之間(默認包括 score 值等于 min 或 max )的成員的數量。關于參數 min 和 max 的詳細使用方法,請參考 ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 命令。返回值為score 值在 min 和 max 之間的成員的數量。

    redis> ZRANGE salary 0 -1 WITHSCORES # 測試數據 1) "jack" 2) "2000" 3) "peter" 4) "3500" 5) "tom" 6) "5000"redis> ZCOUNT salary 2000 5000 # 計算薪水在 2000-5000 之間的人數 (integer) 3redis> ZCOUNT salary 3000 5000 # 計算薪水在 3000-5000 之間的人數 (integer) 2

    9.zremrangebyrank
    ZREMRANGEBYRANK key start stop
    時間復雜度: O(log(N)+M), N 為有序集的基數,而 M 為被移除成員的數量。

    移除有序集 key 中,指定排名(rank)區間內的所有成員。區間分別以下標參數 start 和 stop 指出,包含 start 和 stop 在內。

    下標參數 start 和 stop 都以 0 為底,也就是說,以 0 表示有序集第一個成員,以 1 表示有序集第二個成員,以此類推。 你也可以使用負數下標,以 -1 表示最后一個成員, -2 表示倒數第二個成員,以此類推。返回值為被移除成員的數量。

    redis> ZADD salary 2000 jack (integer) 1 redis> ZADD salary 5000 tom (integer) 1 redis> ZADD salary 3500 peter (integer) 1redis> ZREMRANGEBYRANK salary 0 1 # 移除下標 0 至 1 區間內的成員 (integer) 2redis> ZRANGE salary 0 -1 WITHSCORES # 有序集只剩下一個成員 1) "tom" 2) "5000"

    10.zremrangebyscore
    ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

    時間復雜度: O(log(N)+M), N 為有序集的基數, M 為結果集的基數。

    返回有序集 key 中, score 值介于 max 和 min 之間(默認包括等于 max 或 min )的所有的成員。有序集成員按 score 值遞減(從大到小)的次序排列。具有相同 score 值的成員按字典序的逆序(reverse lexicographical order )排列。
    除了成員按 score 值遞減的次序排列這一點外, ZREVRANGEBYSCORE 命令的其他方面和 ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 命令一樣。
    返回值為指定區間內,帶有 score 值(可選)的有序集成員的列表。

    redis > ZADD salary 10086 jack (integer) 1 redis > ZADD salary 5000 tom (integer) 1 redis > ZADD salary 7500 peter (integer) 1 redis > ZADD salary 3500 joe (integer) 1redis > ZREVRANGEBYSCORE salary +inf -inf # 逆序排列所有成員 1) "jack" 2) "peter" 3) "tom" 4) "joe"redis > ZREVRANGEBYSCORE salary 10000 2000 # 逆序排列薪水介于 10000 和 2000 之間的成員 1) "peter" 2) "tom" 3) "joe"

    我們看一下它具體的應用:
    首先最容易想到的就是排行榜了。
    (博主在long long ago 也曾第一過)

    類似的,zset 還可以?來存儲學?的成績,value 值是學?的 ID,score 是他的考試成績。我們可以對成績按分數進?排序就可以得到他的名次。
    除此之外,zset 可以?來存粉絲列表,value 值是粉絲的?戶 ID,score 是關注時間。我們可以對粉絲列表按關注時間進?排序。

    不太常見API有這些,文章上面已經給大家推薦查看API的網站了,用到的時候查一查哈
    ◆ zrevrank
    ◆ zrevrange
    ◆ zrevrangebyscore
    ◆ interstore
    ◆ zunionstore

    上面的API可以分成這三類

    三、Redis 客戶端操作

    帶大家過一遍Redis 的API之后,這部分非常非常簡單了。

    所有語言的操作,其實都是對Redis API的封裝,封裝前的東西你都知道了,封裝后的不是易如反掌?

    以Java為例吧,基于Java語言的客戶端比較熱的是Jedis

    在連接方面有兩種方式,一種是直連、一種是線程池(一般都要用到線程池)

    直連方式

    // 1.生成一個 Jedis對象,這個對象負責和指定Reds節點進行通信 Jedis jedis = new Jedis("localhost", 6379); // Sting set get 操作 jedis.set("Jedis", "hello jedis!"); jedis.get("Jedis"); // 對結果進行自增 jedis.incr("counter")

    構造方法最多有四個參數

    hash操作

    jedis hset("myhash",f1",v1); jedis hset("myhash",f2,v2); // 輸出結果:{1=v1,f2=V2} jedis.hgetAll(myhash");

    是不是很熟悉,其他的類似。

    Jedis連接池
    直連的弊端很明顯:消耗資源。如果在不同方面,頻繁用到Redis,編寫程序也很麻煩。

    所以,連接池是非常重要的的。

    方案對比

    方案優點 缺點
    直連簡單方便;適用于少量長期連接的場景1.存在每次新建/關閉TCP開銷;2.資源無法控制,存在連接泄露的可能;3.Jedis對象線程不安全
    連接池Jedis預先生成,降低開銷使用連接池的形式保護和控制資源的使用相對于直連,使用相對麻煩尤其在資源的管理上需要很多參數來保證,一旦規劃不合理也會出現問題。
    // 初始化 Jedis連接池,通常來講 Jedis Pool是單例的。GenericObjectPoolConf poolConfig new GenericObjectPoolConfigO Jedis Pool jedis Pool new JedisPool(poolConfig, 127.0.0. 1",6379;

    簡單使用:

    Jedis jedis = null; try{//1.從連接池獲取 jedis對象jedis = jedisPool.getResource();//2.執行操作jedis set(hello","world");} catch(Exception e){eprintStackTrace();}finally {if (jedis != null)//如果使用 JedisPool,cose操作不是關閉連接,代表歸還連接池jedis closedjedis.close();} }

    說明

    唉,寫得太長了,CSDN編輯器不允許我在一篇文章上繼續發揮了。

    這是下一篇文章。

    傳送門:【大廠面試】面試官看了贊不絕口的Redis筆記(二)

    目錄:

    這五個專題串過之后,你會對Redis單體,有著非常好的理解了,后面再走就是看源碼了。相信你到這一步已經可以獨當一面了。

    我再往下面寫,就是Redis分布式領域相關的東西了,比如說Redis的主從復制、哨兵機制、 Redis cluster特性等。

    傳送門: 【大廠面試】面試官看了贊不絕口的Redis筆記(三)分布式篇

    目錄:

    最后還有一些拓展的內容需要補充。


    對了,兄dei,如果你覺得這篇文章可以的話,給俺點個贊再走,管不管?這樣可以讓更多的人看到這篇文章,對我來說也是一種激勵。

    如果你有什么問題的話,歡迎留言或者CSDN APP直接與我交流。

    總結

    以上是生活随笔為你收集整理的【大厂面试】面试官看了赞不绝口的Redis笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    久久黄网站 | 国产精品欧美久久久久三级 | 黄色日本免费 | 中文字幕婷婷 | 色偷偷网站视频 | 日韩免费一级a毛片在线播放一级 | 久久综合久久综合久久综合 | 久久露脸国产精品 | 亚洲欧美日韩精品一区二区 | 又湿又紧又大又爽a视频国产 | 国产精品久久久久久久久久久不卡 | 九九日九九操 | 久爱综合 | 日韩精品一区二区免费 | 午夜视频在线观看一区二区三区 | 正在播放国产一区二区 | 久久综合久久久久88 | 97在线资源 | 成 人 免费 黄 色 视频 | 一区二区三区四区精品 | 草久久精品| 国产性xxxx | 狠狠色噜噜狠狠狠狠2022 | 97天天干| 国产色视频网站 | 国产婷婷久久 | 亚洲蜜桃在线 | 在线成人免费电影 | 久久久久北条麻妃免费看 | 亚洲第一区在线播放 | 日韩中文字幕在线看 | 欧美一级片免费播放 | 日韩亚洲在线视频 | 亚洲精品网站在线 | 成人免费一区二区三区在线观看 | 成人免费在线电影 | 精品特级毛片 | 亚洲成人一区 | 热久久最新地址 | 精品一区精品二区 | 日本精a在线观看 | 欧美精品黑人性xxxx | 久久久久久综合网天天 | 麻豆精品国产传媒 | 就要干b | 欧美极品少妇xbxb性爽爽视频 | 国产精选视频 | 亚洲人天堂 | 久久,天天综合 | bayu135国产精品视频 | 蜜桃av久久久亚洲精品 | 少妇精品久久久一区二区免费 | 最近更新好看的中文字幕 | 成人免费在线网 | 欧美视屏一区二区 | 国色天香在线观看 | 国产高清视频在线播放 | 国产aa免费视频 | 国产视频九色蝌蚪 | av解说在线 | 一级黄色片在线观看 | 在线观看av不卡 | 午夜影院一级片 | 精品国产电影一区 | 久久久国际精品 | 狠狠干网址 | 视频直播国产精品 | 亚洲成人一区 | 久久久久久黄色 | 亚洲成人午夜在线 | 久久久久免费观看 | 亚洲高清视频在线播放 | 伊人黄色网 | 亚洲精品乱码久久久一二三 | 一区二区毛片 | 久久免费看av | 天天干天天操天天干 | 伊人亚洲综合 | 五月婷婷一区二区三区 | 5月丁香婷婷综合 | 国产亚洲午夜高清国产拍精品 | 国产日韩精品一区二区在线观看播放 | 久久久久国产免费免费 | 亚洲激情综合网 | 欧美在线视频a | 久久国产精品久久精品 | 日日天天av | 久久久精品一区二区 | 亚洲综合导航 | 色瓜| 欧美日韩激情视频8区 | 黄色毛片在线观看 | 久久国内精品视频 | 玖操| 欧美a级成人淫片免费看 | 亚洲一二区视频 | 天天舔天天射天天操 | 在线a亚洲视频播放在线观看 | 久久久免费少妇 | 国产精品网在线观看 | 91爱爱网址 | 国产男女免费完整视频 | 91精品国产自产在线观看永久 | 99这里只有久久精品视频 | 91精品1区2区 | 四虎国产 | 国产精品永久久久久久久www | 最新日韩中文字幕 | 91av在线免费视频 | 免费黄色av电影 | 天干啦夜天干天干在线线 | 久久大片网站 | 久久99久久久久久 | 日韩欧美黄色网址 | av片无限看 | 国产成人99久久亚洲综合精品 | 欧洲亚洲精品 | 日本黄色大片免费看 | av九九九 | 国产精品一区二区免费 | 日韩欧美综合精品 | 91高清视频免费 | 午夜久久久影院 | 久久理伦片 | 久久免费视屏 | 香蕉久草 | 日日躁你夜夜躁你av蜜 | 精品999久久久 | 一级片视频在线 | 色网站免费在线看 | 日韩精品一区二区三区不卡 | 国产一区二区三区免费在线 | 国外调教视频网站 | 日本久久久久久科技有限公司 | 成人性生交大片免费观看网站 | www.亚洲视频| 久久天天躁狠狠躁亚洲综合公司 | 日韩激情精品 | 欧美日韩国产一区二 | 久久精品久久精品久久 | 久久综合久久久 | 国产91免费在线观看 | 在线免费观看羞羞视频 | 日韩一区二区三区在线看 | 日p视频在线观看 | 中文字幕乱在线伦视频中文字幕乱码在线 | 日韩中文在线视频 | 久久噜噜少妇网站 | 久草在线免费播放 | 国产精品久久久久久久久免费 | 99精品视频免费观看视频 | 国产精品五月天 | 狠狠色丁香久久婷婷综 | 久久国产精品视频 | 亚洲九九影院 | 天天操天天插 | 久久精品一级片 | 亚洲精品一区二区18漫画 | 亚洲综合射 | 韩日电影在线观看 | 黄色大片av | 免费在线观看视频一区 | 欧美精品久久久久久久久老牛影院 | 亚洲综合在线五月天 | 国产又粗又猛又黄又爽 | 国产精品正在播放 | 亚洲欧美激情插 | 中文字幕在线播放一区 | 亚洲精品三级 | 97国产精品 | 亚洲成人精品国产 | 久久综合色一综合色88 | 免费在线黄网 | 韩国av免费在线观看 | 99精品热视频只有精品10 | 91精品国产一区二区在线观看 | www.久久精品视频 | 激情大尺度视频 | 久久99国产视频 | 中文字幕有码在线播放 | 在线看毛片网站 | 国产精品久久网站 | 波多野结衣电影一区 | 69精品| 午夜精品久久久久久久99水蜜桃 | 欧美精品亚洲二区 | 黄色影院在线免费观看 | 成人av免费在线 | 伊人五月婷 | av黄色av | 国产精品ⅴa有声小说 | 久久成电影 | 96香蕉视频 | 国内精品久久久久久久影视简单 | 日韩亚洲在线视频 | 96精品高清视频在线观看软件特色 | 国产精品久久久久婷婷 | 最新免费av在线 | 狠狠色网 | 日日综合网 | 嫩草伊人久久精品少妇av | 日本久久中文字幕 | 亚洲国产精品成人av | 久久五月精品 | 精品播放 | 午夜久久成人 | 久av在线 | 伊人久久精品久久亚洲一区 | 999国产精品视频 | 一区二区三区手机在线观看 | 成年人黄色大全 | 日韩免费中文字幕 | 黄色一级大片在线观看 | 欧美一级免费片 | 国产亚洲无 | 久久久精品综合 | 日韩色中色 | 九九日九九操 | 中文字幕在线看视频国产 | 天堂av免费在线 | 玖玖综合网 | 中文字幕中文字幕在线中文字幕三区 | 精品二区久久 | av7777777| 日韩伦理一区二区三区av在线 | 亚洲精品国产精品国自 | 午夜在线观看一区 | 欧美精品久久久久久久久久白贞 | 美女国产免费 | 91免费国产在线观看 | 99热超碰在线 | 国产黄色片一级 | 日日草夜夜操 | 九色琪琪久久综合网天天 | 日韩天堂在线观看 | 91激情| 免费视频 你懂的 | 亚洲在线高清 | 国产日韩欧美在线免费观看 | 久久精品二区 | 日韩电影中文,亚洲精品乱码 | 国产群p视频 | 成人黄色在线观看视频 | 色亚洲激情 | 欧美色图亚洲图片 | 亚洲视频专区在线 | 99精品观看 | 日韩一区在线播放 | 视频直播国产精品 | 中文字幕免费高清在线观看 | 日本成人中文字幕在线观看 | 国产精品嫩草影院9 | 99精品免费久久久久久久久日本 | 免费在线激情电影 | 久久精品国产99 | 精品久久久久久久 | 特级aaa毛片 | 嫩草伊人久久精品少妇av | 久久九九九九 | 91污污 | 国产高清在线永久 | 婷婷中文字幕在线观看 | 成人久久久精品国产乱码一区二区 | 国产精品18久久久久久首页狼 | 欧美另类xxx| 天天超碰| 2024av| 日日操日日 | 97**国产露脸精品国产 | 久一久久 | 91在线精品秘密一区二区 | 精品99久久久久久 | 美女视频黄免费网站 | 免费观看9x视频网站在线观看 | 又黄又爽又刺激 | 99在线观看精品 | 精品欧美日韩 | 97超碰色 | 天天操夜夜操 | 久草在线精品观看 | 狠狠激情中文字幕 | 婷婷丁香五 | 亚洲一二区视频 | 在线播放av网址 | 在线亚洲欧美日韩 | 国产一级一级国产 | 在线观看日韩免费视频 | 国产91免费在线观看 | 久久精品网 | 黄网av在线| 国产97av | 久免费| 日本动漫做毛片一区二区 | 亚洲禁18久人片 | 欧美日韩高清国产 | 精产嫩模国品一二三区 | 五月开心激情 | 久久久午夜视频 | 国产精品久久久久久久久久久久久久 | 永久免费的av电影 | 久久久九色精品国产一区二区三区 | 欧美一区二区三区不卡 | 国产91精品看黄网站 | 伊人电影在线观看 | 97成人资源| 国产精品一区电影 | 国产午夜在线观看视频 | 日本色小说视频 | 精品在线小视频 | 国产在线1区 | 亚洲精品免费在线观看视频 | 中国美女一级看片 | 国产精品久久久久久久av电影 | 国产91对白在线播 | 国产精品久久久久婷婷二区次 | 在线涩涩| 激情久久久久久久久久久久久久久久 | 久久精品福利视频 | 成人sm另类专区 | 激情五月婷婷综合网 | 国产成人久久av | 免费国产在线视频 | 亚洲乱码精品久久久 | 精品一二三四五区 | 国产男女无遮挡猛进猛出在线观看 | 国产综合在线观看视频 | 天天干,天天射,天天操,天天摸 | 国产一级大片在线观看 | 日韩欧美在线免费观看 | 欧美地下肉体性派对 | 欧美一区二区三区在线看 | av色网站 | 亚洲做受高潮欧美裸体 | 国产xxxx | 久操视频在线播放 | 成人动漫一区二区 | 狠狠的干狠狠的操 | 国产亚洲高清视频 | 国产一级片免费播放 | 日本激情视频中文字幕 | 午夜精品一区二区三区四区 | 91片黄在线观 | 国产 字幕 制服 中文 在线 | 亚洲精品美女久久久 | 最近日本韩国中文字幕 | 激情在线免费视频 | 中文字幕在线免费观看视频 | 免费国产在线精品 | av中文字幕第一页 | 制服丝袜天堂 | 色婷婷国产在线 | 久久综合九色综合欧美就去吻 | 国产精品美女在线 | 久久国产精品99久久久久久老狼 | www.伊人网.com| 91av免费看 | 国产精品欧美激情在线观看 | av网站免费线看精品 | 国产精品久久久久久久久久久免费看 | 97电影在线 | 午夜视频导航 | 草草草影院 | 免费在线观看不卡av | www日| 91视频com| 欧美激情h | 韩日在线一区 | 久久高清片 | 天天草天天干天天射 | 91九色精品 | 最近中文字幕完整视频高清1 | 激情网站五月天 | 欧美一级视频在线观看 | 91香蕉视频好色先生 | 亚洲人成人在线 | 日韩专区一区二区 | 日本久久成人中文字幕电影 | 久久久网| 久久精品中文字幕免费mv | 国产高清网站 | 免费看一级特黄a大片 | 久久久久久久免费 | 国产精品久久久久av免费 | 91福利视频在线 | 亚洲专区一二三 | 久久天天躁夜夜躁狠狠躁2022 | 手机在线看片日韩 | 欧美精品v国产精品v日韩精品 | 丁香婷婷射 | 日韩欧美视频一区二区 | 亚洲国产丝袜在线观看 | 999久久a精品合区久久久 | 福利视频午夜 | 91精品国产99久久久久久久 | 99超碰在线观看 | 日日夜夜中文字幕 | 午夜精品久久久久久久久久久 | 999精品 | 精品国产一区二区三区蜜臀 | 美女免费视频网站 | 久草久视频 | 天天色天天综合网 | 国产老妇av | 免费日韩一级片 | 欧美一级电影在线观看 | 国产一区二区在线免费播放 | 91中文字幕在线 | 国产中出在线观看 | 亚洲欧美999 | 成人av免费网站 | 午夜精品福利在线 | 久久不卡日韩美女 | 免费视频91蜜桃 | 天天艹天天干天天 | 夜夜干夜夜 | 国产精品av免费观看 | 成人黄色电影在线播放 | 欧美日韩国产成人 | 麻豆国产网站入口 | 国产视频97 | 人人干人人做 | 成人av影视 | 毛片美女网站 | 在线免费观看视频一区二区三区 | 嫩嫩影院理论片 | 三上悠亚一区二区在线观看 | 亚洲影音先锋 | 操操综合| 国产无套精品久久久久久 | 精品在线观看一区二区 | 国产在线播放一区二区三区 | 免费观看完整版无人区 | 中日韩在线视频 | 婷婷av综合 | 超碰免费成人 | 一二三区在线 | 丁香视频五月 | 久久久久激情 | 91网页版在线观看 | 美女网站在线免费观看 | 日韩三级免费观看 | 国产一级特黄毛片在线毛片 | 欧洲成人免费 | 日本黄色黄网站 | 午夜精品电影一区二区在线 | 免费成人在线视频网站 | 久久久片 | 91麻豆国产| 91精品网站在线观看 | av女优中文字幕在线观看 | 伊人黄 | 色片网站在线观看 | 尤物九九久久国产精品的分类 | 久久97精品 | 日韩欧美国产激情在线播放 | 久久综合久久综合久久综合 | 亚洲视频专区在线 | 又黄又刺激的网站 | 97在线观看 | 国产日韩在线看 | 欧美国产日韩在线观看 | 99热999| 国产欧美精品xxxx另类 | 久久高清毛片 | 国产 在线 高清 精品 | ww亚洲ww亚在线观看 | 欧美va在线观看 | 日韩有码中文字幕在线 | 精品91视频| 91九色porny在线 | 日韩久久片 | www.成人精品 | 中文字幕字幕中文 | 97在线视频观看 | av电影在线免费观看 | 涩涩网站在线 | 人人玩人人爽 | 国产欧美精品一区二区三区 | 国产精品 美女 | 成年人免费在线播放 | 97久久久免费福利网址 | 国产亚洲精品久久久久久大师 | v片在线播放 | 91桃色在线免费观看 | 中文字幕在线免费97 | 久久久久久久久久福利 | 免费亚洲电影 | 久热电影 | 婷婷综合成人 | 国内精品久久久久久 | 天天插日日射 | 亚洲精品一区二区三区四区高清 | 国产成人av网站 | 国产免费亚洲高清 | 久久婷婷一区二区三区 | 五月在线视频 | 天天爽天天射 | 久久久免费 | 色综合天天色 | 欧美吞精 | 成人免费观看网站 | 成 人 黄 色 视频播放1 | 中文字幕免费在线 | 国产亚洲精品美女久久 | 亚洲国产资源 | 久久久久久久久久久久久久av | 婷婷视频导航 | 2019中文字幕网站 | 午夜av激情 | 国产在线高清精品 | 黄色毛片网站在线观看 | 欧美日韩在线视频免费 | 黄色视屏在线免费观看 | 国产免费三级在线观看 | 免费观看性生活大片 | 黄网站污 | 久草在线免费电影 | a视频在线| 综合婷婷| 日韩在线观看a | 欧美a级成人淫片免费看 | 最新av网址在线观看 | 91在线影院| 午夜影院在线观看18 | 久久成视频 | 久久久久黄 | 婷婷激情影院 | 亚洲精品美女久久久久网站 | 亚洲精品乱码久久久久久 | 国产中文字幕免费 | zzijzzij亚洲成熟少妇 | 中文字幕一区二区三区在线观看 | 91在线影院 | 成人免费视频网址 | 黄色av免费看 | 日韩av伦理片 | 欧美日韩在线免费视频 | 欧美一级免费在线 | 人人爽爽人人 | 伊人色综合网 | 99精品免费在线 | 婷婷久久丁香 | 成人久久电影 | 欧美日韩国产在线一区 | 国产高清视频色在线www | 九九免费精品视频 | 中文在线字幕免 | 精品国产乱子伦一区二区 | 日韩午夜剧场 | 成人影音av | 九九激情视频 | 日韩精品视频一二三 | 日一日干一干 | 91av看片| 国产精品激情在线观看 | 亚洲va在线va天堂va偷拍 | 国产三级精品三级在线观看 | 97电影在线看视频 | 久久综合欧美精品亚洲一区 | 免费福利在线 | 最新av网址在线 | 精品国产午夜 | 蜜臀av夜夜澡人人爽人人桃色 | 免费看三级| 黄色a一级视频 | 国产一区欧美一区 | 国产一区二区午夜 | 国产理论片在线观看 | 国产精品毛片一区视频播 | 国产在线一区二区 | 久久精品国产免费看久久精品 | 久久精品艹| 亚洲黄色软件 | 天天色天天色 | 欧美一级日韩免费不卡 | 成人免费视频播放 | 国内小视频在线观看 | 免费视频黄 | 91精品国产福利 | 国产精品午夜久久 | 中国一级片在线播放 | 国产r级在线观看 | 免费视频资源 | 三级黄免费看 | 俺要去色综合狠狠 | 欧美另类人妖 | 精品一二三区视频 | 色网址99 | ,午夜性刺激免费看视频 | 97电影在线观看 | 色欲综合视频天天天 | 最近中文字幕免费观看 | 91在线中字 | 国产精品亚洲综合久久 | 夜夜夜草 | 黄色亚洲| 在线观看免费中文字幕 | 欧美国产日韩一区二区三区 | 成人免费中文字幕 | 欧美一进一出抽搐大尺度视频 | 久久男人中文字幕资源站 | 免费成人在线视频网站 | 日本中文字幕在线看 | 色综合久久久久综合体桃花网 | 精品久久久久久久久久久院品网 | 九九热在线视频 | 精品美女久久久久久免费 | 人人澡人人爽欧一区 | 99久久婷婷 | 性色av免费在线观看 | 成人在线视频你懂的 | 91精品在线免费观看视频 | 亚洲 欧美 日韩 综合 | 亚洲欧美一区二区三区孕妇写真 | 五月色婷 | 日韩不卡高清视频 | 国产成人av在线 | 成人亚洲免费 | 日本黄色免费播放 | 超碰在线观看97 | 久久久www成人免费毛片麻豆 | 51久久夜色精品国产麻豆 | 青青草国产精品 | 亚洲免费高清视频 | 69国产盗摄一区二区三区五区 | 亚洲精品中文字幕视频 | 日韩av电影中文字幕在线观看 | 免费黄色a网站 | 久久久久久久久国产 | 综合在线亚洲 | 久久精品视频4 | 久视频在线播放 | av在线等 | 日韩丝袜在线观看 | 婷婷久久网站 | 亚洲精品综合欧美二区变态 | 欧美日韩在线免费观看 | 亚洲一级电影在线观看 | 永久免费视频国产 | 欧美孕妇与黑人孕交 | 日韩av网页 | 婷婷深爱| 日韩中文字幕免费看 | 91av官网| 最近中文字幕大全中文字幕免费 | www.伊人色.com| 国产亚洲精品成人av久久ww | 国产日本在线播放 | 久草视频在线看 | 国产精品久久久久久69 | 精品爱爱| 一区二区三区 中文字幕 | 久久久久国产精品午夜一区 | 久久久黄视频 | 欧美99热 | 国产在线视频一区二区 | 日本一区二区不卡高清 | 欧美日韩精品网站 | 色爱区综合激月婷婷 | 国产高清不卡 | 九九热中文字幕 | 人人爽久久涩噜噜噜网站 | 日韩高清成人 | 九九热精品视频在线播放 | 国产一级片不卡 | 91av蜜桃| 国内久久精品视频 | 国产精品9999久久久久仙踪林 | 久在线观看视频 | 91看片淫黄大片一级在线观看 | 免费污片| 精品久久国产精品 | 亚洲精品国产精品国 | av播放在线| 精品一区av | 国产免费人成xvideos视频 | 日韩精品首页 | 亚洲精品中文字幕在线观看 | 99精品欧美一区二区三区黑人哦 | 中文字幕精品三区 | av国产网站 | 国产一区精品在线 | 青青草国产在线 | 中文字幕中文字幕 | 国产亚洲欧美在线视频 | 四虎影视国产精品免费久久 | 国产小视频在线免费观看 | 亚洲电影图片小说 | 久久视频网 | 亚洲一区精品人人爽人人躁 | 一区二区精品在线 | 91免费高清在线观看 | 色综合狠狠干 | 国产成人av电影在线 | 欧美性受极品xxxx喷水 | 美女久久网站 | 五月婷婷久久综合 | 久草在线资源网 | 久久理论电影 | 久久无码av一区二区三区电影网 | 97偷拍视频 | 69xx视频| 黄色一级大片在线观看 | 中文字幕日韩在线播放 | 日韩在线中文字幕 | 伊人丁香 | 最近中文字幕国语免费高清6 | 亚洲成熟女人毛片在线 | 1024手机在线看 | 91成人精品一区在线播放 | 51久久成人国产精品麻豆 | 亚洲精品国产欧美在线观看 | 国产麻豆视频 | 亚洲国产视频直播 | 国产精品视频永久免费播放 | 成人av免费 | 久久久www成人免费毛片麻豆 | 国产网站色 | 亚洲国产精品女人久久久 | 美女网站在线看 | 午夜影院一级片 | 免费看特级毛片 | av中文字幕网站 | 99久e精品热线免费 99国产精品久久久久久久久久 | 97在线免费视频观看 | 国产黄色网 | 91精品国产自产在线观看 | 久久刺激视频 | 欧美精品三级 | 久草精品在线播放 | 成人av影院在线观看 | 色在线高清 | 中文字幕在线视频免费播放 | 在线观看完整版免费 | 在线91观看 | 国产精品第三页 | 2023av| 欧美综合在线视频 | 91视频在线播放视频 | 免费在线激情视频 | 2022久久国产露脸精品国产 | 999男人的天堂 | 九色视频网站 | 91精品国产自产91精品 | 香蕉久久久久久av成人 | 久久久久久久久久免费 | 久久精品免视看 | 天天综合入口 | 五月丁婷婷 | 久草在线国产 | 中文字幕在线观看国产 | 奇米7777狠狠狠琪琪视频 | 中文字幕综合在线 | 日韩av综合网站 | 黄色成年| 久久五月婷婷丁香社区 | 中文字幕乱偷在线 | 99热精品国产一区二区在线观看 | 色国产精品 | 色香蕉网 | 欧美色图30p | 特级西西444www高清大视频 | 欧美淫aaa免费观看 日韩激情免费视频 | 成人午夜电影久久影院 | 97视频免费看 | 欧美性超爽 | 一区二区三区免费在线观看视频 | 精品女同一区二区三区在线观看 | 日韩av男人的天堂 | 色婷av | 久久久久久久亚洲精品 | 在线影院av | 国产精品美女久久 | 国产不卡免费 | 涩涩色亚洲一区 | 精品一二三四视频 | 久久精品一区二区国产 | 91中文字幕在线视频 | 激情久久影院 | 五月婷婷综合在线观看 | 丁香婷五月| 97视频入口免费观看 | 午夜18视频在线观看 | 香蕉视频亚洲 | 久久久国产一区二区三区四区小说 | 欧美视频二区 | 毛片基地黄久久久久久天堂 | 久久婷五月 | 成人免费中文字幕 | 久久综合久久88 | 久久久久观看 | 国产精品地址 | 99久久超碰中文字幕伊人 | 丰满少妇一级 | 91精品秘密在线观看 | 综合国产在线 | 国产精品久久久免费 | av亚洲产国偷v产偷v自拍小说 | 国产视频在线观看一区二区 | 天天操天天吃 | 久久电影网站中文字幕 | 一区二区三区日韩在线观看 | 精品在线观看一区二区 | 国产日韩视频在线观看 | 在线观看亚洲免费视频 | 五月婷婷激情综合 | 成人在线观看网址 | 亚洲国产三级在线观看 | 91成人在线视频 | 91成人观看 | 久草电影在线 | 天天干视频在线 | 国产三级午夜理伦三级 | 欧美专区日韩专区 | 蜜臀av性久久久久av蜜臀三区 | 99视频在线精品 | 亚洲激情网站免费观看 | 六月色婷 | 亚洲成a人片在线观看网站口工 | 中文字幕免费高清在线 | 97国产精品久久 | 九色精品在线 | 成人av电影免费 | 国产福利免费看 | 日韩国产欧美在线播放 | 九九爱免费视频 | 在线观看韩日电影免费 | 国产精品69久久久久 | 最近中文字幕免费av | 亚洲视频在线视频 | 免费观看一级成人毛片 | 久久成人一区二区 | www.天天色 | 黄色a三级 | 久久久久久久国产精品视频 | 欧美日韩后 | 久草久热| 成年人在线观看 | www黄色com | 久草视频免费在线观看 | 二区三区视频 | 在线电影中文字幕 | 色噜噜噜噜 | 精品视频在线视频 | 四虎国产 | 黄色视屏av | 国产精品一区在线 | 国产中文字幕视频在线观看 | 久久av电影| 91精品一 | 黄色三级免费看 | 毛片网站免费 | 丁香六月婷婷开心 | 日本久久影视 | 免费91在线| 人人澡人人澡人人 | 九九精品视频在线 | 91九色成人蝌蚪首页 | 四虎永久免费 | 久久线视频 | 欧美一二三四在线 | 91九色丨porny丨丰满6 | 亚洲精品综合一二三区在线观看 | 久久精品香蕉 | 久久精品99久久 | 91香蕉视频 | 91视频久久久久久 | 国产成人福利在线 | 国产精品一区二区三区免费看 | 人人爽人人爱 | 久久国产亚洲视频 | a级国产乱理伦片在线观看 亚洲3级 | 99免费在线观看视频 | 日韩专区av | 午夜黄网| 久久爱资源网 | 最新中文字幕在线资源 | 亚洲免费色 | 97综合网 | 91九色网站 | 国产精品久久久久久影院 | 日本电影黄色 | 看污网站 | 亚洲专区在线播放 | 国产高清久久 | 91中文视频 | 亚洲一级黄色av | 91九色porny蝌蚪主页 | 色婷婷激情 | 人人草在线观看 | 国产在线高清精品 | 色婷婷狠狠干 | 天天干夜夜操视频 | 国产精品久久久久久a | 欧美日韩1区2区 | 欧美色图88| 成人黄色在线观看视频 | 日本资源中文字幕在线 | 99精品国产99久久久久久福利 | 亚洲精品综合在线 | 人人爽久久久噜噜噜电影 | 精品久久久久久久久久岛国gif | 成人黄色在线观看视频 | 欧美黄污视频 | 国产精品永久免费 | 天天综合五月天 | 黄色免费视频在线观看 | 久久久久成人免费 | 最新国产在线 | 又黄又刺激的视频 | 亚洲精品免费播放 | av亚洲产国偷v产偷v自拍小说 | 成人性生活大片 | 中文字幕在线看视频国产 | 一区 在线 影院 | 成年人免费电影 | 99亚洲国产| 99久久精品免费看国产四区 | 亚洲国产精品电影在线观看 | 久久精品久久99 | 国产超碰在线 | 成人片在线播放 | 伊人手机在线 | 97免费在线观看 | 日韩欧美精品一区二区三区经典 | 免费成人结看片 | 色综合狠狠干 | 91资源在线播放 | 成人一区二区三区在线观看 | 99综合影院在线 | 国产一区av在线 | 三级视频片 | 久草网站在线 | 四虎国产精 | 久久国产高清 | 综合在线亚洲 | 人人插人人看 | 麻豆视传媒官网免费观看 | 亚洲91中文字幕无线码三区 | 亚洲视频观看 | 国产做aⅴ在线视频播放 | 亚洲精品一区二区精华 | 久久精品视频中文字幕 | 日韩精品一区二区免费视频 | 深爱激情亚洲 | 亚洲无吗av| 午夜精品久久久久久久99水蜜桃 | 国产在线精品区 | 亚洲九九爱 | 中文字幕成人在线观看 | 成人亚洲欧美 | av一级在线观看 | 91av视频免费在线观看 | 成人网色| 欧美激情视频在线观看免费 | 天天看天天干天天操 | 色妞色视频一区二区三区四区 | 中文一区在线 | 久久99国产精品久久99 | 天天干夜夜夜操天 | 久久久久久久久久久国产精品 | 中文字幕资源在线观看 | 日韩av免费在线电影 | 国产伦精品一区二区三区四区视频 | 91九色蝌蚪国产 | 中文视频在线 | 成人在线视频你懂的 | 国产一区二区在线影院 | 欧美成人91 | 亚洲一级电影在线观看 | 色欲综合视频天天天 | 国产精品自产拍在线观看网站 | 免费av黄色 | 久草在线视频免费资源观看 | 亚洲国产剧情 | 免费黄在线看 | 日韩午夜电影 | 四虎在线视频 | 久久激五月天综合精品 | 2023天天干 | 中文字幕一区二区三区精华液 | 911精品视频 | 精品嫩模福利一区二区蜜臀 | 欧美色综合 | 日本三级久久 | 日韩 国产 | 99成人在线视频 | 久久久91精品国产 | 亚洲精品videossex少妇 | 日韩一二三 | 日韩系列在线 | 久久成人国产精品一区二区 | 成人网色| jizz欧美性9| www.狠狠色.com | 人人揉人人揉人人揉人人揉97 | 在线看不卡av | 五月亚洲综合 |