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

拆解Redis Cluster,怎么實(shí)現(xiàn)“寫安全”這個(gè)重要特性?

開發(fā) 前端 Redis
本文詳細(xì)介紹Redis Cluster的實(shí)現(xiàn)細(xì)節(jié),從而分析Redis Cluster的Write Safety的保證程度。

Redis是非常流行的緩存。在Redis升級到3.0版本后,升級到集群版本,被稱之為Redis Cluster。在集群版本中,會將數(shù)據(jù)分成多份,被保存到多個(gè)server中,從而保證集群的水平擴(kuò)展能力,加之每份數(shù)據(jù)保存多個(gè)副本,從而保證可用性,并且集群版本保證一定程度的Write Safety。本文詳細(xì)介紹Redis Cluster的實(shí)現(xiàn)細(xì)節(jié),從而分析Redis Cluster的Write Safety的保證程度。

一、接口和架構(gòu)

1、接口

Redis Cluster的接口基本向前兼容,仍然是key-value類型。

2、架構(gòu)

Redis Cluster包含server和client兩個(gè)組件。一個(gè)Redis Cluster可以包含多個(gè)server,可以包含多個(gè)客戶端。每個(gè)客戶端可以連接任意的server,讀取寫入數(shù)據(jù)。保存在Redis Cluster中的數(shù)據(jù)會被分成多份,分散地保存在多個(gè)server中,并且每一份數(shù)據(jù)也會保存多個(gè)副本。

二、實(shí)現(xiàn)

1、節(jié)點(diǎn)

在Redis Cluster中,數(shù)據(jù)會被保存到多個(gè)Redis server中,每個(gè)Redis server都是一個(gè)獨(dú)立的進(jìn)程,具有獨(dú)立的IP和Port,也被稱之為一個(gè)實(shí)例,或者叫做節(jié)點(diǎn)(Node)。Client通過這個(gè)IP和Port連接到這個(gè)Node。

每個(gè)節(jié)點(diǎn)都有個(gè)node id,node id是一個(gè)全局唯一的標(biāo)識,它是在集群創(chuàng)建時(shí)隨機(jī)生成的。一個(gè)節(jié)點(diǎn)的id始終都不會變化的,但是node的IP和port是可以變化的。

2、Node table

一個(gè)Redis Cluster包含哪些節(jié)點(diǎn),也就是這個(gè)集群包含哪些節(jié)點(diǎn)的id,也就是集群成員關(guān)系,這個(gè)信息被保存在一個(gè)表結(jié)構(gòu)中,被稱之為node table。node table類似于:

node table會在每個(gè)節(jié)點(diǎn)上都保存一份,Redis Cluster通過gossip協(xié)議把node table復(fù)制到所有的節(jié)點(diǎn)。后面會繼續(xù)講述node table的復(fù)制。

3、集群成員關(guān)系變更

當(dāng)添加一個(gè)節(jié)點(diǎn)或者刪除一個(gè)節(jié)點(diǎn)時(shí),只需要將命令發(fā)給集群中的任意一個(gè)節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)會修改本地的node table,并且這個(gè)修改會最終復(fù)制到所有的節(jié)點(diǎn)上去。添加節(jié)點(diǎn)的命令是CLUSTER MEET,刪除節(jié)點(diǎn)的命令是CLUSTER FORGET。

舉例來說明:

  • 打算搭建一個(gè)3個(gè)master節(jié)點(diǎn)的集群,當(dāng)集群創(chuàng)建以前,所有3個(gè)節(jié)點(diǎn)的node table都只包含自己。給其中的一個(gè)節(jié)點(diǎn)A發(fā)送命令,CLUSTER MEET NodeB,節(jié)點(diǎn)A修改自己的node table,將NodeB添加到自己的node table中,并且連接節(jié)點(diǎn)B,把自己的node table發(fā)送給節(jié)點(diǎn)B,節(jié)點(diǎn)B收到節(jié)點(diǎn)A發(fā)送過來的node table,會更新自己的node table,這時(shí)節(jié)點(diǎn)B就知道集群中還有節(jié)點(diǎn)A存在。

這時(shí),給節(jié)點(diǎn)A再發(fā)送CLUSTER MEET NodeC,節(jié)點(diǎn)A會把節(jié)點(diǎn)C添加到自己的node table,并且把自己的node table復(fù)制給節(jié)點(diǎn)B,節(jié)點(diǎn)B把接收到的node table更新自己的本地的node table,從而知道節(jié)點(diǎn)C的加入。同樣節(jié)點(diǎn)A會把自己的node table發(fā)給節(jié)點(diǎn)C,節(jié)點(diǎn)C會更新自己本地的node table,從而知道要加入的集群中已經(jīng)存在節(jié)點(diǎn)A和節(jié)點(diǎn)B。

4、槽

前面說過Redis Cluster會把數(shù)據(jù)分成多份,也就是把數(shù)據(jù)進(jìn)行分片。Redis Cluster中的每一份數(shù)據(jù)被稱為槽(Slot)。Redis Cluster將數(shù)據(jù)拆分成16384份,也就是說有16384個(gè)槽。

Redis Cluster采用哈希(Hash)機(jī)制來拆分?jǐn)?shù)據(jù)。首先,數(shù)據(jù)的key通過CRC16算法計(jì)算出一個(gè)哈希值。這個(gè)哈希值再對16384取余,這個(gè)余數(shù)就是槽位,被稱為hash slot。具體的CRC16算法可以參看Redis官方文檔。所有余數(shù)相同的key都在一個(gè)slot中,也就是說,一個(gè)slot其實(shí)就是一批hash余數(shù)相同的key。

每個(gè)hash slot都會保存在Redis Cluster一個(gè)節(jié)點(diǎn)中。具體哪個(gè)hash slot被保存在哪個(gè)實(shí)例中,就形成了類似于一個(gè)map的數(shù)據(jù)結(jié)構(gòu),被稱之為hash slot map。hash slot map類似于:

 

  1. 0 -> NodeA 
  2. 1 -> NodeA 
  3. 2 -> NodeB 
  4. ... 
  5. 16383 -> NodeN 

與node table相同,hash slot map也會在每個(gè)節(jié)點(diǎn)上都會保存一份,Redis Cluster通過gossip協(xié)議把hash slot map復(fù)制到所有節(jié)點(diǎn)。同樣,后面還會講述hash slot map的復(fù)制。

《Redis官方文檔》Redis Cluster Specification, https://redis.io/topics/cluster-spec.

5、數(shù)據(jù)分片變更

要修改數(shù)據(jù)分片關(guān)系,可以連接任意一個(gè)節(jié)點(diǎn),給這個(gè)節(jié)點(diǎn)發(fā)送CLUSTER ADDSLOTS, CLUSTER SETSLOT, CLUSTER DELSLOT命令,修改這個(gè)節(jié)點(diǎn)上的hash slot map,該節(jié)點(diǎn)會把這個(gè)修改復(fù)制到所有其他節(jié)點(diǎn),其他節(jié)點(diǎn)會用接收到的hash slot map更新自己的hash slot map。

CLUSTER ADDSLOTS、CLUSTER DELSLOTS、CLUSTER SETSLOT命令的使用如下:

 

  1. CLUSTER ADDSLOTS slot1 [slot2] ... [slotN]  
  2. CLUSTER DELSLOTS slot1 [slot2] ... [slotN] 
  3. CLUSTER SETSLOT slot NODE node 

CLUSTER ADDSLOTS用來把多個(gè)slot分配給當(dāng)前連接的節(jié)點(diǎn)。例如,連接到節(jié)點(diǎn)A執(zhí)行:

  1. CLUSTER ADDSLOTS 1 2 3 

這個(gè)命令會把slot1、slot2、slot3分配給節(jié)點(diǎn)A。

CLUSTER DELSLOTS用來把多個(gè)slot從當(dāng)前連接的節(jié)點(diǎn)刪除。例如,連接到節(jié)點(diǎn)A執(zhí)行:

  1. CLUSTER DELSLOTS 1 2 3 

這個(gè)命令會把slot1、slot2、slot3從節(jié)點(diǎn)A上刪除。

CLUSTER SETSLOT用來把一個(gè)slot分配給指定的節(jié)點(diǎn),可以不是當(dāng)前連接的節(jié)點(diǎn),另外這個(gè)命令還可以設(shè)定MIGRATING和IMPORTING兩個(gè)狀態(tài),我們后面再講。例如,連接到節(jié)點(diǎn)A執(zhí)行:

  1. CLUSTER SETSLOT 1 nodeB 

這個(gè)命令會把slot1分配給節(jié)點(diǎn)B。

6、Slave

一個(gè)slot會被保存多個(gè)副本,既一個(gè)slot會保存在多個(gè)節(jié)點(diǎn)上,也就是slot會復(fù)制到多個(gè)節(jié)點(diǎn)上。Redis Cluster的復(fù)制是以節(jié)點(diǎn)為單位的,一個(gè)節(jié)點(diǎn)上的所有slot會采用相同的復(fù)制。

具體來說就是,其中一個(gè)節(jié)點(diǎn)會負(fù)責(zé)處理這個(gè)節(jié)點(diǎn)上所有slot的寫操作,這個(gè)節(jié)點(diǎn)被稱為master,而其余的節(jié)點(diǎn)被稱為slave節(jié)點(diǎn)。一個(gè)master可以有多個(gè)slave。在同一個(gè)節(jié)點(diǎn)上的所有slot的所有的寫操作都會被從master節(jié)點(diǎn)異步復(fù)制到所有的slave節(jié)點(diǎn)。所以slave會具有與master相同的slot。

通過SLAVEOF命令來設(shè)置slave節(jié)點(diǎn)。SLAVEOF命令用來改變一個(gè)slave節(jié)點(diǎn)的復(fù)制設(shè)置。SLAVEOF命令有兩種格式:

  • SLAVEOF NO ONE
  • SLAVEOF host port

具體來講,SLAVEOF NO ONE命令會停止一個(gè)slave節(jié)點(diǎn)的復(fù)制,并且把這個(gè)slave節(jié)點(diǎn)變成master節(jié)點(diǎn)。SLAVEOF host port命令會停止一個(gè)slave節(jié)點(diǎn)的復(fù)制,丟棄數(shù)據(jù)集,開始從host和port指定的新master節(jié)點(diǎn)復(fù)制。

master和slave的關(guān)系會被記錄在hash slot table中,相當(dāng)于一個(gè)slot會映射到多個(gè)節(jié)點(diǎn)上,其中一個(gè)節(jié)點(diǎn)是master,其他記錄的節(jié)點(diǎn)是slave。

加入了master/slave信息后的hash slot map類似于:

 

  1. 0 -> NodeA,NadeA1(slave) 
  2. 1 -> NodeA,NadeA1(slave) 
  3. 2 -> NodeB,NadeB1(slave) 
  4. ... 
  5. 16383 -> NodeN,NadeN1(slave) 

作為hash slot map的一部分,master/slave信息也會通過gossip協(xié)議復(fù)制到集群中的每個(gè)節(jié)點(diǎn)。

7、Configuration

node table和hash slot map這兩個(gè)信息,被稱之為Configuration,在其他的分布式系統(tǒng)中,也被稱之為元數(shù)據(jù)。

前面我們講過,hash slot map和node table在每個(gè)節(jié)點(diǎn)都會保存一份,并且對這兩個(gè)信息的任何改動,都會通過gossip協(xié)議傳播(propogate)到所有的節(jié)點(diǎn)。本文我們就不繼續(xù)展開gossip協(xié)議了,對Redis Cluster的gossip協(xié)議的實(shí)現(xiàn)感興趣的同學(xué)可以參看redis的文檔。

在某些分布式系統(tǒng)中,元數(shù)據(jù)被存儲在一個(gè)單獨(dú)的架構(gòu)組件中的。Redis Cluster并沒有這樣一個(gè)元數(shù)據(jù)存儲的組件,而是把元數(shù)據(jù)分散的存儲在所有的節(jié)點(diǎn)上。

hash slot map和node table在集群創(chuàng)建時(shí)會被創(chuàng)建出來,并且會隨著后續(xù)的集群變更(比如failover和擴(kuò)容、縮容等運(yùn)維操作,后面會講述)而跟隨著變更。變動會在一個(gè)Node上發(fā)起,通過gossip協(xié)議傳播到所有其他的節(jié)點(diǎn)。這兩個(gè)信息在所有節(jié)點(diǎn)都會保存,并且會最終達(dá)到一致。

8、集群創(chuàng)建

創(chuàng)建Redis Cluster時(shí),首先要以cluster模式運(yùn)行多個(gè)redis server,redis server運(yùn)行起來后,這些server的node id就已經(jīng)生成了。但是這些redis server并沒有形成集群,也就是server彼此之間并不知道相互的存在。接下來運(yùn)行CLUSTER MEET命令,讓這些節(jié)點(diǎn)形成一個(gè)集群。但是這時(shí)集群仍然沒有處于運(yùn)行狀態(tài),需要分配slot,通過CLUSTER SLOTADD命令把slot分配給具體的節(jié)點(diǎn)之后,集群就可以處理client的命令了。

9、客戶端的讀寫操作

客戶端要讀取、寫入數(shù)據(jù)時(shí),雖然client可以連接任意server,但是實(shí)際中,client需要根據(jù)實(shí)際需求連接到server讀取、寫入數(shù)據(jù)。client需要先根據(jù)key計(jì)算出hash slot,連接到負(fù)責(zé)這個(gè)hash slot的節(jié)點(diǎn)進(jìn)行讀寫操作。這樣的話,client就要需要知道hash slot -> node的映射關(guān)系,也就是需要知道hash slot map。

前面講過hash slot map被保存在server端的每個(gè)節(jié)點(diǎn)上。client可以從任意節(jié)點(diǎn)獲取hash slot map,并且把它緩存到client本地,下次操作時(shí)根據(jù)本地緩存直接進(jìn)行操作,但是需要處理緩存信息過期的問題,如果client發(fā)現(xiàn)hash slot map發(fā)生變化(即client讀取寫入數(shù)據(jù)時(shí)server回錯(cuò)誤,接下來會詳細(xì)講述),會重新從server端獲取新的hash slot map。通過hash slot map可以判斷某個(gè)key應(yīng)該存在在哪個(gè)節(jié)點(diǎn)上,client再連接這個(gè)節(jié)點(diǎn)進(jìn)行讀寫操作。

10、MOVED Redirection

hash slot map會發(fā)生變更,這些變更會復(fù)制到所有的節(jié)點(diǎn),但是gossip保證的是最終復(fù)制到所有的節(jié)點(diǎn),再加上client會緩存hash slot map,client可能會把某個(gè)key的請求發(fā)給錯(cuò)誤的節(jié)點(diǎn)來處理。

錯(cuò)誤的節(jié)點(diǎn)收到請求后,發(fā)現(xiàn)這個(gè)key不應(yīng)該自己來處理,會給客戶端返回MOVED的錯(cuò)誤,在錯(cuò)誤消息中,會告訴客戶端,哪個(gè)節(jié)點(diǎn)應(yīng)該負(fù)責(zé)這個(gè)slot。Client收到MOVED消息后,會向消息中指定的節(jié)點(diǎn)再次發(fā)送請求。

client可以將slot的節(jié)點(diǎn)信息更新到本地緩存的hash slot map中,但是更好的方法是,重新獲取完整的hash slot map,替換本地的緩存。因?yàn)樵诖蠖鄶?shù)情況下,hash slot map中的變更不僅僅只修改一個(gè)slot。

雖然client按照MOVED消息中的節(jié)點(diǎn)信息重新發(fā)送請求,但是client仍然可能再次從新節(jié)點(diǎn)收到MOVED錯(cuò)誤消息,因?yàn)樯弦粋€(gè)節(jié)點(diǎn)的hash slot map可能也不是最新的。但是因?yàn)閔ash slot map最終會在所有的節(jié)點(diǎn)上一致,所以client在幾次收到MOVED錯(cuò)誤后,最終會獲取到最新的hash slot map。

11、Failover、currentEpoch、lastVoteEpoch

當(dāng)master發(fā)生故障宕機(jī)后,Redis Cluster會選出一個(gè)slave來接替這個(gè)master。

如果有多個(gè)slave存在,那么每個(gè)slave都可能都會發(fā)現(xiàn)master發(fā)生了宕機(jī),并且試圖把自己變成為master,如果有多個(gè)slave成為master,那么這些新master都會更新本地hash slot map,把舊master負(fù)責(zé)的slot更新成自己,并且把自己對hash slot map的更新傳播給其他的節(jié)點(diǎn)。這會導(dǎo)致hash slot map在節(jié)點(diǎn)間出現(xiàn)差異。

從而導(dǎo)致,因?yàn)樗B接的節(jié)點(diǎn)不同,client拿到不同的hash slot map,對于同一個(gè)slot,不同的client會連接不同的節(jié)點(diǎn),最終導(dǎo)致節(jié)點(diǎn)上的數(shù)據(jù)出現(xiàn)差異。所以failover要保證,只有一個(gè)slave被選成新master。

Redis Cluster采用了類似Raft算法的技術(shù)來防止多個(gè)slave被選成master。每個(gè)節(jié)點(diǎn)都會有叫做currentEpoch、lastVoteEpoch的兩個(gè)值。在集群剛創(chuàng)建時(shí),每個(gè)節(jié)點(diǎn)的currentEpoch都是0。

當(dāng)slave發(fā)現(xiàn)master宕機(jī)時(shí),這個(gè)slave會增加currentEpoch(即currentEpoch++)。并且向所有的master發(fā)送FAILOVER_AUTH_REQUEST請求,請求中會攜帶自己currentEpoch,master收到FAILOVER_AUTH_REQUEST,如果請求中的currentEpoch比自己的currentEpoch和lastVoteEpoch都大,則記錄請求中的currentEpoch值到自己的currentEpoch、lastVoteEpoch中,并且回復(fù)FAILOVER_AUTH_ACK給slave,回復(fù)中攜帶master的currentEpoch。

所以可以看出,F(xiàn)AILOVER_AUTH_ACK中的Epoch一定與slave的currentEpoch相同。Slave從大多數(shù)的master收到FAILOVER_AUTH_ACK后,則成為master。

上面的過程保證只有一個(gè)slave被選出,我們來舉例說明。五個(gè)master的集群,master節(jié)點(diǎn)分別是A、B、C、D、E,A節(jié)點(diǎn)有兩個(gè)slave,分別是A1和A2。A節(jié)點(diǎn)發(fā)生宕機(jī),A1增加自己的currentEpoch=5(4+1),A1給所有的master發(fā)送FAILOVER_AUTH_REQUEST,節(jié)點(diǎn)B、C、D收到FAILOVER_AUTH_REQUEST,把自己的currentEpoch和lastVoteEpoch更新成5,并且給A1回復(fù)FAILOVER_AUTH_ACK,A1贏得選舉,成為新的master。但是與此同時(shí),A2也發(fā)現(xiàn)A宕機(jī),也試圖選舉成master。A2增加自己的currentEpoch=5(4+1),A2給所有的master發(fā)送FAILOVER_AUTH_REQUEST,但這時(shí)的B、C、D的lastVoteEpoch已經(jīng)是5,所以B、C、D不會給A2回復(fù),E還沒有收到A1的請求,所以只有E會給A2回復(fù),但是不能形成大多數(shù),所以A2不能稱為master。

上面講述的過程已經(jīng)可以保證只有一個(gè)master被選出,但是除此之外,Redis Cluster還做了一個(gè)優(yōu)化,那就是master回復(fù)了一個(gè)請求后不會在給這個(gè)master的其他的slave發(fā)送回復(fù)。

12、Configuration epoch

failover完成后,新master會修改hash slot map,把相應(yīng)的slot記錄的節(jié)點(diǎn)改成自己,并且把這次對hash slot map的改動傳播給其他節(jié)點(diǎn)。

雖然currentEpoch和lastVoteEpoch能保證每次failover只能有一個(gè)節(jié)點(diǎn)被選成新的master,但是先后兩次failover,可能選出的兩個(gè)不同的master,但是他們對hash slot map的修改的傳播卻是異步,也就是后面一次failover的改動可能先于第一次failover的改動到達(dá)某個(gè)節(jié)點(diǎn),從而導(dǎo)致節(jié)點(diǎn)間對hash slot map這個(gè)信息產(chǎn)生不一致。

Redis Cluster通過configEpoch來解決這個(gè)問題。每個(gè)節(jié)點(diǎn)會保存一個(gè)configEpoch的值。相當(dāng)于在node table中還會有一列數(shù)據(jù)叫configEpoch,類似于下面的表:

 

每次failover完成后,新選出的master會用currentEpoch覆蓋configEpoch。Failover的機(jī)制保證兩次failover的新master一定是具有不同的currentEpoch,并且后一次的failover的currentEpoch一定比前一次大。這樣就可以保證,即便采用了gossip這樣傳播協(xié)議,仍然能夠保證最后一次failover的hash slot map的變更會生效,也就是configEpoch更大的變更會生效,并且最終所有的節(jié)點(diǎn)上的hash slot map是一致的。

Slave節(jié)點(diǎn)的configEpoch就是其master的configEpoch。

由于gossip保證的hash slot map最終保持一致的,所以可能存在slave的hash slot map舊于master,failover不能基于舊的hash slot map基礎(chǔ)上做變更,所以前面failover的過程中還需要補(bǔ)充一個(gè)規(guī)則要遵守:

  • 在FAILOVER_AUTH_REQUEST中會攜帶slave節(jié)點(diǎn)的configEpoch,如果這個(gè)slave的configEpoch比這個(gè)slave負(fù)責(zé)的所有slot的master的configEpoch中任意一個(gè)要小,則master不會給slave回復(fù)FAILOVER_AUTH_ACK。

13、Resharding

在集群創(chuàng)建之后,我們還會有對Redis cluster做擴(kuò)容、縮容、balancing這樣的運(yùn)維需求,這些需求本質(zhì)上都可以用 Resharding 操作解決,resharding操作就是把slot在節(jié)點(diǎn)間重新的分布,把slot從一個(gè)節(jié)點(diǎn)轉(zhuǎn)移到另外一個(gè)節(jié)點(diǎn)上。在Redis Cluster中,擴(kuò)容需求實(shí)質(zhì)上就是加入一個(gè)新的節(jié)點(diǎn),再把一些slot分配到這個(gè)新節(jié)點(diǎn)上。

縮容需求實(shí)質(zhì)上就是先把這個(gè)節(jié)點(diǎn)上的所有slot分配到其他節(jié)點(diǎn)上,再把這個(gè)節(jié)點(diǎn)從集群中移出。當(dāng)節(jié)點(diǎn)間的流量不均衡時(shí),我們有balancing這樣的需求,balancing就是把流量比較大的節(jié)點(diǎn)上的一些slot分配都流量比較少的節(jié)點(diǎn)上。

Resharding操作可以是對整個(gè)hash slot map的調(diào)整,也就是可以包括對多個(gè)slot的遷移(migration),遷移就是把一個(gè)slot從一個(gè)節(jié)點(diǎn)遷移到另外一個(gè)節(jié)點(diǎn)。一個(gè)slot migration操作包括前面講的hash slot map變更,另外還包括key的遷移操作。要把一個(gè)slot遷移到另外的節(jié)點(diǎn)上,首先把這個(gè)slot上的所有的key遷移到這個(gè)節(jié)點(diǎn),當(dāng)把所有key都遷移完后,再進(jìn)行hash slot map變更,當(dāng)hash slot map變更完成,這次slot migration結(jié)束。

Redis Cluster使用CLUSTER SETSLOT來設(shè)置遷移。舉例說明,將slot1從節(jié)點(diǎn)A遷移到節(jié)點(diǎn)B。分別對節(jié)點(diǎn)A和節(jié)點(diǎn)B執(zhí)行下面的命令:

  • 節(jié)點(diǎn)A上:CLUSTER SETSLOT 1 MIGRATING NODEB
  • 節(jié)點(diǎn)B上:CLUSTER SETSLOT 1 IMPORTING NODEA

其中,MIGRATING表示數(shù)據(jù)要從這個(gè)節(jié)點(diǎn)遷出,而IMPORTING表示數(shù)據(jù)要往這個(gè)節(jié)點(diǎn)遷入。

執(zhí)行完這兩個(gè)命令后,節(jié)點(diǎn)A中的slot1不在創(chuàng)建新的key。一個(gè)叫做redis-trib的特殊的程序負(fù)責(zé)把所有的key從節(jié)點(diǎn)A遷移到節(jié)點(diǎn)B。redis-trib會執(zhí)行下面的命令:

  1. CLUSTER GETKEYSINSLOT slot count 

這個(gè)命令會返回count個(gè)key,對于每個(gè)返回的key,redis-trib執(zhí)行下面的命令:

  1. MIGRATE target_host target_port key target_database id timeout 

這個(gè)命令會原子地把一個(gè)key從節(jié)點(diǎn)A遷移到節(jié)點(diǎn)B。具體來說,MIGRATE命令會連接目標(biāo)節(jié)點(diǎn),并發(fā)向目標(biāo)節(jié)點(diǎn)發(fā)送這個(gè)key,一旦目標(biāo)節(jié)點(diǎn)收到這個(gè)key,則從自己的數(shù)據(jù)庫中刪除這個(gè)key,在這個(gè)過程中,節(jié)點(diǎn)A和節(jié)點(diǎn)B都會加鎖。

在把所有的key遷移完后,再分別在兩個(gè)節(jié)點(diǎn)上執(zhí)行下面的命令:

  1. CLUSTER SETSLOT slot NODE nodeA 

把所有的key遷移完一般需要一些時(shí)間,也就是說在開始遷移后和完成遷移前,在這個(gè)窗口期內(nèi),key的實(shí)際的分布,與hash slot map里記錄的是不一致的,client按照hash slot map訪問key,會出現(xiàn)錯(cuò)誤。

Redis Cluster通過ASK redirection來解決這個(gè)問題。按照client端的hash slot map,slot1的key一定會發(fā)給節(jié)點(diǎn)A,節(jié)點(diǎn)A收到這個(gè)請求后,如果發(fā)現(xiàn)這個(gè)key已經(jīng)遷移到節(jié)點(diǎn)B了,那么就會給client回復(fù)ASK redirection,client收到ASK redirection后,會向節(jié)點(diǎn)b先發(fā)送一個(gè)ASKING命令,之后在發(fā)送對這個(gè)key的請求。

14、Configuration的實(shí)際存儲

Hash slot map和node table都是邏輯上的結(jié)構(gòu),他們在Redis Cluster中的實(shí)際存儲結(jié)構(gòu)稍有不同(詳情看結(jié)尾參考資料1、2、3、4)。

在節(jié)點(diǎn)的內(nèi)存中,用兩個(gè)變量來存儲這兩個(gè)信息:

  • myself變量:myself代表本節(jié)點(diǎn),是一個(gè)ClusterNode類型的變量,這個(gè)變量中,包含本節(jié)點(diǎn)的configEpoch,還包括slaveof,如果是slave節(jié)點(diǎn)則在slaveof中記錄著它的master節(jié)點(diǎn),還包括一個(gè)bitmap,代表這個(gè)節(jié)點(diǎn)負(fù)責(zé)的所有的slot的槽位值。這個(gè)bitmap有2048個(gè)byte組成,一總是16384(2048*8)個(gè)bit,每個(gè)bit代表一個(gè)slot,bit置1,代表這個(gè)節(jié)點(diǎn)負(fù)責(zé)這個(gè)slot;
  • cluster變量:代表了所在集群的狀態(tài),它包含currentEpoch、lastVoteEpoch和slots數(shù)組,slots數(shù)組的index代表了slot,數(shù)組的每個(gè)成員都指向一個(gè)節(jié)點(diǎn),是一個(gè)ClusterNode類型的變量,與myself變量的類型一樣。

所有Configuration的更改都會被保存到磁盤中,具體來講是保存到一個(gè)名字叫node.conf的文件中,這個(gè)文件是Redis Cluster負(fù)責(zé)寫入的,不需要人工配置。

node.conf按照節(jié)點(diǎn)維度進(jìn)行保存。每一行對應(yīng)一個(gè)節(jié)點(diǎn),每行分別包含這些信息:id,ip:port,flag,slaveof,ping timestamp, pong timespamp,configEpoch,link status,slots。

所有的節(jié)點(diǎn)結(jié)束后,會在文件的最后保存curruntEpoch和lastVoteEpoch兩個(gè)變量。其中flag字段是枚舉類型,會指明這個(gè)節(jié)點(diǎn)是不是自己,節(jié)點(diǎn)類型是master還是slave。

如果是slave節(jié)點(diǎn),則會在slaveof字段記錄其master節(jié)點(diǎn)的id。如果是master節(jié)點(diǎn),則在最后多一個(gè)slots字段,記錄著這個(gè)節(jié)點(diǎn)負(fù)責(zé)著哪些slot。Flags字段還記錄著其他非常重

要的狀態(tài),本文就不繼續(xù)展開了。

同樣,ping timestamp、pong timestmap、link staus三個(gè)字段本文也不繼續(xù)展開了。

具體的node.conf文件類似下面的例子:

 

  1. [root@10.112.178.141 data]# cat nodes-6384.conf 
  2. fb763117270d14205c41174605b15741co03a945 10.112.178.174:6383 slave 5e35bda1a44c8d781eb54e08be88a3bab42070f3 0 1596683852819 2 connected 
  3. 3dc5890fb1591e3b20196f81eb5f2f99754253e8 10.112.178.141:6383 master - 0 1596683851915 1 connected 0-5461 
  4. f1967b687c9b2c27108cce08517e98e7a80d5e7e 10.112.178.171:6383 slave 3dc5890fb1591e3b20196f81eb5f2f99754253e8 0 1596683850813 1 connected 
  5. 2bbab7353e973e991566df3bb52afb4857a7bf25 10.112.178.171:6384 slave 1f0a8cf1bfd0c915ef404482f3dc6bf5c7cf41f5 0 1596683848812 3 connected 
  6. 5e35bda1a44c8d781eb54e08be88a3bab42070f3 10.112.178.142:6383 master - 0 1596683849813 2 connected 5462-10923 
  7. 1f0a8cf1bfd0c915ef404482f3dc6bf5c7cf41f5 10.112.178.141:6384 myself,master - 0 0 3 connected 10924-16383 

節(jié)點(diǎn)啟動時(shí)會讀取node.conf文件,把里面的信息加載到myself和cluster兩個(gè)變量中。Slot信息會被轉(zhuǎn)換成bitmap保存在myself變量中。并且slot信息還會逆向的轉(zhuǎn)換成slot到節(jié)點(diǎn)的映射保存在cluster變量中。

hash slot map變更或者node table變更,就是修改內(nèi)存中的myself變量和cluater變量,并且每次變更都會把這兩個(gè)變量序列化轉(zhuǎn)化后保存到node.conf中。

15、查看configuration

Redis Cluster提供了兩個(gè)命令來查看configuration:

第一個(gè)是CLUSTER SLOT命令,用來展示hash slot維度的信息,CLUSTER SLOT命令的展示如下:

 

  1. 127.0.0.1:7000> cluster slots 
  2. 1)  1) (integer) 5461 
  3. 2) (integer) 10922 
  4. 3)  1) "127.0.0.1" 
  5. 2) (integer) 7001 
  6. 4)  1) "127.0.0.1" 
  7. 2) (integer) 7004 
  8. 2)  1) (integer) 0 
  9. 2) (integer) 5460 
  10. 3)  1) "127.0.0.1" 
  11. 2) (integer) 7000 
  12. 4)  1) "127.0.0.1" 
  13. 2) (integer) 7003 
  14. 3)  1) (integer) 10923 
  15. 2) (integer) 16383 
  16. 3)  1) "127.0.0.1" 
  17. 2) (integer) 7002 
  18. 4)  1) "127.0.0.1" 
  19. 2) (integer) 7005 

第二個(gè)是CLUSTER NODE命令,用來展示node table維度的信息,CLUSTER NODE命令的展示如下:

 

  1. $ redis-cli cluster nodes 
  2. d1861060fe6a534d42d8a19aeb36600e18785e04 127.0.0.1:6379 myself - 0 1318428930 1 connected 0-1364 
  3. 3886e65cc906bfd9b1f7e7bde468726a052d1dae 127.0.0.1:6380 master - 1318428930 1318428931 2 connected 1365-2729 
  4. d289c575dcbc4bdd2931585fd4339089e461a27d 127.0.0.1:6381 master - 1318428931 1318428931 3 connected 2730-4095 

CLUSTER NODE、CLUSTER SLOT兩個(gè)命令可以連接到任意節(jié)點(diǎn)上執(zhí)行,這兩個(gè)命令都是讀取的這個(gè)節(jié)點(diǎn)的本地信息,根據(jù)gossip的特性,存在這兩個(gè)命令展示的不是最新的configuration的可能性。

16、Conflict

雖然前面講的failover過程通過大多數(shù)master投票的方式保證只有一個(gè)slave選中,并且產(chǎn)生唯一的configEpoch。但是Resharding的過程卻沒有經(jīng)過大多數(shù)的master的投票。

執(zhí)行slot遷移時(shí),僅僅是在集群中所有configEpoch中最大的那個(gè)configEpoch的基礎(chǔ)上,再加一而得到的。并且由于Resharding一般包括多個(gè)slot的遷移,Redis cluster目前的做法是,在一次resharding過程中,所有的slot遷移使用的configEpoch都是第一個(gè)slot遷移時(shí)產(chǎn)生的那個(gè)configEpoch。

而failover和resharding都會修改hash slot map,如果在resharding的過程中發(fā)生了failover,這就有可能導(dǎo)致對hash slot map的修改產(chǎn)生沖突。另外,手動failover也是不經(jīng)過master投票的,也就是執(zhí)行CLUSTER FAILOVER命令(帶TAKEOVER參數(shù))。

產(chǎn)生沖突就是指針對同一個(gè)slot,slot被修改成映射到不同的節(jié)點(diǎn)上,并且這些修改具有相同的configEpoch。

為了解決這個(gè)問題,Redis cluster需要存在一個(gè)沖突解決的機(jī)制。如果一個(gè)master發(fā)現(xiàn)相同的configEpoch,則比較一下兩個(gè)節(jié)點(diǎn)的id,id小的節(jié)點(diǎn),把自己currentEpoch加一,作為自己的configEpoch。

三、Write Safety

由于有沖突的存在,可能導(dǎo)致不同的節(jié)點(diǎn)上的hash slot map不一致,取決于連接的節(jié)點(diǎn)不同,一部分client可能會把某個(gè)slot的key寫入到一個(gè)節(jié)點(diǎn)中,而另外一部分client會把同樣slot的key寫入到另外一個(gè)節(jié)點(diǎn)中。當(dāng)沖突被解決后,其中一個(gè)節(jié)點(diǎn)上接受的寫入會丟失。

另外,由于master和slave之間的數(shù)據(jù)復(fù)制是異步的,在failover時(shí)如果slave還沒有收到最新的數(shù)據(jù),就發(fā)生了failover,那么這部分寫入就會丟失。Redis cluster在這方面做了一個(gè)優(yōu)化,當(dāng)一個(gè)slave發(fā)現(xiàn)master發(fā)生了宕機(jī),它不會立即開始選舉的過程,它會等待一個(gè)時(shí)間,這個(gè)時(shí)間計(jì)算公式如下:

  1. DELAY = 500 milliseconds + random delay between 0 and 500 milliseconds + SLAVE_RANK * 1000 milliseconds 

這個(gè)計(jì)算公式中,

  • 第一部分是一個(gè)固定500毫秒的時(shí)間,這是為了給master充分的時(shí)間,也發(fā)現(xiàn)節(jié)點(diǎn)宕機(jī)這個(gè)事實(shí);
  • 第二個(gè)是隨機(jī)等待一段時(shí)間,這是為了盡量避免多個(gè)slave同時(shí)發(fā)現(xiàn)master宕機(jī),然后同時(shí)開始選舉,導(dǎo)致master被瓜分,從而導(dǎo)致所有的選舉都不成功;
  • 第三部分是slave的rank,rank主要取決于slave的復(fù)制進(jìn)度,復(fù)制的數(shù)據(jù)越多,則rank越小,也就是越短的等待時(shí)間,越先開始選舉,有更大的可能性被選成新的master。但這僅僅是個(gè)優(yōu)化,不能完全防止丟數(shù)據(jù)的可能。

作者介紹

陳東明,現(xiàn)任國美在線基礎(chǔ)架構(gòu)總監(jiān)。曾任餓了么北京技術(shù)中心任架構(gòu)組負(fù)責(zé)人,負(fù)責(zé)產(chǎn)品線架構(gòu)設(shè)計(jì)及基礎(chǔ)架構(gòu)研發(fā)工作;曾任百度架構(gòu)師,負(fù)責(zé)即時(shí)通訊產(chǎn)品的架構(gòu)設(shè)計(jì)。具有豐富的大規(guī)模系統(tǒng)構(gòu)建和基礎(chǔ)架構(gòu)研發(fā)經(jīng)驗(yàn),善于復(fù)雜業(yè)務(wù)需求下的大并發(fā)、分布式系統(tǒng)設(shè)計(jì)和持續(xù)優(yōu)化。個(gè)人公眾號:dongming_cdm。

 

責(zé)任編輯:未麗燕 來源: DBAplus社群
相關(guān)推薦

2022-01-26 00:06:08

Redis分布式客戶端

2019-08-26 18:45:59

RedisRedis4.0數(shù)據(jù)庫

2020-08-19 08:20:23

Python開發(fā)GitHub

2012-10-22 16:46:19

IBMdw

2014-11-12 14:43:10

2024-03-07 16:03:56

RedisDocker

2016-03-07 13:16:15

2025-10-14 08:10:54

CSSJS 庫Firefox

2009-12-24 09:34:47

調(diào)用ADO.NET

2011-11-18 13:52:03

2012-07-20 09:11:51

2011-08-19 14:37:47

2022-02-06 21:14:57

Redis命令

2020-07-17 09:58:31

Python開發(fā)工具

2018-03-05 17:29:52

SparkApache SparSpark 2.3

2025-05-09 00:22:00

2021-12-04 22:06:54

Kubernetes平臺容器

2009-06-19 11:38:15

JavaFX 1.2

2021-04-06 06:04:36

Redis 6.X C集群搭建操作系統(tǒng)

2023-04-07 08:28:14

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

99久热在线精品996热是什么| 日本特级黄色大片| 在线观看 亚洲| 一区二区三区免费在线看| 亚洲.国产.中文慕字在线| 欧美成人综合一区| 国产精品一区二区人人爽| 欧美亚洲激情| 色激情天天射综合网| 精品一区二区国产| 中文字幕av资源| 欧美日韩一区二区国产| 亚洲视频欧美视频| 99riav国产精品视频| 91精品影视| 亚洲444eee在线观看| 亚洲一区二区免费视频软件合集 | 国产女同互慰高潮91漫画| 成人免费观看网址| 国产寡妇亲子伦一区二区三区四区 | 亚洲综合区在线| 日本一区二区久久精品| 免费国产羞羞网站视频| 久久99久久99精品免视看婷婷 | 五月天婷婷在线视频| 成人毛片在线观看| 亚洲精品日韩激情在线电影| 青青草偷拍视频| 精品一区电影| 日韩精品久久久久久福利| 国产中文字幕免费观看| 天堂av中文在线| 亚洲女同女同女同女同女同69| 3d动漫啪啪精品一区二区免费| 欧美日韩国产精品综合| 欧美深夜视频| 精品久久久久久久久久久久久久久久久 | 欧美影院午夜播放| 最近看过的日韩成人| 精彩国产在线| 久久久综合视频| 成人免费网站在线| 国产精品久久久久久久电影| 尤蜜粉嫩av国产一区二区三区| 久草视频在线看| 91在线视频免费观看| 国产精品美女诱惑| 农村少妇久久久久久久| 日本在线不卡视频一二三区| 欧美性受xxx| 国产成人无码精品久在线观看| 日本一二区不卡| 一区二区三区精品99久久| 国产黄片一区二区三区| 一区二区三区视频免费观看| 91精品国产免费| 九一精品久久久| 国产资源一区| 在线成人午夜影院| 两性午夜免费视频| 亚洲3区在线| 亚洲国产精品高清久久久| 国产嫩草在线观看| 久久久久久久性潮| 欧美一区二区网站| 国产清纯白嫩初高中在线观看性色| 日韩一级二级| 五月天亚洲婷婷| 日本成年人网址| 天堂av在线电影| 亚洲国产精品久久久久秋霞影院 | 亚洲成人在线网站| 国产91xxx| 丝袜美腿诱惑一区二区三区| 欧美天堂亚洲电影院在线播放| 国产精品专区在线| 欧美成人h版| 欧美日本一区二区三区四区| 99中文字幕在线| av毛片精品| 亚洲精品有码在线| 三级黄色在线观看| 一区在线视频观看| 国产精品va在线播放我和闺蜜| 国产尤物在线视频| 久久精品99国产精品| 91亚洲精品丁香在线观看| 天天操天天干天天| 国产精品嫩草久久久久| 免费日韩在线观看| 在线精品亚洲欧美日韩国产| 欧美喷潮久久久xxxxx| 国产日韩成人内射视频| 国产午夜久久av| 日韩精品免费看| 久久人妻无码aⅴ毛片a片app| 日本a级不卡| 久久99精品久久久久久青青91| 亚洲不卡在线播放| 亚洲综合国产| 亚洲va码欧洲m码| 激情小视频在线| 亚洲激情第一区| 日本熟妇人妻中出| 国产91精品入| 精品国产一区二区三区久久久狼| av在线播放中文字幕| 亚洲特级毛片| 成人黄色午夜影院| 久久精品国产亚洲a∨麻豆| 亚洲欧美日韩中文播放| 91视频免费版污| 欧美亚洲国产日韩| 亚洲天堂男人天堂女人天堂| 欧美成人手机视频| 久久国产精品99久久人人澡| 国产日韩欧美91| 日韩精品系列| 亚洲国产精品天堂| 爱情岛论坛亚洲自拍| 凹凸成人精品亚洲精品密奴| 久久久国产视频91| 日日夜夜狠狠操| 99久久国产综合精品色伊| 日本一区二区三区www| 高清在线视频不卡| 91传媒视频在线播放| 玖玖爱在线精品视频| 亚洲最新色图| 国产日韩在线视频| 丰满人妻妇伦又伦精品国产| 1024国产精品| 国产无色aaa| 99久久99热这里只有精品| 欧美大奶子在线| 黑人一级大毛片| 成人av在线影院| 日本免费a视频| 日韩欧美中文字幕在线视频| 久久视频在线看| 一级特黄色大片| av一区二区三区| 国产自产在线视频| 亚洲精品555| 在线观看欧美日韩| 久久精品国产亚洲AV无码男同| 亚洲欧美日韩在线观看a三区 | 国内少妇毛片视频| 亚洲超碰在线观看| 欧美精品激情blacked18| 午夜精品久久久久久久99老熟妇| 国产日韩欧美综合在线| 国产又黄又猛视频| 欧美艳星介绍134位艳星| 国产精品九九久久久久久久| av在线电影免费观看| 欧美中文字幕一区二区三区亚洲| 91视频福利网| 欧美精品色网| 国产精品加勒比| 性国裸体高清亚洲| 日韩精品资源二区在线| 中文字幕影音先锋| 风间由美性色一区二区三区| 亚洲国产午夜伦理片大全在线观看网站| 色在线视频网| 亚洲成人激情在线| 久久久久久无码午夜精品直播| 国产很黄免费观看久久| 无码日本精品xxxxxxxxx| 日本国产精品| 午夜精品久久17c| 成人免费网视频| 国产99对白在线播放| 一区二区国产盗摄色噜噜| 中国一级特黄录像播放| 久久午夜影视| 国产a级片免费看| jizz性欧美23| 中文字幕精品一区二区精品| yjizz国产| 国产精品九色蝌蚪自拍| 亚洲AV成人精品| 亚洲成av人电影| 国产精品视频区1| 青青在线视频| 国产亚洲视频中文字幕视频| 一级片免费观看视频| 亚洲成人免费在线| 激情高潮到大叫狂喷水| 成人晚上爱看视频| 韩国中文字幕av| 欧美精品18| 日本成人三级电影网站| 久久综合给合| 国产福利精品av综合导导航| 老司机午夜在线| 亚洲人成绝费网站色www | 成人免费小视频| free性中国hd国语露脸| 极品销魂美女一区二区三区| 国产午夜福利在线播放| 欧美国产一级| 欧美1o一11sex性hdhd| 国产色99精品9i| 国产精品久久久久久久久久免费| 国产九色在线| 亚洲成人av在线| 高清乱码免费看污| 亚洲一区二区在线观看视频| 欧美精品日韩在线| 国内精品伊人久久久久av一坑| 永久免费网站视频在线观看| 国产亚洲电影| 久久精品国产综合精品 | 中文字幕一区二区三| 国产不卡一二三| 国产成人免费在线观看| 免费一区二区三区在线观看| 亚洲影视一区二区三区| 日韩中文一区| 老司机亚洲精品一区二区| 色综合久久久久久中文网| av福利在线播放| 亚洲天堂av图片| 欧洲毛片在线| 亚洲国产精品免费| 波多野结衣网站| 欧美性猛交xxxx久久久| 中文字幕一区二区三区手机版| 国产亚洲欧洲一区高清在线观看| 亚洲视频一二三四| 日本欧美在线观看| 激情婷婷综合网| 久久九九国产| 国产成人综合一区| 热久久国产精品| 成年人网站大全| 久久在线精品| 国产成人精品无码播放| 日韩电影一二三区| 免费一级特黄录像| 美女视频黄免费的久久| 亚洲77777| 久久狠狠亚洲综合| 亚洲精品成人在线播放| 国精产品一区一区三区mba视频| 成人国产在线看| 欧美日韩国产欧| 色综合久久久久久久久五月| 国内成人精品| 亚洲欧美日韩精品在线| 水蜜桃久久夜色精品一区| 精品国产一区二区三区四区vr| 国产美女久久| 成人国产亚洲精品a区天堂华泰 | 国产成人在线一区| 特级毛片在线| 久久久中文字幕| 麻豆mv在线看| 全亚洲最色的网站在线观看| 日韩和的一区二在线| 国产精品免费视频xxxx| www.欧美视频| 国产激情一区二区三区在线观看| 素人啪啪色综合| 国产伊人精品在线| 亚洲天堂av资源在线观看| 国产欧美日本在线| 国产精品一区二区三区av麻| 岛国一区二区三区高清视频| 精品福利网址导航| 人偷久久久久久久偷女厕| www.丝袜精品| 欧美日韩国产综合视频在线| 欧美精选视频在线观看| 国产日韩在线一区二区三区| 亚洲美女久久| 正在播放国产精品| 伊人蜜桃色噜噜激情综合| 日韩欧美国产免费| 另类的小说在线视频另类成人小视频在线 | 国产资源中文字幕| 日本不卡高清视频| 毛片毛片毛片毛片毛| 99精品久久只有精品| 国产一二三四视频| 国产日本欧美一区二区| av网站免费在线看| 亚洲精品一二三| 手机在线看片1024| 欧美午夜精品在线| 国产又粗又猛又爽| 欧美三级电影在线看| 好吊色一区二区| 在线亚洲午夜片av大片| 国产三级伦理在线| 国产精品天天狠天天看| 2019年精品视频自拍| 91高跟黑色丝袜呻吟在线观看| 精品国产三区在线| 欧美日韩另类综合| 欧美二区视频| 久久久精品麻豆| 国内久久精品视频| 亚洲区自拍偷拍| 亚洲国产精品视频| 国产aⅴ一区二区三区| 国产高清精品二区| 日本婷婷久久久久久久久一区二区| 狠狠做深爱婷婷综合一区| 日本大片免费看| 激情都市一区二区| 香蕉视频免费网站| 亚洲三级在线免费观看| 最近中文字幕在线免费观看| 亚洲精品福利在线观看| 图片区小说区亚洲| 91免费看国产| 欧美韩日高清| 婷婷六月天在线| 91免费国产在线| 免费无码毛片一区二区app| 欧美丰满少妇xxxxx高潮对白| 男人天堂成人网| 奇米一区二区三区av| 最近中文字幕免费视频| 天天综合网天天综合色| 手机看片1024日韩| 欧美极品美女电影一区| 日韩三级精品| 看一级黄色录像| 精品一区二区三区香蕉蜜桃| 91视频免费在观看| 91黄色小视频| 飘雪影视在线观看免费观看| 69av在线视频| av日韩在线免费观看| 亚洲图片都市激情| 99热免费精品| 捆绑凌虐一区二区三区| 亚洲欧洲美洲一区二区三区| 婷婷激情综合网| 欧美主播一区二区三区美女 久久精品人 | 久久成人免费视频| 亚洲欧美在线精品| 老司机在线永久免费观看| 91麻豆.com| 亚洲一区二区三区sesese| 538精品在线观看| 99国产成+人+综合+亚洲欧美| 久久精品久久久久久国产 免费| 特大黑人巨人吊xxxx| 国产精品一区二区三区av| 亚洲精品123区| 欧美激情图片区| 中文字幕黄色片| 国产一区二区三区国产| 精品国产一区二区三区四区vr| 国产精品午夜福利| 99热99精品| 一道本在线观看视频| 小视频免费在线观看| 91精品国产一区二区三区香蕉| 午夜精品在线免费观看| 国产一区二区三区免费在线 | 女性女同性aⅴ免费观女性恋 | 男人av在线| 久久综合九色综合欧美亚洲| 欧美一区二区影视| 中文字幕中文字幕在线中一区高清| 天天躁日日躁aaaa视频| 91禁在线看| 中文无字幕一区二区三区| 777午夜精品福利在线观看| 最新黄色av网址| 一区二区三区四区在线看| 伊人色综合久久天天五月婷| 国产精品亚洲专一区二区三区 | 男女性色大片免费观看一区二区 | 国产日本一区二区| 91在线视频国产| 国产做受高潮69| 精品视频免费| 欧美人与性动交α欧美精品| 激情久久av一区av二区av三区| 亚洲av综合色区无码一区爱av| 久久视频国产精品免费视频在线| 另类一区二区三区| 黄色激情在线视频| 国产v综合v亚洲欧| 日本免费精品视频| 久久激情视频免费观看| 国产精品成人3p一区二区三区| 日本精品一区| 国产成人综合精品三级| 中文字幕 国产精品| 欧美老女人在线视频| 嫩草一区二区三区| 麻豆tv在线观看|