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

Redis內部數據結構SDS詳解

存儲 存儲軟件 Redis
Redis是使用C寫的,而C中根本不存在string,list,hash,set和zset這些數據類型,那么C是如何將這些數據類型實現出來的呢?我們從該篇開始,就要開始分析源碼啦??。

本文轉載自微信公眾號「 學習Java的小姐姐」,作者學習Java的小姐姐0618。轉載本文請聯系學習Java的小姐姐公眾號。

[[331487]]

前言

Redis是使用C寫的,而C中根本不存在string,list,hash,set和zset這些數據類型,那么C是如何將這些數據類型實現出來的呢?我們從該篇開始,就要開始分析源碼啦??。

API使用

我們這篇來學習string的底層實現,首先看下API的簡單應用,設置str1變量為helloworld,然后我們使用debug object +變量名的方式看下,注意標紅的編碼為embstr。

如果我們將str2設置為helloworldhelloworldhelloworldhelloworldhell,字符長度為44,再使用下debug object+變量名的方式看下,注意標紅的編碼為embstr。

但是當我們設置為helloworldhelloworldhelloworldhelloworldhello,字符長度為45,再使用debug object+變量名的方式看下,注意標紅的編碼為raw。

最后我們將str3設置為整數100,再使用debug object+變量名的方式看下,注意標紅的編碼為int。

所以Redis的string類型一共有三種存儲方式,當字符串長度小于等于44,底層采用embstr;當字符串長度大于44,底層采用raw;當設置是整數,底層則采用int。

embstr和raw的區別

所有類型的數據結構最外層都是RedisObject,這部分會說,先這樣大致了解下,因為這篇的重點不在這。如果字符串小于等于44,實際的數據和RedisObject在內存中地址相鄰,如下圖。

如果字符串大于44,實際的數據和RedisObject在內存中地址不相鄰,如下圖。

再次強調,這些不重要,以后會講,現在提下,只是為了能讓Redis的String類型有個大致了解,先從整體把握。我們今天要說的其實是實際的數據,即上圖指針指向的位置??。

SDSHdr的定義

其實的數據并不是直接存儲,也有封裝,看下面的代碼就知道分為五種,分別是sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64。sdshdr5和另外四種的區別比較明顯,sdshrd5其實對內存空間的更加節約。其他四種乍一看都差不多,包括已用長度len,總長度alloc,標記flags(感覺沒啥用,要是有知道的小伙伴,歡迎指教),實際數據buf。

  1. //定義五種不同的結構體,sdshdr5,sdshdr8, sdshdr16,sdshdr32,sdshdr64 
  2. struct __attribute__ ((__packed__)) sdshdr5 { 
  3.     unsigned char flags; // 8位的標記 
  4.     char buf[];//實際數據的指針 
  5. }; 
  6. struct __attribute__ ((__packed__)) sdshdr8 { 
  7.     uint8_t len; /* 已使用長度 */ 
  8.     uint8_t alloc; /* 總長度*/ 
  9.     unsigned char flags; 
  10.     char buf[]; 
  11. }; 
  12. struct __attribute__ ((__packed__)) sdshdr16 { 
  13.     uint16_t len; 
  14.     uint16_t alloc; 
  15.     unsigned char flags; 
  16.     char buf[]; 
  17. }; 
  18. struct __attribute__ ((__packed__)) sdshdr32 { 
  19.     uint32_t len; 
  20.     uint32_t alloc; 
  21.     unsigned char flags; 
  22.     char buf[]; 
  23. }; 
  24. struct __attribute__ ((__packed__)) sdshdr64 { 
  25.     uint64_t len; 
  26.     uint64_t alloc; 
  27.     unsigned char flags; 
  28.     char buf[]; 
  29. }; 

SDS具體邏輯圖

假設我們設置某個字符串為hello,那么他SDS的可用長度len為8,已用長度len為6,如下圖。注意:Redis會根據具體的字符長度,選擇相應的sdshdr,但是各個類型都差不多,所以下圖加簡單畫了。

SDS的優勢

我們可以看到是對字符數組的再封裝,但是為什么呢,直接使用字符數組不是更簡單嗎?這要從C和Java語言的根本區別說起。

更快速的獲取字符串長度

我們都知道Java的字符串有提供length方法,列表有提供size方法,我們可以直接獲取大小。但是C卻不一樣,更偏向底層實現,所以沒有直接的方法使用。這樣就帶來一個問題,如果我們想要獲取某個數組的長度,就只能從頭開始遍歷,當遇到第一個'\0'則表示該數組結束。這樣的速度太慢了,不能每次因為要獲取長度就變量數組。所以設計了SDS數據結構,在原來的字符數組外面增加總長度,和已用長度,這樣每次直接獲取已用長度即可。復雜度為O(1)。

數據安全,不會截斷

如果傳統字符串保存圖片,視頻等二進制文件,中間可能出現'\0',如果按照原來的邏輯,會造成數據丟失。所以可以用已用長度來表示是否字符數組已結束。

SDS關鍵代碼分析

獲取常見值(抽象出常見方法)

在sds.h中寫了一些常見方法,比如計算sds的長度(即sdshdr的len),計算sds的空閑長度(即sdshdr的可用長度alloc-已用長度len),計算sds的可用長度(即sdshdr的alloc)等等。但是大家有沒有疑問,這不是一行代碼搞定的事嗎,為啥要抽象出方法呢?那么問題在于在上面,我們有將sdshdr分為五種類型,分別是sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64。那么我們在實際使用的時候,想要區分當前是哪個類型,并取其相應字段或設置相應字段。

  1. //計算sds對應的字符串長度,其實上取得是字符串所對應的哪種sdshdr的len值 
  2. static inline size_t sdslen(const sds s) { 
  3.     // 柔性數組不占空間,所以倒數第二位的是flags 
  4.     unsigned char flags = s[-1]; 
  5.     //flags與上面定義的宏變量7做位運算 
  6.     switch(flags&SDS_TYPE_MASK) { 
  7.         case SDS_TYPE_5://0 
  8.             return SDS_TYPE_5_LEN(flags); 
  9.         case SDS_TYPE_8://1 
  10.             return SDS_HDR(8,s)->len;//取上面結構體sdshdr8的len 
  11.         case SDS_TYPE_16://2 
  12.             return SDS_HDR(16,s)->len; 
  13.         case SDS_TYPE_32://3 
  14.             return SDS_HDR(32,s)->len; 
  15.         case SDS_TYPE_64://5 
  16.             return SDS_HDR(64,s)->len; 
  17.     } 
  18.     return 0; 
  19. //計算sds對應的空余長度,其實上是alloc-len 
  20. static inline size_t sdsavail(const sds s) { 
  21.     unsigned char flags = s[-1]; 
  22.     switch(flags&SDS_TYPE_MASK) { 
  23.         case SDS_TYPE_5: { 
  24.             return 0; 
  25.         } 
  26.         case SDS_TYPE_8: { 
  27.             SDS_HDR_VAR(8,s); 
  28.             return sh->alloc - sh->len; 
  29.         } 
  30.         case SDS_TYPE_16: { 
  31.             SDS_HDR_VAR(16,s); 
  32.             return sh->alloc - sh->len; 
  33.         } 
  34.         case SDS_TYPE_32: { 
  35.             SDS_HDR_VAR(32,s); 
  36.             return sh->alloc - sh->len; 
  37.         } 
  38.         case SDS_TYPE_64: { 
  39.             SDS_HDR_VAR(64,s); 
  40.             return sh->alloc - sh->len; 
  41.         } 
  42.     } 
  43.     return 0; 
  44. //設置sdshdr的len 
  45. static inline void sdssetlen(sds s, size_t newlen) { 
  46.     unsigned char flags = s[-1]; 
  47.     switch(flags&SDS_TYPE_MASK) { 
  48.         case SDS_TYPE_5: 
  49.             { 
  50.                 unsigned char *fp = ((unsigned char*)s)-1; 
  51.                 *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); 
  52.             } 
  53.             break; 
  54.         case SDS_TYPE_8: 
  55.             SDS_HDR(8,s)->len = newlen; 
  56.             break; 
  57.         case SDS_TYPE_16: 
  58.             SDS_HDR(16,s)->len = newlen; 
  59.             break; 
  60.         case SDS_TYPE_32: 
  61.             SDS_HDR(32,s)->len = newlen; 
  62.             break; 
  63.         case SDS_TYPE_64: 
  64.             SDS_HDR(64,s)->len = newlen; 
  65.             break; 
  66.     } 
  67. //給sdshdr的len添加多少大小 
  68. static inline void sdsinclen(sds s, size_t inc) { 
  69.     unsigned char flags = s[-1]; 
  70.     switch(flags&SDS_TYPE_MASK) { 
  71.         case SDS_TYPE_5: 
  72.             { 
  73.                 unsigned char *fp = ((unsigned char*)s)-1; 
  74.                 unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; 
  75.                 *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); 
  76.             } 
  77.             break; 
  78.         case SDS_TYPE_8: 
  79.             SDS_HDR(8,s)->len += inc; 
  80.             break; 
  81.         case SDS_TYPE_16: 
  82.             SDS_HDR(16,s)->len += inc; 
  83.             break; 
  84.         case SDS_TYPE_32: 
  85.             SDS_HDR(32,s)->len += inc; 
  86.             break; 
  87.         case SDS_TYPE_64: 
  88.             SDS_HDR(64,s)->len += inc; 
  89.             break; 
  90.     } 
  91. //獲取sdshdr的總長度 
  92. static inline size_t sdsalloc(const sds s) { 
  93.     unsigned char flags = s[-1]; 
  94.     switch(flags&SDS_TYPE_MASK) { 
  95.         case SDS_TYPE_5: 
  96.             return SDS_TYPE_5_LEN(flags); 
  97.         case SDS_TYPE_8: 
  98.             return SDS_HDR(8,s)->alloc; 
  99.         case SDS_TYPE_16: 
  100.             return SDS_HDR(16,s)->alloc; 
  101.         case SDS_TYPE_32: 
  102.             return SDS_HDR(32,s)->alloc; 
  103.         case SDS_TYPE_64: 
  104.             return SDS_HDR(64,s)->alloc; 
  105.     } 
  106.     return 0; 
  107. //設置sdshdr的總長度 
  108. static inline void sdssetalloc(sds s, size_t newlen) { 
  109.     unsigned char flags = s[-1]; 
  110.     switch(flags&SDS_TYPE_MASK) { 
  111.         case SDS_TYPE_5: 
  112.             /* Nothing to do, this type has no total allocation info. */ 
  113.             break; 
  114.         case SDS_TYPE_8: 
  115.             SDS_HDR(8,s)->alloc = newlen; 
  116.             break; 
  117.         case SDS_TYPE_16: 
  118.             SDS_HDR(16,s)->alloc = newlen; 
  119.             break; 
  120.         case SDS_TYPE_32: 
  121.             SDS_HDR(32,s)->alloc = newlen; 
  122.             break; 
  123.         case SDS_TYPE_64: 
  124.             SDS_HDR(64,s)->alloc = newlen; 
  125.             break; 
  126.     } 

創建對象

我們通過sdsnew方法來創建對象,顯示通過判斷init是否為空來確定初始大小,接著調用方法sdsnew(這邊方法名一樣,但是參數不一樣,其為方法的重載),先根據長度確定類型(上面有提過五種類型,不記得的可以往上翻),然后根據類型分配相應的內存資源,最后追加C語言的結尾符'\0'。

  1. sds sdsnew(const char *init) { 
  2.     size_t initlen = (init == NULL) ? 0 : strlen(init); 
  3.     return sdsnewlen(init, initlen); 
  4.  
  5. sds sdsnewlen(const void *init, size_t initlen) { 
  6.     void *sh; 
  7.     sds s; 
  8.     char type = sdsReqType(initlen);//根據長度確定類型 
  9.     /*空字符串,用sdshdr8,這邊是經驗寫法,當想構造空串是為了放入超過32長度的字符串 */ 
  10.     if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; 
  11.     int hdrlen = sdsHdrSize(type);//到下一個方法,已經把他們放在一起了 
  12.     unsigned char *fp; /* flags pointer. */ 
  13.  
  14.     //分配內存 
  15.     sh = s_malloc(hdrlen+initlen+1); 
  16.     if (!init) 
  17.         memset(sh, 0, hdrlen+initlen+1); 
  18.     if (sh == NULLreturn NULL
  19.     s = (char*)sh+hdrlen; 
  20.     fp = ((unsigned char*)s)-1; 
  21.     //根據不同的類型,創建不同結構體,調用SDS_HDR_VAR函數 
  22.     //為不同的結構體賦值,如已用長度len,總長度alloc 
  23.     switch(type) { 
  24.         case SDS_TYPE_5: { 
  25.             *fp = type | (initlen << SDS_TYPE_BITS); 
  26.             break; 
  27.         } 
  28.         case SDS_TYPE_8: { 
  29.             SDS_HDR_VAR(8,s); 
  30.             sh->len = initlen; 
  31.             sh->alloc = initlen; 
  32.             *fp = type; 
  33.             break; 
  34.         } 
  35.         case SDS_TYPE_16: { 
  36.             SDS_HDR_VAR(16,s); 
  37.             sh->len = initlen; 
  38.             sh->alloc = initlen; 
  39.             *fp = type; 
  40.             break; 
  41.         } 
  42.         case SDS_TYPE_32: { 
  43.             SDS_HDR_VAR(32,s); 
  44.             sh->len = initlen; 
  45.             sh->alloc = initlen; 
  46.             *fp = type; 
  47.             break; 
  48.         } 
  49.         case SDS_TYPE_64: { 
  50.             SDS_HDR_VAR(64,s); 
  51.             sh->len = initlen; 
  52.             sh->alloc = initlen; 
  53.             *fp = type; 
  54.             break; 
  55.         } 
  56.     } 
  57.     if (initlen && init) 
  58.         memcpy(s, init, initlen); 
  59.     //最后追加'\0' 
  60.     s[initlen] = '\0'
  61.     return s; 
  62.  
  63. //根據實際字符長度確定類型 
  64. static inline char sdsReqType(size_t string_size) { 
  65.     if (string_size < 1<<5) 
  66.         return SDS_TYPE_5; 
  67.     if (string_size < 1<<8) 
  68.         return SDS_TYPE_8; 
  69.     if (string_size < 1<<16) 
  70.         return SDS_TYPE_16; 
  71. #if (LONG_MAX == LLONG_MAX) 
  72.     if (string_size < 1ll<<32) 
  73.         return SDS_TYPE_32; 
  74. #endif 
  75.     return SDS_TYPE_64; 

刪除

String類型的刪除并不是直接回收內存,而是修改字符,讓其為空字符,這其實是惰性釋放,等待將來使用。在調用sdsempty方法時,再次調用上面的sdsnewlen方法。

  1. /*修改sds字符串使其為空(零長度)。 
  2.  
  3. *但是,所有現有緩沖區不會被丟棄,而是設置為可用空間 
  4.  
  5. *這樣,下一個append操作將不需要分配到 
  6.  
  7. *當要縮短SDS保存的字符串時,程序并不立即使用內存充分配來回收縮短后多出來的字節,并等待將來使用。*/ 
  8. void sdsclear(sds s) { 
  9.     sdssetlen(s, 0); 
  10.     s[0] = '\0'
  11.  
  12. sds sdsempty(void) { 
  13.     return sdsnewlen("",0); 

添加字符(擴容)重點!!!

添加字符串,sdscat輸入參數為sds和字符串t,首先調用sdsMakeRoomFor擴容方法,再追加新的字符串,最后添加上結尾符'\0'。我們來看下擴容方法里面是如何實現的?第一步先調用常見方法中的sdsavail方法,獲取還剩多少空閑空間。如果空閑空間大于要添加的字符串t的長度,則直接返回,不想要擴容。如果空閑空間不夠,則想要擴容。第二步判斷想要擴容多大,這邊有分情況,如果目前的字符串小于1M,則直接擴容雙倍,如果目前的字符串大于1M,則直接添加1M。第三個判斷添加字符串之后的數據類型還是否和原來的一致,如果一致,則沒啥事。如果不一致,則想要新建一個sdshdr,把現有的數據都挪過去。

這樣是不是有點抽象,舉個例子,現在str的字符串為hello,目前是sdshdr8,總長度50,已用6,空閑44。現在想要添加長度為50的字符t,第一步想要看下是否要擴容,50明顯大于44,需要擴容。第二步擴容多少,str的長度小于1M,所以擴容雙倍,新的長度為50*2=100。第三步50+50所對應sdshdr類型還是sdshdr8嗎?明顯還是sdshdr8,所以不要數據遷移,還在原來的基礎上添加t即可。

  1. sds sdscat(sds s, const char *t) { 
  2.     return sdscatlen(s, t, strlen(t)); 
  3.  
  4. sds sdscatlen(sds s, const void *t, size_t len) { 
  5.     //調用sds.h里面的sdslen,即取已用長度 
  6.     size_t curlen = sdslen(s); 
  7.     //擴容方法 
  8.     s = sdsMakeRoomFor(s,len); 
  9.     if (s == NULLreturn NULL
  10.     memcpy(s+curlen, t, len); 
  11.     sdssetlen(s, curlen+len); 
  12.     s[curlen+len] = '\0'
  13.     return s; 
  14.  
  15. sds sdsMakeRoomFor(sds s, size_t addlen) { 
  16.     void *sh, *newsh; 
  17.     //調用sds.h,獲取空閑長度alloc 
  18.     size_t avail = sdsavail(s); 
  19.     size_t len, newlen; 
  20.     char type, oldtype = s[-1] & SDS_TYPE_MASK; 
  21.     int hdrlen; 
  22.  
  23.    //空閑長度大于需要增加的,不需要擴容,直接返回 
  24.     if (avail >= addlen) return s; 
  25.  
  26. //調用sds.h里面的sdslen,即取可用長度 
  27.     len = sdslen(s); 
  28.  
  29.     sh = (char*)s-sdsHdrSize(oldtype); 
  30.     //len加上要添加的大小 
  31.     newlen = (len+addlen); 
  32.  
  33.     //#define SDS_MAX_PREALLOC (1024*1024) 
  34.     //當新長度小于 1024*1024,直接擴容兩倍 
  35.     if (newlen < SDS_MAX_PREALLOC) 
  36.         newlen *= 2; 
  37.     else //當新長度大于 1024*1024,加2014*1024 
  38.         newlen += SDS_MAX_PREALLOC; 
  39.  
  40. //根據長度計算新的類型 
  41.     type = sdsReqType(newlen); 
  42.  
  43.     /* Don't use type 5: the user is appending to the string and type 5 is 
  44.      * not able to remember empty space, so sdsMakeRoomFor() must be called 
  45.      * at every appending operation. */ 
  46.     if (type == SDS_TYPE_5) type = SDS_TYPE_8; 
  47.  
  48. //獲取不同結構體的頭部大小 
  49.     hdrlen = sdsHdrSize(type); 
  50.     //如果類型一樣,直接使用原地址,長度加上就行 
  51.     if (oldtype==type) { 
  52.         newsh = s_realloc(sh, hdrlen+newlen+1); 
  53.         if (newsh == NULLreturn NULL
  54.         s = (char*)newsh+hdrlen; 
  55.     } else {//如果類型不一樣,重新開辟內存,把原來的數據復制過去 
  56.         newsh = s_malloc(hdrlen+newlen+1); 
  57.         if (newsh == NULLreturn NULL
  58.         memcpy((char*)newsh+hdrlen, s, len+1); 
  59.         s_free(sh); 
  60.         s = (char*)newsh+hdrlen; 
  61.         s[-1] = type; 
  62.         sdssetlen(s, len); 
  63.     } 
  64.     //設置新的總長度 
  65.     sdssetalloc(s, newlen); 
  66.     return s; 
  67.  
  68. //計算不同類型的結構體的大小 
  69. static inline int sdsHdrSize(char type) { 
  70.     switch(type&SDS_TYPE_MASK) { 
  71.         case SDS_TYPE_5: 
  72.             return sizeof(struct sdshdr5); 
  73.         case SDS_TYPE_8: 
  74.             return sizeof(struct sdshdr8); 
  75.         case SDS_TYPE_16: 
  76.             return sizeof(struct sdshdr16); 
  77.         case SDS_TYPE_32: 
  78.             return sizeof(struct sdshdr32); 
  79.         case SDS_TYPE_64: 
  80.             return sizeof(struct sdshdr64); 
  81.     } 
  82.     return 0; 

總結

該篇主要講了Redis的底層實現SDS,包括SDS是什么,與傳統的C語言相比的優勢,具體的邏輯圖,常見的方法(包括創建,刪除,擴容等)。同時也知道了Redis的embstr和raw的區別。

如果覺得寫得還行,麻煩給個贊??,您的認可才是我寫作的動力!

如果覺得有說的不對的地方,歡迎評論指出。

好了,拜拜咯。

 

責任編輯:武曉燕 來源: 學習Java的小姐姐
相關推薦

2019-03-07 15:43:22

Redis數據SDS

2023-01-09 08:42:04

String數據類型

2019-10-29 08:59:16

Redis底層數據

2023-11-12 21:49:10

Redis數據庫

2020-07-14 08:53:43

Redis數據存儲

2024-01-26 06:42:05

Redis數據結構

2019-06-12 22:51:57

Redis軟件開發

2019-09-02 09:48:39

Redis數據結構對象

2019-04-17 15:35:37

Redis數據庫數據結構

2021-06-08 06:01:00

C++數據結構向量和數組

2021-08-29 07:41:48

數據HashMap底層

2024-08-12 16:09:31

2025-01-07 08:00:00

有序集合數據結構

2021-05-21 08:31:09

數據結構二叉樹

2020-10-21 12:45:12

Redis數據結構

2023-10-31 08:51:25

數據結構存儲數據

2019-09-27 08:53:47

Redis數據C語言

2011-03-31 15:41:51

Cacti數據表結構

2012-04-28 14:21:47

Java數據結構線性結構

2019-09-18 08:31:47

數據結構設計
點贊
收藏

51CTO技術棧公眾號

精品久久人人做人人爰| 久久婷婷国产综合精品青草| 美女精品久久久| 日本不卡视频一区| 偷拍中文亚洲欧美动漫| 中文字幕日韩一区| 精品视频导航| 国产免费高清视频| 国产欧美一区二区三区国产幕精品| 亚洲男女自偷自拍图片另类| 一级做a免费视频| 大菠萝精品导航| 国产精品久久三| 国产欧美日韩一区| 在线免费观看av片| 亚洲黄色一区| 日韩中文第一页| 国产原创剧情av| 日韩成人免费av| 精品人伦一区二区三区蜜桃网站| 美国av在线播放| 免费毛片在线| av电影在线观看完整版一区二区| 成人福利在线视频| 天堂中文字幕在线观看| 午夜亚洲福利| 中文字幕欧美日韩在线| jizz日本免费| 黄色成人美女网站| 69堂精品视频| 日日碰狠狠丁香久燥| 亚洲毛茸茸少妇高潮呻吟| 精油按摩中文字幕久久| 国产午夜精品美女视频明星a级| 99精品视频国产| 日韩精选视频| 欧美日韩免费观看中文| 日本男女交配视频| av免费在线免费| 国产精品国产三级国产普通话蜜臀 | 欧美人与性动xxxx| 精品视频一区二区在线| 超碰在线视屏| 亚洲成a人片综合在线| 男人j进女人j| a视频在线观看| 中文字幕日韩一区| 中文字幕人成一区| 亚洲精品承认| 一区免费观看视频| 综合国产精品久久久| 免费网站成人| 日韩美女久久久| 一区二区三区视频| 人人干在线视频| 亚洲欧洲一区二区三区| 一本一本久久a久久精品综合妖精| 国产色a在线| 国产精品污网站| 亚洲在线色站| 国产超级va在线视频| 亚洲精品国产无套在线观 | 日韩一级网站| 91精品成人久久| 国产午夜视频在线| 日韩视频一区| 日韩av成人在线| 波多野结衣视频免费观看| 蜜芽一区二区三区| 亚洲xxxx3d| 日本黄色三级视频| 久久精品亚洲精品国产欧美 | 尤物精品在线| 国产69久久精品成人看| 国产午夜无码视频在线观看| 免费久久精品视频| 成人乱色短篇合集| 黄色成人一级片| 91麻豆国产精品久久| 日韩理论片在线观看| 久草中文在线观看| 亚洲一区欧美一区| 无遮挡又爽又刺激的视频 | 久久国产精品99国产| 国产精品黄视频| jlzzjlzzjlzz亚洲人| 成人av电影在线| 日本视频一区二区在线观看| 里番在线观看网站| 亚洲国产精品一区二区www在线| 国产青青在线视频| 自拍偷拍亚洲图片| 亚洲精品wwwww| 日本免费网站视频| 激情综合网址| 国产日本欧美视频| 少妇av一区二区| 日本一区二区免费在线观看视频 | 午夜啪啪免费视频| 国产传媒av在线| 欧美三级电影网站| 亚洲欧美日韩色| 日本久久一二三四| 97视频在线免费观看| 一级片视频网站| 久久综合色婷婷| 国产高清不卡无码视频| 国产亚洲一区二区手机在线观看| 日韩欧美国产一区二区在线播放| 中文字幕国产专区| 亚洲欧美一级二级三级| 国产精品视频最多的网站| 丰满人妻一区二区三区免费| 国产精品日韩精品欧美在线| 日本韩国欧美在线观看| 精品亚洲二区| 最新日韩中文字幕| 国产精品人人人人| 国产aⅴ精品一区二区三区色成熟| 欧洲久久久久久| 黄视频网站在线观看| 欧美一区二区三区免费观看视频 | 97精品视频| 日本高清不卡在线| 日本高清视频在线| 亚洲影院理伦片| 国产一区二区在线观看免费视频| 久久最新网址| 欧美亚洲视频在线看网址| 成人午夜免费福利| 一区二区免费看| 91性高潮久久久久久久| 97精品97| 91精品在线观看视频| jyzzz在线观看视频| 欧美日韩在线影院| 国产性生活毛片| 欧美午夜a级限制福利片| 成人网在线观看| 麻豆网站在线免费观看| 欧美精品久久久久久久多人混战| 亚洲天堂av中文字幕| 丝瓜av网站精品一区二区| 免费av在线一区二区| 国产在线精彩视频| 亚洲精品理论电影| 特级做a爱片免费69| 99国产精品久久久久久久久久久| 精品无码一区二区三区在线| 国产伦理久久久久久妇女| 欧美精品福利在线| 少妇人妻偷人精品一区二区| 亚洲成a人v欧美综合天堂下载| 亚洲熟女一区二区三区| 黄色综合网站| 精品一区二区三区视频日产| 日韩欧美精品一区二区三区| 亚洲欧美国产精品久久久久久久| 青草视频在线观看免费| 国产亚洲欧美一区在线观看| 狠狠躁狠狠躁视频专区| 91视频久久| 亚洲综合精品伊人久久| 最爽无遮挡行房视频在线| 欧美成人三级在线| 亚洲一区欧美在线| 久久久精品一品道一区| 天天综合网久久| 在线看片不卡| 国产一区二区精品免费| 三上悠亚国产精品一区二区三区| 亚洲视频欧洲视频| 亚洲一二区视频| 亚洲免费成人av| 中文字幕人妻一区二区三区| 免费亚洲视频| 五月天男人天堂| 超碰97久久国产精品牛牛| 欧美与欧洲交xxxx免费观看| 99中文字幕一区| 日韩免费观看高清完整版| 日韩欧美a级片| 国产亚洲欧美中文| 亚洲欧洲日韩综合| 久久激情中文| 成人免费看片视频在线观看| 女同一区二区三区| 国产色综合天天综合网| 182在线视频观看| 一本色道久久88亚洲综合88| av小说天堂网| 日韩欧美国产高清91| fc2ppv在线播放| www.性欧美| 成人不卡免费视频| 国产伦理一区| 天天综合五月天| 九九久久电影| av日韩免费电影| 国产成+人+综合+亚洲欧美| 欧美疯狂性受xxxxx另类| 黄视频在线观看免费| 日韩午夜精品视频| 在线观看亚洲黄色| 一级特黄大欧美久久久| 中文字幕av久久爽一区| 成人激情小说网站| 911福利视频| 免费在线亚洲| www.夜夜爱| 99久久夜色精品国产亚洲狼 | 成人在线免费观看| 日韩av在线免播放器| 精品国产亚洲一区二区麻豆| 欧美综合色免费| 日韩伦理在线视频| 亚洲综合色区另类av| 欧美a级片免费看| 久久天天做天天爱综合色| 亚洲精品乱码久久久久久蜜桃欧美| 蜜桃视频一区二区三区在线观看| 日韩少妇内射免费播放18禁裸乳| 欧美精品激情| 一级黄色免费在线观看| 日韩国产综合| 日本午夜精品电影| 欧美精品第一区| 国产呦系列欧美呦日韩呦| 欧美视频二区欧美影视| 国产原创欧美精品| 成人在线免费av| 国产精品久久久久久久久久东京 | 成人免费毛片xxx| 国产精品免费丝袜| 谁有免费的黄色网址| 久久综合精品国产一区二区三区| 中文字幕第3页| 成人免费毛片高清视频| 女同性αv亚洲女同志| 国产精品888| 韩国三级与黑人| 国产在线视频一区二区三区| 在线不卡一区二区三区| 久久成人久久爱| 久久精品国产露脸对白| 韩国av一区二区三区| 手机在线视频一区| 国产精品中文字幕日韩精品| 色男人天堂av| 国产乱码精品一区二区三区忘忧草| 中文字幕色网站| 国产成人精品综合在线观看| 国模无码视频一区| 97精品国产露脸对白| 欧美bbbbb性bbbbb视频| 国产亚洲人成网站| 黄色国产在线播放| 亚洲精品视频一区二区| 精品深夜av无码一区二区老年| 亚洲国产一二三| 国产成人亚洲精品自产在线| 日韩欧美国产免费播放| 在线观看国产成人| 69堂成人精品免费视频| 精品人妻久久久久一区二区三区| 欧美xxxx在线观看| 亚洲欧洲综合在线| 亚洲欧美综合另类中字| 日本在线观看| 久久久久久国产精品三级玉女聊斋 | 亚洲精品欧美在线| 日本少妇毛茸茸高潮| 欧美日韩国产影院| 中文字幕 自拍偷拍| 日韩欧美精品在线视频| 亚洲AV第二区国产精品| 尤物tv国产一区| 制服丝袜中文字幕在线| 国产69久久精品成人| 日韩黄色三级在线观看| 99视频日韩| 精品国产乱码久久久久久1区2匹| 亚洲一区二区在线免费观看| 国产精品a久久久久| 亚洲国产精品久久久久爰色欲| 久久精品国产免费| 人妻av一区二区| 国产婷婷色一区二区三区四区| 中文字幕av播放| 激情成人中文字幕| 91av久久久| 亚洲精品久久久久久久久久久久久| а天堂8中文最新版在线官网| 美女精品视频一区| 国产 日韩 欧美一区| 亚洲最大成人网色| 九九热爱视频精品视频| 欧美高清中文字幕| 蜜桃精品视频在线| 中文字幕一区二区久久人妻网站| 亚洲色图欧美激情| 欧美国产成人精品一区二区三区| 在线成人免费观看| 美女毛片在线看| 欧美日韩成人在线视频| 99只有精品| 欧美激情第一页在线观看| 欧美激情视频一区二区三区在线播放| 免费毛片小视频| 国产精品888| fc2ppv在线播放| 在线观看日韩av先锋影音电影院| 丰满人妻熟女aⅴ一区| 久久精品一本久久99精品| 欧美美女日韩| 国产亚洲一区二区三区在线播放| 五月天久久网站| 国产高清视频网站| 久久久蜜桃精品| 男人的天堂一区二区| 欧美一级欧美三级在线观看| se在线电影| 国产v综合ⅴ日韩v欧美大片 | 亚洲熟女综合色一区二区三区| 欧美不卡一区二区三区四区| 免费网站免费进入在线| 国产精品高精视频免费| 妖精视频一区二区三区| 欧美日韩黄色一级片| 国产成人免费在线| 好吊色视频在线观看| 91精品国产综合久久香蕉的特点| 成在在线免费视频| 国产成人精品综合| 婷婷综合一区| 妺妺窝人体色www在线小说| 波多野结衣在线aⅴ中文字幕不卡| 日韩视频中文字幕在线观看| 欧美精品日韩综合在线| av播放在线观看| 国产精品爽爽爽爽爽爽在线观看| 国产成人一区| 中文字幕在线观看第三页| 久久精品夜夜夜夜久久| 亚洲欧美一区二区三区在线观看| 亚洲精品一区av在线播放| 这里有精品可以观看| 久久伊人资源站| 日韩中文字幕一区二区三区| 色一情一交一乱一区二区三区| 色综合久久99| 成人午夜在线观看视频| 国产精品爽爽爽| 久久久久久久久久久9不雅视频| 特黄视频免费观看| 亚洲少妇最新在线视频| www.激情五月.com| 海角国产乱辈乱精品视频| 成人另类视频| 九九九九免费视频| 久久精品视频网| 中文字幕一区二区在线视频| 美女久久久久久久久久久| 91久久精品无嫩草影院| 亚洲人精品午夜射精日韩 | 日韩欧美视频一区二区| 久久成人麻豆午夜电影| 九九热视频精品| 亚洲国产精品久久久久秋霞不卡| 成人免费影院| 亚洲一卡二卡区| 国产精品一区二区久激情瑜伽| 伊人国产在线观看| 国产性色av一区二区| 天堂综合在线播放| 很污的网站在线观看| 久久精品视频一区二区三区| 国产精品欧美综合亚洲| 欧美国产日本高清在线| 日本成人7777| 免费看a级黄色片| 有码一区二区三区| 伦理片一区二区三区| 国产欧美一区二区三区在线| 欧美精品国产一区| 久久午夜福利电影| 欧美不卡123| 韩国成人在线| 日韩av新片网| 国产精品国产三级国产普通话蜜臀 | 久久久视频精品| 国产欧美日韩免费观看| 69久久久久久| 午夜欧美视频在线观看| 日本免费视频在线观看| 久久久久久a亚洲欧洲aⅴ| 久久99国产精品免费网站| 在线观看黄网站| 日韩视频欧美视频| 国产91久久精品一区二区|