久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

深入学习Redis

發(fā)布時間:2025/3/21 数据库 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入学习Redis 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • Redis
    • 前言
    • 一、概念和基礎(chǔ)
      • 概念
      • 優(yōu)勢
      • 官方資料
      • 使用場景
    • 二、數(shù)據(jù)類型:5種基礎(chǔ)的數(shù)據(jù)類型
      • Redis數(shù)據(jù)結(jié)構(gòu)簡介
      • 基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)詳解
        • String字符串
        • List列表
        • Set集合
        • Hash散列
        • Zset有序集合
    • 三、特殊的數(shù)據(jù)類型
      • 三種特殊類型講解
        • HyperLogLog
        • Bitmap
        • Geospatial
    • 四、數(shù)據(jù)庫
      • 常用指令
      • 過期數(shù)據(jù)的刪除策略
        • 定時刪除
        • 惰性刪除
        • 定期刪除
    • 五、持久化
      • RDB持久化
        • 觸發(fā)方式
        • RDB的優(yōu)缺點
      • AOF持久化
        • 實現(xiàn)AOF
        • 配置文件
        • 重寫機制
      • 重啟加載
    • 六、事務(wù)
    • 七、主從復(fù)制
      • 舊版復(fù)制功能的實現(xiàn)
        • 復(fù)制
        • 命令傳播
      • 新版復(fù)制功能的實現(xiàn)
        • 部分重同步
        • 復(fù)制的完整流程
      • 心跳檢測
    • 八、哨兵
    • 九、集群
      • 集群的作用
      • 集群的搭建
        • 執(zhí)行Redis命令搭建集群
        • 使用Ruby腳本搭建集群
      • 集群設(shè)計
      • 數(shù)據(jù)結(jié)構(gòu)
      • 集群命令的實現(xiàn)
      • 實踐須知
        • 集群伸縮
        • ASK錯誤

Redis

前言

Redis是一種支持key-value等多種數(shù)據(jù)結(jié)構(gòu)的存儲系統(tǒng),通過在內(nèi)存中讀取數(shù)據(jù),大大提高了數(shù)據(jù)的讀取速度,作為一個緩存中間件,是實現(xiàn)網(wǎng)站高并發(fā)以及高可用不可或缺的一部分。可應(yīng)用于緩存,事件發(fā)布或者訂閱,高速隊列等場景,支持網(wǎng)絡(luò),提供字符串、哈希表、列表、隊列、集合結(jié)構(gòu)直接存儲,基于內(nèi)存并可持久化。

一、概念和基礎(chǔ)

概念

Redis是一款基于內(nèi)存的高速緩存數(shù)據(jù)庫,全稱為:Remote Dictionary Server(遠程數(shù)據(jù)服務(wù)),使用C語言編寫,支持豐富的數(shù)據(jù)類型。

Redis與其他key-value緩存產(chǎn)品有以下幾個特點:

  • Redis支持數(shù)據(jù)的持久化,可以將內(nèi)存中的數(shù)據(jù)保存在磁盤,重啟的時候再次加載進行使用
  • Redis不僅僅支持簡單的key-value類型數(shù)據(jù),同時還提供list、set、zset以及hash等數(shù)據(jù)結(jié)構(gòu)的存儲
  • Redis支持數(shù)據(jù)的備份,即master-slave模式的數(shù)據(jù)備份。

優(yōu)勢

  • 性能極高 — Redis能讀的速度是110000次/s,寫的速度是81000次/s
  • 原子性 — Redis所有操作都是原子性的,同時Redis還支持對幾個操作全并之后的原子性執(zhí)行
  • 豐富的特性 — Redis支持publish/subscribe,通知,key過期等等特性。
  • 持久化 — Redis支持RDB,AOF等持久化方式
  • 發(fā)布訂閱 — Redis支持發(fā)布/訂閱模式
  • 分布式 — Redis Cluster 集群

官方資料

Redis官網(wǎng):http://redis.io/

Redis官方文檔:http://redis.io/documentation

Redis下載:http://redis.io/download

參考資料:
http://redisdoc.com/index.html
https://www.cnblogs.com/kismetv/p/8654978.html#t41
https://www.pdai.tech/md/outline/x-outline.html#nosql-db—redis%E8%AF%A6%E8%A7%A3


使用場景

熱點數(shù)據(jù)的緩存

緩存是Redis中最常見的應(yīng)用場景,Redis讀寫性能優(yōu)異,在高并發(fā)服務(wù)中成為首選的緩存組件,并且Redis支持事務(wù),能保證數(shù)據(jù)的一致性。

限時活動

Redis中可以使用expire命令射在一個鍵的生存時間,過期之后Redis會自動刪除,利用這一特性可以應(yīng)用在限時搶購活動、獲取手機驗證碼等常見業(yè)務(wù)場景。

計數(shù)器相關(guān)問題

Redis中有incr命令可以實現(xiàn)原子性的遞增,可以運用于高并發(fā)的秒殺活動、分布式序列號的生成以及其他限制次數(shù)的業(yè)務(wù)中。

分布式鎖

Redis中有setnx命令,該命令全寫為:“set if not exists”,如果不存在則設(shè)置成功并返回1,否則返回0。在分布式集群系統(tǒng)中,一個定時任務(wù)可能會在多個機器上運行,為了保持數(shù)據(jù)的一致性,可以先在定時任務(wù)中運用setnx命令設(shè)置一個鎖,如果成功設(shè)置則執(zhí)行任務(wù),否則說明任務(wù)已經(jīng)開始執(zhí)行,從而避免多個任務(wù)同時執(zhí)行對數(shù)據(jù)產(chǎn)生影響。該分布式鎖常運用于大型秒殺系統(tǒng)。

延時操作

在電商系統(tǒng)中,當(dāng)用戶下單,訂單產(chǎn)生之后會占用庫存,可以設(shè)置一個時效檢驗用戶是否已經(jīng)付款購買,如果超時則讓該單據(jù)失效,同時還原庫存。在Redis2.8.0版本之后還提供了Keyspace Notifications功能,運行客戶端訂閱Pub/Sub頻道,接受Redis數(shù)據(jù)集的變化事件。通過上述我們就可以解決實際應(yīng)用的問題,當(dāng)訂單產(chǎn)生時,設(shè)置一個key,同時設(shè)置15分鐘后過期,在后臺實現(xiàn)一個監(jiān)聽器,監(jiān)聽key的時效,key失效后仍沒有完成訂單則取消訂單。

點贊、好友等相互關(guān)系的存儲

Redis利用集合的一些命令,如求交集、并集、差集等。

在微信朋友圈中,每個用戶的好友存入一個集合中,很容易實現(xiàn)求出兩個人的共同好友,并將共同好友的信息呈現(xiàn)在朋友圈中。


二、數(shù)據(jù)類型:5種基礎(chǔ)的數(shù)據(jù)類型

Redis數(shù)據(jù)結(jié)構(gòu)簡介

Redis所有的key(鍵)都是字符串,基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)包括String、List、Set、Zset、Hash。每種結(jié)構(gòu)都至少有兩種編碼,這樣的好處在于:一方面接口與實現(xiàn)實現(xiàn)了分離,需要增加或改變內(nèi)部編碼時,用戶不受影響,另一方面可以根據(jù)不同的應(yīng)用場景切換內(nèi)部編碼,提高效率。

結(jié)構(gòu)類型結(jié)構(gòu)存儲的值結(jié)構(gòu)的讀寫能力
String字符串二進制安全的,可以包含任何數(shù)據(jù),比如jpg格式的圖片或者可以序列化的對象,一個鍵最大能存儲512MB對整個字符串或字符串的一部分進行操作;對整數(shù)或浮點數(shù)進行自增或自減操作;
List列表本質(zhì)是鏈表,鏈表上的每個節(jié)點都包含一個字符串鏈表的兩端都可以進行push和pop操作,讀取單個或多個數(shù)據(jù)
Set集合包含字符串的無序集合并且存儲的值唯一,集合是通過哈希表實現(xiàn)的,所以添加,刪除,查找的復(fù)雜度都是 O(1)。字符串的集合,包含基礎(chǔ)的方法有看是否存在添加、獲取、刪除;還包含計算交集、并集、差集等
Zset有序集合包含鍵值對的無序散列表,是一個 string 類型的field 和 value 的映射表,hash 特別適合用于存儲對象。添加、獲取、刪除單個元素
Hash散列表string 類型元素的集合,且不允許重復(fù)的成員,不同的是每個元素都會關(guān)聯(lián)一個 double 類型的分數(shù)。redis 正是通過分數(shù)來為集合中的成員進行從小到大的排序。字符串成員與浮點數(shù)分數(shù)之間的有序映射;元素的排列順序由分數(shù)的大小決定;包含方法有添加、獲取、刪除單個元素以及根據(jù)分值范圍或成員來獲取元素

基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)詳解

String字符串

String是redis中最基本的數(shù)據(jù)類型,一個key對應(yīng)一個value。

  • 命令使用

    命令簡述使用
    SET將字符串值 value 關(guān)聯(lián)到 key,如果 key 已經(jīng)持有其他值, SET 就覆寫舊值, 無視類型SET key value
    SETNX“Set If Not Exists”的縮寫,鍵key 不存在的情況,將key設(shè)為 valule,并返回1,若鍵 key 已經(jīng)存在, 則 SETNX 命令不做任何動作,并返回0SETNX key value
    SETEX將鍵 key 的值設(shè)置為 value , 并將鍵 key 的生存時間設(shè)置為 seconds 秒鐘。如果鍵 key 已經(jīng)存在, 那么 SETEX 命令將覆蓋已有的值SETEX key seconds value
    PSETEX這個命令和 SETEX 命令相似, 但它以毫秒為單位設(shè)置 key 的生存時間, 而不是像 SETEX 命令那樣以秒為單位進行設(shè)置PSETEX key milliseconds value
    GET返回與鍵 key 相關(guān)聯(lián)的字符串值GET key
    GETSET將鍵 key 的值設(shè)為 value , 并返回鍵 key 在被設(shè)置之前的舊值GETSET key value
    APPEND如果鍵 key 已經(jīng)存在并且它的值是一個字符串, APPEND 命令將把 value 追加到鍵 key 現(xiàn)有值的末尾。如果key不存在,則該命令與SET,命令效果相同APPEND key value
    INCR為鍵 key 儲存的數(shù)字值加上一。如果鍵 key 不存在, 那么它的值會先被初始化為 0 , 然后再執(zhí)行 INCR 命令。如果鍵 key 儲存的值不能被解釋為數(shù)字, 那么 INCR 命令將返回一個錯誤INCR key
    INCRBY為鍵 key 儲存的數(shù)字值加上增量 incrementINCRBY key increment
    INCRBYFLOAT為鍵 key 儲存的值加上浮點數(shù)增量 incrementINCRBYFLOAT key increment
    DECR為鍵 key 儲存的數(shù)字值減去一。如果鍵 key 不存在, 那么鍵 key 的值會先被初始化為 0 , 然后再執(zhí)行 DECR 操作。如果鍵 key 儲存的值不能被解釋為數(shù)字, 那么 DECR 命令將返回一個錯誤DECR key
    DECRBY將鍵 key 儲存的整數(shù)值減去減量 decrementDECRBY key decrement
    MSET同時為多個鍵設(shè)置值。MSET key value [key value …]
    MGET返回給定的一個或多個字符串鍵的值。MGET key [key …]
  • 實戰(zhàn)場景

    • 緩存:把常用消息,字符串,照片等信息存入Redis中,把Redis當(dāng)作緩存層,Mysql作為持久層,以減輕Musql的讀寫壓力
    • 計數(shù)器:Redis是單線程模型,一個命令執(zhí)行完才會執(zhí)行下一個。
    • Session:Spring Session + Redis實現(xiàn)Session共享。

List列表

實質(zhì)為鏈表,用雙端鏈表實現(xiàn)

  • 命令使用

    命令簡述使用
    LPUSH/RPUSH將一個或多個值 value 插入到列表 key 的表頭(表尾)LPUSH /RPUSH key value [value …]
    LPUSHX將值 value 插入到列表 key 的表頭,當(dāng)且僅當(dāng) key 存在并且是一個列表。和 LPUSH 命令相反,當(dāng) key 不存在時 LPUSHX命令什么也不做。LPUSHX key value
    LPOP/RPOP移除并返回列表 key 的頭(尾)元素。LPOP /RPOP key
    RPOPLPUSH將列表 source 中的最后一個元素(尾元素)彈出,并返回給客戶端。 將 source 彈出的元素插入到列表 destination ,作為 destination 列表的的頭元素。RPOPLPUSH source destination
    LREM根據(jù)參數(shù) count 的值,移除列表中與參數(shù) value 相等的元素;count > 0: 從表頭開始向表尾搜索,移除與value相等的元素,數(shù)量為count; count < 0 : 從表尾開始向表頭搜索,移除與 value 相等的元素,數(shù)量為 count 的絕對值。count = 0 : 移除表中所有與 value 相等的值。LREM key count value
    LLEN返回列表 key 的長度。LLEN key
    LINDEX返回列表 key 中,下標(biāo)為 index 的元素。LINDEX key index
    LSET將列表 key 下標(biāo)為 index 的元素的值設(shè)置為 value 。LSET key index value
    LRANGE返回列表 key 中指定區(qū)間內(nèi)的元素,區(qū)間以偏移量 start 和 stop 指定。LRANGE key start stop
    LTRIM對一個列表進行修剪(trim),就是說,讓列表只保留指定區(qū)間內(nèi)的元素,不在指定區(qū)間之內(nèi)的元素都將被刪除。LTRIM key start stop
    BLPOP它是 LPOP key 命令的阻塞版本,當(dāng)給定列表內(nèi)沒有任何元素可供彈出的時候,連接將被 BLPOP 命令阻塞,直到等待超時或發(fā)現(xiàn)可彈出元素為止。BLPOP key [key …] timeout
  • 列表的使用技巧

    • lpush+lpop=Stack(棧)
    • lpush+rpop=Queue(隊列)
    • lpush+ltrim=Capped Collection(有限列表)
    • push+brpop=Message Queue(消息隊列)
  • 實戰(zhàn)場景

    • 朋友圈:lpush新的動態(tài),lpop展示新的動態(tài)
    • 消息隊列:利用RPOPLPUSH實現(xiàn)一個安全的消息隊列,不僅返回一個消息同時將這個消息備份到一個備份列表中,如果使用LPUSH將消息放入隊列,而另一個客戶端中 BRPOP取出隊列,該隊列方式是不安全的,如果一個客戶端取出消息后崩潰,而未處理完的消息也將因此丟失
    • 事件提醒:有時候為了等待一個新的元素到達數(shù)據(jù)中,需要使用輪詢的方式對數(shù)據(jù)進行探查,另一種更好的方式是,使用系統(tǒng)提供的阻塞原語,在新元素到達時立即進行處理,而新元素沒達到時,一直阻塞,避免輪詢占用資源

    Set集合

    Redis 的 Set 是 String 類型的無序集合。集合成員是唯一的,這就意味著集合中不能出現(xiàn)重復(fù)的數(shù)據(jù)。

    通過哈希表實現(xiàn)的,所以添加,刪除,查找的復(fù)雜度都是 O(1)。

    • 命令使用

      命令簡述使用
      SADD將一個或多個 member 元素加入到集合 key 當(dāng)中,已經(jīng)存在于集合的 member 元素將被忽略SADD key member [member …]
      SISMEMBER判斷 member 元素是否集合 key 的成員;如果 member 元素是集合的成員,返回 1 。 如果 member 元素不是集合的成員,或 key 不存在,返回 0SISMEMBER key member
      SPOP移除并返回集合中的一個隨機元素。SPOP key
      SRANDMEMBER只提供 key 參數(shù)時,返回一個元素; 如果提供了 count 參數(shù),那么返回一個數(shù)組;如果集合為空,返回空數(shù)組SRANDMEMBER key [count]
      SREM移除集合 key 中的一個或多個 member 元素,不存在的 member 元素會被忽略。SREM key member [member …]
      SMOVE將 member 元素從 source 集合移動到 destination 集合。SMOVE source destination member
      SCARD返回集合 key 的基數(shù)(集合中元素的數(shù)量)。SCARD key
      SMEMBERS返回集合 key 中的所有成員。SMEMBERS key
      SINTER返回一個集合的全部成員,該集合是所有給定集合的交集。不存在的 key 被視為空集。當(dāng)給定集合當(dāng)中有一個空集時,結(jié)果也為空集(根據(jù)集合運算定律)。SINTER key [key …]
      SINTERSTORE這個命令類似于 SINTER命令,但它將結(jié)果保存到 destination 集合,而不是簡單地返回結(jié)果集。SINTERSTORE destination key [key …]
      SUNION返回一個集合的全部成員,該集合是所有給定集合的并集。SUNION key [key …]
      SDIFF返回一個集合的全部成員,該集合是所有給定集合之間的差集。SDIFF key [key …]
    • 實戰(zhàn)場景

      • 標(biāo)簽(tag):給用戶添加標(biāo)簽,或者用戶給消息添加標(biāo)簽,這樣有同一標(biāo)簽或者類似標(biāo)簽的可以給推薦關(guān)注的事或者關(guān)注的人
      • 點贊,或點踩,收藏等,可以放到set中實現(xiàn)

Hash散列

一個 string 類型的 field(字段) 和 value(值) 的映射表,hash 特別適合用于存儲對象。

  • 命令使用

    命令簡述使用
    HSET將哈希表 hash 中域 field 的值設(shè)置為 value 。如果給定的哈希表并不存在, 那么一個新的哈希表將被創(chuàng)建并執(zhí)行 HSET 操作。如果域 field 已經(jīng)存在于哈希表中, 那么它的舊值將被新值 value 覆蓋。HSET hash field value
    HSETNX當(dāng)且僅當(dāng)域 field 尚未存在于哈希表的情況下, 將它的值設(shè)置為 value ;如果給定域已經(jīng)存在于哈希表當(dāng)中, 那么命令將放棄執(zhí)行設(shè)置操作;如果哈希表 hash 不存在, 那么一個新的哈希表將被創(chuàng)建并執(zhí)行 HSETNX 命令HSETNX hash field value
    HGETHGET 命令在默認情況下返回給定域的值。HGET hash field
    HEXISTS檢查給定域 field 是否存在于哈希表 hash 當(dāng)中。HEXISTS hash field
    HDEL刪除哈希表 key 中的一個或多個指定域,不存在的域?qū)⒈缓雎浴?/td>HDEL key field [field …]
    HLEN返回哈希表 key 中域的數(shù)量。HLEN key
    HINCRBY為哈希表 key 中的域 field 的值加上增量 increment 。HINCRBY key field increment
    HMSET同時將多個 field-value (域-值)對設(shè)置到哈希表 key 中。HMSET key field value [field value …]
    HMGET返回哈希表 key 中,一個或多個給定域的值。HMGET key field [field …]
    HVALS返回哈希表 key 中所有域的值。HVALS key
    HGETALL返回哈希表 key 中,所有的域和值。HGETALL key
  • 實戰(zhàn)場景

    • 緩存:可以更方便更直觀地維護緩存信息,如果用戶信息等,并且比String更節(jié)省空間,方便管理。

Zset有序集合

Redis 有序集合和集合一樣也是 string 類型元素的集合,且不允許重復(fù)的成員。不同的是每個元素都會關(guān)聯(lián)一個 double 類型的分數(shù)。redis 正是通過分數(shù)來為集合中的成員進行從小到大的排序。

  • 命令使用

    命令簡述使用
    ZADD將一個或多個 member 元素及其 score 值加入到有序集 key 當(dāng)中。ZADD key score member [[score member] [score member] …]
    ZSCORE返回有序集 key 中,成員 member 的 score 值。ZSCORE key member
    ZINCRBY為有序集 key 的成員 member 的 score 值加上增量 increment 。ZINCRBY key increment member
    ZREVRANGE返回有序集 key 中,指定區(qū)間內(nèi)的成員。ZREVRANGE key start stop [WITHSCORES]
    ZREM移除有序集 key 中的一個或多個成員,不存在的成員將被忽略。ZREM key member [member …]
    ZRANK返回有序集 key 中成員 member 的排名。其中有序集成員按 score 值遞增(從小到大)順序排列。ZRANK key member
    ZREVRANK返回有序集 key 中成員 member 的排名。其中有序集成員按 score 值遞減(從大到小)排序。ZREVRANK key member
    ZREMRANGEBYRANK移除有序集 key 中,指定排名(rank)區(qū)間內(nèi)的所有成員。ZREMRANGEBYRANK key start stop
    ZREMRANGEBYSCORE移除有序集 key 中,所有 score 值介于 min 和 max 之間(包括等于 min 或 max )的成員。ZREMRANGEBYSCORE key min max
  • 實戰(zhàn)場景

    • 排行榜:可以應(yīng)用于一些需要排序的排行榜中,例如常見的微博熱搜等。

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

三種特殊類型講解

除了上文中的五種基礎(chǔ)數(shù)據(jù)類型,還有三種特殊的數(shù)據(jù)類型分別是 **HyperLogLog(基數(shù)統(tǒng)計), Bitmaps (位圖) 和 geospatial (地理位置)。

HyperLogLog

Redis 2.8.9 版本更新:新增Hyperloglog 數(shù)據(jù)結(jié)構(gòu)

數(shù)據(jù)存入后無法取出,只能用于基數(shù)的統(tǒng)計

  • 什么是基數(shù)?

    例如,A={1, 3, 5, 7, 5, 7, 8,}那么基數(shù)(不重復(fù)的元素)為5,基數(shù)集為{1,3,5,7,8};基數(shù)估計就是在誤差可接受的范圍內(nèi),快速計算基數(shù)。

  • HyperLogLog 基數(shù)統(tǒng)計用來解決什么問題

    作為一個高級不精確去重的數(shù)據(jù)結(jié)構(gòu),它常常用于統(tǒng)計數(shù)據(jù)時去重的操作。特點是可以利用極小的內(nèi)存空間完成獨立總數(shù)的統(tǒng)計,比如注冊 IP 數(shù)、每日訪問 IP 數(shù)、頁面實時UV、在線用戶數(shù),共同好友數(shù)等。

  • 優(yōu)勢體現(xiàn)在哪?

    一個大型網(wǎng)站中需要統(tǒng)計IP數(shù),粗略計算一個IP消耗10字節(jié),100萬的IP就是15MB,而HyperLogLog在Redis中每個鍵占用的內(nèi)容都是12字節(jié),理論存儲近似接近264個值,它基于一個基數(shù)估算的算法,只能比較準(zhǔn)確的估算出基數(shù),仍然會存在一定不可避免的誤差,但一個帶有0.81%誤差的近似值在實際應(yīng)用場景也是可以接受的。

  • 命令使用

    命令簡述使用返回值
    PFADD將任意數(shù)量的元素添加到指定的 HyperLogLog 里面PFADD key element [element …]整數(shù)回復(fù): 如果 HyperLogLog 的內(nèi)部儲存被修改了, 那么返回 1 , 否則返回 0
    PFCOUNT命令作用于單個鍵時, 返回儲存在給定鍵的 HyperLogLog 的近似基數(shù), 如果鍵不存在, 那么返回 0PFCOUNT key [key …]返回的可見集合(observed set)基數(shù)并不是精確值, 而是一個帶有 0.81% 標(biāo)準(zhǔn)錯誤(standard error)的近似值。
    PFMERGE將多個 HyperLogLog 合并(merge)為一個 HyperLogLog , 合并后的 HyperLogLog 的基數(shù)接近于所有輸入 HyperLogLog 的可見集合(observed set)的并集。PFMERGE destkey sourcekey [sourcekey …]字符串回復(fù):返回 OK

Bitmap

itmap 即位圖數(shù)據(jù)結(jié)構(gòu),都是操作二進制位來進行記錄,只有0 和 1 兩個狀態(tài)。

  • Bitmap位存儲可以解決什么問題?

    由于只能存儲0和1兩個數(shù)據(jù),所以它適用于一些只需記錄狀態(tài)而不需要記錄具體數(shù)值的數(shù)據(jù),如用戶登錄狀態(tài)等。

  • 命令使用

    命令簡述使用返回值
    SETBIT對 key 所儲存的字符串值,設(shè)置或清除指定偏移量上的位(bit);位的設(shè)置或清除取決于 value 參數(shù),可以是 0 也可以是 1 ;當(dāng) key 不存在時,自動生成一個新的字符串值。字符串會進行伸展(grown)以確保它可以將 value 保存在指定的偏移量上,當(dāng)字符串值進行伸展時,空白位置以 0 填充。SETBIT key offset value指定偏移量原來儲存的位
    GETBIT對 key 所儲存的字符串值,獲取指定偏移量上的位(bit),當(dāng) offset 比字符串值的長度大,或者 key 不存在時,返回 0 。GETBIT key offset字符串值指定偏移量上的位(bit)
    BITCOUNT計算給定字符串中,被設(shè)置為 1 的比特位的數(shù)量。BITCOUNT key [start] [end]被設(shè)置為 1 的位的數(shù)量。
    BITPOS返回位圖中第一個值為 bit 的二進制位的位置。BITPOS key bit [start] [end]整數(shù)回復(fù)。
    BITOP對一個或多個保存二進制位的字符串 key 進行位元操作,并將結(jié)果保存到 destkey 上。operation 可以是 AND 、 OR 、 NOT 、 XOR 這四種操作中的任意一種: BITOP AND destkey key [key ...] :對一個或多個 key 求邏輯并,并將結(jié)果保存到 destkey ;BITOP OR destkey key [key ...] :對一個或多個 key 求邏輯或,并將結(jié)果保存到 destkey ; BITOP XOR destkey key [key ...] :對一個或多個 key 求邏輯異或,并將結(jié)果保存到 destkey ; BITOP NOT destkey key:對給定 key 求邏輯非,并將結(jié)果保存到 destkeyBITOP operation destkey key [key …]保存到 destkey 的字符串的長度,和輸入 key 中最長的字符串長度相等。
  • 實戰(zhàn)應(yīng)用

    • 使用Bitmap實現(xiàn)用戶上線次數(shù)的統(tǒng)計

      假設(shè)我們希望記錄網(wǎng)站上用戶的上線頻率,可以通過SETBIT key offset value 和 BITCOUNT key [start] [end]來實現(xiàn)。

      比如,當(dāng)用戶某一天上線時,我們就使用SETBIT key offset value ,將用戶名username作為key,將那天的上線日作為offset參數(shù),并將這個offset賦值value為1

      當(dāng)需要計算某用戶的上線次數(shù)時,就使用 BITCOUNT key [start] [end]命令,執(zhí)行:BITCOUNT username計算得出結(jié)果就是該用戶的總上線天數(shù)。

      性能分析

      上述案例中,即使網(wǎng)站運行10年,每個用戶存儲的信息占用的空間也不過10*365bit,也就是每個用戶456字節(jié),對于這種大小的數(shù)據(jù)處理速度相當(dāng)快速。


Geospatial

Redis 3.2 .0版本更新:新增Geospatial 數(shù)據(jù)結(jié)構(gòu),可以用于推算地理位置的信息

常用命令

  • GEOADD key longitude latitude member [longitude latitude member …]

    GEOADD 命令以標(biāo)準(zhǔn)的 x,y 格式接受參數(shù), 所以用戶必須先輸入經(jīng)度, 然后再輸入緯度。 GEOADD 能夠記錄的坐標(biāo)是有限的: 非常接近兩極的區(qū)域是無法被索引的。 精確的坐標(biāo)限制由 EPSG:900913 / EPSG:3785 / OSGEO:41001 等坐標(biāo)系統(tǒng)定義, 具體如下:

    • 有效的經(jīng)度介于 -180 度至 180 度之間。
    • 有效的緯度介于 -85.05112878 度至 85.05112878 度之間。
    127.0.0.1:6379> geoadd china:city 144.05 22.52 shenzhen 120.16 30.24 hangzhou 108.96 34.26 xian (integer) 3
  • GEOPOS key member [member …]

    從鍵里面返回所有給定位置元素的位置(經(jīng)度和緯度)。

    因為 GEOPOS 命令接受可變數(shù)量的位置元素作為輸入, 所以即使用戶只給定了一個位置元素, 命令也會返回數(shù)組回復(fù)。

    GEOPOS 命令返回一個數(shù)組, 數(shù)組中的每個項都由兩個元素組成: 第一個元素為給定位置元素的經(jīng)度, 而第二個元素則為給定位置元素的緯度。 當(dāng)給定的位置元素不存在時, 對應(yīng)的數(shù)組項為空值。

    127.0.0.1:6379> GEOPOS china:city shenzhen hangzhou 1) 1) "144.05000120401382446"2) "22.5200000879503861" 2) 1) "120.1600000262260437"2) "30.2400003229490224"
  • GEODIST key member1 member2 [unit]

    返回兩個給定位置之間的距離。

    指定單位的參數(shù) unit 必須是以下單位的其中一個:

    • m 表示單位為米。
    • km 表示單位為千米。
    • mi 表示單位為英里。
    • ft 表示單位為英尺。

    如果用戶沒有顯式地指定單位參數(shù), 那么 GEODIST 默認使用米作為單位。

    127.0.0.1:6379> GEODIST china:city shenzhen hangzhou "2524417.0471"
  • GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

    以給定的經(jīng)緯度為中心, 返回鍵包含的位置元素當(dāng)中, 與中心的距離不超過給定最大距離的所有位置元素。

    在給定以下可選項時, 命令會返回額外的信息:

    • WITHDIST : 在返回位置元素的同時, 將位置元素與中心之間的距離也一并返回。 距離的單位和用戶給定的范圍單位保持一致。
    • WITHCOORD : 將位置元素的經(jīng)度和維度也一并返回。
    • WITHHASH : 以 52 位有符號整數(shù)的形式, 返回位置元素經(jīng)過原始 geohash 編碼的有序集合分值。 這個選項主要用于底層應(yīng)用或者調(diào)試, 實際中的作用并不大。

    命令默認返回未排序的位置元素。 通過以下兩個參數(shù), 用戶可以指定被返回位置元素的排序方式:

    • ASC : 根據(jù)中心的位置, 按照從近到遠的方式返回位置元素。
    • DESC : 根據(jù)中心的位置, 按照從遠到近的方式返回位置元素。

    在默認情況下, GEORADIUS 命令會返回所有匹配的位置元素。 雖然用戶可以使用 COUNT <count> 選項去獲取前 N 個匹配元素, 但是因為命令在內(nèi)部可能會需要對所有被匹配的元素進行處理, 所以在對一個非常大的區(qū)域進行搜索時, 即使只使用 COUNT 選項去獲取少量元素, 命令的執(zhí)行速度也可能會非常慢。 但是從另一方面來說, 使用 COUNT 選項去減少需要返回的元素數(shù)量, 對于減少帶寬來說仍然是非常有用的。

    127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km 1) "xian" 2) "hangzhou" 127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist 1) 1) "xian"2) "483.8340" 2) 1) "hangzhou"2) "977.5143" 127.0.0.1:6379> geoadd china:city 118.76 32.04 manjing 112.55 37.86 taiyuan 123.43 41.80 shenyang (integer) 3 127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist count 2 1) 1) "xian"2) "483.8340" 2) 1) "manjing"2) "864.9816" 127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 2 1) 1) "xian"2) "483.8340"3) 1) "108.96000176668167114"2) "34.25999964418929977" 2) 1) "manjing"2) "864.9816"3) 1) "118.75999957323074341"2) "32.03999960287850968"
  • GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

    這個命令和 GEORADIUS 命令一樣, 都可以找出位于指定范圍內(nèi)的元素, 但是 GEORADIUSBYMEMBER 的中心點是由給定的位置元素決定的, 而不是像 GEORADIUS 那樣, 使用輸入的經(jīng)度和緯度來決定中心點。

    127.0.0.1:6379> GEORADIUSBYMEMBER china:city taiyuan 1000 km withcoord withdist 1) 1) "manjing"2) "859.5256"3) 1) "118.75999957323074341"2) "32.03999960287850968" 2) 1) "taiyuan"2) "0.0000"3) 1) "112.54999905824661255"2) "37.86000073876942196" 3) 1) "xian"2) "514.2264"3) 1) "108.96000176668167114"2) "34.25999964418929977"

四、數(shù)據(jù)庫

常用指令

命令簡述使用返回值
EXISTS檢查給定 key 是否存在EXISTS key若 key 存在,返回 1 ,否則返回 0
TYPE返回 key 所儲存的值的類型TYPE keynone (key不存在) 、string (字符串) 、list (列表) 、set (集合) 、zset (有序集) 、hash (哈希表) 、stream (流)
RENAME將 key 改名為 newkey ,當(dāng) key 和 newkey 相同,或者 key 不存在時,返回一個錯誤。RENAME key newkey改名成功時提示 OK ,失敗時候返回一個錯誤。
RENAMENX當(dāng)且僅當(dāng) newkey 不存在時,將 key 改名為 newkey ;當(dāng) key 不存在時,返回一個錯誤RENAMENX key newkey修改成功時,返回 1 ; 如果 newkey 已經(jīng)存在,返回 0 。
MOVE將當(dāng)前數(shù)據(jù)庫的 key 移動到給定的數(shù)據(jù)庫 db 當(dāng)中,如果當(dāng)前數(shù)據(jù)庫(源數(shù)據(jù)庫)和給定數(shù)據(jù)庫(目標(biāo)數(shù)據(jù)庫)有相同名字的給定 key ,或者 key 不存在于當(dāng)前數(shù)據(jù)庫,那么 MOVE 沒有任何效果MOVE key db移動成功返回 1 ,失敗則返回 0 。
DEL刪除給定的一個或多個 key ,不存在的 key 會被忽略。DEL key [key …]被刪除 key 的數(shù)量
DBSIZE返回當(dāng)前數(shù)據(jù)庫的 key 的數(shù)量。DBSIZE當(dāng)前數(shù)據(jù)庫的 key 的數(shù)量。
KEYS查找所有符合給定模式 pattern 的 key , 比如說: KEYS * 匹配數(shù)據(jù)庫中所有 key ;KEYS h?llo 匹配 hello , hallo 和 hxllo 等; KEYS h*llo 匹配 hllo 和 heeeeello 等; KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hilloKEYS pattern符合給定模式的 key 列表。
FLUSHDB清空當(dāng)前數(shù)據(jù)庫中的所有 key,此命令從不失敗FLUSHDB總是返回 OK 。
FLUSHALL清空整個 Redis 服務(wù)器的數(shù)據(jù)(刪除所有數(shù)據(jù)庫的所有 key ),此命令從不失敗FLUSHALL總是返回 OK 。
SELECT切換到指定的數(shù)據(jù)庫,數(shù)據(jù)庫索引號 index 用數(shù)字值指定,以 0 作為起始索引值,默認使用 0 號數(shù)據(jù)庫SELECT indexOK
SWAPDB db1 db2對換指定的兩個數(shù)據(jù)庫, 使得兩個數(shù)據(jù)庫的數(shù)據(jù)立即互換SWAPDB db1 db2OK

設(shè)置鍵的生存時間或過期時間

通過EXPIRE 或者PEXPIRE命令,客戶端可以以秒或者毫秒精度為數(shù)據(jù)庫中的某個鍵設(shè)置生存時間(Time To Live,后面簡稱TTL),在經(jīng)過了指定的時間后服務(wù)器就會刪除生存時間為0的鍵。

127.0.0.1:6379> SET key value OK 127.0.0.1:6379> EXPIRE key 5 //設(shè)置過期時間為5s (integer) 1 127.0.0.1:6379> get key //5s之內(nèi) "value" 127.0.0.1:6379> get key //5s后 (nil)

在之前提到的SETEX命令可以在設(shè)置一個字符串鍵的同時為鍵設(shè)置過期時間,這個命令是一個限制了類型的命令,只適用于字符串類型的數(shù)據(jù),但SETEX命令設(shè)置過期時間的原理與EXPIRE命令設(shè)置過期時間的原理是完全一致的。

TTL 和PTTL命令可以接收一個待用生存時間或者過期時間的鍵,返回這個鍵的剩余生存時間。

127.0.0.1:6379> EXPIRE key 1000 (integer) 1 127.0.0.1:6379> TTL key (integer) 996

EXPIREAT key timestamp命令可以設(shè)置鍵key的過期時間為timestamp指定的秒數(shù)時間戳

PEXPIREAT key timestamp命令可以設(shè)置鍵key的過期時間為timestamp指定的毫秒數(shù)時間戳

雖然有多種不同的單位以及不同形式的設(shè)置命令,但實際上EXPIRE PEXPIRE EXPIREAT都是基于PEXPIREAT來實現(xiàn)的,所以客戶端中無論執(zhí)行的是上面四個指令中的哪一個,經(jīng)過轉(zhuǎn)換后,執(zhí)行的效果與PEXPIREAT指令效果相同。

保存設(shè)置時間

RedisDB結(jié)構(gòu)的expires字典保存了數(shù)據(jù)庫中所有鍵的過期時間,稱之為過期字典

  • 過期字典的鍵是一個指針,這個指針指向鍵空間的某個鍵對象,即某個數(shù)據(jù)庫的鍵
  • 過期字典的值是一個long long類型的整數(shù),這個整數(shù)保存了鍵指向的數(shù)據(jù)庫鍵的過期時間——一個毫秒精度的UNIX時間戳

移除過期時間

PERSIST命令可以移除一個鍵的過期時間

127.0.0.1:6379> EXPIRE key 1989 (integer) 1 127.0.0.1:6379> TTL key (integer) 1987 127.0.0.1:6379> PERSIST key (integer) 1 127.0.0.1:6379> ttl key (integer) -1

PERSIST命令就是PEXPIREAT命令的反操作,PERSIST命令在過期字典中查找給定的鍵,并接觸鍵和值(過期時間)在過期字典中的關(guān)聯(lián)。


過期數(shù)據(jù)的刪除策略

如果一個鍵過期了,那么它什么時候會被刪除呢?

這個問題有三種可能的答案,分別代表了三種不同的過期刪除策略:

  • 定時刪除:在設(shè)置鍵的過期時間時,創(chuàng)建一個定時器(timer),讓定時器在鍵過期時間來臨時,立刻執(zhí)行對鍵的刪除
  • 惰性刪除:放任鍵過期不管,但是每次從鍵空間中獲取鍵,都會檢查取得的鍵是否過期,如果過期,就刪除該鍵,否則就返回該鍵
  • 定期刪除:每隔一段時間,程序就對數(shù)據(jù)庫進行一次檢查,刪除里面的過期鍵,至于要刪除多少過期鍵,檢查多少個數(shù)據(jù)庫,如何檢查(因為數(shù)據(jù)量龐大時,不可能每個數(shù)據(jù)都進行檢查,要設(shè)計算法隨機檢查),則由算法決定。

以上三種刪除策略中,第一、三種屬于主動刪除,第二種則屬于被動刪除。


定時刪除

定時刪除策略對內(nèi)存是最友好的,屬于用時間換空間的一種策略,通過使用定時器,定時刪除鍵會保證過期鍵盡快被刪除,并釋放過期鍵占用的內(nèi)存。

另一方面,定時刪除策略的缺點是,它對CPU時間是最不友好的,在過期鍵數(shù)量龐大時,刪除過期鍵會占用一部分CPU處理時間,內(nèi)存不緊張但是CPU壓力大時,采用這種策略無疑會對服務(wù)器響應(yīng)時間和吞吐量產(chǎn)生相當(dāng)明顯的負面影響。

如果有大量的請求命令在等待服務(wù)器處理,而此時服務(wù)器當(dāng)前并不缺少內(nèi)存, 那么服務(wù)器應(yīng)該優(yōu)先將CPU占用分配給請求處理,而不是用在刪除過期鍵上。

除此之外,創(chuàng)建一個定時器需要用到Redis服務(wù)器中的時間事件,當(dāng)前時間事件的實現(xiàn)方式——無序鏈表,查找一個事件的時間復(fù)雜度為O(N),并不能高效地處理時間事件。

因此要讓服務(wù)器創(chuàng)建大量的定時器來執(zhí)行定時刪除策略,在現(xiàn)階段是不現(xiàn)實的。

惰性刪除

惰性刪除策略對CPU時間來說是最友好的:程序只會在取出鍵的時候才會對鍵進行過期檢查,這可以保證刪除鍵的操作只會在非做不可的情況下才進行,并且刪除的目標(biāo)僅限于當(dāng)前鍵,并不是大量地刪除,這個策略不會讓CPU在刪除無關(guān)的鍵上花費過多時間。

但是這種策略的確定是,它對內(nèi)存是最不友好的,如果數(shù)據(jù)庫中有相當(dāng)龐大數(shù)量的過期鍵并且占著大量的內(nèi)存,而這些鍵如果不被訪問到的話,它可能永遠不會被刪除,它所占據(jù)的內(nèi)存就永遠不會被釋放,服務(wù)器的內(nèi)存空間會被這些垃圾數(shù)據(jù)一直占據(jù)吞噬,服務(wù)器內(nèi)存也會越來越緊張。

舉個例子,對于一些和事件有關(guān)的數(shù)據(jù),比如日志,在某個時間點之后,它的訪問量就會大大減少,甚至不再訪問,如果這類過期數(shù)據(jù)一直堆積在數(shù)據(jù)庫中,那么造成的影響肯定是非常嚴(yán)重的。

定期刪除

從上述對兩種刪除策略的討論后得知,這兩種策略都是相當(dāng)極端的,在同樣極端的情況下會造成嚴(yán)重的后果。

而定期刪除策略是這兩種策略的一種整合和折中:

  • 定期刪除策略每隔一段時間執(zhí)行一次過期鍵刪除,并通過限制刪除操作執(zhí)行的時間和頻率來減少操作對CPU的影響
  • 除此之外,定期刪除過期鍵,有效地減少了過期鍵帶來的內(nèi)存浪費,避免了內(nèi)存泄漏的危險

定期刪除策略的難點是確定刪除操作執(zhí)行時長和頻率:

  • 如果刪除得太頻繁或執(zhí)行時間過長,定期刪除策略會退化成定時刪除策略
  • 如果刪除操作執(zhí)行的太少或者執(zhí)行時間過少,又會跟惰性刪除策略一樣出現(xiàn)內(nèi)存浪費的情況

因此,如果采用定期刪除策略,服務(wù)器必須根據(jù)情況,合理設(shè)置刪除操作的時間和執(zhí)行頻率。


五、持久化

Redis作為一個緩存組件為什么需要持久化?

Redis是一個基于內(nèi)存的數(shù)據(jù)庫,如果服務(wù)出現(xiàn)宕機的情況,數(shù)據(jù)將全部丟失,通常的解決方案是通過后端數(shù)據(jù)庫恢復(fù),但是后端數(shù)據(jù)庫如常見的Mysql數(shù)據(jù)庫有性能瓶頸,如果是大量丟失數(shù)據(jù)的恢復(fù),會對數(shù)據(jù)庫造成相當(dāng)大的壓力,開銷大效率低下,所以對于Redis實現(xiàn)數(shù)據(jù)持久化是相當(dāng)重要的,在出現(xiàn)數(shù)據(jù)丟失災(zāi)難中可以避免從后端數(shù)據(jù)庫恢復(fù)數(shù)據(jù)。

Redis提供了多種持久化方式

  • RDB持久化可以在指定的時間間隔里生成數(shù)據(jù)集的快照(point-in-time-snapshot)并保存到磁盤上,由于是某一時刻的快照,所以快照中的數(shù)據(jù)要早于或等于內(nèi)存中的數(shù)據(jù)
  • AOF持久化記錄服務(wù)器執(zhí)行的所有寫操作命令并以aof格式保存在磁盤上,并在服務(wù)器啟動時,通過執(zhí)行這些命令來還原數(shù)據(jù)集。AOF文件中的命令全部以Redis協(xié)議的格式來保存,新命令會追加到文件的末尾。Redis還可以使用BGREWRITEAOF命令來重寫文件,去除一些不影響最終數(shù)據(jù)結(jié)果的命令,這樣保證AOF文件保存的數(shù)據(jù)集占用內(nèi)存不會過大
  • Redis還可以同時使用AOF和RDB來實現(xiàn)持久化,這是Redis4.0版本推出的,官方也支持在實際開發(fā)中使用這種用法。簡單來說,內(nèi)存快照以一定的頻率執(zhí)行,在兩次快照之間,使用 AOF 日志記錄這期間的所有命令操作。在這種情況下,快照不用很頻繁地執(zhí)行,避免了頻繁的fork對主線程的影響,避免主線程阻塞,也不需要記錄所有操作了,因此不會出現(xiàn)文件過大的問題,也可以避免重寫開銷過大。
  • 虛擬內(nèi)存(VM)方式存儲,從Redis Version2.4開始,官方就明確表示不再使用,Version 3.2版本中更找不到關(guān)于虛擬內(nèi)存(VM)的任何配置范例,Redis的主要作者Salvatore Sanfilippo還專門寫了一篇論文,來反思Redis對虛擬內(nèi)存(VM)存儲技術(shù)的支持問題。

RDB持久化

RDB 就是 Redis DataBase 的縮寫,中文名為快照/內(nèi)存快照,RDB持久化是把當(dāng)前進程數(shù)據(jù)生成快照保存到磁盤上的過程

觸發(fā)方式

觸發(fā)RDB持久化的方式有兩種,分別是手動觸發(fā)和自動觸發(fā)

  • 手動觸發(fā)

    手動觸發(fā)分別對應(yīng)save和bgsave命令

    • save命令

      阻塞當(dāng)前Redis服務(wù)器主線程,直至RDB過程完成位置,當(dāng)數(shù)據(jù)占用內(nèi)存較大時,這個過程會造成相當(dāng)長時間的阻塞,并且如果出現(xiàn)服務(wù)器宕機,這個過程中寫入的所有數(shù)據(jù)都會丟失,線上環(huán)境中慎重使用!

    • bgsave命令

      Redis進程執(zhí)行fork操作時會創(chuàng)建子進程,RDB持久化過程由子進程負責(zé),完成后自動結(jié)束。這個過程中的保存工作全部由子進程完成,主進程無需進行任何磁盤的I/O操作。但是如果數(shù)據(jù)集相當(dāng)龐大時,fork可能會非常耗時,并且在CPU緊張時,服務(wù)器可能會在某時刻停止處理服務(wù)器,這種停止情況甚至可能長達整整一秒。

      bgsave命令執(zhí)行期間,客戶端的save命令和bgsave命令會被服務(wù)器拒絕,因為要防止保存命令之間產(chǎn)生競爭條件。

      其次bgsave和bgrewriteaof兩個命令不能同時執(zhí)行:

      • 如果bgsave命令正在執(zhí)行,那么客戶端bgrewriteaof命令會延遲到bgsave命令完成后執(zhí)行
      • 如果bgrewriteaof命令正在執(zhí)行,那么客戶端發(fā)送的bgsave命令會被拒絕

    bgsave流程圖如下圖所示:

    具體流程如下:

    • Redis客戶端執(zhí)行bgsave命令或者自動觸發(fā)bgsave命令
    • 主進程判斷是否已經(jīng)存在已經(jīng)在執(zhí)行的子進程,如果存在直接返回,父進程繼續(xù)處理命令請求,等待子進程完成快照任務(wù)后的信號;
    • 如果不存在正在執(zhí)行的子進程,那么就調(diào)用fork函數(shù)創(chuàng)建一個新的子進程進程持久化,fork過程是一個阻塞的,創(chuàng)建完子進程主進程即可執(zhí)行其他操作
    • 子進程先將數(shù)據(jù)寫到臨時的rdb文件,待快照數(shù)據(jù)寫入完成后再原子替換舊的rdb文件
    • 發(fā)送信號給主進程,通知主進程rdb持久化已經(jīng)完成,主進程更新相關(guān)的統(tǒng)計信息。
  • 自動觸發(fā)

    以下四種情況會自動觸發(fā)

    • redis.conf中配置的save m n,即在m秒中發(fā)生了n次數(shù)據(jù)修改,則會觸發(fā)bgsave命令生成rdb文件
    • 主從復(fù)制時,從節(jié)點要從主節(jié)點中進行全量復(fù)制也會觸發(fā)bgsave操作,生成當(dāng)時的快照發(fā)送到從節(jié)點,進行主從復(fù)制的數(shù)據(jù)同步
    • 執(zhí)行debug reload命令重新加載reids時也會觸發(fā)bgsave操作
    • 默認情況下執(zhí)行shutdown命令,如果沒有開啟AOF持久化,也會自動觸發(fā)bgsave操作

    redis.conf配置RDB持久化

    快照周期:內(nèi)存快照雖然可以通過技術(shù)人員手動執(zhí)行save和bgsave命令來執(zhí)行,但是實際生產(chǎn)環(huán)境下都會設(shè)置周期執(zhí)行條件

    • Redis中默認的周期設(shè)置

      # 周期性執(zhí)行條件的設(shè)置格式為 save <seconds> <changes># 默認的設(shè)置為: save 900 1 //900s中有一條key信息發(fā)生變化執(zhí)行快照 save 300 10 //300s中有十條key信息發(fā)生變化執(zhí)行快照 save 60 10000 //60s中有10000條key信息發(fā)生變化執(zhí)行快照# 以下設(shè)置方式為關(guān)閉RDB快照功能 save ""
    • 其他配置信息

      #文件名稱 dbfilename "dump-6379.rdb"#端口號 port 6379#文件保存路徑 dir /www/server/redis/data/redis_cache# 如果持久化出錯,主進程是否停止寫入 stop-writes-on-bgsave-error yes#是否壓縮 rdbcompression yes#導(dǎo)入時進行檢查 rdbchecksum yes

      dbfilename:RDB文件在磁盤上的名稱。

      dir:RDB文件的存儲路徑。默認設(shè)置為“./”,也就是Redis服務(wù)的主目錄。

      stop-writes-on-bgsave-error:上文提到的在快照進行過程中,主進程照樣可以接受客戶端的任何寫操作的特性,是指在快照操作正常的情況下。如果快照操作出現(xiàn)異常(例如操作系統(tǒng)用戶權(quán)限不夠、磁盤空間寫滿等等)時,Redis就會禁止寫操作。這個特性的主要目的是使運維人員在第一時間就發(fā)現(xiàn)Redis的運行錯誤,并進行解決。一些特定的場景下,您可能需要對這個特性進行配置,這時就可以調(diào)整這個參數(shù)項。該參數(shù)項默認情況下值為yes,如果要關(guān)閉這個特性,指定即使出現(xiàn)快照錯誤Redis一樣允許寫操作,則可以將該值更改為no。

      rdbcompression:該屬性將在字符串類型的數(shù)據(jù)被快照到磁盤文件時,啟用LZF壓縮算法。Redis官方的建議是請保持該選項設(shè)置為yes,因為“it’s almost always a win”。

      rdbchecksum:從RDB快照功能的version 5 版本開始,一個64位的CRC冗余校驗編碼會被放置在RDB文件的末尾,以便對整個RDB文件的完整性進行驗證。這個功能大概會多損失10%左右的性能,但獲得了更高的數(shù)據(jù)可靠性。所以如果您的Redis服務(wù)需要追求極致的性能,就可以將這個選項設(shè)置為no。


    深入理解RDB

    • 在生產(chǎn)環(huán)境中為Redis開辟的內(nèi)存區(qū)域都比較大,那么將內(nèi)存中的數(shù)據(jù)同步到硬盤的過程可能就會持續(xù)較長的時間,而實際情況中,這段時間都會收到數(shù)據(jù)的讀寫請求,那么如何保持數(shù)據(jù)一致性呢?

      RDB的核心思想是Copy-On-Write,來保證進行快照操作的時間里需要壓縮寫入磁盤的的數(shù)據(jù)在內(nèi)存中不會發(fā)生變化。正常的快照操作中,一方面Redis主進程會fork一個快照子進程來做持久化,保持Redis不會停止對客戶端的數(shù)據(jù)請求處理,另一方面這段時間內(nèi)的數(shù)據(jù)變化會以副本的方式放在一個新的內(nèi)存區(qū)域,與此同時bgsave進程會將該副本寫入rdb文件,等待快照操作執(zhí)行完畢后才會同步到原來的數(shù)據(jù)存儲內(nèi)存區(qū)域。

    • 在進行快照操作的這段時間里,如果服務(wù)器發(fā)生宕機怎么辦?

      快照操作過程中不會影響上一次的備份數(shù)據(jù)rdb文件,Redis在進行快照操作的時候會創(chuàng)建一個臨時文件進行數(shù)據(jù)寫入,操作成功之后才會將這個臨時文件覆蓋掉上一次的備份文件。而如果快照操作中服務(wù)器發(fā)生宕機,服務(wù)器重啟時將以上次的備份文件作為數(shù)據(jù)恢復(fù)參考,而服務(wù)器宕機之前更新的數(shù)據(jù)則無法恢復(fù)會丟失。

    • 參照上述問題,服務(wù)器發(fā)生宕機會導(dǎo)致數(shù)據(jù)丟失,那么可以一秒做一次快照而減少數(shù)據(jù)丟失量嗎?

      對于快照來說,所謂的“連拍”就是連續(xù)地進行快照持久化,這樣做,快照的間隔時間很短,確實可以減少丟失數(shù)據(jù)的數(shù)量。

      那么快照間隔時間是不是可以縮短到很短呢?因為每次快照操作都是由bgsave子進程去完成的,并不會影響主進程對客戶端請求的處理,也不會阻塞主進程。

      其實這種想法是錯誤的,雖然bgsave子進程執(zhí)行時不會阻塞主進程,但是如果頻繁地執(zhí)行全量快照,也會帶來其他的開銷:

      • 頻繁將全量數(shù)據(jù)寫入磁盤,這樣大量數(shù)據(jù)的IO操作本身就會對磁盤造成相當(dāng)大的壓力,并且多個快照競爭有限的磁盤寬帶,前一個快照還沒執(zhí)行完,后一個快照又開始進行了,從而使得快照操作越來越慢,但是新的快照又開始進行了,從而導(dǎo)致惡性循環(huán)。
      • bgsave子進程需要通過fork操作會從主進程中創(chuàng)建出來,雖然子進程創(chuàng)建過后不會阻塞主進程,但是fork子進程這個操作本身是會阻塞主進程的,而且如果主進程已經(jīng)被大量請求占用,而此時又頻繁地創(chuàng)建子進程,這樣也是會阻塞主進程的,只是方式與save有所不同,但本質(zhì)都是一樣的,這樣又回到了原來的問題。

RDB的優(yōu)缺點

  • 優(yōu)點
    • RDB文件是某個時間節(jié)點的快照,默認使用LZF算法進行壓縮,壓縮后的文件體積遠小于于內(nèi)存大小,適合備份、全量復(fù)制等場景。
    • Redis加載RDB文件恢復(fù)數(shù)據(jù)遠遠快于AOF,因此RDB非常適用于災(zāi)難恢復(fù)(Disaster Recovery)。
  • 缺點
    • RDB方式實時性不夠,無法做到秒級的數(shù)據(jù)持久化,雖然Redis允許設(shè)置不同的保存條件來控制保存RDB文件的頻率,但是RDB文件需要做的是全量復(fù)制需要保存整個數(shù)據(jù)集,所以它并不是一個輕松的過程,當(dāng)服務(wù)器故障發(fā)生宕機,可能因此丟失相當(dāng)多的數(shù)據(jù)。
    • 每次進行快照時,主進程需要fork()出一個子進程,由子進程來完成持久化工作。在數(shù)據(jù)集比較龐大時,fork()過程都可能會非常耗時,造成服務(wù)器某時刻停止對服務(wù)器的請求處理。

AOF持久化

Redis是寫后日志,先執(zhí)行命令,將數(shù)據(jù)寫入內(nèi)存,再記錄日志,日志里記錄的是Redis收到的每一條命令

為什么采用寫后日志?

  • 避免額外的檢查開銷:Redis在想AOF里面記錄日志時,并不會先去執(zhí)行對命令的語法檢測,所以先記錄日志再執(zhí)行命令的話,日志中有可能記錄下錯誤的命令,當(dāng)Redis利用日志恢復(fù)數(shù)據(jù)時,可能就會出錯。
  • 不會阻塞當(dāng)前的寫操作

當(dāng)也同樣存在風(fēng)險:

  • 如果命令執(zhí)行完即將寫入日志時,服務(wù)器宕機,這些數(shù)據(jù)操作未能寫入日志,則這些數(shù)據(jù)將丟失。

實現(xiàn)AOF

AOF日志記錄Redis的每個寫操作,因此不需要觸發(fā),具體步驟分為命令追加append,文件寫入write和文件同步sync以及文件重寫rewrite

  • 命令追加append

    當(dāng)AOF持久化功能開啟后,服務(wù)器在執(zhí)行完一個寫命令之后,會將寫命令追加到服務(wù)器的aof_buf緩沖區(qū),而不是直接寫入文件,主要是避免了每次寫命令直接寫入磁盤,導(dǎo)致磁盤IO操作成為Redis負載的瓶頸。

    命令追加的格式是Redis命令請求的協(xié)議格式,它是一種純文本格式,有兼容性好,可讀性強,容易處理等優(yōu)點。

  • 文件寫入write和文件同步sync

    Redis提供了多種AOF緩存區(qū)的文件同步策略,策略涉及到操作系統(tǒng)的write函數(shù)和fsync函數(shù),為了提高文件寫入效率,現(xiàn)代操作系統(tǒng)中,當(dāng)用戶調(diào)用write函數(shù)將數(shù)據(jù)寫入文件時,操作系統(tǒng)常會將數(shù)據(jù)暫存到一個內(nèi)存緩沖區(qū)中,當(dāng)緩沖區(qū)被存滿或者達到一定時限后才會寫入磁盤中,這樣的操作雖然提高了效率但是也存在安全隱患,如果服務(wù)器宕機,內(nèi)存緩沖區(qū)的數(shù)據(jù)將全部丟失。因此系統(tǒng)里也提供了fsync函數(shù)等同步函數(shù),可以強制將緩沖區(qū)的數(shù)據(jù)寫入磁盤,以此保證數(shù)據(jù)的安全性。

    AOF持久化中aof_buf緩存區(qū)的文件同步策略由appendfsync參數(shù)控制,各個參數(shù)含義如下:

    • always:命令寫入aof_buf后立刻調(diào)用系統(tǒng)的fsync函數(shù)同步到AOF文件,fsync操作完成后線程返回。這種情況下,aof_buf緩沖區(qū)已然失去作用,每次寫命令都要同步到AOF文件中,磁盤IO成為性能瓶頸,嚴(yán)重降低了Redis的性能;即便是用固態(tài)硬盤,每秒也只能處理幾萬個請求,而且會大大降低硬盤的壽命。
    • no:命令寫入aof_buf后調(diào)用系統(tǒng)write操作,不對AOF文件進行fsync同步,同步操作由操作系統(tǒng)負責(zé),通常同步周期為30s。這種情況下,同步操作完全由系統(tǒng)控制,文件的同步時間變得不可控,而且緩沖區(qū)堆積的數(shù)據(jù)會很多,安全性無法得到保證。
    • everysec:命令寫入aof_buf后調(diào)用系統(tǒng)的write,write完成后線程返回;fsync同步文件操作由專門的線程每秒調(diào)用一次。everysec是前述兩種策略的折中,是性能和數(shù)據(jù)安全性的平衡,實際開發(fā)中我們會優(yōu)先選擇這種策略。

  • 文件重寫rewrite

    Redis執(zhí)行的寫命令越來越大,AOF文件也會越來越大,過大的文件會影響服務(wù)器的運行,也會使文件恢復(fù)時用時過長。

    文件重寫是指定期對AOF文件進行重寫,減少AOF文件的體積。Redis通過創(chuàng)建一個新的AOF文件來替換現(xiàn)有的AOF,新舊兩個AOF文件保存的數(shù)據(jù)相同,但新AOF文件沒有了冗余命令。

    文件重寫之所以可以壓縮AOF文件原因在于:

    • 過期的數(shù)據(jù)不再寫入文件

    • 無效的命令不再寫入文件:如有些數(shù)據(jù)被重復(fù)設(shè)值,有些數(shù)據(jù)被刪除了等待

    • 多條命令可以合并為一個:如sadd key value1,sadd key value2,sadd key value3可以合并為sadd key value1 value2 value3,不過為了防止單條命令過大造成客戶端緩沖區(qū)溢出,對于list,set,hash,zset類型的key,并不一定只使用一條命令,而是以某個常量為界將命令拆分為多條。這個常量在redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD中定義,不可更改,3.0版本中值是64。

      #define REDIS_AOF_REWRITE_ITEMS_PRE_CMD 64

配置文件

#文件名稱 appendfilename "appendonly-6379.aof"# appendonly參數(shù)開啟AOF持久化 appendonly yes# 同步策略 # appendfsync always appendfsync everysec # appendfsync no# aof重寫期間是否同步 no-appendfsync-on-rewrite no# 重寫觸發(fā)配置 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb# 加載aof出錯如何處理 aof-load-truncated yes# 文件重寫策略 aof-rewrite-incremental-fsync yes

要特別注意:no-appendfsync-on-rewrite:always和everysec的設(shè)置會使真正的I/O操作高頻度的出現(xiàn),甚至?xí)霈F(xiàn)長時間的卡頓情況,這個問題出現(xiàn)在操作系統(tǒng)層面上,所有靠工作在操作系統(tǒng)之上的Redis是沒法解決的。為了盡量緩解這個情況,Redis提供了這個設(shè)置項,保證在完成fsync函數(shù)調(diào)用時,不會將這段時間內(nèi)發(fā)生的命令操作放入操作系統(tǒng)的Page Cache(這段時間Redis還在接受客戶端的各種寫操作命令)。


重寫機制

  • AOF重寫會阻塞嗎?

    AOF重寫過程是由后臺進程bgrewriteaof完成的,主線程需要fork出子進程,這個過程會占用主進程內(nèi)存,所以如果頻繁進行重寫是會造成主線程阻塞的。

  • AOF日志何時會重寫?

    auto-aof-rewrite-min-size:表示運行AOF重寫文件的最小大小,默認為64MB

    auto-aof-rewrite-percentage:這個值的計算方式是,當(dāng)前aof文件大小比上次重寫后的aof文件大小的差值與上次重寫后aof文件大小的比值;percentage = (last - now) / last

  • 重寫日志時,有新的數(shù)據(jù)寫入怎么做?

    關(guān)于文件重寫,要特別注意兩點:

    • 重寫由子進程進行
    • 重寫階段Redis執(zhí)行的寫命令需要追加到新的AOF文件中,為此Redis引入了aof_rewrite_buf緩存區(qū)

    ?

    ? 對比上圖,文件重寫的流程如下:

    • 1>執(zhí)行AOF請求

      如果當(dāng)前進程正在執(zhí)行bgrewriteaof,則返回請求,請求不執(zhí)行

      如果當(dāng)前進程正在執(zhí)行bgsave,則重寫命令延遲到bgsave完成之后進行

    • 2>父進程fork創(chuàng)建子進程,開銷相當(dāng)于bgsave創(chuàng)建子進程的開銷

    • 3.1>主進程fork操作完成后繼續(xù)相應(yīng)其他命令

      所有修改命令依然寫入aof_buf緩沖區(qū)根據(jù)appendfsync參數(shù)采取策略同步到磁盤,保證原有數(shù)據(jù)同步

    • 3.2>fork操作運用寫時復(fù)制基數(shù),子進程只能共享fork操作時的內(nèi)存數(shù)據(jù)

      由于父進程依然響應(yīng)命令,會有新的數(shù)據(jù)寫入,Redis使用aof_rewrite_buf重寫緩沖區(qū)來保存這部分數(shù)據(jù),防止新生成的文件生成期間丟失這部分數(shù)據(jù)。

    • 4>子進程按照命令合并規(guī)則寫入到新的AOF文件

      每次批量寫入的硬盤數(shù)據(jù)量由aof-rewrite-incremental-fsync參數(shù)控制,默認為32MB,防止單次寫入數(shù)據(jù)造成磁盤IO阻塞

    • 5.1>新的AOF文件寫入完成后,發(fā)送信號給主進程,通知主進程更新統(tǒng)計信息

    • 5.2>父進程將aof_rewrite_buf重寫緩沖區(qū)新寫入的數(shù)據(jù)更新到新的AOF文件中

    • 5.3>使用新的AOF文件代替舊的AOF文件

    總結(jié)

    1.父進程fork子進程完成AOF文件重寫

    2.父進程將新寫入的數(shù)據(jù)保存到aof_buf和aof_rewrite_buf緩沖區(qū),子進程重寫完畢后從aof_rewrite_buf緩存區(qū)將新的數(shù)據(jù)寫入新的AOF文件

    3.新的AOF文件替代舊的AOF文件

  • 為什么AOF重寫不復(fù)用舊的AOF文件?

    1.父子進程寫同一個文件會產(chǎn)生競爭關(guān)系,影響了父進程的性能

    2.如果AOF重寫失敗,會污染原本的AOF文件,無法再作為數(shù)據(jù)恢復(fù)的參考


重啟加載

AOF和RDB文件都可以用于服務(wù)器重啟時的數(shù)據(jù)恢復(fù)。下面展示Redis持久化文件加載流程:

流程說明:

1)AOF持久化開啟且存在AOF文件時,優(yōu)先加載AOF文件。

2)AOF關(guān)閉或者AOF文件不存在時,加載RDB文件。

3)加載AOF/RDB文件成功后,Redis啟動成功。

4)AOF/RDB文件存在錯誤時,Redis啟動失敗并打印錯誤信息。

那么為什么會優(yōu)先加載AOF呢?因為AOF保存的數(shù)據(jù)更完整,通過上面的分析我們知道AOF基本上最多損失1s的數(shù)據(jù)。


六、事務(wù)

Redis通過MULTI,EXEC,WATCH,DISCARD等命令來實現(xiàn)事務(wù)Transaction功能。事務(wù)體哦概念股了一種將多個命令請求打包,然后一次性按順序地執(zhí)行多個命令的機制,并且事務(wù)執(zhí)行期間,服務(wù)器不會中斷事務(wù)而去改去執(zhí)行其他客戶端的命令請求,它會將事務(wù)中的所有命令都執(zhí)行完畢,然后 才去執(zhí)行其他客戶端的命令請求。

以下是一個事務(wù)執(zhí)行的過程,該事務(wù)從一個MULTI開始,接著將多個操作命令放入事務(wù)中,然后最后EXEC將事務(wù)提交給服務(wù)器執(zhí)行。

127.0.0.1:6379> MULTI OK 127.0.0.1:6379(TX)> set key value QUEUED 127.0.0.1:6379(TX)> get key QUEUED 127.0.0.1:6379(TX)> set key otherValue QUEUED 127.0.0.1:6379(TX)> get key QUEUED 127.0.0.1:6379(TX)> EXEC 1) OK 2) "value" 3) OK 4) "otherValue"

特別注意

  • 事務(wù)是一個單獨的隔離操作:事務(wù)中的所有命令都會序列化,按順序地執(zhí)行,事務(wù)執(zhí)行過程中也不會被其他客戶端發(fā)來的命令請求打斷
  • 事務(wù)是一個原子操作:事務(wù)中的命令要么全部被執(zhí)行,要么全部不執(zhí)行

EXEC命令復(fù)制觸發(fā)并執(zhí)行事務(wù)中的所有命令

  • 如果客戶端使用MULTI開啟一個事務(wù)后,因為斷線導(dǎo)致EXEC沒有執(zhí)行,那么事務(wù)中的所有命令都不會被執(zhí)行
  • 而如果EXEC成功執(zhí)行,那么事務(wù)中所有命令都會被執(zhí)行

當(dāng)使用AOF持久化時,Redis會使用單個write命令將事務(wù)寫入磁盤中,如果Redis服務(wù)器宕機,那么只有部分事務(wù)命令會成功寫入磁盤。

如果Redis重新啟動發(fā)現(xiàn)了AOF文件有這樣的問題,那么它會退出并匯報一個錯誤。

使用redis-check-aof可以修復(fù)這一問題,它會移除AOF文件中不完整事務(wù)信息,以保證服務(wù)器順利啟動。


放棄事務(wù)

當(dāng)執(zhí)行DISCARD命令時,事務(wù)會被放棄,事務(wù)隊列清空,并且客戶端從事務(wù)狀態(tài)退出:

127.0.0.1:6379> MULTI OK 127.0.0.1:6379(TX)> set key value QUEUED 127.0.0.1:6379(TX)> DISCARD OK 127.0.0.1:6379> get key "otherValue"

WATCH命令

WATCH命令是一個樂觀鎖Optimistic Locking,它可以在EXEC命令執(zhí)行之前,監(jiān)視任意順序的數(shù)據(jù)庫鍵,并在執(zhí)行EXEC命令時,檢查被監(jiān)視的值是否已經(jīng)被修改,如果是,服務(wù)器將拒絕執(zhí)行事務(wù),并向客戶端代表事務(wù)執(zhí)行已經(jīng)失敗的回復(fù)。

redis> set key oldValue OK redis> watch key OK redis> MULTI OK redis(TX)> set key newValue QUEUED redis(TX)> EXEC (nil) //事務(wù)失敗

為什么上述事務(wù)失敗了呢?

時間客戶端A客戶端B
T1SET key oldValue
T2WATCH key
T3MULTI
T4SET key newValue
T5SET key otherValue
T6EXEC

由上圖所知,T5時刻,在客戶端A執(zhí)行EXEC命令前,key值已經(jīng)被客戶端B修改,此時服務(wù)器發(fā)現(xiàn)被監(jiān)視的鍵key的值已經(jīng)被修改了,所以服務(wù)器會拒絕執(zhí)行客戶端A的事務(wù),并向客戶端A返回空回復(fù)。

上述客戶端A的事務(wù)是不安全的,服務(wù)器會拒絕執(zhí)行客戶端提交的不安全的事務(wù),以保證數(shù)據(jù)的一致性。

上述這種形式的鎖叫做樂觀鎖,是一種強大的鎖機制。

同時可以使用UNWATCH命令取消對所有鍵key的監(jiān)視,注意!不是取消對單個或著幾個鍵的監(jiān)視,是取消所有。


事務(wù)的ACID性質(zhì)

  • 原子性

    事務(wù)中具有原子性是指:事務(wù)中的多個操作當(dāng)作一個整體來執(zhí)行,服務(wù)器要么執(zhí)行所有操作,要么一個操作都不執(zhí)行。

    以下展示的是一個成功執(zhí)行的事務(wù),所有命令都被執(zhí)行:

    127.0.0.1:6379> MULTI OK 127.0.0.1:6379(TX)> get key QUEUED 127.0.0.1:6379(TX)> set key value QUEUED 127.0.0.1:6379(TX)> get key QUEUED 127.0.0.1:6379(TX)> EXEC 1) (nil) 2) OK 3) "value"

    與此相反,以下展示了一個執(zhí)行錯誤的事務(wù),這個事務(wù)因為命令入隊時錯誤而被服務(wù)器拒絕執(zhí)行,事務(wù)中的所有命令都不會被執(zhí)行:

    127.0.0.1:6379> set key value OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379(TX)> set key newValue QUEUED 127.0.0.1:6379(TX)> gett key (error) ERR unknown command `gett`, with args beginning with: `key`, 127.0.0.1:6379(TX)> EXEC (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get key "value"

    Redis的事務(wù)與傳統(tǒng)的關(guān)系型數(shù)據(jù)庫事務(wù)的最大區(qū)別就在于,Redis不支持事務(wù)回滾機制rollback,即使隊列中某個命令執(zhí)行期間出現(xiàn)了錯誤,整個事務(wù)也不會回滾,而是繼續(xù)執(zhí)行下去。

    在下面的例子中,SADD命令執(zhí)行期間發(fā)生了錯誤,后續(xù)的命令也會繼續(xù)執(zhí)行下去,而且之前執(zhí)行的事務(wù)也不會受到影響

    127.0.0.1:6379> MULTI OK 127.0.0.1:6379(TX)> set key newValue QUEUED 127.0.0.1:6379(TX)> SADD key ERROR QUEUED 127.0.0.1:6379(TX)> get key QUEUED 127.0.0.1:6379(TX)> EXEC 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) "newValue"

    Redis的作者在事務(wù)功能的文檔中解釋說,不支持事務(wù)回滾是因為這種復(fù)雜的功能與Redis追求簡單高效的設(shè)計初衷不相符,并且他認為Redis事務(wù)執(zhí)行時的錯誤都是因為程序錯誤產(chǎn)生的,這種錯誤通常只會出現(xiàn)在開發(fā)環(huán)境中,而很少在實際的生成環(huán)境中出現(xiàn),所以他認為沒有必要為Redis開發(fā)事務(wù)回滾功能。


  • 一致性

    事務(wù)具有一致性是指:如果數(shù)據(jù)庫在執(zhí)行事務(wù)之前是一致的,那么事務(wù)執(zhí)行之后無論事務(wù)是否執(zhí)行成功,數(shù)據(jù)庫也應(yīng)該仍然是一致的。

    一致性是指數(shù)據(jù)庫符合數(shù)據(jù)庫本身的定義和要求,沒有包含非法或者無效的錯誤數(shù)據(jù)

    Redis通過謹慎的錯誤檢測和簡單的設(shè)計來保證事務(wù)的一致性。

  • 隔離性

    事務(wù)的隔離性指的是,即使數(shù)據(jù)庫中有多個事務(wù)并發(fā)執(zhí)行,各個事務(wù)也不會互相影響,并且在并發(fā)情況下執(zhí)行的事務(wù)和串行執(zhí)行的事務(wù)產(chǎn)生的結(jié)果完全相同。

    因為Redis使用單線程的方式來執(zhí)行事務(wù),并且服務(wù)器保證,在事務(wù)執(zhí)行期間,其他客戶端不會中斷該事務(wù),因此Redis的事務(wù)總是以串行的方式執(zhí)行,并且事務(wù)也是具有隔離性的。

  • 耐久性

    事務(wù)的耐久性指定是,當(dāng)一個事務(wù)執(zhí)行完畢時,執(zhí)行這個事務(wù)所得的結(jié)果已經(jīng)被保存到永久性存儲介質(zhì)中,即使服務(wù)器執(zhí)行完事務(wù)后發(fā)生宕機,執(zhí)行事務(wù)的結(jié)果也不會丟失。

    因為Redis的事務(wù)不過是簡單地用隊列包裹起了一組Redis命令,使之成為一個整體,Redis并沒有為事務(wù)提供任何額外的持久化功能,所以Redis事務(wù)中的耐久性是基于Redis所使用的持久化模式。

    • 當(dāng)服務(wù)器在無持久化的內(nèi)存模式運作時,事務(wù)不具有耐久性,一旦服務(wù)器停機,包括事務(wù)數(shù)據(jù)在內(nèi)的所有服務(wù)器數(shù)據(jù)都將丟失。
    • 當(dāng)服務(wù)器在RDB持久化模式運作下,服務(wù)器只會在特定的保存條件被滿足時,才會執(zhí)行bgsave命令,對數(shù)據(jù)庫進行保存操作,并且異步執(zhí)行的bgsave不能保證事務(wù)數(shù)據(jù)被第一時間保存的硬盤里面,因此RDB持久化模式下的事務(wù)也不具有耐久性
    • 當(dāng)服務(wù)器運行在AOF持久化模式下,并且appendfsync參數(shù)的值為always時,程序總會執(zhí)行命令之后調(diào)用同步sync函數(shù),將數(shù)據(jù)真正保存到硬盤里面,這種配置下的事務(wù)是具有耐久性
    • 當(dāng)服務(wù)器運行在AOF持久化模式下,并且appendfsync參數(shù)的值為everysec時,程序會每秒同步一次命令數(shù)據(jù)到硬盤中,如果服務(wù)器發(fā)生宕機,可能會造成事務(wù)數(shù)據(jù)丟失,這種配置下的事務(wù)也不具有耐久性
    • 當(dāng)服務(wù)器運行在AOF持久化模式下,并且appendfsync參數(shù)的值為no時,程序會交由操作系統(tǒng)為決定何時,將命令數(shù)據(jù)同步到硬盤中,因為事務(wù)數(shù)據(jù)可能在等待同步中丟失,這種配置下的事務(wù)也不具有耐久性

七、主從復(fù)制

在Redis中,用戶可以通過執(zhí)行SLAVEOF命令或者設(shè)置slaveof配置,讓一個服務(wù)器去復(fù)制(replicate)另一個服務(wù)器,被復(fù)制的的服務(wù)器叫主服務(wù)器(master),對主服務(wù)器進行復(fù)制的服務(wù)器叫從服務(wù)器(slave)。

進行復(fù)制的主從服務(wù)器雙方的數(shù)據(jù)庫都將保存相同的數(shù)據(jù),概念上將這種現(xiàn)象稱為“數(shù)據(jù)庫狀態(tài)一致”。


舊版復(fù)制功能的實現(xiàn)

Redis2.8版本之前的復(fù)制功能分為同步(sync)和命令傳播(command propagate)兩個操作。

  • 同步用于將從服務(wù)器的數(shù)據(jù)庫狀態(tài)更新至主服務(wù)器當(dāng)前所處的數(shù)據(jù)庫狀態(tài)
  • 命令傳播用于當(dāng)主服務(wù)器數(shù)據(jù)庫的狀態(tài)被修改,導(dǎo)致主從服務(wù)器數(shù)據(jù)庫狀態(tài)不一致,讓主從服務(wù)器數(shù)據(jù)庫回到一致狀態(tài)。

復(fù)制

當(dāng)從服務(wù)器復(fù)制主服務(wù)器時,需要先進行執(zhí)行同步操作,從服務(wù)器需要通過向主服務(wù)器發(fā)送SYNC命令來完成,以下是SYNC命令的執(zhí)行步驟:

  • 從服務(wù)器向主服務(wù)器發(fā)送SYNC命令

  • 收到SYNC命令的主服務(wù)器執(zhí)行BGSAVE命令,在后于生成RDB文件,并使用一個緩沖區(qū)記錄從現(xiàn)在開始執(zhí)行的所有寫命令。

  • 當(dāng)主服務(wù)器的BGSAVE命令執(zhí)行完畢時,主服務(wù)器會將BGSAVE生成的RDB文件發(fā)送給從服務(wù)器,從服務(wù)器接收并載入RDB文件,將自己數(shù)據(jù)庫狀態(tài)更新至主服務(wù)器執(zhí)行BGSAVE命令時的數(shù)據(jù)庫狀態(tài)。

  • 主服務(wù)器將記錄在緩沖區(qū)里面所有寫命令發(fā)送給從服務(wù)器,從服務(wù)器執(zhí)行這些寫命令,將自己的數(shù)據(jù)庫狀態(tài)更新至主服務(wù)器數(shù)據(jù)庫當(dāng)前所處的狀態(tài)。

    同步完整過程:

命令傳播

在同步操作執(zhí)行完畢之后,主從服務(wù)器兩者的數(shù)據(jù)庫達到一致,每當(dāng)主服務(wù)器執(zhí)行客戶端發(fā)送的寫入命令時,數(shù)據(jù)庫就會被修改,主從服務(wù)器數(shù)據(jù)庫狀態(tài)不一致。

為了讓主從服務(wù)器回到一致狀態(tài),主服務(wù)器需要對從服務(wù)器執(zhí)行命令傳播操作:主服務(wù)器會將自己執(zhí)行的寫命令,發(fā)送給從服務(wù)器執(zhí)行相同的寫命令之后,主從服務(wù)器再次回到一致狀態(tài)。


舊版復(fù)制的缺陷:

斷線后重新復(fù)制:處于命令傳播階段的主從服務(wù)器因為網(wǎng)絡(luò)問題斷開了連接終止了復(fù)制,但從服務(wù)器重寫連接主服務(wù)器后會重新進行復(fù)制,但是這種復(fù)制是全量復(fù)制,開銷相當(dāng)大。主從服務(wù)器斷線期間,主服務(wù)器執(zhí)行的寫命令可能多或少,但是服務(wù)器為了彌補這一小部分缺失的數(shù)據(jù),就要主從服務(wù)器重寫執(zhí)行一次SYNC,這種做法無疑是低效的。


新版復(fù)制功能的實現(xiàn)

為了解決舊版復(fù)制在處理斷線重復(fù)值情況的低效率問題,Redis從2.8版本之后推出了PSYNC命令來代替SYNC命令執(zhí)行復(fù)制操作。

PSYNC命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)兩種模式:

  • 完整重同步用于處理初次復(fù)制情況,完整重同步的執(zhí)行步驟和SYNC命令的執(zhí)行步驟基本一樣,通過主服務(wù)器創(chuàng)建并發(fā)送RDB文件,以及向從服務(wù)器發(fā)送保存在緩沖區(qū)里面的寫命令來進行同步
  • 部分重同步則用于處理斷線后重復(fù)值的情況:當(dāng)從服務(wù)器斷線后重新連接主服務(wù)器,只復(fù)制斷開期間主服務(wù)器寫入的數(shù)據(jù)即可,不需要再做一次完全重同步。


部分重同步

部分重同步功能由以下三個部分構(gòu)成:

  • 主從服務(wù)器的復(fù)制偏移量(replication offset)
  • 主服務(wù)器的復(fù)制積壓緩沖區(qū)(replication backlog)
  • 服務(wù)器的運行ID(run ID)

復(fù)制偏移量

執(zhí)行復(fù)制的主從服務(wù)器都會分別維護一個復(fù)制偏移量:

  • 主服務(wù)器每次向從服務(wù)器傳播N個字節(jié)的數(shù)據(jù)時,就會將自己的復(fù)制偏移量加入N
  • 從服務(wù)器收到主服務(wù)器傳播來的N個字節(jié)的數(shù)據(jù)時,也會將自己的復(fù)制偏移量加入N

通過對比主從服務(wù)器的復(fù)制偏移量,程序可以很容易地知道主從服務(wù)器是否處于一致。


復(fù)制積壓緩沖區(qū)

復(fù)制積壓緩沖區(qū)是由主服務(wù)器維護的一個固定長度(fixed-size)先進先出(FIFO)隊列組成,默認存儲大小為1MB。

當(dāng)主服務(wù)器進行命令傳播時,不僅會將寫命令發(fā)送給所有從服務(wù)器,還會將寫命令入隊到復(fù)制積壓緩沖區(qū)。

因此,主服務(wù)器的復(fù)制積壓緩沖區(qū)里面會保存著一部分最近播放的寫命令,并且復(fù)制緩沖區(qū)會為隊列中的每個字節(jié)記錄相應(yīng)的復(fù)制偏移量。當(dāng)從服務(wù)器重寫連接上主服務(wù)器之后,從服務(wù)器會通過PSYNC將自己的復(fù)制偏移量offset發(fā)送給主服務(wù)器,主服務(wù)器會根據(jù)這個復(fù)制偏移量來決定對從服務(wù)器執(zhí)行何種同步操作:

  • 如果offset偏移量之后的數(shù)據(jù)仍然存在于復(fù)制積壓緩沖區(qū)中,那么主服務(wù)器將對從服務(wù)器進行部分重同步操作。
  • 相反,如果offset偏移量之后的數(shù)據(jù)已經(jīng)不存在于復(fù)制積壓緩沖區(qū),那么主服務(wù)器將對從服務(wù)器執(zhí)行完整重同步操作。

根據(jù)需要調(diào)整復(fù)制積壓緩沖區(qū)的大小

Redis為復(fù)制積壓緩沖區(qū)設(shè)置的默認大小為1MB,如果主服務(wù)器需要執(zhí)行大量的寫操作,或者從服務(wù)器斷線后重連接的時間較長,那么這個值可能并不合適,這個值設(shè)置不得當(dāng),可能會讓從服務(wù)器重新連接主服務(wù)器后,讓主服務(wù)器判定從服務(wù)器需要進行完整重同步,那么PSYNC命令的部分重同步模式就不能正常發(fā)揮作用。

復(fù)制積壓緩沖區(qū)的最小大小可以根據(jù)公式reconnect_second*write_size_per_second來估算,reconnect_second為重新連接所需要時間,write_size_per_second為主服務(wù)器平均每秒寫入的命令數(shù)據(jù)量,然后在此基礎(chǔ)上將這個大小翻倍,即可滿足大部分斷線情況下重連后都能用部分重同步。

可參考:repl-backlog-size = 2*reconnect_second*write_size_per_second


服務(wù)器運行ID

  • 每個Redis服務(wù)器無論主從都會自己的運行ID

  • 運行ID在服務(wù)器啟動時,自動生成,由40個隨機的十六進制字符組成。

    當(dāng)從服務(wù)器對主服務(wù)器進行初次復(fù)制時,主服務(wù)器會將自己的運行ID傳送給從服務(wù)器,而從服務(wù)器也會將這個運行ID保存起來。

    當(dāng)從服務(wù)器斷線重連上一個主服務(wù)器時,會向主服務(wù)器將之前保存的運行ID:

    • 如果從服務(wù)器保存的運行ID與當(dāng)前連接的主服務(wù)器運行ID相同,則說明之前連接的就是這個服務(wù)器,主服務(wù)器可以嘗試執(zhí)行部分重同步。
    • 反之,則說明之前連接的主服務(wù)器不是當(dāng)前連接的服務(wù)器,主服務(wù)器會對從服務(wù)器執(zhí)行完整同步操作。

PSYNC命令的實現(xiàn)

PSYNC命令的調(diào)用有兩種方式:

  • 如果從服務(wù)器以前沒有復(fù)制過任何服務(wù)器,或者之前執(zhí)行過SLAVEOF no one命令,那么從服務(wù)器在開始依次新的復(fù)制時將對主服務(wù)器發(fā)送PSYNC ? -1命令,主動請求主服務(wù)器進行完整重同步。
  • 相反,從服務(wù)器已經(jīng)復(fù)制過某個主服務(wù)器的數(shù)據(jù),那么從服務(wù)器在開始一次新的復(fù)制前,會向主服務(wù)器發(fā)送PSYNC <runid> <offset>命令,runid是上次復(fù)制的主服務(wù)器的運行ID,而offset是當(dāng)前從服務(wù)器的復(fù)制偏移量,接受到這個命令的主服務(wù)器會根據(jù)這兩個參數(shù)來決定對從服務(wù)器執(zhí)行哪種同步操作。

根據(jù)情況,收到PSYNC的主服務(wù)器回向從服務(wù)器返回以下的三種回復(fù)中的其中一種:

  • 如果主服務(wù)器返回+FULLRESYNC <runid> <offset>,表示主服務(wù)器將與從服務(wù)器執(zhí)行完整重同步操作,runid是這個主服務(wù)器的運行ID,而從服務(wù)器會將這個運行ID保存起來,在下次發(fā)送PSYNC命令時使用,而offset是主服務(wù)器當(dāng)前的復(fù)制偏移量,從服務(wù)器會將這個值作為自己的初始化偏移量
  • 如果主服務(wù)器返回+CONTINUE,那么表示主服務(wù)器將與從服務(wù)器執(zhí)行部分重同步操作,從服務(wù)器只需要等候主服務(wù)器將自己缺少的那部分數(shù)據(jù)發(fā)送過來完成同步即可。
  • 如果主服務(wù)器返回-ERR,那么表示主服務(wù)器的版本低于2.8識別不了PSYNC。

復(fù)制的完整流程

通過向從服務(wù)器發(fā)送SLAVEOF命令,可以讓一個從服務(wù)器去復(fù)制主服務(wù)器:SLAVEOF <master_ip> <master_port>

步驟一:設(shè)置主服務(wù)器的地址和端口

從服務(wù)器將客戶端給定的主服務(wù)器IO地址以及端口保存到服務(wù)器狀態(tài)里的masterhost屬性和masterport屬性里面。

需要注意的是:SLAVEOF命令是一個異步任務(wù),在完成masterhost屬性和masterport屬性的設(shè)置工作之后,從服務(wù)器將發(fā)送SLAVEOF命令的客戶端返回OK,表示復(fù)制指令已經(jīng)被接受,但是真正的復(fù)制工作是在OK返回之后才開始真正執(zhí)行的。

步驟二:建立套接字連接

在SLAVEOF命令執(zhí)行后,從服務(wù)器會根據(jù)命令所設(shè)置的IP地址以及端口號,創(chuàng)建連向主服務(wù)器的套接字(socket)連接。

如果連接成功,那么從服務(wù)器將會為這個套接字關(guān)聯(lián)一個專門用于處理復(fù)制工作的文件事件處理器,這個處理器將負責(zé)執(zhí)行后續(xù)的復(fù)制工作,比如接受RDB文件以及接受主服務(wù)器傳播過來的寫命令。

主服務(wù)器在接受從服務(wù)器的套接字連接之后,將會該套接字創(chuàng)建相應(yīng)的客戶端狀態(tài),并將從服務(wù)器看作是一個連接到主服務(wù)器的客戶端來對待,此時從服務(wù)器同時具有服務(wù)器和客戶端兩個身份,而接下來的復(fù)制工作都會以從服務(wù)器向主服務(wù)器發(fā)送命令請求的形式來執(zhí)行,因此理解“從服務(wù)器是主服務(wù)器的客戶端”這點相當(dāng)重要。

步驟三:發(fā)送PING命令

從服務(wù)器成為主服務(wù)器的客戶端之后,做的第一件事情就是向主服務(wù)器發(fā)送一個PING命令。

PING命令有以下作用:

  • 因為主從服務(wù)器創(chuàng)建了套接字連接之后未進行過任何通信,所以先要檢查套接字的讀寫狀態(tài)是否正常。
  • 檢查主服務(wù)器是否能正常處理命令請求。

從服務(wù)器發(fā)送PING命令之后會遇到以下三種情況:

  • 主服務(wù)器向從服務(wù)器返回一個命令回復(fù),但從服務(wù)器未能在限定時間里讀取命令回復(fù)的內(nèi)容,則表示主從服務(wù)器之間的網(wǎng)絡(luò)連接狀態(tài)不佳,此時需要從服務(wù)器斷開連接并重新建立連向主服務(wù)器的套接字
  • 主服務(wù)器返回一個錯誤,表示主服務(wù)器暫時無法處理從服務(wù)器的處理請求,不能執(zhí)行之后的復(fù)制工作,此時需要從服務(wù)器斷開連接并重新建立連向主服務(wù)器的套接字。
  • 如果從服務(wù)器成功讀取到PONG回復(fù),那么表示主從服務(wù)器的連接狀態(tài)正常,可以繼續(xù)執(zhí)行以下的復(fù)制操作。

步驟四:身份驗證

從服務(wù)器收到主服務(wù)器返回的PONG之后,下一步要進行的就是是否需要進行身份驗證:

  • 如果從服務(wù)器設(shè)置了masterauth,那么需要進行
  • 反之,則不需要進行

需要進行身份驗證的情況下,從服務(wù)器向主服務(wù)器發(fā)送一條AUTH命令,命令的參數(shù)為從服務(wù)器中masterauth參數(shù)的值。

從服務(wù)器身份驗證階段可能遇到的情況有以下幾種:

  • 主服務(wù)器沒有設(shè)置requirepass選項,而且從服務(wù)器也沒有設(shè)置masterauth,那么主服務(wù)器將繼續(xù)從服務(wù)器發(fā)送的命令,復(fù)制操作可以繼續(xù)執(zhí)行
  • 如果從服務(wù)器通過AUTH命令發(fā)送的密碼與主服務(wù)器requirepass所設(shè)置的密碼相同,則繼續(xù)執(zhí)行復(fù)制操作,反之,主服務(wù)器將返回一個invalid password錯誤
  • 如果主服務(wù)器設(shè)置了requirepass但是從服務(wù)器沒有設(shè)置masterauth,那么主服務(wù)器會返回NOAUTH錯誤
  • 如果主服務(wù)器沒有設(shè)置requirepass,但是從服務(wù)器設(shè)置了masterauth,那么主服務(wù)器將返回no password is set錯誤

所有錯誤都會讓從服務(wù)器終止當(dāng)前的復(fù)制工作,并重新創(chuàng)建套接字開始重新進行驗證,直至身份驗證通過或者從服務(wù)器放棄復(fù)制為止。

步驟五:發(fā)送端口信息

身份驗證過后,從服務(wù)器將執(zhí)行REPLCONF listening-port <port-number>,向主服務(wù)器發(fā)送從服務(wù)器的監(jiān)聽端口號。

主服務(wù)器接受該命令之后會將端口號記錄在slave_listening_port屬性中。目前該屬性的唯一作用就是主服務(wù)器執(zhí)行INFO replication命令時打印出從服務(wù)器的端口號信息。

步驟六:同步

這一步中,從服務(wù)器將向主服務(wù)器發(fā)送PSYNC命令,執(zhí)行同步操作,將自己的數(shù)據(jù)更新至與主服務(wù)器數(shù)據(jù)庫當(dāng)前所處狀態(tài)

步驟七:命令傳播

完成同步之后,主從服務(wù)器會進入命令傳播階段,主服務(wù)器會一直將執(zhí)行的寫命令發(fā)送給從服務(wù)器,從服務(wù)器會一直接受并執(zhí)行主服務(wù)器發(fā)送來的命令,以此保證主從服務(wù)器數(shù)據(jù)庫的一致性。


心跳檢測

在命令傳播階段,從服務(wù)器會默認以每秒一次的頻率向主服務(wù)器發(fā)送命令:REPLCONF ACK <replication_offset>

其中replication_offset是當(dāng)前從服務(wù)器的復(fù)制偏移量,發(fā)送REPLCONF ACK <replication_offset>命令對主從復(fù)制有以下三個作用:

  • 檢測主從服務(wù)器的網(wǎng)絡(luò)連接狀態(tài)
  • 輔助實現(xiàn)min-slaves參數(shù)
  • 檢測命令丟失

以下將介紹這三個作用的具體實現(xiàn)

檢測主從服務(wù)器的網(wǎng)絡(luò)連接狀態(tài)

主從服務(wù)器通過發(fā)送和接收REPLCONF ACK <replication_offset>命令來檢查兩者之間的網(wǎng)絡(luò)連接是否正常:如果主服務(wù)器超過一秒鐘沒有接收到REPLCONF ACK <replication_offset>命令,那么主服務(wù)器就知道從服務(wù)器的連接狀態(tài)出現(xiàn)問題了。

通過向主服務(wù)器發(fā)送INFO replication命令,在列出的從服務(wù)器列表中的lag一欄中,會看到對應(yīng)的從服務(wù)器上一次向主服務(wù)器發(fā)送REPLCONF ACK <replication_offset>命令距離現(xiàn)在過了多少秒:

在一般情況下,lag值應(yīng)該在0秒和1秒之間跳動,如果超過1秒,則說明主從服務(wù)器可能出現(xiàn)問題。

輔助實現(xiàn)min-slaves選項

Redis的min-slaves-to-write和min-slaves-max-lag兩個選項可以防止主服務(wù)器在不安全的情況下執(zhí)行寫命令。

min-slaves-to-write 3 min-slaves-max-lag 10

以上配置中,如果從服務(wù)器數(shù)量少于3個或者三個從服務(wù)器的延遲(lag)值都大于10秒時,則主服務(wù)器將拒絕執(zhí)行寫命令。

檢測命令丟失

如果因為網(wǎng)絡(luò)故障,主服務(wù)器傳播給從服務(wù)器的寫命令在中途丟失,那么從服務(wù)器向主服務(wù)器發(fā)送REPLCONF ACK <replication_offset>命令,主服務(wù)器發(fā)現(xiàn)從服務(wù)器當(dāng)前復(fù)制偏移量少于自己的偏移量,然后就會根據(jù)從服務(wù)器提交的偏移量在復(fù)制積壓緩沖區(qū)里找到從服務(wù)器缺失的數(shù)據(jù),并將這些數(shù)據(jù)重新發(fā)送給從服務(wù)器。

主服務(wù)器向從服務(wù)器補發(fā)缺失數(shù)據(jù)這一操作的原理與部分重同步操作的原理相似,兩者的區(qū)別在于,補發(fā)缺失數(shù)據(jù)是在主從服務(wù)器沒有斷開連接的情況下執(zhí)行的,而部分重同步是主從服務(wù)器斷線后重連執(zhí)行的,


八、哨兵

哨兵(Sentinel)是Redis高可用性(High Availability)解決方案:由一個或多個Sentinel實例組成的哨兵系統(tǒng)可以監(jiān)視多個主服務(wù)器以及這些主服務(wù)器屬下的從服務(wù)器,并在被監(jiān)視的主服務(wù)器進行下線狀態(tài)后,進行故障轉(zhuǎn)移,讓某個從服務(wù)器升級為主服務(wù)器,代替已下線的主服務(wù)器繼續(xù)處理請求命令。

哨兵實現(xiàn)的功能:

  • 監(jiān)控(Monitoring):哨兵會不斷檢查主節(jié)點和從節(jié)點是否正常運作
  • 自動故障轉(zhuǎn)移(Automaitc Failover):當(dāng)主節(jié)點不能正常工作時,哨兵會開始自動轉(zhuǎn)移故障,將失效主節(jié)點的其中一個從節(jié)點升級為主節(jié)點,并讓其他從節(jié)點復(fù)制新的主節(jié)點
  • 配置提供者(Configuration Provider):客戶端初始化時,通過連接哨兵來獲得當(dāng)前Redis服務(wù)的主節(jié)點地址
  • 通知(Notification):哨兵可以將故障轉(zhuǎn)移的結(jié)果發(fā)送給客戶端

以下是一個哨兵系統(tǒng)監(jiān)視服務(wù)器的例子:

其中,雙環(huán)的server1是當(dāng)前的主服務(wù)器,單環(huán)的表示主服務(wù)器的三個從服務(wù)器,哨兵系統(tǒng)監(jiān)視著四個服務(wù)器。

此時server1出現(xiàn)故障,進入下線狀態(tài),三個從服務(wù)器的復(fù)制操作將被終止,并且哨兵系統(tǒng)會監(jiān)察到server1已下線。

當(dāng)server1的下線時長超過了用戶設(shè)定的下線時長上限時,哨兵系統(tǒng)就會對server1進行故障轉(zhuǎn)移

  • 首先,哨兵系統(tǒng)會挑選server1的下屬的其中之一從服務(wù)器,并將這個從服務(wù)器升級為主服務(wù)器。
  • 哨兵系統(tǒng)會向其他的服務(wù)器發(fā)送新的復(fù)制指令,讓他們成為新的主服務(wù)器的從服務(wù)器,當(dāng)所有從服務(wù)器都開始復(fù)制新的主服務(wù)器時,故障轉(zhuǎn)移操作執(zhí)行完畢。
  • 另外,哨兵系統(tǒng)還會監(jiān)視已下線的server1,并在它重新上線時,將它設(shè)置為新的主服務(wù)器的從服務(wù)器。


啟動并初始化Sentinel

啟動一個Sentinel:

redis-sentinel sentinel.conf

當(dāng)一個Sentinel啟動時,它需要執(zhí)行以下步驟:

  • 初始化服務(wù)器
  • 將普通Redis服務(wù)器使用的代碼替換成Sentinel專用代碼
  • 初始化Sentinel狀態(tài)
  • 根據(jù)給定的配置文件,初始化Sentinel的監(jiān)視主服務(wù)器列表
  • 創(chuàng)建連向主服務(wù)器的網(wǎng)絡(luò)連接

初始化服務(wù)器

Sentinel本質(zhì)上就是一個運行在特殊模式下的Redis服務(wù)器,這個模式叫做哨兵模式。啟動Sentinel的第一步就是初始化一個普通的Redis服務(wù)器。不過Sentinel執(zhí)行的工作跟普通的Redis服務(wù)器執(zhí)行的工作不一樣,所以兩者的初始化過程也不完全相同。

例如,Sentinel服務(wù)器并不使用數(shù)據(jù)庫,所以初始化Sentinel不會載入RDB文件或者AOF文件。

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-M2QS5iXy-1631271000165)(C:/Users/Supreme%20honor/Desktop/NoteBook/redis21.jpg)]

使用Sentinel專用代碼

啟動Sentinel的第二個步驟就是將一部分普通Redis服務(wù)器使用的代碼進行替換。

例如普通Redis服務(wù)器使用redis.h/REDIS_SERVERPORT常量值作為服務(wù)器端口

# define REDIS_SERVERPORT 6379

而Sentinel服務(wù)器使用sentinel.c/REDIS_SENTINEL_PORT常量作為服務(wù)器端口

# define REDIS_SENTINEL_PORT 26379

并且Sentinel會使用sentinel.c/sentinelcmds作為服務(wù)器的專用命令表,sentinelcmds命令表也解釋了為什么Sentinel模式下,Redis服務(wù)器不能執(zhí)行諸如SET,DEL,DBSIZE等命令,因為服務(wù)器載入的時候就沒有載入這些命令。

Sentinel模式下客戶端可以執(zhí)行的全部命令有:PING SENTINEL INFO SUBCRIBE UNSUBSCRIBE PSUBSCRIBE PUNSUBSCRIBE

初始化Sentinel狀態(tài)

服務(wù)器 初始化一個sentinel.c/sentinelState結(jié)構(gòu)(簡稱:Sentinel狀態(tài)),這個結(jié)構(gòu)中保存了服務(wù)器中所有和Sentinel有關(guān)的狀態(tài)信息。

struct sentinelState {//當(dāng)前紀(jì)元,用于實現(xiàn)故障轉(zhuǎn)移uint64_t current_epoch;//保存了所有被sentinel監(jiān)視的主服務(wù)器//字典的鍵是主服務(wù)器的名字,值是指向主服務(wù)器的指針dict *masters;//是否進入TILT模式 int tilt;//正在執(zhí)行的腳本數(shù)量int running_scripts;//最后一次執(zhí)行時間處理器的時間mstime_t previous_time;//進入TILT模式的時間mastime_t tilt_start_time;//一個FIFO隊列。包含了所有需要執(zhí)行的用戶腳本list *scripts_queue; }

初始化Sentinel狀態(tài)的masters屬性

如上述代碼可知,sentinelState結(jié)構(gòu)體中的masters字典記錄了所有被Sentinel監(jiān)視的主服務(wù)器的相關(guān)信息。

其中:

  • 字典的鍵是被監(jiān)視的主服務(wù)器的名稱

  • 字典的值是被監(jiān)視的主服務(wù)器對應(yīng)的sentinel.c/sentinelRedisInstance結(jié)構(gòu)。

    每一個sentinel.c/sentinelRedisInstance都代表一個被Sentinel監(jiān)視的Redis服務(wù)器實例,這個實例可以是主服務(wù)器,從服務(wù)器或另外一個哨兵Sentinel。

    typedef struct sentinelRedisInstance {//標(biāo)識值,記錄了實例的類型,以及該實例的狀態(tài)int flags;//實例的名稱//主服務(wù)器的名稱在配置文件中設(shè)置//從服務(wù)器以及Sentinel的名稱由Sentinel自動設(shè)置//格式為 ip:portchar *name;//運行IDchar *runid;//配置紀(jì)元,實現(xiàn)故障轉(zhuǎn)移uint64_t config_epoch;//實例的地址sentinelAddr * addr;//SENTINEL down-after-milliseconds選項設(shè)定的值//實例無響應(yīng)多少秒之后才會被判定為主觀下線mstime_t down_after_period;//SENTINEL monitor <master-name> <ip> <port> <quorum>//判斷該實例客觀下線需要的的支持投票數(shù)量int quorum;//SENTINEL parallel-synuc <master-name> <number>選項的值//在執(zhí)行故障轉(zhuǎn)移操作時,可以同時對新的主服務(wù)器進行同步的從服務(wù)器數(shù)量int parallel_syncs;//SENTINEL failover-timeout <master-name> <ms>選項的值//刷新故障遷移狀態(tài)的最大時限mstime_t failover_timeout;//... }sentinelRedisInstance;typedef struct sentinelAddr {char *ip;int port; }sentinelAddr;

    創(chuàng)建連向主服務(wù)器的網(wǎng)絡(luò)連接

    初始化Sentinel的最后一步是創(chuàng)建連向被監(jiān)視的主服務(wù)器的網(wǎng)絡(luò)連接,Sentinel將成為主服務(wù)器的客戶端,它可以向主服務(wù)器發(fā)送命令,并從命令回復(fù)中獲取相關(guān)的信息。

    對于每個被Sentinel監(jiān)視的主服務(wù)器來說,Sentinel會創(chuàng)建兩個連向主服務(wù)器的異步網(wǎng)絡(luò)連接:

    • 一個是命令連接,這個連接用于向主服務(wù)器發(fā)送命令,并接受命令回復(fù)。
    • 另一個是訂閱連接,這個連接用于訂閱主服務(wù)器的__sentinel__:hello頻道

    為什么要有兩個連接?

    • Redis目前的發(fā)布與訂閱功能中,被發(fā)送的信息都不會在Redis服務(wù)器中保存,如果這條信息發(fā)送時,要接受信息的客戶端不在線或者掉線,那么這個客戶端就會丟失該信息,因此為了不丟失__sentinel__:hello頻道的信息,Sentinel必須專門用另外一個訂閱連接來接受該頻道的信息。
    • 除了訂閱頻道之外,Sentinel還必須向被它監(jiān)視的主服務(wù)器發(fā)送命令,以此來與主服務(wù)器進行通訊,所有Sentinel還必須向主服務(wù)器創(chuàng)建命令連接。
    • 因為Sentinel需要和多個實例創(chuàng)建多個網(wǎng)絡(luò)連接,所以Sentinel使用的是異步連接。

獲取主服務(wù)器信息

Sentinel默認會以每十秒一次的頻率,通過連接向被監(jiān)視的主服務(wù)器發(fā)送INFO命令,并通過分析INFO命令的回復(fù)來獲取主服務(wù)器的當(dāng)前信息。

通過分析主服務(wù)器返回的INFO命令回復(fù),Sentinel可以獲得以下兩方面信息:

  • 一方面是關(guān)于主服務(wù)器 本身的信息,包括run_id域記錄的服務(wù)器運行ID,以及role域記錄的服務(wù)器角色。
  • 另一方面是關(guān)于主服務(wù)器屬下的所有從服務(wù)器的信息,每一個從服務(wù)器由一個slave字符串開頭的行記錄,每行中會顯示從服務(wù)器的IP地址和port端口號,根據(jù)這些信息,Sentinel無需用戶提供從服務(wù)器的地址信息就可以自動發(fā)現(xiàn)從服務(wù)器。

根據(jù)上述信息,Sentinel對主服務(wù)器的實例結(jié)構(gòu)進行更新,例如,主服務(wù)器重啟后的運行ID與之前保存的運行ID不同,Sentinel會檢測到該情況,對實例結(jié)構(gòu)的運行ID進行更新。

獲取從服務(wù)器信息

當(dāng)Sentinel發(fā)現(xiàn)主服務(wù)器有新的從服務(wù)器出現(xiàn)時,Sentinel除了會為這個新的從服務(wù)器創(chuàng)建新的實例結(jié)構(gòu)之外,還會創(chuàng)建連接到從服務(wù)器的命令連接和訂閱連接。

創(chuàng)建命令連接之后,Sentinel會以十秒一次的頻率通過向從服務(wù)器發(fā)送INFO命令,并獲得以下內(nèi)容的回復(fù):

  • 從服務(wù)器的運行ID
  • 從服務(wù)器的角色role
  • 主服務(wù)器的IP地址master_host,以及主服務(wù)器的端口號master_port
  • 主從服務(wù)器的連接狀態(tài)master_link_status
  • 從服務(wù)器的優(yōu)先級slave_priority
  • 從服務(wù)器的復(fù)制偏移量slave_repl_offset

根據(jù)這些信息,Sentinel會對從服務(wù)器的實例結(jié)構(gòu)進行更新。

向主從服務(wù)器發(fā)送信息

默認情況下,Sentinel會以每兩秒一次的頻率通過命令連接向所有被監(jiān)視的主服務(wù)器和從服務(wù)器發(fā)送以下格式命令:

PUBLISH _sentinel_:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"

分別記錄Sentinel和其監(jiān)視的主服務(wù)器的IP地址,端口號,運行ID以及當(dāng)前的配置紀(jì)元。

接收來自主從服務(wù)器的的頻道信息

當(dāng)Sentinel與一個主服務(wù)器或從服務(wù)器建立起訂閱連接之后,Sentinel就會通過訂閱連接向服務(wù)器發(fā)送以下命令:

SUBCRIBE _sentinel_:hello

Sentinel對_sentinel_:hello頻道的訂閱會一直持續(xù)到Sentinel與服務(wù)器的連接斷開為止。

每個與Sentinel連接到服務(wù)器,Sentinel既通過命令連接向服務(wù)器的_sentinel_:hello頻道發(fā)送信息,又通過訂閱連接從服務(wù)器的_sentinel_:hello頻道接收信息。

當(dāng)一個Sentinel從_sentinel_:hello頻道接收到信息之后,會對該信息進行分析,提取出Sentinel IP Sentinel post Sentinel runID等八個參數(shù)信息,并進行以下檢查:

  • 如果信息中的運行ID與接收信息的運行ID相同,則說明是自己發(fā)送的,丟棄該信息,不予處理。
  • 反之,說明這條信息是由監(jiān)視同一個服務(wù)器的其他Sentinel發(fā)來的,接收信息的Sentinel會根據(jù)信息重點各種參數(shù),而對主服務(wù)器的實例結(jié)構(gòu)進行調(diào)整更新。

更新sentinels字典

Sentinel為主服務(wù)器創(chuàng)建的實例結(jié)構(gòu)中的sentinels字典,不僅保存Sentinel本身,還有所有同樣監(jiān)視這個主服務(wù)器的其他Sentienl資料。

創(chuàng)建連向其他Sentinel的命令連接

當(dāng)Sentinel通過頻道信息發(fā)現(xiàn)了一個新的Sentinel時,它不僅為會新Sentinel在sentinels字典中創(chuàng)建對應(yīng)的實例結(jié)構(gòu),還會創(chuàng)建一個連向新Sentinel的命令連接,而新的Sentinel也會創(chuàng)建連接到這個Sentinel的命令連接,從而讓哨兵系統(tǒng)中的多個Sentinel形成相互連接的網(wǎng)絡(luò)。

使用命令連接的各個Sentinel通過命令請求來進行信息交換。

Sentinel之間不會創(chuàng)建訂閱連接

Sentinel在連接主從服務(wù)器時會創(chuàng)建命令連接和訂閱連接,但是在連接Sentinel時只會創(chuàng)建命令連接,這是因為Sentinel需要通過接收主從服務(wù)器發(fā)來的頻道信息發(fā)現(xiàn)未知的Sentinel,所以才需要創(chuàng)建訂閱連接,而互相已知的Sentinel則只需要通過命令連接進行通訊即可。

檢測主觀下線狀態(tài)

在默認情況下,Sentinel會以每秒一次的頻率向其他創(chuàng)建了命令連接的實例(主從服務(wù)器、其他Sentinel在內(nèi))發(fā)送PING命令,通過實例返回的回復(fù)的來判斷實例是否在線。

服務(wù)器對PING命令的有效回復(fù)是以下三種的其中一種:

  • +PONG
  • -LOADING錯誤
  • -MASTERDOWN錯誤

如果服務(wù)器返回了除以上三種之外的其他回復(fù),又或者在指定時間內(nèi)沒有回復(fù)PING命令,則Sentinel認為服務(wù)器返回的回復(fù)無效。

一個服務(wù)器在master-down-after-milliseconds毫秒內(nèi)一直返回?zé)o效信息則會被Sentinel判定為主觀下線。

檢查客觀下線狀態(tài)

當(dāng)Sentinel將一個主服務(wù)器判定為主觀下線之后,為了確認這個主服務(wù)器是否真的下線,它會向其他監(jiān)視這一主服務(wù)器的其他Sentinel進行詢問,當(dāng)Sentinel從其他Sentinel接收到足夠的已下線判斷時,Sentinel就會將這個主服務(wù)器判定為客觀下線,并進行故障轉(zhuǎn)移。

發(fā)送SENTINEL is-master-down-by-addr <ip> <port> <current-epoch> <runid>命令向其他Sentinel詢問意見。

參數(shù)意義
ip被Sentinel判定為主觀下線的主服務(wù)器的IP地址
port被Sentinel判定為主觀下線的主服務(wù)器的端口號
current_epochSentinel當(dāng)前的配置紀(jì)元,用于選舉領(lǐng)頭Sentienl
runid可以是* 符號或者是Sentinel的運行ID:* 符號表示命令僅僅用于主服務(wù)器的客觀下線狀態(tài),而Sentinel的運行ID用于選舉領(lǐng)頭Sentinel

目標(biāo)Sentinel接收SENTINEL is-master-down-addr命令

當(dāng)一個Sentinel(目標(biāo)Sentinel)接收到另一個Sentinel(源Sentinel)發(fā)來的SENTINEL is-master-down-addr命令時,目標(biāo)Sentinel會分析并取出命令請求中的各個參數(shù),并根據(jù)主服務(wù)器的IP和端口號,判斷主服務(wù)器是否已經(jīng)下線,然后向源Sentinel返回SENTINEL is-master-down-by <down_state> <leader_runid> <leader_epoch>命令

參數(shù)意義
down_state返回目標(biāo)Sentinel對服務(wù)器的檢查結(jié)果,1表示主服務(wù)器已經(jīng)下線,0表示主服務(wù)器未下線
leader_runid可以是* 符號或者目標(biāo)Sentinel的局部領(lǐng)頭Sentinel的運行ID,* 符號表示主服務(wù)器的下線狀態(tài),而局部領(lǐng)頭Sentinel 的運行ID則用于選舉領(lǐng)頭Sentinel
leader_epoch目標(biāo)Sentinel的局部領(lǐng)頭Sentinel的配置紀(jì)元,用于選舉領(lǐng)頭Sentinel,僅在leader_runid不為* 時有效,如果leader_runid的值為* ,那么leader_epoch的值為0

舉例:一個目標(biāo)Sentinel返回SENTINEL is-master-down-by <1> <*> <0>命令給源Sentinel,則說明目標(biāo)Sentinel同意主服務(wù)器已經(jīng)下線。

源Sentinel接收SENTINEL is-master-down-by命令

源Sentinel根據(jù)其他目標(biāo)Sentinel發(fā)回的SENTINEL is-master-down-by命令,Sentinel統(tǒng)計其他Sentinel同意主服務(wù)器下線的數(shù)量,當(dāng)這一數(shù)量達到配置指定的判斷客觀下線所需數(shù)量時,Sentinel就會將主服務(wù)器實例結(jié)構(gòu)的flags屬性的SRI_O_DOWN標(biāo)識打開,表示該服務(wù)器已經(jīng)下線。

客觀下線的判斷條件

Sentinel配置文件中寫入了sentinel monitor mymaster 127.0.0.1 6379 2——配置的含義是:該哨兵節(jié)點監(jiān)控192.168.92.128:6379這個主節(jié)點,該主節(jié)點的名稱是mymaster,最后的2的含義與主節(jié)點的故障判定有關(guān):至少需要2個哨兵節(jié)點同意,才能判定主節(jié)點故障并進行故障轉(zhuǎn)移。

選舉領(lǐng)頭Sentinel

當(dāng)一個主服務(wù)器被判斷為客觀下線,監(jiān)視這個下線的主服務(wù)器的各個Sentinel回進行協(xié)商,選出一個領(lǐng)頭Sentinel,并由領(lǐng)頭Sentinel對下線的主服務(wù)器進行故障轉(zhuǎn)移操作。

以下是Redis選舉領(lǐng)頭Sentinel的規(guī)則和方法:

故障轉(zhuǎn)移

在選舉出領(lǐng)頭Sentinel之后,領(lǐng)頭Sentinel將對已下線的主服務(wù)器進行故障轉(zhuǎn)移操作:

  • 從已下線的主服務(wù)器屬性的從服務(wù)器中挑選一個轉(zhuǎn)換為主服務(wù)器
  • 讓已下線的主服務(wù)器屬性的其他從服務(wù)器改為復(fù)制新的主服務(wù)器
  • 將已下線的主服務(wù)器設(shè)置為新的主服務(wù)器的從服務(wù)器,當(dāng)它重新連接上來時就會成為新的主服務(wù)器的從服務(wù)器

挑選新的主服務(wù)器

在已下線的主服務(wù)器屬下的所有從服務(wù)器中,挑選一個狀態(tài)良好,數(shù)據(jù)完整的從服務(wù)器,然后向它發(fā)送SLAVEOF no one命令,將這個從服務(wù)器轉(zhuǎn)換為主服務(wù)器。

新的主服務(wù)器是如何挑選的呢?

領(lǐng)頭Sentinel回將已下線的主服務(wù)器的所有從服務(wù)器保存到一個列表中,如何進行一項一項地篩選:

  • 刪除列表中所有處于下線或者斷線的從服務(wù)器,保證列表中的服務(wù)器都是在線狀態(tài)良好的

  • 刪除列表中所有最近五秒內(nèi)沒有回復(fù)過領(lǐng)頭Sentinel的INFO命令的從服務(wù)器,保證列表中都是最近進行成功通訊的服務(wù)器

  • 刪除所有與已下線主服務(wù)器斷開連接超過down-after-millisecond * 10 毫秒的從服務(wù)器,保證列表中的從服務(wù)器都沒有過早地與主服務(wù)器斷開連接,以此保證數(shù)據(jù)完整。

  • 從以上淘汰中存留下來的服務(wù)器,會根據(jù)復(fù)制偏移量來繼續(xù)進行篩選,(復(fù)制偏移量最大的從服務(wù)就是保存著最新數(shù)據(jù)的服務(wù)器);

    如果復(fù)制偏移量不可用,則會根據(jù)服務(wù)器的runID來進行選擇,選擇runID小的服務(wù)器成為主服務(wù)器。

修改從服務(wù)器的復(fù)制目標(biāo)

當(dāng)新的主服務(wù)器出現(xiàn)之后,領(lǐng)頭Sentinel下一步做的就是,讓其他從服務(wù)器去復(fù)制新的主服務(wù)器,可以通過向從服務(wù)器發(fā)送SLAVEOF實現(xiàn)。

將舊的主服務(wù)器變成從服務(wù)器

故障轉(zhuǎn)移操作最后要做的就是將已下線的主服務(wù)器設(shè)置為新的主服務(wù)器的從服務(wù)器。

當(dāng)已下線的主服務(wù)器重新連接后Sentinel就會向其發(fā)送SLAVEOF命令,使其成為新的主服務(wù)器的從服務(wù)器,如下圖所示:

Sentinel自動故障轉(zhuǎn)移的一致性特質(zhì)

Sentinel自動故障轉(zhuǎn)移使用Raft算法來選舉領(lǐng)頭Sentinel,從而確保在一個給定的紀(jì)元里面,只有一個領(lǐng)頭產(chǎn)生。

這表示同一個紀(jì)元中,不會有兩個Sentinel同時被選為領(lǐng)頭,并且各個Sentinel在同一個紀(jì)元中,只會對一個領(lǐng)頭進行投票。

更高的配置紀(jì)元總是優(yōu)于較低的紀(jì)元,因此每個Sentinel都會主動使用更新的紀(jì)元來代替自己的配置。

可以這樣說,我們將Sentinel配置看作一個帶有版本號的狀態(tài),一個狀態(tài)會以最后寫入者的方式保留下來,當(dāng)一個有著比較舊的配置的Sentinel接收到其他Sentinel發(fā)來的版本更新的配置時,就會將自己的配置進行更新。

Sentinel狀態(tài)的持久化

Sentinel 的狀態(tài)會被持久化在 Sentinel 配置文件里面。

每當(dāng) Sentinel 接收到一個新的配置, 或者當(dāng)領(lǐng)頭 Sentinel 為主服務(wù)器創(chuàng)建一個新的配置時, 這個配置會與配置紀(jì)元一起被保存到磁盤里面。

這意味著停止和重啟 Sentinel 進程都是安全的。


九、集群

上述的高可用方案:持久化,主從復(fù)制和哨兵,但這些方案仍然存在不足,其中主要的問題就是存儲能力受單機限制,以及無法實現(xiàn)寫操作的負載均衡。

集群的作用

集群,即Redis Cluster,是Redis3.0開始引入的分布式存儲方案。

集群有多個節(jié)點(Node)組成,Redis的數(shù)據(jù)分布在這些節(jié)點中。集群中的節(jié)點分為主節(jié)點和從節(jié)點:主節(jié)點負責(zé)讀寫請求和集群信息的維護,從節(jié)點進行主節(jié)點數(shù)據(jù)和狀態(tài)信息的復(fù)制。

集群的作用歸納為以下兩點:

  • 數(shù)據(jù)分區(qū)(數(shù)據(jù)分片)

    集群將數(shù)據(jù)分散到多個節(jié)點,一方面突破了Redis單機內(nèi)存大小的限制,另一方面每個節(jié)點都可以對外提供讀寫服務(wù),極大提高了集群的響應(yīng)能力。

  • 高可用

    集群支持主從復(fù)制和主節(jié)點的自動故障轉(zhuǎn)移,轉(zhuǎn)移機制與哨兵機制類似;當(dāng)某一節(jié)點出現(xiàn)故障時,集群仍然可以對外提供服務(wù)。


集群的搭建

集群的搭建有兩種方式:(1)手動執(zhí)行Redis命令,一步步完成搭建;(2)使用Ruby腳本搭建。兩者原理相同,后者對前者使用到的Redis命令進行封裝打包。

執(zhí)行Redis命令搭建集群

集群的搭建分為四步:

  • 啟動節(jié)點:將節(jié)點以集群模式啟動,此時節(jié)點是獨立的,沒有建立與其他節(jié)點的連接
  • 節(jié)點握手:讓各個獨立的節(jié)點連接成一個網(wǎng)絡(luò)
  • 分配槽:將16384個槽分配給各個主節(jié)點
  • 指定主從關(guān)系

啟動節(jié)點

集群節(jié)點的啟動依然是使用redis-server命令,但需要以集群模式啟動,以下是節(jié)點的配置文件

#redis-6379.conf dbfilename "dump-6379.rdb" port 6379 daemonize no rdbcompression yes rdbchecksum yes save 10 2 appendonly yes appendfsync always appendfilename "appendonly-6379.aof" bind 127.0.0.1 #logfile "/www/server/redis/redis-6379.log" databases 16 cluster-enabled yes cluster-config-file "nodes-6379.conf" cluster-node-timeout 10000

cluster-enabled yes:Redis實例可以分為單機模式standAlone和集群模式cluster,這個設(shè)置可以開啟節(jié)點的集群模式

集群模式下的節(jié)點,其redis-mode為cluster,如下圖所示:

cluster-config-file:指定了集群配置文件的位置,每個節(jié)點運行過程中會維護一份集群配置文件;當(dāng)集群信息發(fā)生變化,集群中的所有節(jié)點會將最新信息更新到該配置文件中;當(dāng)節(jié)點重啟時會讀取該配置文件,獲取集群信息。Redis節(jié)點以集群模式啟動時,會首先尋找是否有集群信息文件,如果有則使用文件中的配置啟動,如果沒有,則初始化配置并將配置保存到文件中。

編輯好配置文件后,通過redis-server命令啟動節(jié)點:

redis-server redis-6379.conf

節(jié)點啟動以后,通過cluster nodes命令可以查看節(jié)點的情況,如下圖所示。

其中返回值第一項表示節(jié)點id,由40個16進制字符串組成,集群模式下的節(jié)點的run_id與單機模式下的節(jié)點run_id有所不同,Redis每次啟動都會重新創(chuàng)建run_id,但是集群模式下只會在初始時創(chuàng)建一次,然后保存到集群配置文件中,之后節(jié)點重啟會從配置文件從讀取,而不再重新創(chuàng)建。

需要注意的是:啟動節(jié)點階段,節(jié)點之間是沒有主從關(guān)系的,因此節(jié)點中不需要添加slaveof配置。

節(jié)點握手

節(jié)點啟動后是互相獨立的,并不知道其他節(jié)點存在,因此集群模式中需要進行節(jié)點握手,將獨立的節(jié)點組成一個網(wǎng)絡(luò)。

節(jié)點握手使用cluster meet {ip} {port}命令實現(xiàn)。

分配槽

在Redis集群中,借助槽實現(xiàn)數(shù)據(jù)分區(qū),集群有16384個槽,槽是數(shù)據(jù)管理和遷移的基本單位,當(dāng)數(shù)據(jù)庫中的16384個槽分配了節(jié)點,集群處于上線狀態(tài)(ok),如果有一個槽沒有分配節(jié)點,則集群處于下線狀態(tài)(fail)。

redis-cli -p 7000 cluster addslots {0..5461} redis-cli -p 7001 cluster addslots {5462..10922} redis-cli -p 7002 cluster addslots {10923..16383}

此時查看集群狀態(tài),顯示所有槽分配完畢,集群進入上線狀態(tài):

指定主從關(guān)系

集群中指定關(guān)系不再使用slaveof命令,而是使用cluster replicate run_id

例如:

redis-cli -p 8000 cluster replicate be816eba968bc16c884b963d768c945e86ac51ae redis-cli -p 8001 cluster replicate 788b361563acb175ce8232569347812a12f1fdb4 redis-cli -p 8002 cluster replicate a26f1624a3da3e5197dde267de683d61bb2dcbf1

至此,集群搭建完畢。


使用Ruby腳本搭建集群

在{REDIS_HOME}/src目錄下有一個redis-trib.rb文件,這是一個Ruby腳本,可以實現(xiàn)集群的自動搭建。

安裝Ruby環(huán)境

輸入以下命令

apt-get install ruby #安裝ruby環(huán)境 gem install redis #gem是ruby的包管理工具

啟動節(jié)點

redis-server redis-6379.conf

搭建集群

redis-trib.rb腳本提供了眾多命令,其中create用于搭建集群:

redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1

-replicas 1:表示每個主節(jié)點有一個從節(jié)點;多個{id:port}表示節(jié)點地址,前面的做主節(jié)點,后面的做從節(jié)點。

注意:使用redis-trib.rb腳本搭建集群時,要求節(jié)點不能包含任何槽和數(shù)據(jù),否則會報以下錯誤:

執(zhí)行創(chuàng)建命令之后,腳本會給出創(chuàng)建集群的計劃,如下圖所示,計劃包括哪些節(jié)點是主節(jié)點,哪些是從節(jié)點,以及如何分配槽。

#是否執(zhí)行計劃 Can I set the above configuration?(type 'yes' to accept):yes

輸入yes執(zhí)行計劃,至此,集群搭建完畢。


集群設(shè)計

設(shè)計集群方案時,需要考慮以下因素:

  • 高可用要求:根據(jù)故障自動轉(zhuǎn)移原理,至少需要3個主節(jié)點才能完成故障轉(zhuǎn)移,且三個主節(jié)點應(yīng)在不同的物理機上,每個主節(jié)點至少需要一個從節(jié)點,主從節(jié)點應(yīng)在不同的物理機上,因此高可用集群至少需要6個節(jié)點來支持。
  • 數(shù)據(jù)量和訪問量:估算應(yīng)用需要的數(shù)據(jù)量和總訪問量,結(jié)合每個主節(jié)點的容量和能承受的訪問量(可以通過benchmark估算),計算所需的主節(jié)點個數(shù)。
  • 節(jié)點數(shù)量限制:Redis官方給出的節(jié)點數(shù)量限制是1000,主要是考慮節(jié)點間通信帶來的消耗。實際應(yīng)用中需要避免大量集群,如果節(jié)點數(shù)量不足以滿足應(yīng)用對Redis數(shù)據(jù)量和訪問量的要求,可以考慮:(1)業(yè)務(wù)分割,大集群劃分為多個小集群;(2)減少不必要的數(shù)據(jù);(3)調(diào)整過期數(shù)據(jù)刪除策略。
  • 適度冗余:Redis可以在不影響集群服務(wù)的情況下適度增加節(jié)點,保證數(shù)據(jù)容冗余。

數(shù)據(jù)結(jié)構(gòu)

節(jié)點需要專門的數(shù)據(jù)結(jié)構(gòu)來存儲集群的狀態(tài)。所謂集群的狀態(tài),是一個很大的概念,包括:集群是否處于上線狀態(tài),集群中有哪些節(jié)點,節(jié)點的主從狀態(tài),槽指派的分布等。

節(jié)點為了存儲集群狀態(tài)而提供的數(shù)據(jù)結(jié)構(gòu)中,最關(guān)鍵的是clusterNode和clusterState結(jié)構(gòu),前者記錄集群中一個節(jié)點的狀態(tài),后者記錄了集群作為一個整體的狀態(tài)。

每個節(jié)點都會使用一個clusterNode結(jié)構(gòu)來記錄自己的狀態(tài),并為集群中所有的節(jié)點創(chuàng)建一個clusterNode結(jié)構(gòu),以此記錄其他節(jié)點的狀態(tài):

struct clusterNode {//節(jié)點創(chuàng)建時間mstime_t ctime;//節(jié)點名稱char name[REDIS_CLUSTER_NAMELEN];//節(jié)點的ip和端口號char ip[REDIS_IP_STR_LEN];int port;//節(jié)點標(biāo)識:整型,每個bit都代表了不同狀態(tài),如節(jié)點的主從狀態(tài)、是否在線、是否在握手等int flags;//配置紀(jì)元:故障轉(zhuǎn)移時起作用,類似于哨兵的配置紀(jì)元uint64_t configEpoch;//槽在該節(jié)點中的分布:占用16384/8個字節(jié),16384個比特;每個比特對應(yīng)一個槽:比特值為1,則該比特對應(yīng)的槽在節(jié)點中;比特值為0,則該比特對應(yīng)的槽不在節(jié)點中unsigned char slots[16384/8];//節(jié)點中槽的數(shù)量int numslots;//... };

除了上述字段,clusterNode還包含了節(jié)點連接、主從復(fù)制、故障發(fā)現(xiàn)和轉(zhuǎn)移需要的信息等。

clusterState

typedef struct clusterState {//自身節(jié)點clusterNode *myself;//配置紀(jì)元uint64_t currentEpoch;//集群狀態(tài):在線還是下線int state;//集群中至少包含一個槽的節(jié)點數(shù)量int size;//哈希表,節(jié)點名稱->clusterNode節(jié)點指針dict *nodes;//槽分布信息:數(shù)組的每個元素都是一個指向clusterNode結(jié)構(gòu)的指針;如果槽還沒有分配給任何節(jié)點,則為NULLclusterNode *slots[16384]; };

集群命令的實現(xiàn)

cluster meet

通過向節(jié)點A發(fā)送cluster meet命令,客戶端可以讓接收命令的節(jié)點A將另一個節(jié)點B添加到節(jié)點A 所在的集群中。

CLUSTER MEET <ip> <port>

收到命令的節(jié)點A將與節(jié)點B進行握手,以此確定彼此的存在,并為將來進一步的通信打好基礎(chǔ)。具體步驟:

  • 節(jié)點A為節(jié)點B創(chuàng)建一個clusterNode結(jié)構(gòu)來存儲節(jié)點B的信息,并將該結(jié)構(gòu)添加到自己的clusterState.nodes字典里。
  • 之后,節(jié)點A根據(jù)CLUSTER MEET <ip> <port>命令中指定的IP地址和端口號,向節(jié)點B發(fā)送一條MEET消息。
  • 節(jié)點B接收到節(jié)點A發(fā)送的MEET消息,節(jié)點B為節(jié)點A創(chuàng)建一個clusterNode結(jié)構(gòu),并將該結(jié)構(gòu)添加到自己的clusterState.nodes字典里。
  • 之后節(jié)點B向節(jié)點A返回一條PONG消息。
  • 節(jié)點A將接收到節(jié)點B返回的PONG消息,通過這條消息,節(jié)點A可以得知節(jié)點B已經(jīng)成功地接收自己的MEET消息。
  • 之后節(jié)點A將向節(jié)點B返回一條PING消息。
  • 節(jié)點B將接收到節(jié)點A返回的PING消息,通過這條消息,節(jié)點B可以得知節(jié)點A已經(jīng)成功接收了自己返回的PONG消息,至此,握手完成。


cluster addslots

集群中槽的分配信息,存儲在clusterNode的slots數(shù)組中和clusterState的slots數(shù)組中,兩個數(shù)組之間的區(qū)別是,前者存儲的該節(jié)點中分配了哪些槽,而后者存儲的每個槽所指向的節(jié)點,即集群中所有槽分別分布在哪個節(jié)點。

cluster addslots命令接收一個或多個槽作為參數(shù),例如在A節(jié)點上執(zhí)行cluster addslots {0,1989}命令,是將編號為0-1989的槽分配給A節(jié)點,具體執(zhí)行步驟如下:

  • 遍歷槽,檢查它們0-1989是否都沒有分配節(jié)點,如果有一個槽已經(jīng)分配,則命令執(zhí)行失敗;檢查方法是遍歷槽在clusterState.slots[]中對應(yīng)的值是否為NULL值。
  • 遍歷槽,將其分配給節(jié)點A,將clusterNode.slots[]中對應(yīng)的比特修改為1,以及clusterState.slots[]中對應(yīng)的指針指向節(jié)點A。
  • 執(zhí)行完畢后,通過節(jié)點通信機制通知其他節(jié)點,所有節(jié)點都會知道0-1989的槽分配給了節(jié)點A。

實踐須知

集群伸縮

實際場景中常常需要對集群進行伸縮,如果訪問量增大時,集群的擴容操作。Redis集群可以在不影響對外服務(wù)的情況下對集群進行伸縮;其核心是槽遷移:修改槽與節(jié)點之間的關(guān)系,實現(xiàn)槽在節(jié)點中的遷移。例如,如果槽均勻分配在三個節(jié)點中,現(xiàn)需要新增一個節(jié)點,則需要從3個節(jié)點中取出一部分槽分配給新的節(jié)點,從而實現(xiàn)槽的重新分配。

新增節(jié)點

  • 啟動節(jié)點
  • 節(jié)點握手
  • 遷移槽,使用redis-trib.rb 的reshard(重新分區(qū))工具實現(xiàn),reshard自動化程度很高,只需要輸入redis-trib.rb reshard ip:port即可自動實現(xiàn)槽遷移。
  • 指定主從關(guān)系

減少節(jié)點

  • 遷移槽,使用reshard將需要刪除的節(jié)點的槽均勻遷移到其他節(jié)點上
  • 下線節(jié)點:使用redis-trib.rb del-node工具,先下線從節(jié)點再下線主節(jié)點。

ASK錯誤

當(dāng)客戶端向源節(jié)點發(fā)送一個與數(shù)據(jù)庫有關(guān)的命令,并且命令要處理的數(shù)據(jù)庫鍵剛好就屬于正在被遷移的槽時:

  • 源節(jié)點會現(xiàn)在自己的數(shù)據(jù)庫中查找指定的鍵,如果找到就執(zhí)行客戶端發(fā)送的命令。
  • 相反,如果源節(jié)點沒有在數(shù)據(jù)庫中找到指定的鍵,則這個鍵有可能已經(jīng)被遷移到了其他節(jié)點,此時源節(jié)點將向客戶端返回一個ASK錯誤,指引客戶端轉(zhuǎn)向正在導(dǎo)入槽的目標(biāo)節(jié)點,并且再次發(fā)送之前要執(zhí)行的命令。

客戶端收到ASK錯誤后,從中讀取目標(biāo)節(jié)點的地址信息,并向目標(biāo)節(jié)點重新發(fā)送請求,就像收到MOVED錯誤時一樣。但是二者有很大區(qū)別:ASK錯誤說明數(shù)據(jù)正在遷移,不知道何時遷移完成,因此重定向是臨時的,SMART客戶端不會刷新slots緩存;MOVED錯誤重定向則是(相對)永久的,SMART客戶端會刷新slots緩存。


參考文獻:

《Redis設(shè)計與實現(xiàn)》
《Redis開發(fā)與運維》

以上

創(chuàng)作不易,如果文章對你有幫助,留個三連再走吧。

如果不足或錯誤歡迎評論指正。

總結(jié)

以上是生活随笔為你收集整理的深入学习Redis的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

国产欧美精品一区二区三区 | 久久婷婷五月综合色国产香蕉 | 一个人免费观看的www视频 | 丁香花在线影院观看在线播放 | 日日躁夜夜躁狠狠躁 | 99精品视频在线观看免费 | 久久久久se色偷偷亚洲精品av | 久久 国产 尿 小便 嘘嘘 | 精品厕所偷拍各类美女tp嘘嘘 | 1000部夫妻午夜免费 | 欧美精品一区二区精品久久 | 国产内射老熟女aaaa | 久久久久久a亚洲欧洲av冫 | 天天做天天爱天天爽综合网 | 无码纯肉视频在线观看 | 色综合久久久久综合一本到桃花网 | 亚洲精品一区三区三区在线观看 | 一本久久a久久精品亚洲 | 成人aaa片一区国产精品 | 熟妇人妻无乱码中文字幕 | 十八禁视频网站在线观看 | 色一情一乱一伦一视频免费看 | 亚洲乱码日产精品bd | 久久久久久久人妻无码中文字幕爆 | 精品无码av一区二区三区 | 麻豆av传媒蜜桃天美传媒 | 久久无码人妻影院 | 永久免费观看国产裸体美女 | 色窝窝无码一区二区三区色欲 | 一本久久伊人热热精品中文字幕 | 黑森林福利视频导航 | 香蕉久久久久久av成人 | 国产成人无码a区在线观看视频app | 无码成人精品区在线观看 | 亚洲欧美日韩国产精品一区二区 | 天海翼激烈高潮到腰振不止 | 久久久久se色偷偷亚洲精品av | 婷婷六月久久综合丁香 | 88国产精品欧美一区二区三区 | 中文字幕av日韩精品一区二区 | 成 人影片 免费观看 | 久久无码专区国产精品s | 少妇太爽了在线观看 | 久久五月精品中文字幕 | 久久精品国产一区二区三区肥胖 | 国产乱人偷精品人妻a片 | 好男人社区资源 | 亚洲男人av香蕉爽爽爽爽 | 成熟妇人a片免费看网站 | 国产福利视频一区二区 | 亚洲人交乣女bbw | www国产亚洲精品久久网站 | 一本色道久久综合亚洲精品不卡 | 正在播放东北夫妻内射 | 国产午夜无码视频在线观看 | 真人与拘做受免费视频一 | 一区二区传媒有限公司 | 精品无码国产一区二区三区av | 国产人妖乱国产精品人妖 | 熟女俱乐部五十路六十路av | 樱花草在线播放免费中文 | 无码国产激情在线观看 | 真人与拘做受免费视频一 | 亚洲国产精品成人久久蜜臀 | 无码人妻精品一区二区三区不卡 | 中文字幕人妻无码一夲道 | 国产精品嫩草久久久久 | 国产黄在线观看免费观看不卡 | 亚洲精品一区二区三区在线 | 亚洲欧美日韩综合久久久 | 亚洲一区二区三区四区 | 我要看www免费看插插视频 | 人妻有码中文字幕在线 | 牛和人交xxxx欧美 | 一区二区三区高清视频一 | 日本护士xxxxhd少妇 | 秋霞成人午夜鲁丝一区二区三区 | 国内少妇偷人精品视频免费 | 狠狠综合久久久久综合网 | 中国女人内谢69xxxxxa片 | 国产三级久久久精品麻豆三级 | 亚洲天堂2017无码中文 | 亚洲精品综合一区二区三区在线 | 中文字幕乱码人妻无码久久 | 久久久久久久人妻无码中文字幕爆 | 香港三级日本三级妇三级 | 欧美放荡的少妇 | 亚洲无人区一区二区三区 | 亚洲精品国偷拍自产在线麻豆 | 少妇邻居内射在线 | 97夜夜澡人人爽人人喊中国片 | 亚洲人成无码网www | 精品厕所偷拍各类美女tp嘘嘘 | 伊人久久大香线焦av综合影院 | √8天堂资源地址中文在线 | 国产97在线 | 亚洲 | 日韩 欧美 动漫 国产 制服 | 亚洲最大成人网站 | 国产又爽又猛又粗的视频a片 | 亚洲精品欧美二区三区中文字幕 | 欧美激情综合亚洲一二区 | 丝袜美腿亚洲一区二区 | 精品久久久久久人妻无码中文字幕 | 亚洲精品一区二区三区婷婷月 | 成人亚洲精品久久久久软件 | 婷婷综合久久中文字幕蜜桃三电影 | 亚洲成av人片天堂网无码】 | 日本饥渴人妻欲求不满 | 婷婷丁香五月天综合东京热 | 人妻aⅴ无码一区二区三区 | 1000部夫妻午夜免费 | 欧美丰满少妇xxxx性 | 黑人巨大精品欧美黑寡妇 | 日韩av无码一区二区三区 | 亚洲の无码国产の无码影院 | 日韩精品无码免费一区二区三区 | 中文字幕无码日韩专区 | 99久久精品国产一区二区蜜芽 | 午夜无码人妻av大片色欲 | 国产在线精品一区二区高清不卡 | 一本色道婷婷久久欧美 | 亚洲国产精品成人久久蜜臀 | 初尝人妻少妇中文字幕 | 窝窝午夜理论片影院 | 日本精品少妇一区二区三区 | 国产精品对白交换视频 | 亚洲熟妇色xxxxx欧美老妇 | 成人无码精品一区二区三区 | 国产亚洲欧美在线专区 | 亚洲热妇无码av在线播放 | 欧美日本免费一区二区三区 | 国产偷自视频区视频 | 黑人玩弄人妻中文在线 | 久久综合久久自在自线精品自 | 极品尤物被啪到呻吟喷水 | 亚洲欧美日韩国产精品一区二区 | 无码任你躁久久久久久久 | 永久黄网站色视频免费直播 | 欧美日韩在线亚洲综合国产人 | 亚洲一区二区三区国产精华液 | 综合网日日天干夜夜久久 | 亲嘴扒胸摸屁股激烈网站 | 小鲜肉自慰网站xnxx | 国产成人午夜福利在线播放 | 国产综合在线观看 | 在线视频网站www色 | 中文字幕无码日韩专区 | 国产亚洲日韩欧美另类第八页 | 日韩精品成人一区二区三区 | 国产乱人伦av在线无码 | 最近的中文字幕在线看视频 | 久久国产精品_国产精品 | 精品久久久无码中文字幕 | 中文字幕无码免费久久99 | 波多野结衣av一区二区全免费观看 | 亚洲人亚洲人成电影网站色 | 97精品国产97久久久久久免费 | 日本丰满熟妇videos | 丝袜人妻一区二区三区 | 99久久99久久免费精品蜜桃 | 亚洲精品综合一区二区三区在线 | 久久久无码中文字幕久... | 大色综合色综合网站 | 亚拍精品一区二区三区探花 | 人人妻人人澡人人爽人人精品浪潮 | 精品国产乱码久久久久乱码 | 国产亚洲美女精品久久久2020 | 亚洲国产一区二区三区在线观看 | 国产一区二区不卡老阿姨 | 沈阳熟女露脸对白视频 | 日日摸天天摸爽爽狠狠97 | 亚洲欧洲日本综合aⅴ在线 | 国产在线精品一区二区三区直播 | 亚洲日韩av片在线观看 | 日本在线高清不卡免费播放 | 亚洲欧美综合区丁香五月小说 | 99久久人妻精品免费一区 | 人妻尝试又大又粗久久 | 久久综合给久久狠狠97色 | 精品人妻人人做人人爽 | 亚洲の无码国产の无码步美 | 亚洲国产成人av在线观看 | 欧美日韩一区二区三区自拍 | 国产精品亚洲五月天高清 | a国产一区二区免费入口 | 国产绳艺sm调教室论坛 | 又黄又爽又色的视频 | 又粗又大又硬又长又爽 | 精品无码成人片一区二区98 | 亚洲 另类 在线 欧美 制服 | 麻豆精产国品 | 国产真人无遮挡作爱免费视频 | 疯狂三人交性欧美 | 久久国语露脸国产精品电影 | 国产乱码精品一品二品 | 国产免费久久久久久无码 | 亲嘴扒胸摸屁股激烈网站 | 国产熟妇高潮叫床视频播放 | 亚拍精品一区二区三区探花 | 亚洲国产成人av在线观看 | 领导边摸边吃奶边做爽在线观看 | 永久免费观看美女裸体的网站 | 国产精品鲁鲁鲁 | 99久久人妻精品免费二区 | 午夜福利不卡在线视频 | 好爽又高潮了毛片免费下载 | 亚洲欧美日韩综合久久久 | 131美女爱做视频 | 国产九九九九九九九a片 | 欧美乱妇无乱码大黄a片 | 天天摸天天透天天添 | 亚洲高清偷拍一区二区三区 | 精品国产青草久久久久福利 | 九九热爱视频精品 | 99久久久无码国产aaa精品 | 精品亚洲成av人在线观看 | 欧洲精品码一区二区三区免费看 | 精品国偷自产在线视频 | 色诱久久久久综合网ywww | 日日夜夜撸啊撸 | 少妇人妻大乳在线视频 | 国产精品沙发午睡系列 | 人妻有码中文字幕在线 | 欧美 日韩 人妻 高清 中文 | 久久精品国产日本波多野结衣 | 欧美精品在线观看 | 精品一区二区三区无码免费视频 | 又大又硬又爽免费视频 | 性色欲情网站iwww九文堂 | 国内精品久久久久久中文字幕 | 装睡被陌生人摸出水好爽 | 高潮毛片无遮挡高清免费视频 | 国产乱码精品一品二品 | 国产三级久久久精品麻豆三级 | а天堂中文在线官网 | 无遮挡国产高潮视频免费观看 | 久久综合香蕉国产蜜臀av | 久久国语露脸国产精品电影 | 亚洲中文字幕久久无码 | 久久成人a毛片免费观看网站 | 夜精品a片一区二区三区无码白浆 | 亚无码乱人伦一区二区 | 精品人妻人人做人人爽夜夜爽 | 久久国产精品萌白酱免费 | 亚洲日韩av一区二区三区四区 | 色老头在线一区二区三区 | 成人性做爰aaa片免费看不忠 | 欧美熟妇另类久久久久久不卡 | 水蜜桃色314在线观看 | 欧美性黑人极品hd | 欧美人与物videos另类 | 九九在线中文字幕无码 | 精品国产av色一区二区深夜久久 | 日本饥渴人妻欲求不满 | 99久久久无码国产精品免费 | 鲁大师影院在线观看 | 图片小说视频一区二区 | 一本色道久久综合亚洲精品不卡 | 亚洲精品一区三区三区在线观看 | 国产亚av手机在线观看 | 精品一区二区三区无码免费视频 | 国产在热线精品视频 | 国产一区二区三区精品视频 | 国产熟妇高潮叫床视频播放 | 亚洲爆乳大丰满无码专区 | 99久久婷婷国产综合精品青草免费 | 久久综合激激的五月天 | 中文字幕无线码 | 国产成人一区二区三区在线观看 | 伊人久久婷婷五月综合97色 | 国产一区二区不卡老阿姨 | 中文字幕人成乱码熟女app | 国产婷婷色一区二区三区在线 | 玩弄人妻少妇500系列视频 | 亚洲日本va中文字幕 | 国产精品va在线观看无码 | 欧美xxxx黑人又粗又长 | 少妇人妻大乳在线视频 | 国产人妖乱国产精品人妖 | 国产亚洲精品久久久久久大师 | 黑森林福利视频导航 | 巨爆乳无码视频在线观看 | 女人高潮内射99精品 | 午夜精品久久久内射近拍高清 | 性啪啪chinese东北女人 | 久久99久久99精品中文字幕 | 在线播放亚洲第一字幕 | 成人无码视频免费播放 | 国产精品久久久久久久影院 | 国产猛烈高潮尖叫视频免费 | 中文久久乱码一区二区 | 丰满人妻精品国产99aⅴ | 午夜福利一区二区三区在线观看 | 成在人线av无码免观看麻豆 | 亚洲国产精品久久久天堂 | 中文字幕无码av激情不卡 | 88国产精品欧美一区二区三区 | 丰满人妻精品国产99aⅴ | 美女毛片一区二区三区四区 | 亚洲国产高清在线观看视频 | 精品无码国产自产拍在线观看蜜 | 日韩欧美中文字幕在线三区 | 在线a亚洲视频播放在线观看 | 精品久久综合1区2区3区激情 | 国产舌乚八伦偷品w中 | 亚洲人成网站免费播放 | 麻豆果冻传媒2021精品传媒一区下载 | 国产真人无遮挡作爱免费视频 | 亚洲国产综合无码一区 | а√资源新版在线天堂 | 精品水蜜桃久久久久久久 | 青青草原综合久久大伊人精品 | 亚洲精品中文字幕久久久久 | 国产午夜无码视频在线观看 | a在线观看免费网站大全 | 18禁止看的免费污网站 | 狂野欧美性猛交免费视频 | 牲欲强的熟妇农村老妇女 | 亚洲精品一区三区三区在线观看 | 97精品人妻一区二区三区香蕉 | 久久久久99精品国产片 | 狠狠cao日日穞夜夜穞av | 亚洲精品久久久久久久久久久 | 国产亚洲精品久久久久久久 | 成人精品视频一区二区三区尤物 | 欧洲欧美人成视频在线 | 精品国精品国产自在久国产87 | 99久久无码一区人妻 | 88国产精品欧美一区二区三区 | 国产精品亚洲综合色区韩国 | 日韩精品无码一本二本三本色 | 一本久久a久久精品vr综合 | 秋霞成人午夜鲁丝一区二区三区 | 久热国产vs视频在线观看 | 又大又紧又粉嫩18p少妇 | 亚洲の无码国产の无码步美 | 亚洲理论电影在线观看 | 波多野结衣aⅴ在线 | 亚洲爆乳无码专区 | 国产亲子乱弄免费视频 | 精品久久久无码中文字幕 | 色欲人妻aaaaaaa无码 | 国内精品人妻无码久久久影院蜜桃 | 少妇一晚三次一区二区三区 | 激情内射亚州一区二区三区爱妻 | 国内揄拍国内精品少妇国语 | 荫蒂添的好舒服视频囗交 | 人人妻人人澡人人爽欧美一区 | 久久天天躁夜夜躁狠狠 | aa片在线观看视频在线播放 | 天天躁夜夜躁狠狠是什么心态 | 乱码午夜-极国产极内射 | 欧美日韩人成综合在线播放 | 亚洲精品美女久久久久久久 | 秋霞特色aa大片 | 亚洲精品国偷拍自产在线麻豆 | 天天av天天av天天透 | 亚洲啪av永久无码精品放毛片 | 青青青爽视频在线观看 | 欧美 亚洲 国产 另类 | 亚洲熟女一区二区三区 | 成人亚洲精品久久久久软件 | 久久人人爽人人爽人人片av高清 | 亚洲一区二区三区四区 | 国产成人av免费观看 | 日韩视频 中文字幕 视频一区 | 在线观看国产一区二区三区 | 3d动漫精品啪啪一区二区中 | 免费网站看v片在线18禁无码 | 小泽玛莉亚一区二区视频在线 | 国产精品第一国产精品 | 日韩无套无码精品 | 精品欧洲av无码一区二区三区 | 又色又爽又黄的美女裸体网站 | 67194成是人免费无码 | 黄网在线观看免费网站 | 中文字幕 人妻熟女 | 99久久久国产精品无码免费 | 人人妻在人人 | 亚洲精品中文字幕乱码 | 中文字幕无码日韩欧毛 | 一二三四在线观看免费视频 | 性生交片免费无码看人 | 精品厕所偷拍各类美女tp嘘嘘 | 午夜福利不卡在线视频 | 国产福利视频一区二区 | 精品久久久无码中文字幕 | 免费人成在线视频无码 | 性做久久久久久久久 | 国产区女主播在线观看 | 51国偷自产一区二区三区 | 亚洲国产精品久久久久久 | 自拍偷自拍亚洲精品10p | 美女黄网站人色视频免费国产 | 大乳丰满人妻中文字幕日本 | 国产明星裸体无码xxxx视频 | 性色欲网站人妻丰满中文久久不卡 | 亚洲综合色区中文字幕 | 国精产品一区二区三区 | 日韩精品成人一区二区三区 | 国产一区二区三区精品视频 | 国产两女互慰高潮视频在线观看 | 天堂久久天堂av色综合 | 亚洲成a人片在线观看无码 | 国产精品无码一区二区三区不卡 | 免费观看又污又黄的网站 | 久久综合给合久久狠狠狠97色 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 亚洲熟妇自偷自拍另类 | 久久久无码中文字幕久... | 久久精品国产大片免费观看 | 天天躁日日躁狠狠躁免费麻豆 | 国产人妻人伦精品1国产丝袜 | 亚洲一区二区三区国产精华液 | 亚洲经典千人经典日产 | 国产成人一区二区三区别 | 狠狠色噜噜狠狠狠7777奇米 | 中文无码精品a∨在线观看不卡 | 少妇性l交大片欧洲热妇乱xxx | 日韩成人一区二区三区在线观看 | 丰满少妇人妻久久久久久 | 强伦人妻一区二区三区视频18 | 三上悠亚人妻中文字幕在线 | 亚洲一区av无码专区在线观看 | 欧美精品一区二区精品久久 | 亚洲一区av无码专区在线观看 | 国产av无码专区亚洲a∨毛片 | 国产亚洲欧美在线专区 | 青青青爽视频在线观看 | 国产精品视频免费播放 | 久久精品女人的天堂av | 狠狠色色综合网站 | 黑人巨大精品欧美一区二区 | 又黄又爽又色的视频 | 中文字幕乱码亚洲无线三区 | 男女猛烈xx00免费视频试看 | 亚洲色成人中文字幕网站 | 97久久国产亚洲精品超碰热 | 日本精品人妻无码免费大全 | 亚洲精品鲁一鲁一区二区三区 | 精品午夜福利在线观看 | 最近免费中文字幕中文高清百度 | 扒开双腿疯狂进出爽爽爽视频 | 成人精品视频一区二区三区尤物 | 国产av无码专区亚洲a∨毛片 | 国产农村乱对白刺激视频 | 欧美激情综合亚洲一二区 | 久久久久免费精品国产 | 少妇性俱乐部纵欲狂欢电影 | 久久久久成人精品免费播放动漫 | 四虎4hu永久免费 | 97夜夜澡人人爽人人喊中国片 | 亚洲一区二区三区含羞草 | 97无码免费人妻超级碰碰夜夜 | 久久精品中文字幕一区 | 日产精品高潮呻吟av久久 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 国产精品内射视频免费 | 国产超碰人人爽人人做人人添 | 国语精品一区二区三区 | 偷窥村妇洗澡毛毛多 | 亚洲天堂2017无码中文 | 兔费看少妇性l交大片免费 | 丁香啪啪综合成人亚洲 | 亚洲男女内射在线播放 | 国内揄拍国内精品人妻 | 国产高清不卡无码视频 | 国产一区二区三区四区五区加勒比 | 97久久超碰中文字幕 | 人妻夜夜爽天天爽三区 | 国产成人无码av片在线观看不卡 | 东京一本一道一二三区 | 丰满妇女强制高潮18xxxx | 国产精品鲁鲁鲁 | 18禁黄网站男男禁片免费观看 | 鲁鲁鲁爽爽爽在线视频观看 | 亚洲精品一区二区三区在线观看 | 国产两女互慰高潮视频在线观看 | 日本欧美一区二区三区乱码 | 又色又爽又黄的美女裸体网站 | 亚洲第一无码av无码专区 | 色五月丁香五月综合五月 | 午夜性刺激在线视频免费 | 四虎永久在线精品免费网址 | 高潮毛片无遮挡高清免费视频 | 99久久精品无码一区二区毛片 | 丰满肥臀大屁股熟妇激情视频 | 国产色精品久久人妻 | 国产莉萝无码av在线播放 | 人妻无码αv中文字幕久久琪琪布 | 成在人线av无码免费 | 性欧美熟妇videofreesex | 在线 国产 欧美 亚洲 天堂 | 乱码av麻豆丝袜熟女系列 | 性色欲网站人妻丰满中文久久不卡 | 欧美喷潮久久久xxxxx | 色五月五月丁香亚洲综合网 | 一本色道久久综合狠狠躁 | 久久综合狠狠综合久久综合88 | 少妇人妻av毛片在线看 | 国语精品一区二区三区 | 无人区乱码一区二区三区 | 日韩av激情在线观看 | 国产精品久久久一区二区三区 | 无套内谢老熟女 | 亚洲精品一区二区三区在线 | 无码人妻出轨黑人中文字幕 | 精品国产精品久久一区免费式 | 日本熟妇乱子伦xxxx | 狠狠综合久久久久综合网 | 亚洲男人av香蕉爽爽爽爽 | 精品乱码久久久久久久 | 大地资源网第二页免费观看 | 亚洲精品成人福利网站 | 欧美丰满熟妇xxxx性ppx人交 | 伊人久久大香线焦av综合影院 | 成人无码视频免费播放 | 装睡被陌生人摸出水好爽 | 又大又硬又黄的免费视频 | 国产午夜精品一区二区三区嫩草 | av在线亚洲欧洲日产一区二区 | 日韩精品乱码av一区二区 | 亚洲欧美国产精品久久 | 亚洲精品成人福利网站 | 国产精品高潮呻吟av久久4虎 | 在线а√天堂中文官网 | 又紧又大又爽精品一区二区 | 综合人妻久久一区二区精品 | 亚洲中文无码av永久不收费 | 国产精品a成v人在线播放 | 天堂在线观看www | 乱人伦中文视频在线观看 | 夜精品a片一区二区三区无码白浆 | 欧美猛少妇色xxxxx | 熟女体下毛毛黑森林 | 精品国产一区二区三区av 性色 | 亚洲精品国产第一综合99久久 | 伊人久久大香线蕉av一区二区 | 2020久久香蕉国产线看观看 | 大肉大捧一进一出好爽视频 | 亚洲一区二区观看播放 | 荫蒂被男人添的好舒服爽免费视频 | 人妻插b视频一区二区三区 | 国产疯狂伦交大片 | 国产av人人夜夜澡人人爽麻豆 | 美女黄网站人色视频免费国产 | 又大又硬又黄的免费视频 | 99久久亚洲精品无码毛片 | 欧美日本日韩 | 国产亚洲精品久久久久久久 | 婷婷丁香五月天综合东京热 | 无码一区二区三区在线 | 东京无码熟妇人妻av在线网址 | 精品国产青草久久久久福利 | 欧美乱妇无乱码大黄a片 | 亚洲s色大片在线观看 | 国产内射爽爽大片视频社区在线 | 亚洲精品一区二区三区大桥未久 | 久久无码中文字幕免费影院蜜桃 | 无码福利日韩神码福利片 | 野狼第一精品社区 | 免费国产黄网站在线观看 | 欧美丰满老熟妇xxxxx性 | 欧美国产亚洲日韩在线二区 | 久久久久久亚洲精品a片成人 | 午夜精品久久久久久久久 | 99久久人妻精品免费一区 | 日韩人妻系列无码专区 | 在线观看免费人成视频 | 熟女体下毛毛黑森林 | 色综合久久久久综合一本到桃花网 | 综合人妻久久一区二区精品 | 精品国产aⅴ无码一区二区 | 国产精品人人爽人人做我的可爱 | 亚洲色无码一区二区三区 | 国产香蕉尹人综合在线观看 | 领导边摸边吃奶边做爽在线观看 | 久久精品中文字幕一区 | 成人无码视频在线观看网站 | 日欧一片内射va在线影院 | 色噜噜亚洲男人的天堂 | 欧美丰满老熟妇xxxxx性 | 亚洲欧美精品伊人久久 | 国产激情精品一区二区三区 | 欧美日韩人成综合在线播放 | 精品乱子伦一区二区三区 | 日本在线高清不卡免费播放 | 国产乱人伦av在线无码 | 色 综合 欧美 亚洲 国产 | 欧美日韩综合一区二区三区 | 国产尤物精品视频 | 熟妇激情内射com | 亚洲一区二区三区播放 | 少妇无套内谢久久久久 | 亚洲 a v无 码免 费 成 人 a v | 欧洲极品少妇 | 图片区 小说区 区 亚洲五月 | 亚洲高清偷拍一区二区三区 | 人人澡人人妻人人爽人人蜜桃 | 亚洲精品成人av在线 | 精品乱子伦一区二区三区 | 成人免费视频视频在线观看 免费 | 天天摸天天碰天天添 | 欧美成人午夜精品久久久 | 日韩精品一区二区av在线 | 国产精品无码久久av | 88国产精品欧美一区二区三区 | 天堂一区人妻无码 | 成人性做爰aaa片免费看不忠 | 最新国产麻豆aⅴ精品无码 | 婷婷综合久久中文字幕蜜桃三电影 | 7777奇米四色成人眼影 | 久久午夜夜伦鲁鲁片无码免费 | 精品偷自拍另类在线观看 | 亚洲成av人在线观看网址 | 亚洲爆乳大丰满无码专区 | 国产手机在线αⅴ片无码观看 | 欧美人妻一区二区三区 | 97久久国产亚洲精品超碰热 | 蜜桃臀无码内射一区二区三区 | 熟妇女人妻丰满少妇中文字幕 | 亚洲热妇无码av在线播放 | 性欧美熟妇videofreesex | 性欧美牲交xxxxx视频 | 动漫av一区二区在线观看 | 初尝人妻少妇中文字幕 | 免费观看又污又黄的网站 | 免费国产成人高清在线观看网站 | 亚洲男人av香蕉爽爽爽爽 | 国产无遮挡又黄又爽免费视频 | av在线亚洲欧洲日产一区二区 | 小泽玛莉亚一区二区视频在线 | 红桃av一区二区三区在线无码av | 国产免费久久精品国产传媒 | 88国产精品欧美一区二区三区 | 国内精品九九久久久精品 | 日本饥渴人妻欲求不满 | 午夜无码人妻av大片色欲 | 色欲人妻aaaaaaa无码 | 人人爽人人爽人人片av亚洲 | 国产美女精品一区二区三区 | 婷婷五月综合缴情在线视频 | 99er热精品视频 | 国产无av码在线观看 | 97久久超碰中文字幕 | 午夜免费福利小电影 | 在线播放无码字幕亚洲 | 国产av无码专区亚洲a∨毛片 | 久久综合网欧美色妞网 | 亚洲人成影院在线无码按摩店 | 国产精品无码成人午夜电影 | 亚洲欧美综合区丁香五月小说 | 亚洲高清偷拍一区二区三区 | 日韩av激情在线观看 | 婷婷六月久久综合丁香 | 欧美午夜特黄aaaaaa片 | 亚洲国产日韩a在线播放 | 99久久人妻精品免费一区 | 亚洲人成网站色7799 | 国产精品久久久久久亚洲影视内衣 | 国产精品国产自线拍免费软件 | 18精品久久久无码午夜福利 | 国产国语老龄妇女a片 | 免费无码午夜福利片69 | 日本精品久久久久中文字幕 | 久久精品无码一区二区三区 | 精品一区二区三区波多野结衣 | 国产成人精品无码播放 | 动漫av一区二区在线观看 | 牲欲强的熟妇农村老妇女 | 欧美性黑人极品hd | 成 人 网 站国产免费观看 | 无码午夜成人1000部免费视频 | 久久亚洲日韩精品一区二区三区 | 国产亚洲精品精品国产亚洲综合 | 麻豆av传媒蜜桃天美传媒 | 成人免费视频视频在线观看 免费 | 久久97精品久久久久久久不卡 | 国产成人精品视频ⅴa片软件竹菊 | 国产亚洲精品久久久久久久久动漫 | 欧美 丝袜 自拍 制服 另类 | 中文字幕 亚洲精品 第1页 | 无码人中文字幕 | 亚洲毛片av日韩av无码 | 55夜色66夜色国产精品视频 | 97精品人妻一区二区三区香蕉 | 久久精品一区二区三区四区 | 亚洲自偷精品视频自拍 | 日韩av激情在线观看 | 国产9 9在线 | 中文 | 香蕉久久久久久av成人 | 97久久精品无码一区二区 | 国产无遮挡吃胸膜奶免费看 | 丰满肥臀大屁股熟妇激情视频 | 亚洲成av人片在线观看无码不卡 | 亚洲精品国产品国语在线观看 | 荫蒂添的好舒服视频囗交 | 国产成人精品必看 | 最新国产麻豆aⅴ精品无码 | 国产无遮挡吃胸膜奶免费看 | 日日碰狠狠躁久久躁蜜桃 | 成人精品一区二区三区中文字幕 | 一本久久a久久精品亚洲 | 国产精品亚洲综合色区韩国 | 中国大陆精品视频xxxx | 性生交片免费无码看人 | 国产乱码精品一品二品 | 久久精品人人做人人综合 | 亚洲s码欧洲m码国产av | 成人试看120秒体验区 | 黑人巨大精品欧美黑寡妇 | 成人亚洲精品久久久久 | 99久久精品日本一区二区免费 | 欧美激情综合亚洲一二区 | 亚洲男女内射在线播放 | 帮老师解开蕾丝奶罩吸乳网站 | 久久伊人色av天堂九九小黄鸭 | 最新国产乱人伦偷精品免费网站 | 国产午夜精品一区二区三区嫩草 | 国产又爽又猛又粗的视频a片 | 国产成人一区二区三区在线观看 | 精品无码一区二区三区爱欲 | 欧美第一黄网免费网站 | 奇米影视7777久久精品人人爽 | 免费无码肉片在线观看 | 国产成人无码一二三区视频 | 免费无码肉片在线观看 | 人妻互换免费中文字幕 | 丰满肥臀大屁股熟妇激情视频 | 婷婷五月综合缴情在线视频 | 性色av无码免费一区二区三区 | 国产人妻精品午夜福利免费 | 亚洲日韩一区二区 | 亚洲乱亚洲乱妇50p | 中文精品无码中文字幕无码专区 | 成人精品天堂一区二区三区 | 日韩av无码中文无码电影 | 人妻无码αv中文字幕久久琪琪布 | 久久综合久久自在自线精品自 | 又湿又紧又大又爽a视频国产 | 国产亚洲欧美日韩亚洲中文色 | 国产黄在线观看免费观看不卡 | 久久精品中文字幕一区 | 精品国产精品久久一区免费式 | 在线观看欧美一区二区三区 | 久久 国产 尿 小便 嘘嘘 | 欧美xxxx黑人又粗又长 | 性做久久久久久久久 | 色综合久久久久综合一本到桃花网 | 97人妻精品一区二区三区 | 亚洲中文无码av永久不收费 | 亚洲日韩精品欧美一区二区 | 亚洲精品中文字幕乱码 | 中文字幕无码乱人伦 | 西西人体www44rt大胆高清 | 秋霞成人午夜鲁丝一区二区三区 | 色综合天天综合狠狠爱 | 精品国产一区二区三区四区 | 日韩亚洲欧美精品综合 | 人妻无码αv中文字幕久久琪琪布 | 精品人妻av区 | 久久久国产一区二区三区 | 久久久av男人的天堂 | 亚洲国产综合无码一区 | 欧美日韩亚洲国产精品 | 国产免费久久久久久无码 | 久久精品国产日本波多野结衣 | 亚洲欧美中文字幕5发布 | 日本一区二区三区免费播放 | 丰满人妻一区二区三区免费视频 | 日本精品久久久久中文字幕 | 日本xxxx色视频在线观看免费 | 无码纯肉视频在线观看 | 高清无码午夜福利视频 | 欧美人与物videos另类 | ass日本丰满熟妇pics | 久久精品女人的天堂av | 少妇邻居内射在线 | 国产午夜亚洲精品不卡下载 | 亚洲精品一区二区三区大桥未久 | 久久精品国产日本波多野结衣 | 久久久久av无码免费网 | 在线精品亚洲一区二区 | 一二三四社区在线中文视频 | 久久精品丝袜高跟鞋 | 国产精品久久久久久久影院 | 国产人妻人伦精品1国产丝袜 | 中文字幕色婷婷在线视频 | 亚洲理论电影在线观看 | 亚洲色大成网站www国产 | 一本精品99久久精品77 | 漂亮人妻洗澡被公强 日日躁 | 无码人妻av免费一区二区三区 | 国产日产欧产精品精品app | 伊人久久大香线蕉av一区二区 | 青草视频在线播放 | 亚洲gv猛男gv无码男同 | 欧美日韩在线亚洲综合国产人 | 国产又粗又硬又大爽黄老大爷视 | 亚洲gv猛男gv无码男同 | 成熟女人特级毛片www免费 | 国产精品视频免费播放 | 熟女俱乐部五十路六十路av | 国产精品亚洲一区二区三区喷水 | 欧洲熟妇精品视频 | 天堂а√在线中文在线 | 国产成人综合美国十次 | 少妇性l交大片欧洲热妇乱xxx | 欧美人妻一区二区三区 | 婷婷综合久久中文字幕蜜桃三电影 | 六月丁香婷婷色狠狠久久 | 亚洲欧美日韩综合久久久 | 天堂无码人妻精品一区二区三区 | 18禁黄网站男男禁片免费观看 | 奇米影视7777久久精品人人爽 | 天堂а√在线中文在线 | 大乳丰满人妻中文字幕日本 | 精品久久久久香蕉网 | 无码人妻av免费一区二区三区 | 人人妻人人澡人人爽人人精品 | 精品无码av一区二区三区 | 国产成人无码一二三区视频 | 波多野结衣av一区二区全免费观看 | 免费人成在线观看网站 | 国模大胆一区二区三区 | 无码av中文字幕免费放 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 亚洲日韩中文字幕在线播放 | 成人精品天堂一区二区三区 | 亚洲小说春色综合另类 | 亚洲人成网站在线播放942 | 丰满妇女强制高潮18xxxx | 国产熟妇高潮叫床视频播放 | 国内少妇偷人精品视频免费 | 漂亮人妻洗澡被公强 日日躁 | 精品久久久久久亚洲精品 | 国产两女互慰高潮视频在线观看 | 久9re热视频这里只有精品 | 国产精品无码永久免费888 | 国产午夜无码视频在线观看 | 国产精品a成v人在线播放 | 日韩人妻无码一区二区三区久久99 | 青青久在线视频免费观看 | 国产精品久免费的黄网站 | 久久国产自偷自偷免费一区调 | 51国偷自产一区二区三区 | 秋霞成人午夜鲁丝一区二区三区 | 大色综合色综合网站 | 亚洲国产一区二区三区在线观看 | 偷窥村妇洗澡毛毛多 | 欧美变态另类xxxx | 图片小说视频一区二区 | 国产成人人人97超碰超爽8 | 人妻体内射精一区二区三四 | 欧美xxxx黑人又粗又长 | 最新版天堂资源中文官网 | 国产精品第一国产精品 | 亚洲色欲久久久综合网东京热 | 欧洲熟妇色 欧美 | 午夜免费福利小电影 | 日日碰狠狠丁香久燥 | 久久精品99久久香蕉国产色戒 | 两性色午夜视频免费播放 | 真人与拘做受免费视频一 | 国产福利视频一区二区 | 亚洲成a人一区二区三区 | 亚洲欧洲无卡二区视頻 | 国产情侣作爱视频免费观看 | 纯爱无遮挡h肉动漫在线播放 | 天天躁夜夜躁狠狠是什么心态 | 国产成人无码av片在线观看不卡 | 国产精品久久国产精品99 | 18无码粉嫩小泬无套在线观看 | 永久免费观看国产裸体美女 | 国产成人无码av一区二区 | 国产乱人无码伦av在线a | 精品一区二区三区无码免费视频 | 无码吃奶揉捏奶头高潮视频 | 亚洲日韩一区二区三区 | 国内精品人妻无码久久久影院 | 国产99久久精品一区二区 | 日日摸夜夜摸狠狠摸婷婷 | 麻豆md0077饥渴少妇 | 久久久久免费精品国产 | 亚洲经典千人经典日产 | 最近的中文字幕在线看视频 | 网友自拍区视频精品 | 妺妺窝人体色www婷婷 | 国产精品久久久午夜夜伦鲁鲁 | a国产一区二区免费入口 | 狠狠噜狠狠狠狠丁香五月 | 国产精品亚洲五月天高清 | 中文字幕无码视频专区 | 久久精品国产精品国产精品污 | 精品成在人线av无码免费看 | 成 人影片 免费观看 | 蜜臀av无码人妻精品 | 日本又色又爽又黄的a片18禁 | 骚片av蜜桃精品一区 | 亚洲综合在线一区二区三区 | 丰满少妇女裸体bbw | 兔费看少妇性l交大片免费 | 国产两女互慰高潮视频在线观看 | 未满小14洗澡无码视频网站 | 性欧美牲交xxxxx视频 | www国产亚洲精品久久网站 | 无码免费一区二区三区 | 国产美女精品一区二区三区 | 国产福利视频一区二区 | 激情内射亚州一区二区三区爱妻 | 亚洲a无码综合a国产av中文 | 精品成在人线av无码免费看 | 日本熟妇人妻xxxxx人hd | 欧美日韩精品 | 亚洲精品国产品国语在线观看 | 精品日本一区二区三区在线观看 | 国产精品手机免费 | 丰满少妇女裸体bbw | 欧美丰满熟妇xxxx | 老熟女乱子伦 | 成人亚洲精品久久久久 | 日本熟妇人妻xxxxx人hd | 精品一二三区久久aaa片 | 美女黄网站人色视频免费国产 | 麻豆国产人妻欲求不满 | 亚洲色大成网站www | 一本久久伊人热热精品中文字幕 | 夜先锋av资源网站 | 欧美兽交xxxx×视频 | a片免费视频在线观看 | 色综合久久中文娱乐网 | 鲁一鲁av2019在线 | 久久精品人妻少妇一区二区三区 | 国精产品一品二品国精品69xx | 人妻少妇精品视频专区 | 中文无码成人免费视频在线观看 | 成人无码视频在线观看网站 | 97色伦图片97综合影院 | 国产美女极度色诱视频www | 国内精品人妻无码久久久影院蜜桃 | 波多野结衣高清一区二区三区 | 婷婷丁香五月天综合东京热 | 成人无码视频免费播放 | 亚洲熟妇色xxxxx欧美老妇 | 免费乱码人妻系列无码专区 | 久久国产自偷自偷免费一区调 | 国产疯狂伦交大片 | 真人与拘做受免费视频一 | 伊人色综合久久天天小片 | 麻豆国产97在线 | 欧洲 | 久久久www成人免费毛片 | 国产一区二区三区日韩精品 | 草草网站影院白丝内射 | 久久精品人妻少妇一区二区三区 | 欧美猛少妇色xxxxx | 亚洲人成影院在线无码按摩店 | 国产精品沙发午睡系列 | 2020久久香蕉国产线看观看 | 色婷婷久久一区二区三区麻豆 | 精品人妻人人做人人爽夜夜爽 | 色综合视频一区二区三区 | 亚洲自偷自偷在线制服 | 又大又硬又爽免费视频 | 中文字幕av无码一区二区三区电影 | 伊人久久大香线蕉午夜 | 欧美激情内射喷水高潮 | 亚洲小说春色综合另类 | 色一情一乱一伦 | 久久午夜无码鲁丝片 | 中文字幕亚洲情99在线 | 精品人妻av区 | 国产网红无码精品视频 | 性欧美videos高清精品 | 午夜精品一区二区三区在线观看 | 精品 日韩 国产 欧美 视频 | 亚洲 a v无 码免 费 成 人 a v | 少妇性l交大片欧洲热妇乱xxx | 亚洲日韩av一区二区三区中文 | 极品尤物被啪到呻吟喷水 | 午夜无码区在线观看 | 中文字幕人妻无码一夲道 | 亚洲精品成a人在线观看 | 欧美日韩人成综合在线播放 | 日产精品高潮呻吟av久久 | 国产在线aaa片一区二区99 | 国产亚洲精品久久久久久国模美 | 人妻体内射精一区二区三四 | 国产激情一区二区三区 | 亚洲人亚洲人成电影网站色 | 欧美精品无码一区二区三区 | 少妇一晚三次一区二区三区 | 强辱丰满人妻hd中文字幕 | 久久久中文久久久无码 | 福利一区二区三区视频在线观看 | 中文字幕无码av激情不卡 | 精品欧美一区二区三区久久久 | 色五月五月丁香亚洲综合网 | 国产性生大片免费观看性 | 2020久久香蕉国产线看观看 | 亚洲欧美色中文字幕在线 | 欧美丰满熟妇xxxx | 一本久久a久久精品亚洲 | 亚洲 另类 在线 欧美 制服 | 国产精品久免费的黄网站 | 美女黄网站人色视频免费国产 | 亚洲一区av无码专区在线观看 | 欧美老人巨大xxxx做受 | 久久综合久久自在自线精品自 | 性做久久久久久久久 | 亚洲の无码国产の无码影院 | 亚洲 另类 在线 欧美 制服 | 中文毛片无遮挡高清免费 | 天海翼激烈高潮到腰振不止 | 99久久婷婷国产综合精品青草免费 | 无遮挡国产高潮视频免费观看 | 极品嫩模高潮叫床 | 女人被男人爽到呻吟的视频 | 久久精品国产亚洲精品 | 国产精品永久免费视频 | 一本大道久久东京热无码av | 午夜精品久久久内射近拍高清 | 人妻无码αv中文字幕久久琪琪布 | 中文字幕色婷婷在线视频 | 少妇厨房愉情理9仑片视频 | √天堂资源地址中文在线 | 亚洲日韩一区二区三区 | 内射白嫩少妇超碰 | 中文无码成人免费视频在线观看 | 狠狠色欧美亚洲狠狠色www | 成在人线av无码免观看麻豆 | 18无码粉嫩小泬无套在线观看 | 国产亚洲视频中文字幕97精品 | 久久国产精品萌白酱免费 | 国产成人综合色在线观看网站 | 中文字幕人成乱码熟女app | 亚洲色在线无码国产精品不卡 | 免费中文字幕日韩欧美 | 欧美 日韩 亚洲 在线 | 爆乳一区二区三区无码 | 亚洲成av人片在线观看无码不卡 | 国产网红无码精品视频 | 中国女人内谢69xxxx | 成人三级无码视频在线观看 | 色欲av亚洲一区无码少妇 | 久久精品无码一区二区三区 | 精品国产乱码久久久久乱码 | 国产av一区二区三区最新精品 | www国产精品内射老师 | 男女猛烈xx00免费视频试看 | 日本熟妇浓毛 | 熟妇人妻中文av无码 | 青青草原综合久久大伊人精品 | 丝袜 中出 制服 人妻 美腿 | 俺去俺来也在线www色官网 | 欧美性黑人极品hd | 国产美女精品一区二区三区 | 精品一区二区三区波多野结衣 | 影音先锋中文字幕无码 | 精品国产一区二区三区四区 | 久久精品女人的天堂av | 特级做a爰片毛片免费69 | 国产人妻大战黑人第1集 | 中文字幕人妻无码一夲道 | 中文字幕乱妇无码av在线 | 日本大香伊一区二区三区 | 黑森林福利视频导航 | 亚洲s码欧洲m码国产av | 水蜜桃色314在线观看 | 男人扒开女人内裤强吻桶进去 | 国产亚洲欧美在线专区 | 四虎国产精品免费久久 | 天天av天天av天天透 | 天堂а√在线中文在线 | 国产成人综合在线女婷五月99播放 | 人人妻人人澡人人爽精品欧美 | 在线观看免费人成视频 | 亚洲色欲色欲欲www在线 | 2019nv天堂香蕉在线观看 | 国产一精品一av一免费 | 久久精品一区二区三区四区 | 激情五月综合色婷婷一区二区 | 亚洲国产综合无码一区 | 丰满诱人的人妻3 | 国产精品毛多多水多 | 午夜精品久久久久久久 | 色爱情人网站 | 精品国产精品久久一区免费式 | 久久久久av无码免费网 | 国产精品鲁鲁鲁 | 亚洲中文字幕在线无码一区二区 | 亚洲大尺度无码无码专区 | 丰满少妇弄高潮了www | 成人免费无码大片a毛片 | 红桃av一区二区三区在线无码av | 国产精品久久久一区二区三区 | 内射后入在线观看一区 | 国产精品久久久久7777 | 国内少妇偷人精品视频 | 人人妻人人澡人人爽人人精品 | 鲁大师影院在线观看 | 一区二区三区乱码在线 | 欧洲 | 波多野结衣高清一区二区三区 | 国精产品一区二区三区 | 桃花色综合影院 | 国产亚洲精品精品国产亚洲综合 | 亚洲国产综合无码一区 | 高潮毛片无遮挡高清免费 | 天天躁夜夜躁狠狠是什么心态 | 国产精品怡红院永久免费 | 日本精品人妻无码77777 天堂一区人妻无码 | 国产成人精品久久亚洲高清不卡 | 亚洲综合无码一区二区三区 | 无码国模国产在线观看 | 久久精品无码一区二区三区 | 国产av久久久久精东av | 亚洲热妇无码av在线播放 | 狠狠色噜噜狠狠狠7777奇米 | 精品久久久久久亚洲精品 | 亚洲va中文字幕无码久久不卡 | 老太婆性杂交欧美肥老太 | 亚洲男女内射在线播放 | 双乳奶水饱满少妇呻吟 | 成人av无码一区二区三区 | 欧洲欧美人成视频在线 | 国产精品久久久久久久影院 | 国产suv精品一区二区五 | 婷婷综合久久中文字幕蜜桃三电影 | 奇米影视888欧美在线观看 | 熟女俱乐部五十路六十路av | 欧美变态另类xxxx | 日韩人妻无码一区二区三区久久99 | 99精品国产综合久久久久五月天 | 成人欧美一区二区三区黑人免费 | 国产真实乱对白精彩久久 | 国语自产偷拍精品视频偷 | 国产在热线精品视频 | 女人被爽到呻吟gif动态图视看 | 精品欧美一区二区三区久久久 | 任你躁在线精品免费 | 狠狠色噜噜狠狠狠7777奇米 | 中文字幕av无码一区二区三区电影 | 激情内射亚州一区二区三区爱妻 | 国内老熟妇对白xxxxhd | 久久久久亚洲精品男人的天堂 | 波多野结衣aⅴ在线 | 国产真人无遮挡作爱免费视频 | 国产精品爱久久久久久久 | 久久伊人色av天堂九九小黄鸭 | 秋霞成人午夜鲁丝一区二区三区 | 亚洲中文字幕久久无码 | 任你躁国产自任一区二区三区 | 奇米影视7777久久精品 | 在线看片无码永久免费视频 | 国产午夜精品一区二区三区嫩草 | 内射巨臀欧美在线视频 | 在线欧美精品一区二区三区 | 最新国产麻豆aⅴ精品无码 | 成人精品一区二区三区中文字幕 | 老熟女重囗味hdxx69 | 大肉大捧一进一出好爽视频 | 精品国产aⅴ无码一区二区 | 久久久久成人片免费观看蜜芽 | 98国产精品综合一区二区三区 | 无码任你躁久久久久久久 | 色综合久久88色综合天天 | 日日噜噜噜噜夜夜爽亚洲精品 | 久热国产vs视频在线观看 | 性色欲网站人妻丰满中文久久不卡 | 免费无码一区二区三区蜜桃大 | 夜夜影院未满十八勿进 | av人摸人人人澡人人超碰下载 | 丰满人妻被黑人猛烈进入 | 中文字幕精品av一区二区五区 | 娇妻被黑人粗大高潮白浆 | 免费无码肉片在线观看 | 牲交欧美兽交欧美 | 岛国片人妻三上悠亚 | 国产精品亚洲一区二区三区喷水 | 一本久久伊人热热精品中文字幕 | 国产午夜亚洲精品不卡下载 | 亚洲中文字幕在线无码一区二区 | 亚洲熟女一区二区三区 | 无码人妻黑人中文字幕 | 欧美成人高清在线播放 | 欧美精品一区二区精品久久 | 亚洲精品鲁一鲁一区二区三区 | 国产成人av免费观看 | 亚洲中文无码av永久不收费 | 中文字幕无码免费久久9一区9 | 伊在人天堂亚洲香蕉精品区 | 老头边吃奶边弄进去呻吟 | 日本护士毛茸茸高潮 | 欧美第一黄网免费网站 | 亚洲国精产品一二二线 | 国产免费久久久久久无码 | 动漫av网站免费观看 | 国产精品久久久久无码av色戒 | 亚洲色欲色欲欲www在线 | 水蜜桃av无码 | 久久精品中文闷骚内射 | 欧美日韩久久久精品a片 | 色窝窝无码一区二区三区色欲 | 国产午夜亚洲精品不卡 | 精品夜夜澡人妻无码av蜜桃 | 成人影院yy111111在线观看 | 无码吃奶揉捏奶头高潮视频 | 小泽玛莉亚一区二区视频在线 | 欧美日韩精品 | 日日摸日日碰夜夜爽av | 色婷婷欧美在线播放内射 | 国精产品一区二区三区 | 131美女爱做视频 | 久久久久久久女国产乱让韩 | 亚洲中文字幕久久无码 | 色一情一乱一伦一区二区三欧美 | 奇米影视7777久久精品人人爽 | а√天堂www在线天堂小说 | 婷婷五月综合激情中文字幕 | 丝袜美腿亚洲一区二区 | 久久精品国产一区二区三区肥胖 | 色狠狠av一区二区三区 | 国产av一区二区精品久久凹凸 | 亚洲综合无码一区二区三区 | 300部国产真实乱 | 日日躁夜夜躁狠狠躁 | 久久久久99精品成人片 | 少妇厨房愉情理9仑片视频 | 草草网站影院白丝内射 | 麻豆国产人妻欲求不满 | 午夜无码人妻av大片色欲 | 午夜精品久久久久久久 | 2020久久香蕉国产线看观看 | 九月婷婷人人澡人人添人人爽 | 欧美精品免费观看二区 | 中文字幕无码乱人伦 | 天堂亚洲2017在线观看 | 国产在线一区二区三区四区五区 | 午夜福利一区二区三区在线观看 | 国产一区二区三区日韩精品 | 国产成人无码av片在线观看不卡 | 久久精品国产一区二区三区 | 荡女精品导航 | 一本大道伊人av久久综合 | 激情亚洲一区国产精品 | 中国大陆精品视频xxxx | 18禁止看的免费污网站 | 亚洲综合无码久久精品综合 | 久久99国产综合精品 | 少妇性荡欲午夜性开放视频剧场 | 国产va免费精品观看 | 中文字幕无码日韩欧毛 | 色窝窝无码一区二区三区色欲 | 扒开双腿吃奶呻吟做受视频 | 99久久久无码国产aaa精品 | 久久97精品久久久久久久不卡 | 国产精品人人爽人人做我的可爱 | 思思久久99热只有频精品66 | 日本一卡2卡3卡四卡精品网站 | 欧美怡红院免费全部视频 | 色婷婷综合激情综在线播放 | 西西人体www44rt大胆高清 | 国产香蕉尹人综合在线观看 | 风流少妇按摩来高潮 | 少妇高潮喷潮久久久影院 | 免费无码午夜福利片69 | 无码国内精品人妻少妇 | 亚洲欧美色中文字幕在线 | 亚洲色欲久久久综合网东京热 | 一本久道久久综合狠狠爱 | 久久久久久九九精品久 | 强奷人妻日本中文字幕 | 国产69精品久久久久app下载 | 国产精品a成v人在线播放 | 亚洲一区二区三区偷拍女厕 | 亚洲日本一区二区三区在线 | 欧美日韩一区二区三区自拍 | 国产乱人无码伦av在线a | 日本va欧美va欧美va精品 | 少妇无套内谢久久久久 | 狂野欧美激情性xxxx | 狠狠噜狠狠狠狠丁香五月 | 亚拍精品一区二区三区探花 | 麻豆国产人妻欲求不满谁演的 | 亚洲精品中文字幕久久久久 | 中文字幕人妻无码一区二区三区 | 欧美成人高清在线播放 | 午夜时刻免费入口 | 色综合久久久无码网中文 | 欧美zoozzooz性欧美 | 亚洲熟妇自偷自拍另类 | 动漫av一区二区在线观看 | 精品无码av一区二区三区 | 成人无码视频在线观看网站 | 四虎影视成人永久免费观看视频 | 男人扒开女人内裤强吻桶进去 | 欧洲熟妇精品视频 | 国产办公室秘书无码精品99 | 亚洲s码欧洲m码国产av | 国产综合色产在线精品 | 人妻尝试又大又粗久久 | 欧美精品一区二区精品久久 | 亚洲国产综合无码一区 | 亚洲aⅴ无码成人网站国产app | 国产色精品久久人妻 | 日韩精品成人一区二区三区 | 丰满人妻翻云覆雨呻吟视频 | 麻豆国产丝袜白领秘书在线观看 | 国产乱人伦偷精品视频 | 纯爱无遮挡h肉动漫在线播放 | 久久久av男人的天堂 | 精品一区二区三区无码免费视频 | 一本久久a久久精品亚洲 | 亚洲理论电影在线观看 | 国内精品久久毛片一区二区 | 永久免费精品精品永久-夜色 | www国产亚洲精品久久久日本 | 国产口爆吞精在线视频 | 国产精品无码一区二区三区不卡 | 丝袜人妻一区二区三区 | 亚洲欧美日韩综合久久久 | 一本色道婷婷久久欧美 | 亚洲无人区午夜福利码高清完整版 | 国産精品久久久久久久 | 国产激情艳情在线看视频 | 东北女人啪啪对白 | 人人妻人人澡人人爽人人精品浪潮 | 日韩av无码一区二区三区不卡 | 蜜臀av无码人妻精品 | 窝窝午夜理论片影院 | 精品国产精品久久一区免费式 | 久久久久久国产精品无码下载 | 国产性生大片免费观看性 | 性欧美疯狂xxxxbbbb | 亚洲精品成a人在线观看 | 国产精品亚洲专区无码不卡 | 一本色道久久综合亚洲精品不卡 | 无码av中文字幕免费放 | 日本护士毛茸茸高潮 | 色窝窝无码一区二区三区色欲 | 日韩精品成人一区二区三区 | 久久国产自偷自偷免费一区调 | 久久99国产综合精品 | 国产免费久久久久久无码 | 少女韩国电视剧在线观看完整 | 十八禁视频网站在线观看 | 婷婷五月综合缴情在线视频 | 亚洲国产精品美女久久久久 | 亚洲精品成人福利网站 | 亚洲精品一区二区三区在线观看 | 亚洲综合另类小说色区 | 亚洲天堂2017无码 | 宝宝好涨水快流出来免费视频 | 国产精品久久福利网站 | 色窝窝无码一区二区三区色欲 | 久久久久免费精品国产 | 亚洲精品www久久久 | 无码中文字幕色专区 | 久久人人爽人人爽人人片av高清 | 中文字幕日韩精品一区二区三区 | 国产成人无码区免费内射一片色欲 | 无码人妻黑人中文字幕 | 无码免费一区二区三区 | 精品一二三区久久aaa片 | 久久综合久久自在自线精品自 | 欧美乱妇无乱码大黄a片 | 人妻熟女一区 | 狠狠cao日日穞夜夜穞av | 亚洲精品无码国产 | 在线观看国产午夜福利片 | 国产绳艺sm调教室论坛 | 久久精品国产精品国产精品污 | 亚洲色大成网站www | 国产舌乚八伦偷品w中 | 免费中文字幕日韩欧美 | 午夜无码人妻av大片色欲 | 久久综合色之久久综合 | 内射欧美老妇wbb | 天堂无码人妻精品一区二区三区 | 亚洲精品综合五月久久小说 | 成人无码精品1区2区3区免费看 | 一本大道久久东京热无码av | 丰满肥臀大屁股熟妇激情视频 | 国产无套内射久久久国产 | 麻豆人妻少妇精品无码专区 | 国产成人无码专区 | а√天堂www在线天堂小说 | 国产综合久久久久鬼色 | 欧美日韩亚洲国产精品 | 久青草影院在线观看国产 | 黑人玩弄人妻中文在线 | 中文字幕人妻无码一区二区三区 | 亚洲中文字幕乱码av波多ji | 中国大陆精品视频xxxx | 日本一卡2卡3卡四卡精品网站 | 99久久精品国产一区二区蜜芽 | 亚洲区小说区激情区图片区 | 欧美日韩综合一区二区三区 | 国产精品美女久久久久av爽李琼 | 国产乱人伦av在线无码 | 东京热一精品无码av | 亚洲精品欧美二区三区中文字幕 | 激情内射亚州一区二区三区爱妻 | 亚洲爆乳无码专区 | 国产午夜无码精品免费看 | 亚洲一区av无码专区在线观看 | 女人被男人爽到呻吟的视频 | 日韩 欧美 动漫 国产 制服 | 小泽玛莉亚一区二区视频在线 | 国产精品无码久久av | 女人和拘做爰正片视频 | 动漫av一区二区在线观看 | 久久精品国产日本波多野结衣 | 对白脏话肉麻粗话av | 久久99精品国产.久久久久 | www国产亚洲精品久久网站 | 亚洲综合色区中文字幕 | 日日摸夜夜摸狠狠摸婷婷 | 国产精品免费大片 | 国产精品久久久久7777 | 精品无码国产一区二区三区av | 久久亚洲a片com人成 | 乌克兰少妇性做爰 | 国产精品成人av在线观看 | 亚洲精品一区三区三区在线观看 | 国产午夜福利亚洲第一 | 图片小说视频一区二区 | 国产欧美精品一区二区三区 | 伊人久久婷婷五月综合97色 | 人人妻人人澡人人爽精品欧美 | 国产综合久久久久鬼色 | 熟妇人妻中文av无码 | 日本xxxx色视频在线观看免费 | 影音先锋中文字幕无码 | 日韩欧美中文字幕公布 | 国产精品久久福利网站 | 欧美zoozzooz性欧美 | 88国产精品欧美一区二区三区 | 国产熟妇高潮叫床视频播放 | 香蕉久久久久久av成人 | 无码吃奶揉捏奶头高潮视频 | 麻豆果冻传媒2021精品传媒一区下载 | 国产日产欧产精品精品app | 一本加勒比波多野结衣 | 国产内射爽爽大片视频社区在线 | 免费观看又污又黄的网站 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 久久精品一区二区三区四区 | 欧美国产日韩亚洲中文 | 国产精品沙发午睡系列 | 99久久精品日本一区二区免费 | 影音先锋中文字幕无码 | 乱人伦中文视频在线观看 | 亚洲成色www久久网站 | 精品国产乱码久久久久乱码 | 欧美阿v高清资源不卡在线播放 | 色婷婷香蕉在线一区二区 | 亚洲男女内射在线播放 | 亚洲一区二区三区播放 | 小鲜肉自慰网站xnxx | 国产精品成人av在线观看 | 国产午夜视频在线观看 | 国产xxx69麻豆国语对白 | 精品国产一区二区三区四区 | 欧美国产亚洲日韩在线二区 | 双乳奶水饱满少妇呻吟 | 狠狠躁日日躁夜夜躁2020 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 久久99精品国产.久久久久 | 99久久99久久免费精品蜜桃 | 国产精品18久久久久久麻辣 | 亚洲国产av精品一区二区蜜芽 | 免费无码肉片在线观看 | 国产sm调教视频在线观看 | 极品嫩模高潮叫床 | 亚洲中文无码av永久不收费 | 丰满妇女强制高潮18xxxx | 伊人久久婷婷五月综合97色 | 成人无码视频在线观看网站 | 97无码免费人妻超级碰碰夜夜 | 国产精品久久国产三级国 | 欧美日韩一区二区免费视频 | 激情五月综合色婷婷一区二区 | 日韩精品久久久肉伦网站 | 青春草在线视频免费观看 | 人妻中文无码久热丝袜 | 国产三级精品三级男人的天堂 | 亚洲一区二区三区无码久久 | 俄罗斯老熟妇色xxxx | 欧洲欧美人成视频在线 | 奇米影视7777久久精品 | 亚洲日韩一区二区三区 | 日本一区二区三区免费高清 | 国产真实夫妇视频 | 成人三级无码视频在线观看 | 色综合久久88色综合天天 | 久久伊人色av天堂九九小黄鸭 | 麻豆国产97在线 | 欧洲 | 国产成人无码av在线影院 | 亚洲第一无码av无码专区 | 久久精品国产精品国产精品污 | 自拍偷自拍亚洲精品10p | 国内精品人妻无码久久久影院蜜桃 | 国产办公室秘书无码精品99 | 久久久久se色偷偷亚洲精品av | 亚洲国产av精品一区二区蜜芽 | 麻豆精产国品 | 99久久久无码国产aaa精品 | 日本护士xxxxhd少妇 | 午夜丰满少妇性开放视频 | 无码人妻黑人中文字幕 | 大地资源中文第3页 | 欧美丰满少妇xxxx性 | 国产在线无码精品电影网 | 少妇高潮一区二区三区99 | 亚洲一区二区三区无码久久 | 久久综合给合久久狠狠狠97色 | 久久综合香蕉国产蜜臀av | 2019nv天堂香蕉在线观看 | 一个人免费观看的www视频 | 领导边摸边吃奶边做爽在线观看 | 一本无码人妻在中文字幕免费 | 波多野42部无码喷潮在线 | 国产精品18久久久久久麻辣 | 亚洲啪av永久无码精品放毛片 | 国产精品美女久久久 | 一个人看的视频www在线 | 澳门永久av免费网站 | 中文字幕色婷婷在线视频 | 久久久久久久人妻无码中文字幕爆 | 国精产品一区二区三区 | 亚洲成av人片在线观看无码不卡 | 高中生自慰www网站 | av在线亚洲欧洲日产一区二区 | 久久精品人人做人人综合试看 | 小sao货水好多真紧h无码视频 | 国产免费久久久久久无码 | 成人无码视频在线观看网站 | 亚洲小说图区综合在线 | 欧美一区二区三区 | √8天堂资源地址中文在线 | 一个人看的www免费视频在线观看 | 国产精品99爱免费视频 | 免费无码午夜福利片69 | 粉嫩少妇内射浓精videos | 欧美人与动性行为视频 | 亚洲色在线无码国产精品不卡 | 欧美怡红院免费全部视频 | 丰满少妇熟乱xxxxx视频 | 99精品无人区乱码1区2区3区 | 熟女体下毛毛黑森林 | 免费看男女做好爽好硬视频 | 欧美人与动性行为视频 | 国产激情精品一区二区三区 | 无码人妻精品一区二区三区下载 | 国产乱人无码伦av在线a | 欧美野外疯狂做受xxxx高潮 | 欧美日韩精品 | 性欧美videos高清精品 | 亚洲精品中文字幕 | 国产又粗又硬又大爽黄老大爷视 | 亚洲va中文字幕无码久久不卡 | 国产乱人伦偷精品视频 | 欧美黑人巨大xxxxx | 在线 国产 欧美 亚洲 天堂 | 2020最新国产自产精品 | 未满成年国产在线观看 | 国模大胆一区二区三区 | 野外少妇愉情中文字幕 | 国产极品视觉盛宴 | 国产三级久久久精品麻豆三级 | 国产精品怡红院永久免费 | 国内精品九九久久久精品 | 天堂久久天堂av色综合 | 国产亚洲日韩欧美另类第八页 | 人人妻人人澡人人爽人人精品 | 久久人人爽人人爽人人片av高清 | 国产办公室秘书无码精品99 | 国产亚洲日韩欧美另类第八页 | 亚洲精品久久久久久久久久久 | 欧美日韩亚洲国产精品 | 亚洲国精产品一二二线 | 国产一区二区不卡老阿姨 | 少妇性俱乐部纵欲狂欢电影 | 国产在线精品一区二区三区直播 | 国产热a欧美热a在线视频 | 小sao货水好多真紧h无码视频 | 高清国产亚洲精品自在久久 | 国产乱子伦视频在线播放 | 一区二区传媒有限公司 | 亚洲欧洲日本综合aⅴ在线 | 无码国产色欲xxxxx视频 | 国产精品福利视频导航 | 377p欧洲日本亚洲大胆 | 国产精品无码永久免费888 | 亚洲天堂2017无码 | 国产成人一区二区三区在线观看 | 色诱久久久久综合网ywww | 日本va欧美va欧美va精品 | 伊人久久大香线蕉午夜 | 自拍偷自拍亚洲精品10p | 国产精品无套呻吟在线 | 婷婷五月综合激情中文字幕 | 亚洲国产成人a精品不卡在线 | 真人与拘做受免费视频 | 一本色道久久综合狠狠躁 | 曰韩少妇内射免费播放 | 人人妻人人澡人人爽人人精品浪潮 | 国产av无码专区亚洲awww | 精品日本一区二区三区在线观看 | 日本xxxx色视频在线观看免费 | 在线播放无码字幕亚洲 | 欧美肥老太牲交大战 | 色欲av亚洲一区无码少妇 | 夜夜高潮次次欢爽av女 | 欧美国产日韩久久mv | 伊在人天堂亚洲香蕉精品区 | 一区二区三区高清视频一 | 色综合久久中文娱乐网 | 九九热爱视频精品 | 精品久久久久香蕉网 | 久久久亚洲欧洲日产国码αv | 精品国产福利一区二区 | 日本www一道久久久免费榴莲 | 九九在线中文字幕无码 | www国产亚洲精品久久网站 | 国产精品人人妻人人爽 | 少妇性荡欲午夜性开放视频剧场 | 日本护士xxxxhd少妇 | 精品欧美一区二区三区久久久 | 大乳丰满人妻中文字幕日本 | 亚洲综合精品香蕉久久网 | 老太婆性杂交欧美肥老太 | 天堂一区人妻无码 | 少妇人妻大乳在线视频 | 亚洲精品一区国产 | 任你躁国产自任一区二区三区 | 免费无码午夜福利片69 | 国产精品久久久午夜夜伦鲁鲁 | 性色欲网站人妻丰满中文久久不卡 | 中文字幕无码av波多野吉衣 | 黑森林福利视频导航 | 精品无人国产偷自产在线 | 久久久久久av无码免费看大片 | 亚洲日本va中文字幕 | 中文字幕无码乱人伦 | 青草视频在线播放 | 天天av天天av天天透 | 日韩人妻少妇一区二区三区 | 国产成人综合在线女婷五月99播放 | 国产美女精品一区二区三区 | 国产又爽又猛又粗的视频a片 | 亚洲精品国产a久久久久久 | 国产成人精品优优av | 蜜桃臀无码内射一区二区三区 | 亚洲国产欧美国产综合一区 | 性做久久久久久久久 | 国产精品久久福利网站 | 久久天天躁狠狠躁夜夜免费观看 | 国产精品久久国产三级国 | 熟女少妇在线视频播放 | 亚洲精品国产精品乱码视色 | 玩弄中年熟妇正在播放 | 国产高清av在线播放 | 国产精华av午夜在线观看 | 无码人妻久久一区二区三区不卡 | 激情内射日本一区二区三区 | 激情国产av做激情国产爱 | 午夜精品一区二区三区在线观看 | 无码av中文字幕免费放 | 扒开双腿疯狂进出爽爽爽视频 | 日本丰满熟妇videos | 欧美三级a做爰在线观看 | 亚洲中文字幕无码中字 | 性色欲网站人妻丰满中文久久不卡 | 极品尤物被啪到呻吟喷水 | 狠狠色噜噜狠狠狠狠7777米奇 |