欧美阿v视频在线大全_亚洲欧美中文日韩V在线观看_www性欧美日韩欧美91_亚洲欧美日韩久久精品

主頁 > 知識庫 > Redis核心原理與實踐之字符串實現原理

Redis核心原理與實踐之字符串實現原理

熱門標簽:超呼電話機器人 十堰營銷電銷機器人哪家便宜 山東外呼銷售系統招商 日本中國地圖標注 鄭州人工智能電銷機器人系統 貴州電銷卡外呼系統 宿遷便宜外呼系統平臺 魔獸2青云地圖標注 北京400電話辦理收費標準

本文分析Redis字符串的實現原理,內容摘自新書《Redis核心原理與實踐》。這本書深入地分析了Redis常用特性的內部機制與實現方式,內容源自對Redis源碼的分析,并從中總結出設計思路、實現原理。通過閱讀本書,讀者可以快速、輕松地了解Redis的內部運行機制。

Redis是一個鍵值對數據庫(key-value DB),下面是一個簡單的Redis的命令:

> SET msg "hello wolrd"

該命令將鍵“msg”、值“hello wolrd”這兩個字符串保存到Redis數據庫中。
本章分析Redis如何在內存中保存這些字符串。

redisObject

Redis中的數據對象server.h/redisObject是Redis對內部存儲的數據定義的抽象類型,在深入分析Redis數據類型前,我們先了解redisObject,它的定義如下:

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS;
    int refcount;
    void *ptr;
} robj;
  • type:數據類型。
  • encoding:編碼格式,即存儲數據使用的數據結構。同一個類型的數據,Redis會根據數據量、占用內存等情況使用不同的編碼,最大限度地節省內存。
  • refcount,引用計數,為了節省內存,Redis會在多處引用同一個redisObject。
  • ptr:指向實際的數據結構,如sds,真正的數據存儲在該數據結構中。
  • lru:24位,LRU時間戳或LFU計數。

redisObject負責裝載Redis中的所有鍵和值。redisObject.ptr指向真正存儲數據的數據結構,redisObject .refcount、redisObject.lru等屬性則用于管理數據(數據共享、數據過期等)。

提示:type、encoding、lru使用了C語言中的位段定義,這3個屬性使用同一個unsigned int的不同bit位。這樣可以最大限度地節省內存。

Redis定義了以下數據類型和編碼,如表1-1所示。

本書第1部分會對表1-1中前五種數據類型進行分析,最后兩種數據類型會在第5部分進行分析。如果讀者現在對表1-1中內容感到疑惑,則可以先帶著疑問繼續閱讀本書。

sds

我們知道,C語言中將空字符結尾的字符數組作為字符串,而Redis對此做了擴展,定義了字符串類型sds(Simple Dynamic String)。
Redis鍵都是字符串類型,Redis中最簡單的值類型也是字符串類型,
字符串類型的Redis值可用于很多場景,如緩存HTML片段、記錄用戶登錄信息等。

定義

提示:本節代碼如無特殊說明,均在sds.h/sds.c中。
對于不同長度的字符串,Redis定義了不同的sds結構體:

typedef char *sds;

struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; 
    uint8_t alloc;
    unsigned char flags;
    char buf[];
};
...

Redis還定義了sdshdr16、sdshdr32、sdshdr64結構體。為了版面整潔,這里不展示sdshdr16、sdshdr32、sdshdr64 結構體的代碼,它們與sdshdr8結構體基本相同,只是len、alloc屬性使用了 uint16_t、uint32、uint64_t類型。Redis定義不同sdshdr結構體是為了針對不同長度的字符串,使用合適的len、alloc屬性類型,最大限度地節省內存。

  • len:已使用字節長度,即字符串長度。sdshdr5可存放的字符串長度小于32(25),sdshdr8可存放的字符串長度小于256(28),以此類推。由于該屬性記錄了字符串長度,所以sds可以在常數時間內獲取字符串長度。Redis限制了字符串的最大長度不能超過512MB。
  • alloc:已申請字節長度,即sds總長度。alloc-len為sds中的可用(空閑)空間。
  • flag:低3位代表sdshdr的類型,高5位只在sdshdr5中使用,表示字符串的長度,所以sdshdr5中沒有len屬性。另外,由于Redis對sdshdr5的定義是常量字符串,不支持擴容,所以不存在alloc屬性。
  • buf:字符串內容,sds遵循C語言字符串的規范,保存一個空字符作為buf的結尾,并且不計入len、alloc屬性。這樣可以直接使用C語言strcmp、strcpy等函數直接操作sds。

提示:sdshdr結構體中的buf數組并沒有指定數組長度,它是C99規范定義的柔性數組—結構體中最后一個屬性可以被定義為一個大小可變的數組(該屬性前必須有其他屬性)。使用sizeof函數計算包含柔性數組的結構體大小,返回結果不包括柔性數組占用的內存。
另外,attribute((packed))關鍵字可以取消結構體內的字節對齊以節省內存。

操作分析

接下來看一下sds構建函數:

sds sdsnewlen(const void *init, size_t initlen) {
    void *sh;
    sds s;
    // [1]
    char type = sdsReqType(initlen);
    // [2]
    if (type == SDS_TYPE_5  initlen == 0) type = SDS_TYPE_8;
    // [3]
    int hdrlen = sdsHdrSize(type);
    unsigned char *fp; /* flags pointer. */

    sh = s_malloc(hdrlen+initlen+1);
    ...
    // [4]
    s = (char*)sh+hdrlen;
    fp = ((unsigned char*)s)-1;
    switch(type) {
        case SDS_TYPE_5: {
            *fp = type | (initlen  SDS_TYPE_BITS);
            break;
        }
        case SDS_TYPE_8: {
            SDS_HDR_VAR(8,s);
            sh->len = initlen;
            sh->alloc = initlen;
            *fp = type;
            break;
        }
        ...
    }
    if (initlen  init)
        memcpy(s, init, initlen);
    s[initlen] = '\0';
    // [5]
    return s;
}

參數說明:

  • init、initlen:字符串內容、長度。

【1】根據字符串長度,判斷對應的sdshdr類型。
【2】長度為0的字符串后續通常需要擴容,不應該使用sdshdr5,所以這里轉換為sdshdr8。
【3】sdsHdrSize函數負責查詢sdshdr結構體的長度,s_malloc函數負責申請內存空間,申請的內存空間長度為hdrlen+initlen+1,其中hdrlen為sdshdr結構體長度(不包含buf屬性),initlen為字符串內容長度,最后一個字節用于存放空字符“\0”。s_malloc與C語言的malloc函數的作用相同,負責分配指定大小的內存空間。
【4】給sdshdr屬性賦值。
SDS_HDR_VAR是一個宏,負責將sh指針轉化為對應的sdshdr結構體指針。
【5】注意,sds實際上就是char*的別名,這里返回的s指針指向sdshdr.buf屬性,即字符串內容。Redis通過該指針可以直接讀/寫字符串數據。

構建一個內容為“hello wolrd”的sds,其結構如圖1-1所示。

sds的擴容機制是一個很重要的功能。

sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    // [1]
    size_t avail = sdsavail(s);
    size_t len, newlen;
    char type, oldtype = s[-1]  SDS_TYPE_MASK;
    int hdrlen;

    if (avail >= addlen) return s;
    // [2]
    len = sdslen(s);
    
    sh = (char*)s-sdsHdrSize(oldtype);
    newlen = (len+addlen);
    // [3]
    if (newlen  SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;

    // [4]    
    type = sdsReqType(newlen);
    if (type == SDS_TYPE_5) type = SDS_TYPE_8;
    // [5]
    hdrlen = sdsHdrSize(type);
    if (oldtype==type) {
        newsh = s_realloc(sh, hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        s = (char*)newsh+hdrlen;
    } else {
        newsh = s_malloc(hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);
        s_free(sh);
        s = (char*)newsh+hdrlen;
        s[-1] = type;
        sdssetlen(s, len);
    }
    // [6]
    sdssetalloc(s, newlen);
    return s;
}

參數說明:
addlen:要求擴容后可用長度(alloc-len)大于該參數。
【1】獲取當前可用空間長度。如果當前可用空間長度滿足要求,則直接返回。
【2】sdslen負責獲取字符串長度,由于sds.len中記錄了字符串長度,該操作復雜度為O(1)。這里len變量為原sds字符串長度,newlen變量為新sds長度。sh指向原sds的sdshdr結構體。
【3】預分配比參數要求多的內存空間,避免每次擴容都要進行內存拷貝操作。新sds長度如果小于SDS_MAX_PREALLOC(默認為1024×1024,單位為字節),則新sds長度自動擴容為2倍。否則,新sds長度自動增加SDS_MAX_PREALLOC。
【4】sdsReqType(newlen)負責計算新的sdshdr類型。注意,擴容后的類型不使用sdshdr5,該類型不支持擴容操作。
【5】如果擴容后sds還是同一類型,則使用s_realloc函數申請內存。否則,由于sds結構已經變動,必須移動整個sds,直接分配新的內存空間,并將原來的字符串內容復制到新的內存空間。s_realloc與C語言realloc函數的作用相同,負責為給定指針重新分配給定大小的內存空間。它會嘗試在給定指針原地址空間上重新分配,如原地址空間無法滿足要求,則分配新內存空間并復制內容。
【6】更新sdshdr.alloc屬性。

對上面“hello wolrd”的sds調用sdsMakeRoomFor(sds,64),則生成的sds如圖1-2所示。

從圖1-2中可以看到,使用len記錄字符串長度后,字符串中可以存放空字符。Redis字符串支持二進制安全,可以將用戶的輸入存儲為沒有任何特定格式意義的原始數據流,因此Redis字符串可以存儲任何數據,比如圖片數據流或序列化對象。C語言字符串將空字符作為字符串結尾的特定標記字符,它不是二進制安全的。
sds常用函數如表1-2所示。

函數 作用
sdsnew,sdsempty 創建sds
sdsfree,sdsclear,sdsRemoveFreeSpace 釋放sds,清空sds中的字符串內容,移除sds剩余的可用空間
sdslen 獲取sds字符串長度
sdsdup 將給定字符串復制到sds中,覆蓋原字符串
sdscat 將給定字符串拼接到sds字符串內容后
sdscmp 對比兩個sds字符串是否相同
sdsrange 獲取子字符串,不在指定范圍內的字符串將被清除

編碼

字符串類型一共有3種編碼:

  • OBJ_ENCODING_EMBSTR:長度小于或等于OBJ_ENCODING_EMBSTR_SIZE_LIMIT(44字節)的字符串。

在該編碼中,redisObject、sds結構存放在一塊連續內存塊中,如圖1-3所示。

OBJ_ENCODING_EMBSTR編碼是Redis針對短字符串的優化,有如下優點:
(1)內存申請和釋放都只需要調用一次內存操作函數。
(2)redisObject、sdshdr結構保存在一塊連續的內存中,減少了內存碎片。

  • OBJ_ENCODING_RAW:長度大于OBJ_ENCODING_EMBSTR_SIZE_LIMIT的字符串,在該編碼中,redisObject、sds結構存放在兩個不連續的內存塊中。
  • OBJ_ENCODING_INT:將數值型字符串轉換為整型,可以大幅降低數據占用的內存空間,如字符串“123456789012”需要占用12字節,在Redis中,會將它轉化為long long類型,只占用8字節。

我們向Redis發送一個請求后,Redis會解析請求報文,并將命令、參數轉化為redisObjec。
object.c/createStringObject函數負責完成該操作:

robj *createStringObject(const char *ptr, size_t len) {
    if (len = OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
        return createEmbeddedStringObject(ptr,len);
    else
        return createRawStringObject(ptr,len);
}

可以看到,這里根據字符串長度,將encoding轉化為OBJ_ENCODING_RAW或OBJ_ENCODING_EMBSTR的redisObject。

將參數轉換為redisObject后,Redis再將redisObject存入數據庫,例如:

> SET Introduction "Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. "

Redis會將鍵“Introduction”、值“Redis...”轉換為兩個redisObject,再將redisObject存入數據庫,結果如圖1-4所示。

Redis中的鍵都是字符串類型,并使用OBJ_ENCODING_RAW、OBJ_ENCODING_ EMBSTR編碼,而Redis還會嘗試將字符串類型的值轉換為OBJ_ENCODING_INT 編碼。object.c/tryObjectEncoding函數完成該操作:

robj *tryObjectEncoding(robj *o) {
    long value;
    sds s = o->ptr;
    size_t len;
    ...
    // [1]
     if (o->refcount > 1) return o;

    len = sdslen(s);
    // [2]
    if (len = 20  string2l(s,len,value)) {
        // [3]
        if ((server.maxmemory == 0 ||
            !(server.maxmemory_policy  MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) 
            value >= 0 
            value  OBJ_SHARED_INTEGERS)
        {
            decrRefCount(o);
            incrRefCount(shared.integers[value]);
            return shared.integers[value];
        } else {
            // [4]
            if (o->encoding == OBJ_ENCODING_RAW) {
                sdsfree(o->ptr);
                o->encoding = OBJ_ENCODING_INT;
                o->ptr = (void*) value;
                return o;
            } else if (o->encoding == OBJ_ENCODING_EMBSTR) {
                // [5]
                decrRefCount(o);
                return createStringObjectFromLongLongForValue(value);
            }
        }
    }

    // [6]
    if (len = OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {
        robj *emb;

        if (o->encoding == OBJ_ENCODING_EMBSTR) return o;
        emb = createEmbeddedStringObject(s,sdslen(s));
        decrRefCount(o);
        return emb;
    }

    // [7]
    trimStringObjectIfNeeded(o);

    return o;
}

【1】該數據對象被多處引用,不能再進行編碼操作,否則會影響其他地方的正常運行。
【2】如果字符串長度小于或等于20,則調用string2l函數嘗試將其轉換為long long類型,如果成功則返回1。
在C語言中,long long占用8字節,取值范圍是-9223372036854775808~9223372036854775807,因此最多能保存長度為19的字符串轉換后的數值,加上負數的符號位,一共20位。
下面是字符串可以轉換為OBJ_ENCODING_INT 編碼的處理步驟。
【3】首先嘗試使用shared.integers中的共享數據,避免重復創建相同數據對象而浪費內存。shared是Redis啟動時創建的共享數據集,存放了Redis中常用的共享數據。shared.integers是一個整數數組,存放了小數字0~9999,共享于各個使用場景。
注意:如果配置了server.maxmemory,并使用了不支持共享數據的淘汰算法(LRU、LFU),那么這里不能使用共享數據,因為這時每個數據中都必須存在一個redisObjec.lru屬性,這些算法才可以正常工作。
【4】如果不能使用共享數據并且原編碼格式為OBJ_ENCODING_RAW,則將redisObject.ptr原來的sds類型替換為字符串轉換后的數值。
【5】如果不能使用共享數據并且原編碼格式為OBJ_ENCODING_EMBSTR,由于redisObject、sds存放在同一個內存塊中,無法直接替換redisObject.ptr,所以調用createString- ObjectFromLongLongForValue函數創建一個新的redisObject,編碼為OBJ_ENCODING_INT,redisObject.ptr指向long long類型或long類型。
【6】到這里,說明字符串不能轉換為OBJ_ENCODING_INT 編碼,嘗試將其轉換為OBJ_ENCODING_EMBSTR編碼。
【7】到這里,說明字符串只能使用OBJ_ENCODING_RAW編碼,嘗試釋放sds中剩余的可用空間。
字符串類型的實現代碼在t_string.c中,讀者可以查看源碼了解更多實現細節。

提示:server.c/redisCommandTable定義了每個Redis命令與對應的處理函數,讀者可以從這里查找感興趣的命令的處理函數。

struct redisCommand redisCommandTable[] = {
    ...
    {"get",getCommand,2,
     "read-only fast @string",
     0,NULL,1,1,1,0,0,0},

    {"set",setCommand,-3,
     "write use-memory @string",
     0,NULL,1,1,1,0,0,0},
     ...
}

GET命令的處理函數為getCommand,SET命令的處理函數為setCommand,以此類推。

另外,我們可以通過TYPE命令查看數據對象類型,通過OBJECT ENCODING命令查看編碼:

> SET msg "hello world"
OK
> TYPE msg
string
> OBJECT ENCODING  msg
"embstr"
> SET Introduction "Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. "
OK
> TYPE Introduction
string
> OBJECT ENCODING  info
"raw"
> SET page 1
OK
> TYPE page
string
> OBJECT ENCODING  page
"int"

總結:

Redis中的所有鍵和值都是redisObject變量。

  • sds是Redis定義的字符串類型,支持二進制安全、擴容。
  • sds可以在常數時間內獲取字符串長度,并使用預分配內存機制減少內存拷貝次數。
  • Redis對數據編碼的主要目的是最大限度地節省內存。字符串類型可以使用OBJ_ENCODING_ RAW、OBJ_ENCODING_EMBSTR、OBJ_ENCODING_INT編碼格式。

本文內容摘自作者新書《Redis核心原理與實踐》,這本書深入地分析了Redis常用特性的內部機制與實現方式,大部分內容源自對Redis源碼的分析,并從中總結出設計思路、實現原理。通過閱讀本書,讀者可以快速、輕松地了解Redis的內部運行機制。

京東鏈接
豆瓣鏈接

到此這篇關于Redis核心原理與實踐之字符串實現原理的文章就介紹到這了,更多相關Redis字符串實現原理內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • Redis字符串原理的深入理解
  • Redis緩存,泛型集合與json字符串的相互轉換實例
  • redis內部數據結構之SDS簡單動態字符串詳解
  • redis命令行查看中文不亂碼的方法(十六進制字符串處理)
  • Redis字符串類型的常用命令小結
  • Redis中的動態字符串學習教程

標簽:北京 吉安 大慶 江蘇 楊凌 果洛 朝陽 臺州

巨人網絡通訊聲明:本文標題《Redis核心原理與實踐之字符串實現原理》,本文關鍵詞  Redis,核心,原理,與,實踐,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《Redis核心原理與實踐之字符串實現原理》相關的同類信息!
  • 本頁收集關于Redis核心原理與實踐之字符串實現原理的相關信息資訊供網民參考!
  • 推薦文章
    欧美阿v视频在线大全_亚洲欧美中文日韩V在线观看_www性欧美日韩欧美91_亚洲欧美日韩久久精品
  • <rt id="w000q"><acronym id="w000q"></acronym></rt>
  • <abbr id="w000q"></abbr>
    <rt id="w000q"></rt>
    久久综合狠狠综合久久激情| 国产精品自拍av| 91麻豆高清视频| 色综合色综合色综合色综合色综合| 精品国产伦一区二区三区免费| 丝袜美腿成人在线| 日韩少妇一区二区| 欧美日本视频在线| 亚洲一二三专区| 91麻豆国产香蕉久久精品| 色综合av在线| 亚洲欧美日韩综合aⅴ视频| www.激情成人| 日本高清不卡视频| 亚洲美腿欧美偷拍| 91蜜桃在线观看| 欧美视频在线一区| 亚洲国产成人av好男人在线观看| 国产在线观看免费播放| 欧美剧情电影在线观看完整版免费励志电影 | 91传媒视频在线播放| 亚洲欧洲综合另类在线| 91麻豆福利精品推荐| 欧美色视频在线| 天天色综合成人网| 精品人妻少妇嫩草av无码| 久久综合精品国产一区二区三区| 久久se这里有精品| 欧美a级片免费看| 专区另类欧美日韩| 蜜桃色一区二区三区| 欧美不卡一区二区三区四区| 国产美女主播视频一区| 精品无码一区二区三区蜜臀| 亚洲另类在线一区| 日本wwwwwww| 欧美xfplay| 国产不卡在线一区| 欧美午夜免费电影| 免费一级欧美片在线观看| 天天操天天舔天天射| 中文字幕在线不卡| 无码人妻久久一区二区三区蜜桃| 日韩三级免费观看| 国产乱子轮精品视频| 91国偷自产一区二区开放时间| 午夜伊人狠狠久久| 免费看黄色三级| 亚洲欧美日韩电影| 精品国产人妻一区二区三区| 久久精品日韩一区二区三区| 97久久超碰国产精品| 欧美一区二区视频观看视频| 国产精品一卡二| 欧美日韩小视频| 国精品**一区二区三区在线蜜桃 | 蜜桃视频在线观看一区| 999精品视频在线观看播放| 一区二区三区欧美日| 久久美女免费视频| 亚洲三级久久久| 波多野结衣a v在线| 亚洲三级小视频| 亚洲激情视频小说| 亚洲激情自拍偷拍| 97人妻人人揉人人躁人人| 一区二区激情视频| 日本黄区免费视频观看| 亚洲第一会所有码转帖| 五月婷婷综合激情网| 午夜精品国产更新| 999精品在线视频| 三级成人在线视频| 国产女人被狂躁到高潮小说| 日本不卡123| 在线视频中文字幕一区二区| 精品一区二区三区在线播放| 欧美色综合久久| 国产精品2024| 日韩欧美国产成人一区二区| 91小视频免费观看| 国产日韩成人精品| 欧美大片免费播放器| 亚洲免费观看高清完整版在线观看熊| 91成人在线免费视频| 亚洲综合免费观看高清完整版 | 欧美日韩在线综合| 粉嫩一区二区三区性色av| 日韩久久免费av| 性生活一级大片| 国产欧美视频在线观看| aaaaaav| 一级日本不卡的影视| 中文字幕另类日韩欧美亚洲嫩草| 久久国产免费看| 欧美精品久久99| 97se亚洲国产综合自在线观| 国产人成一区二区三区影院| 亚洲天堂久久新| 日日夜夜一区二区| 欧美丝袜自拍制服另类| 成人精品国产福利| 国产欧美日韩不卡| 亚洲午夜精品久久久久久高潮| 日欧美一区二区| 欧美人与z0zoxxxx视频| 91色婷婷久久久久合中文| 国产精品久久久久久久浪潮网站| 波多野在线播放| 蜜臀va亚洲va欧美va天堂| 7777精品久久久大香线蕉 | 99久久综合精品| 国产精品久久三区| sm捆绑调教视频| 国产剧情一区在线| 久久久三级国产网站| 免费在线观看你懂的| 日韩在线一二三区| 91精品国产欧美一区二区18| 91超薄肉色丝袜交足高跟凉鞋| 一区二区三区四区乱视频| 色拍拍在线精品视频8848| 成人sese在线| 日韩一区中文字幕| 色偷偷88欧美精品久久久| av欧美精品.com| 椎名由奈av一区二区三区| 色婷婷久久久综合中文字幕| 99久久伊人精品| 亚洲免费观看高清完整版在线| 色视频欧美一区二区三区| 99精品视频免费在线观看| 亚洲老妇xxxxxx| 欧美亚洲国产怡红院影院| 性生交大片免费看l| 亚洲成精国产精品女| 欧美一级精品在线| 日韩网站在线播放| 免费黄网站欧美| 精品处破学生在线二十三| 貂蝉被到爽流白浆在线观看| 粉嫩绯色av一区二区在线观看| 国产精品久久久久久久岛一牛影视| 日韩欧美综合视频| 99re热这里只有精品视频| 亚洲永久免费视频| 欧美一区二区视频网站| 亚洲а∨天堂久久精品2021| 国产精品一卡二卡在线观看| 亚洲视频一二区| 欧美日韩极品在线观看一区| 天堂久久久久久| 精油按摩中文字幕久久| 国产精品乱人伦中文| 欧美日韩一区二区三区四区| 欲求不满的岳中文字幕| 久久99精品国产.久久久久久| 中文在线免费一区三区高中清不卡| 天天看片中文字幕| 18禁一区二区三区| 美女诱惑一区二区| 国产精品久久777777| 欧美日韩一区在线| caopeng视频| 成人高清免费在线播放| 亚洲第一福利一区| 久久亚洲一区二区三区四区| 校园春色 亚洲| 国产污在线观看| 国产一区二区在线观看免费| 亚洲日本在线视频观看| 在线播放中文一区| 国产欧美小视频| 欧美色图校园春色| 久久国产精品露脸对白| 1024成人网| 欧美电视剧免费全集观看| 永久久久久久久| 亚洲の无码国产の无码步美| 国产精品911| 亚洲成va人在线观看| 欧美国产乱子伦| 欧美日韩夫妻久久| 午夜国产福利视频| 性欧美18—19sex性高清| 国产传媒欧美日韩成人| 亚洲成av人片| 国产精品午夜久久| 制服丝袜亚洲播放| 久久精品黄色片| 亚洲成人网在线播放| 99精品久久免费看蜜臀剧情介绍| 免费视频最近日韩| 亚洲乱码国产乱码精品精可以看| 精品国产乱子伦一区| 欧美三级视频在线| 国产小视频你懂的| 在线观看日韩精品视频| 99精品国产视频| 国产麻豆精品在线观看|