日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

海量數據處理利器 Roaring BitMap 原理介紹

來源: 責編: 時間:2024-06-20 15:23:29 214觀看
導讀一、引言在進行大數據開發時,我們可以使用布隆過濾器和Redis中的HyperLogLog來進行大數據的判重和數量統計,雖然這兩種方法節省內存空間并且效率很高,但是也存在一些誤差。如果需要100%準確的話,我們可以使用BitMap來存儲

m5j28資訊網——每日最新資訊28at.com

一、引言

在進行大數據開發時,我們可以使用布隆過濾器和Redis中的HyperLogLog來進行大數據的判重和數量統計,雖然這兩種方法節省內存空間并且效率很高,但是也存在一些誤差。如果需要100%準確的話,我們可以使用BitMap來存儲數據。m5j28資訊網——每日最新資訊28at.com

BitMap 位圖索引數據結構被廣泛地應用于數據存儲和數據搜索中,但是對于存儲較為分散的數據時,BitMap會占用比較大的內存空間,因此我們更偏向于使用 Roaring BitMap稀疏位圖索引進行存儲。同時,Roaring BitMap廣泛應用于數據庫存儲和大數據引擎中,例如Hive,Spark,Doris,Kylin等。m5j28資訊網——每日最新資訊28at.com

下文將分別介紹 BitMap 和 Roaring BitMap 的原理及其相關應用。m5j28資訊網——每日最新資訊28at.com

二、BitMap原理

BitMap的基本思想就是用bit位來標記某個元素對應的value,而key就是這個元素。m5j28資訊網——每日最新資訊28at.com

例如,在下圖中,是一個字節代表的8位,下標為1,2,4,6的bit位的值為1,則該字節表示{1,2,4,6}這幾個數。m5j28資訊網——每日最新資訊28at.com

圖片m5j28資訊網——每日最新資訊28at.com

在Java中,1個int占用4個字節,如果用int來存儲這四個數字的話,那么將需要4 * 4 = 16字節。m5j28資訊網——每日最新資訊28at.com

BitMap可以用于快速排序,查找,及去重等操作。優點是占用內存少(相較于數組)和運算效率高,但是缺點也非常明顯,無法存儲重復的數據,并且在存儲較為稀疏的數據時,浪費存儲空間較多。m5j28資訊網——每日最新資訊28at.com

三、Roaring BitMap 原理

3.1 存儲方式

為了解決BitMap存儲較為稀疏數據時,浪費存儲空間較多的問題,我們引入了稀疏位圖索引Roaring BitMap。Roaring BitMap 有較高的計算性能及壓縮效率。下面簡單介紹一下Roaring BitMap的基本原理。m5j28資訊網——每日最新資訊28at.com

Roaring BitMap處理int型整數,將32位的int型整數分為高16位和低16位分別進行處理,高16位作為索引分片,而低16位用于存儲實際數據。其中每個索引對應一個數據桶(bucket),那么一共可以包含2^16 = 65536個數據塊。每個數據桶使用container容器來存儲低16位的部分,每個數據桶最多存儲2^16 = 65536個數據。m5j28資訊網——每日最新資訊28at.com

圖片m5j28資訊網——每日最新資訊28at.com

如上圖所示,高16位作為索引查找具體的數據塊,當前索引值為0,低16位作為value進行存儲。m5j28資訊網——每日最新資訊28at.com

Roaring BitMap在進行數據存儲時,會先根據高16位找到對應的索引key(二分查找),低16位作為key對應的value,先通過key檢查對應的container容器,如果發現container不存在的話,就先創建一個key和對應的container,否則直接將低16位存儲到對應的container中。m5j28資訊網——每日最新資訊28at.com

Roaring BitMap的精妙之處在于使用不同類型的container,接下來將對其進行介紹。m5j28資訊網——每日最新資訊28at.com

3.2 container類型

1.ArrayContainerm5j28資訊網——每日最新資訊28at.com

顧名思義,ArrayContainer直接采用數組來存儲低16位數據,沒有采用任何數據壓縮算法,適合存儲比較稀疏的數據,在Java中,使用short數組來存儲,并且占用的內存空間大小和數據量成線性關系。由于short為2字節,因此n個數據為2n字節。ArrayContainer采用二分查找定位有序數組中的元素,因此時間復雜度為O(logN)。ArrayContainer的最大數據量為4096, 4096 * 2b = 8kb。m5j28資訊網——每日最新資訊28at.com

2.BitMapContainerm5j28資訊網——每日最新資訊28at.com

BitMapContainer采用BitMap的原理,就是一個沒有經過壓縮處理的普通BitMap,適合存儲比較稠密的數據,在Java中使用Long數組存儲低16位數據,每一個bit位表示一個數字。由于每個container需要存儲2^16 = 65536個數據,如果通過BitMap進行存儲的話,需要使用2^16個bit進行存儲,即8kb的數據空間。m5j28資訊網——每日最新資訊28at.com

可以從下圖中看出ArrayContainer和BitMapContainer的內存空間使用關系,當數據量小于4096時,使用ArrayContainer比較合適,當數據量大于等于4096時,使用BitMapContainer更佳。m5j28資訊網——每日最新資訊28at.com

圖片m5j28資訊網——每日最新資訊28at.com

因為BitMap直接使用位運算,所以BitMapContainer的時間復雜度為O(1)。m5j28資訊網——每日最新資訊28at.com

3.RunContainerm5j28資訊網——每日最新資訊28at.com

RunContainer采用Run-Length Encoding 行程長度編碼進行壓縮,適合存儲大量連續數據。Java中使用short數組進行存儲。連續bit位程度越高的話越節省存儲空間,最佳場景下(65536個數據全為1)只需要存儲4字節。最差場景為所有數據都不連續,所有存儲數據位置為奇數或者偶數,這種場景需要存儲128kb。由于采用二分查找算法定位元素,因此時間復雜度為O(logN)。m5j28資訊網——每日最新資訊28at.com

行程長度編碼即的原理是對連續出現的數字進行壓縮,只記錄初始數字和后續連續數量。m5j28資訊網——每日最新資訊28at.com

例如:[1,2,3,4,5,8,9,10]使用編碼后的數據為[1,4,8,2]。m5j28資訊網——每日最新資訊28at.com

Java 里可以使用runOptinize()方法來對比RunContainer和其他兩個Container存儲空間大小,如果使用RunContainer存儲空間更佳則會進行轉化。m5j28資訊網——每日最新資訊28at.com

根據上面三個Container類型我們可以得知如何進行選擇:m5j28資訊網——每日最新資訊28at.com

  1. Container默認使用ArrayContainer,當元素數量超過4096時,會由ArrayContainer轉換BitMapContainer。
  2. 當元素數量小于等于4096時,BitMapContainer會逆向轉換回ArrayContainer。
  3.  正常增刪元素不會使Container直接變成RunContainer,而需要用戶進行優化方法調用才會轉換為最節省空間的Container。

3.3 Roaring BitMap 相關源碼

介紹完Roaring BitMap的三種container類型以后,讓我們了解一下,Roaring BitMap的相關源碼。這里介紹一下Java中增加元素的源碼實現。m5j28資訊網——每日最新資訊28at.com

public void add(final int x) {    final short hb = Util.highbits(x);    final int i = highLowContainer.getIndex(hb);    if (i >= 0) {      highLowContainer.setContainerAtIndex(i,          highLowContainer.getContainerAtIndex(i).add(Util.lowbits(x)));    } else {      final ArrayContainer newac = new ArrayContainer();      highLowContainer.insertNewKeyValueAt(-i - 1, hb, newac.add(Util.lowbits(x)));    }  }

Roaring BitMap首先獲取添加元素的高16位,然后再調用getIndex獲取高16位對應的索引,如果索引大于0,表示已經創建該索引對應的container,故直接添加相應的元素低16位即可;否則的話,說明該索引對應的container還沒有被創建,先創建對應的ArrayContainer,再進行元素添加。值得一提的是,在getIndex方法中,使用了二分查找來獲取索引值,所以時間復雜度為O(logn)。m5j28資訊網——每日最新資訊28at.com

// 包含一個二分查找protected int getIndex(short x) {  // 在二分查找之前,我們先對常見情況優化。  if ((size == 0) || (keys[size - 1] == x)) {    return size - 1;  }  // 沒有碰到常見情況,我們只能遍歷這個列表。  return this.binarySearch(0, size, x);}

對于元素添加,三種Container提供了不同的實現方式,下面將分別介紹。m5j28資訊網——每日最新資訊28at.com

1. ArrayContainerm5j28資訊網——每日最新資訊28at.com

if (cardinality == 0 || (cardinality > 0          && toIntUnsigned(x) > toIntUnsigned(content[cardinality - 1]))) {    if (cardinality >= DEFAULT_MAX_SIZE) {      return toBitMapContainer().add(x);    }    if (cardinality >= this.content.length) {      increaseCapacity();    }    content[cardinality++] = x;  } else {    int loc = Util.unsignedBinarySearch(content, 0, cardinality, x);    if (loc < 0) {      // 當標簽中元素數量等于默認最大值時,把ArrayContainer轉換為BitMapContainer      if (cardinality >= DEFAULT_MAX_SIZE) {        return toBitMapContainer().add(x);      }      if (cardinality >= this.content.length) {        increaseCapacity();      }      System.arraycopy(content, -loc - 1, content, -loc, cardinality + loc + 1);      content[-loc - 1] = x;      ++cardinality;    }  }  return this;}

ArrayContainer把添加元素分成兩種場景,一種走二分查找,另外一種不走二分查找。m5j28資訊網——每日最新資訊28at.com

第一種場景:不走二分查找。m5j28資訊網——每日最新資訊28at.com

當基數為0或者值大于container中的最大值,可以直接添加,因為content數組是有序的,最后一個是最大值。m5j28資訊網——每日最新資訊28at.com

當基數大于等于默認最大值4096時,ArrayContainer將轉換為BitMapContainer。如果基數大于content的數組長度的話,需要將content進行擴容。最后進行賦值即可。m5j28資訊網——每日最新資訊28at.com

第二種場景:走二分查找。m5j28資訊網——每日最新資訊28at.com

先通過二分查找找到對應的插入位置,如果返回loc大于等于0,說明存在,直接返回即可,如果小于0才進行后續插入。后續操作同上,當基數大于等于默認最大值4096時,ArrayContainer將轉換為BitMapContainer。如果基數大于content的數組長度的話,需要將content進行擴容。最后通過拷貝數組將元素插入到content數組中。m5j28資訊網——每日最新資訊28at.com

2. BitMapContainerm5j28資訊網——每日最新資訊28at.com

public Container add(final short i) {  final int x = Util.toIntUnsigned(i);  final long previous = BitMap[x / 64];  long newval = previous | (1L << x);   BitMap[x / 64] = newval;  if (USE_BRANCHLESS) {    cardinality += (previous ^ newval) >>> x;  } else if (previous != newval) {    ++cardinality;  }  return this;}

BitMap數組為BitMapContainer的存儲容器存放數據的內容,數據類型為long,在這里我們只需要找到x在BitMap中的位置,并且把相應的bit位置1即可。x/64就是找到對應long的舊值,1L<<x 就是把對應的bit位置為1,再跟舊值進行或操作,就可以得到新值,再將這個新值存回到bitmap數組即可。<="" span="">m5j28資訊網——每日最新資訊28at.com

3. RunContainerm5j28資訊網——每日最新資訊28at.com

public Container add(short k) {     int index = unsignedInterleavedBinarySearch(valueslength, 0, nbrruns, k);  if (index >= 0) {    return this;// already there  }  index = -index - 2;  if (index >= 0) {    int offset = toIntUnsigned(k) - toIntUnsigned(getValue(index));    int le = toIntUnsigned(getLength(index));    if (offset <= le) {      return this;    }    if (offset == le + 1) {      // we may need to fuse      if (index + 1 < nbrruns) {        if (toIntUnsigned(getValue(index + 1)) == toIntUnsigned(k) + 1) {          // indeed fusion is needed          setLength(index,              (short) (getValue(index + 1) + getLength(index + 1) - getValue(index)));          recoverRoomAtIndex(index + 1);          return this;        }      }      incrementLength(index);      return this;    }    if (index + 1 < nbrruns) {      // we may need to fuse      if (toIntUnsigned(getValue(index + 1)) == toIntUnsigned(k) + 1) {        // indeed fusion is needed        setValue(index + 1, k);        setLength(index + 1, (short) (getLength(index + 1) + 1));        return this;      }    }  }  if (index == -1) {    // we may need to extend the first run    if (0 < nbrruns) {      if (getValue(0) == k + 1) {        incrementLength(0);        decrementValue(0);        return this;      }    }  }  makeRoomAtIndex(index + 1);  setValue(index + 1, k);  setLength(index + 1, (short) 0);  return this;}

RunContainer中的兩個數據結構,nbrruns表示有多少段行程,數據類型為int,valueslength數組表示所有的行程,數據類型為short。m5j28資訊網——每日最新資訊28at.com

  1. 首先,使用二分查找+順序查找在valueslength數組中查找元素k的插入位置index。如果查找到的index結果大于等于0那就說明k是某個行程起始值,已經存在,直接返回。
  2. -index-2是為了指向前一個行程起始值的索引。
  3. 接下來是一些偏移量和索引值的判斷,主要是為了確認k是否落在上一個行程里,或者外面,如果落在上一個行程里,則直接返回,否則需要新建一個行程或者就近與一個行程混合并且將行程長度加1。

3.4 BitMap 和 Roaring BitMap 存儲情況對比

public static void count(Integer inputSize) {         RoaringBitMap BitMap = new RoaringBitMap();         BitMap.add(0L, inputSize);         //獲取BitMap個數        int cardinality = BitMap.getCardinality();         //獲取BitMap壓縮大小        int compressSizeIntBytes = BitMap.getSizeInBytes();         //刪除壓縮(移除行程編碼,將container退化為BitMapContainer 或 ArrayContainer)         BitMap.removeRunCompression();         //獲取BitMap不壓縮大小        int uncompressSizeIntBytes = BitMap.getSizeInBytes();         System.out.println("Roaring BitMap個數:" + cardinality);        System.out.println("最好情況,BitMap壓縮大?。? + compressSizeIntBytes / 1024 + "KB");        System.out.println("最壞情況,BitMap不壓縮大?。? + uncompressSizeIntBytes / 1024 / 1024 + "MB");         BitSet bitSet = new BitSet();        for (int i = 0; i < inputSize; i++) {            bitSet.set(i);        }        //獲取BitMap大小        int size = bitSet.size();         System.out.println("BitMap個數:" + bitSet.length());        System.out.println("BitMap大小:" + size / 8 / 1024 / 1024 + "MB");    }

上述代碼使用了Java內置的BitMap(BitSet) 和 Roaring BitMap進行存儲大小對比,輸出結果如下所示。m5j28資訊網——每日最新資訊28at.com

  • Roaring BitMap個數:1000000000
  • 最好情況,BitMap壓縮大?。?49KB
  • 最壞情況,BitMap不壓縮大?。?19MB
  • Roaring BitMap個數:1000000000
  • BitMap大?。?28MB

可以發現,Roaring BitMap的壓縮性能效果非常好,同等情況下,是BitMap占用內存的近一千分之一。在退化成BitMapContainer/arrayContainer之后也仍然比使用基本的BitMap存儲效果好一些。m5j28資訊網——每日最新資訊28at.com

四、Roaring BitMap 使用

4.1 Java 中相關 API 使用

在Java中,Roaring BitMap提供了交并補差集等操作,如下代碼所示,列舉了Java中roaing BitMap的相關API使用方式。m5j28資訊網——每日最新資訊28at.com

//添加單個數字public void add(final int x)//添加范圍數字public void add(final long rangeStart, final long rangeEnd)//移除數字public void remove(final int x)//遍歷RBMpublic void forEach(IntConsumer ic)//檢測是否包含public boolean contains(final int x)//獲取基數public int getCardinality()//位與,取兩個RBM的交集,當前RBM會被修改public void and(final RoaringBitMap x2)//同上,但是會返回一個新的RBM,不會修改原始的RBM,線程安全public static RoaringBitMap and(final RoaringBitMap x1, final RoaringBitMap x2)//位或,取兩個RBM的并集,當前RBM會被修改public void or(final RoaringBitMap x2)//同上,但是會返回一個新的RBM,不會修改原始的RBM,線程安全public static RoaringBitMap or(final RoaringBitMap x1, final RoaringBitMap x2)//異或,取兩個RBM的對稱差,當前RBM會被修改public void xor(final RoaringBitMap x2)//同上,但是會返回一個新的RBM,不會修改原始的RBM,線程安全public static RoaringBitMap xor(final RoaringBitMap x1, final RoaringBitMap x2)//取原始值和x2的差集,當前RBM會被修改public void andNot(final RoaringBitMap x2)//同上,但是會返回一個新的RBM,不會修改原始的RBM,線程安全public static RoaringBitMap andNot(final RoaringBitMap x1, final RoaringBitMap x2)//序列化public void serialize(DataOutput out) throws IOExceptionpublic void serialize(ByteBuffer buffer)//反序列化public void deserialize(DataInput in) throws IOExceptionpublic void deserialize(ByteBuffer bbf) throws IOException

對于序列化來說,Roaring BitMap官方定義了一套序列化規則,用來保證不同語言實現的兼容性。m5j28資訊網——每日最新資訊28at.com

圖片m5j28資訊網——每日最新資訊28at.com

Java中可以使用serialize方法進行序列化,deserialize方法進行反序列化。m5j28資訊網——每日最新資訊28at.com

4.2 業務實際場景應用

Roaring BitMap可以用來構建大數據標簽,針對類型特征來創建對應的標簽。m5j28資訊網——每日最新資訊28at.com

在我們的業務場景中,有很多需要基于人群標簽進行交并補集運算的場景,下面以一個場景為例,我們需要計算每天某個設備接口 在設備標簽A上的查詢成功率,因為設備標簽A中的設備不是所有都活躍在網的,所以我們需要將設備標簽A與每日日活人群標簽取交集,得到的交集大小才能用作成功率計算的分母,另外拿查詢成功的標簽人群做分子來進行計算即可,查詢時長耗時為1s。m5j28資訊網——每日最新資訊28at.com

假如沒有使用標簽保存集合之前,我們需要在hive表中查詢出同時滿足當天在網的活躍用戶和設備A的用戶數量,查詢時長耗時在幾分鐘以上。兩種方式相比之下,使用Roaring BitMap查詢的效率更高。m5j28資訊網——每日最新資訊28at.com

圖片m5j28資訊網——每日最新資訊28at.com

五、總結

本文結合個人理解梳理了BitMap及Roaring BitMap的原理及使用,分別主要介紹了Roaring BitMap的存儲方式及三種container類型及Java中Roaring BitMap相關API使用,如有不足和優化建議,也歡迎大家批評指正。m5j28資訊網——每日最新資訊28at.com

參考資料:m5j28資訊網——每日最新資訊28at.com

  • Chambi S , Lemire D , Kaser O , et al.
    Better BitMap performance with Roaring 
    BitMaps[J]. Software—practice & Experience, 2016, 46(5):709-719.
  • https://RoaringBitMap.org/
  • https://github.com/RoaringBitMap/RoaringFormatSpec

本文鏈接:http://m.www897cc.com/showinfo-26-95169-0.html海量數據處理利器 Roaring BitMap 原理介紹

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 實現一個完美的高并發訂單減庫存方案

下一篇: 我國正加緊建設數字法院,力爭年底實現全國 3500 多家法院“一張網”辦案辦公

標簽:
  • 熱門焦點
  • 2023 年的 Node.js 生態系統

    隨著技術的不斷演進和創新,Node.js 在 2023 年達到了一個新的高度。Node.js 擁有一個龐大的生態系統,可以幫助開發人員更快地實現復雜的應用。本文就來看看 Node.js 最新的生
  • 三言兩語說透柯里化和反柯里化

    JavaScript中的柯里化(Currying)和反柯里化(Uncurrying)是兩種很有用的技術,可以幫助我們寫出更加優雅、泛用的函數。本文將首先介紹柯里化和反柯里化的概念、實現原理和應用
  • 自動化在DevOps中的力量:簡化軟件開發和交付

    自動化在DevOps中扮演著重要角色,它提升了DevOps的效能。通過自動化工具和方法,DevOps團隊可以實現以下目標:消除手動和重復性任務。簡化流程。在整個軟件開發生命周期中實現更
  • .NET 程序的 GDI 句柄泄露的再反思

    一、背景1. 講故事上個月我寫過一篇 如何洞察 C# 程序的 GDI 句柄泄露 文章,當時用的是 GDIView + WinDbg 把問題搞定,前者用來定位泄露資源,后者用來定位泄露代碼,后面有朋友反
  • 虛擬鍵盤 API 的妙用

    你是否在遇到過這樣的問題:移動設備上有一個固定元素,當激活虛擬鍵盤時,該元素被隱藏在了鍵盤下方?多年來,這一直是 Web 上的默認行為,在本文中,我們將探討這個問題、為什么會發生
  • 從零到英雄:高并發與性能優化的神奇之旅

    作者 | 波哥審校 | 重樓作為公司的架構師或者程序員,你是否曾經為公司的系統在面對高并發和性能瓶頸時感到手足無措或者焦頭爛額呢?筆者在出道那會為此是吃盡了苦頭的,不過也得
  • WebRTC.Net庫開發進階,教你實現屏幕共享和多路復用!

    WebRTC.Net庫:讓你的應用更親民友好,實現視頻通話無痛接入! 除了基本用法外,還有一些進階用法可以更好地利用該庫。自定義 STUN/TURN 服務器配置WebRTC.Net 默認使用 Google 的
  • 微軟邀請 Microsoft 365 商業用戶,測試視頻編輯器 Clipchamp

    8 月 1 日消息,微軟近日宣布即將面向 Microsoft 365 商業用戶,開放 Clipchamp 應用,邀請用戶通過該應用來編輯視頻。微軟于 2021 年收購 Clipchamp,隨后開始逐步整合到 Microsof
  • 電視息屏休眠仍有網絡上傳 愛奇藝被質疑“薅消費者羊毛”

    記者丨寧曉敏 見習生丨汗青出品丨鰲頭財經(theSankei) 前不久,愛奇藝發布了一份亮眼的一季報,不僅營收和會員營收創造歷史最佳表現,其運營利潤也連續6個月實現增長。自去年年初
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
亚洲图片欧洲图片av| 亚洲精品久久久久中文字幕欢迎你 | 美日韩丰满少妇在线观看| 欧美 日韩 国产 一区| 欧美日韩免费一区二区三区视频 | 国产一区二区三区四区hd| 一区二区三区在线不卡| 亚洲精品日韩综合观看成人91 | 欧美黄色一区二区| 欧美视频免费在线| 国产亚洲欧美日韩美女| 亚洲精华国产欧美| 亚洲自拍都市欧美小说| 欧美影院视频| 欧美成人精品不卡视频在线观看| 欧美日韩国产在线播放网站| 国产精品腿扒开做爽爽爽挤奶网站| 怡红院av一区二区三区| 一本久久综合亚洲鲁鲁| 欧美在线999| 欧美成人嫩草网站| 欧美特黄一级| 伊人久久婷婷| 亚洲伊人网站| 另类亚洲自拍| 国产精品久久久久久久app| 亚洲成人自拍视频| 亚洲国产成人精品视频 | 国产精品蜜臀在线观看| 国产精品丝袜白浆摸在线| 在线精品国产欧美| 亚洲欧美国内爽妇网| 欧美成人免费全部| 亚洲风情亚aⅴ在线发布| 免费毛片一区二区三区久久久| 亚洲精品精选| 欧美刺激性大交免费视频| 亚洲午夜一二三区视频| 久久黄色级2电影| 国产一区二区三区丝袜| 久久久久久久久综合| 亚洲免费观看高清完整版在线观看熊 | 亚洲精品日韩欧美| 日韩视频免费观看| 久久久噜噜噜久久狠狠50岁| 国产精品网红福利| 亚洲综合首页| 国产欧美一区二区精品秋霞影院| 激情一区二区三区| 免费成人性网站| 一区二区三区不卡视频在线观看 | 欧美精品久久久久久| 欧美资源在线观看| 亚洲女人小视频在线观看| 亚洲一区成人| 一区二区动漫| 欧美特黄视频| 亚洲综合激情| 国产视频丨精品|在线观看| 国产精品视频99| 亚洲国产精品成人va在线观看| 亚洲青色在线| 麻豆freexxxx性91精品| 国产视频一区在线| 亚洲欧美国产三级| 欧美午夜不卡影院在线观看完整版免费| av成人激情| 在线成人性视频| 久久久久久久综合| 亚洲精品资源| 伊人狠狠色丁香综合尤物| 一区在线免费| 久久国产一区二区| 国产亚洲欧美另类中文| 亚洲欧美一区二区三区在线| 国产精品xvideos88| 99在线|亚洲一区二区| 欧美黄色影院| 亚洲精品免费在线播放| 欧美黑人在线观看| 亚洲国产一区二区三区在线播 | 欧美va亚洲va香蕉在线| 亚洲缚视频在线观看| 老司机成人网| 亚洲第一精品电影| 久久三级福利| 亚洲国产欧美另类丝袜| 牛牛国产精品| 亚洲人成高清| 欧美激情综合色| 日韩午夜电影在线观看| 欧美日韩三级| 亚洲午夜一区二区三区| 国产精品毛片一区二区三区| 午夜精品短视频| 国产日韩av在线播放| 久久av红桃一区二区小说| 国产在线精品成人一区二区三区| 欧美中文日韩| 在线不卡中文字幕| 欧美成人网在线| 亚洲最新在线| 国产精品视频精品| 久久成人免费| 亚洲国产精品悠悠久久琪琪| 欧美精品大片| 亚洲午夜激情在线| 国产人成精品一区二区三| 久久久久国色av免费观看性色| 在线国产精品播放| 欧美激情综合五月色丁香小说| 国产精品99久久99久久久二8| 国产精品午夜电影| 久久久久久一区二区| 亚洲国产日韩美| 欧美无乱码久久久免费午夜一区 | 中文亚洲视频在线| 国产欧美视频一区二区| 久久综合中文色婷婷| 9久re热视频在线精品| 国产精品日韩一区二区| 久久久久久久精| 日韩一区二区免费高清| 国产精品视频九色porn| 久久这里只精品最新地址| 亚洲美女色禁图| 国产欧美日韩高清| 欧美电影在线观看| 亚洲欧美日本国产有色| 在线免费观看视频一区| 欧美午夜a级限制福利片| 久久久www成人免费精品| 亚洲理论电影网| 国产日产欧产精品推荐色| 欧美成人日韩| 欧美伊人精品成人久久综合97| 91久久午夜| 国产麻豆一精品一av一免费| 牛人盗摄一区二区三区视频| 一区二区三区视频在线观看| 韩国精品一区二区三区| 欧美色图首页| 狂野欧美激情性xxxx欧美| 亚洲性视频网站| 亚洲高清资源| 国产伦精品一区二区三区| 欧美第十八页| 久久www成人_看片免费不卡| 亚洲精品日韩在线观看| 国产亚洲观看| 欧美午夜免费电影| 久久综合婷婷| 香蕉久久一区二区不卡无毒影院 | 欧美日本乱大交xxxxx| 久久人人97超碰国产公开结果| 亚洲一区二区三区乱码aⅴ蜜桃女| 在线国产精品一区| 国产精品视频网站| 欧美精品三级| 久久亚洲综合网| 性做久久久久久免费观看欧美| 亚洲毛片在线看| 在线免费观看一区二区三区| 国产精品视频不卡| 欧美日韩直播| 欧美黄色aaaa| 欧美成人a∨高清免费观看| 久久国产欧美| 午夜精品久久久久久久白皮肤| 日韩视频在线一区| 亚洲国产高清一区| 国产专区欧美专区| 国产欧美va欧美va香蕉在| 欧美日韩理论| 欧美成人综合网站| 久久欧美中文字幕| 久久福利电影| 午夜久久资源| 亚洲一区二区欧美日韩| 99精品欧美一区二区蜜桃免费| 亚洲电影视频在线| 红桃视频国产精品| 国产视频综合在线| 国产精品丝袜久久久久久app| 欧美视频精品一区| 欧美日韩国产首页在线观看| 欧美电影免费观看高清完整版| 免费观看一级特黄欧美大片| 久久久久久久综合日本| 亚洲一区二区三区精品动漫| av不卡在线看| 亚洲日本欧美在线| 亚洲人成啪啪网站| 亚洲国产精品一区二区第四页av | 久久精品国产精品亚洲综合| 性感少妇一区| 先锋影音一区二区三区| 欧美一级片久久久久久久| 午夜精品久久久久久久99黑人| 亚洲欧美日韩综合aⅴ视频| 午夜欧美精品久久久久久久| 亚洲欧美制服另类日韩| 篠田优中文在线播放第一区| 欧美一级理论片|