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

數據庫中間件 MyCAT源碼分析 —— 跨庫兩表Join

數據庫
MyCAT 支持跨庫表 Join,目前版本僅支持跨庫兩表 Join。雖然如此,已經能夠滿足我們大部分的業務場景。況且,Join 過多的表可能帶來的性能問題也是很麻煩的。

1. 概述

MyCAT 支持跨庫表 Join,目前版本僅支持跨庫兩表 Join。雖然如此,已經能夠滿足我們大部分的業務場景。況且,Join 過多的表可能帶來的性能問題也是很麻煩的。

本文主要分享:

  1. 整體流程、調用順序圖
  2. 核心代碼的分析

前置閱讀:《MyCAT 源碼分析 —— 【單庫單表】查詢》。

OK,Let's Go。

2. 主流程

當執行跨庫兩表 Join SQL 時,經歷的大體流程如下:

 

SQL 上,需要添加注解 /*!mycat:catlet=io.mycat.catlets.ShareJoin */ ${SQL} 。RouteService#route(...) 解析注解 mycat:catlet 后,路由給 HintCatletHandler 作進一步處理。

HintCatletHandler 獲取注解對應的 Catlet 實現類,io.mycat.catlets.ShareJoin 就是其中一種實現(目前也只有這一種實現),提供了跨庫兩表 Join 的功能。從類命名上看,ShareJoin 很大可能性后續會提供完整的跨庫多表的 Join 功能。

核心代碼如下:

  1. // HintCatletHandler.java 
  2. public RouteResultset route(SystemConfig sysConfig, SchemaConfig schema
  3.                            int sqlType, String realSQL, String charset, ServerConnection sc, 
  4.                            LayerCachePool cachePool, String hintSQLValue, int hintSqlType, Map hintMap) 
  5.        throws SQLNonTransientException { 
  6.    String cateletClass = hintSQLValue; 
  7.    if (LOGGER.isDebugEnabled()) { 
  8.        LOGGER.debug("load catelet class:" + hintSQLValue + " to run sql " + realSQL); 
  9.    } 
  10.    try { 
  11.        Catlet catlet = (Catlet) MycatServer.getInstance().getCatletClassLoader().getInstanceofClass(cateletClass); 
  12.        catlet.route(sysConfig, schema, sqlType, realSQL, charset, sc, cachePool); 
  13.        catlet.processSQL(realSQL, new EngineCtx(sc.getSession2())); 
  14.    } catch (Exception e) { 
  15.        LOGGER.warn("catlet error " + e); 
  16.        throw new SQLNonTransientException(e); 
  17.    } 
  18.    return null
  19.  

3. ShareJoin

目前支持跨庫兩表 Join。ShareJoin 將 SQL 拆分成左表 SQL 和 右表 SQL,發送給各數據節點執行,匯總數據結果進行合后返回。

偽代碼如下:

  1. // SELECT u.id, o.id FROM t_order o  
  2. // INNER JOIN t_user u ON o.uid = u.id 
  3. // 【順序】查詢左表 
  4. String leftSQL = "SELECT o.id, u.id FROM t_order o"
  5. List leftList = dn[0].select(leftSQL) + dn[1].select(leftSQL) + ... + dn[n].select(leftsql); 
  6. // 【并行】查詢右表 
  7. String rightSQL = "SELECT u.id FROM t_user u WHERE u.id IN (${leftList.uid})"
  8. for (dn : dns) { // 此處是并行執行,使用回調邏輯 
  9.     for (rightRecord : dn.select(rightSQL)) { // 查詢右表 
  10.         // 合并結果 
  11.         for (leftRecord : leftList) { 
  12.             if (leftRecord.uid == rightRecord.id) { 
  13.                 write(leftRecord + leftRecord.uid 拼接結果); 
  14.             } 
  15.         } 
  16.     } 
  17.  

實際情況會更加復雜,我們接下來一點點往下看。

3.1 JoinParser

JoinParser 負責對 SQL 進行解析。整體流程如下:

 

舉個例子,/*!mycat:catlet=io.mycat.catlets.ShareJoin */ SELECT o.id, u.username from t_order o join t_user u on o.uid = u.id; 解析后,TableFilter 結果如下:

 

  • tName :表名
  • tAlia :表自定義命名
  • where :過濾條件
  • order :排序條件
  • parenTable :左連接的 Join 的表名。t_user表 在 join屬性 的 parenTable 為 "o",即 t_order。
  • joinParentkey :左連接的 Join 字段
  • joinKey :join 字段。t_user表 在 join屬性 為 id。
  • join :子 tableFilter。即,該表連接的右邊的表。
  • parent :和 join屬性 相對。

看到此處,大家可能有疑問,為什么要把 SQL 解析成 TableFilter。JoinParser 根據 TableFilter 生成數據節點執行 SQL。代碼如下:

  1. // TableFilter.java 
  2. public String getSQL() { 
  3.    String sql = ""
  4.    // fields 
  5.    for (Entry<String, String> entry : fieldAliasMap.entrySet()) { 
  6.        String key = entry.getKey(); 
  7.        String val = entry.getValue(); 
  8.        if (val == null) { 
  9.            sql = unionsql(sql, getFieldfrom(key), ","); 
  10.        } else { 
  11.            sql = unionsql(sql, getFieldfrom(key) + " as " + val, ","); 
  12.        } 
  13.    } 
  14.    // where 
  15.    if (parent == null) {    // on/where 等于號左邊的表 
  16.        String parentJoinKey = getJoinKey(true); 
  17.        // fix sharejoin bug: 
  18.        // (AbstractConnection.java:458) -close connection,reason:program err:java.lang.IndexOutOfBoundsException: 
  19.        // 原因是左表的select列沒有包含 join 列,在獲取結果時報上面的錯誤 
  20.        if (sql != null && parentJoinKey != null && 
  21.                !sql.toUpperCase().contains(parentJoinKey.trim().toUpperCase())) { 
  22.            sql += ", " + parentJoinKey; 
  23.        } 
  24.        sql = "select " + sql + " from " + tName; 
  25.        if (!(where.trim().equals(""))) { 
  26.            sql += " where " + where.trim(); 
  27.        } 
  28.    } else {    // on/where 等于號右邊邊的表 
  29.        if (allField) { 
  30.            sql = "select " + sql + " from " + tName; 
  31.        } else { 
  32.            sql = unionField("select " + joinKey, sql, ","); 
  33.            sql = sql + " from " + tName; 
  34.            //sql="select "+joinKey+","+sql+" from "+tName; 
  35.        } 
  36.        if (!(where.trim().equals(""))) { 
  37.            sql += " where " + where.trim() + " and (" + joinKey + " in %s )"
  38.        } else { 
  39.            sql += " where " + joinKey + " in %s "
  40.        } 
  41.    } 
  42.    // order 
  43.    if (!(order.trim().equals(""))) { 
  44.        sql += " order by " + order.trim(); 
  45.    } 
  46.    // limit 
  47.    if (parent == null) { 
  48.        if ((rowCount > 0) && (offset > 0)) { 
  49.            sql += " limit" + offset + "," + rowCount; 
  50.        } else { 
  51.            if (rowCount > 0) { 
  52.                sql += " limit " + rowCount; 
  53.            } 
  54.        } 
  55.    } 
  56.    return sql; 
  57.  
  • 當 parent 為空時,即on/where 等于號左邊的表。例如:select id, uid from t_order。
  • 當 parent 不為空時,即on/where 等于號右邊的表。例如:select id, username from t_user where id in (1, 2, 3)。

3.2 ShareJoin.processSQL(...)

當 SQL 解析完后,生成左邊的表執行的 SQL,發送給對應的數據節點查詢數據。大體流程如下:

 

當 SQL 為 /*!mycat:catlet=io.mycat.catlets.ShareJoin */ SELECT o.id, u.username from t_order o join t_user u on o.uid = u.id; 時, sql = getSql() 的返回結果為 select id, uid from t_order。

生成左邊的表執行的 SQL 后,順序順序順序發送給對應的數據節點查詢數據。具體順序查詢是怎么實現的,我們來看下章 BatchSQLJob。

3.3 BatchSQLJob

 

EngineCtx 對 BatchSQLJob 封裝,提供上層兩個方法:

  • executeNativeSQLSequnceJob :順序(非并發)在每個數據節點執行SQL任務
  • executeNativeSQLParallJob :并發在每個數據節點執行SQL任務

核心代碼如下:

  1. // EngineCtx.java 
  2. public void executeNativeSQLSequnceJob(String[] dataNodes, String sql, 
  3.         SQLJobHandler jobHandler) { 
  4.     for (String dataNode : dataNodes) { 
  5.         SQLJob job = new SQLJob(jobId.incrementAndGet(), sql, dataNode, 
  6.                 jobHandler, this); 
  7.         bachJob.addJob(job, false); 
  8.     } 
  9.  
  10. public void executeNativeSQLParallJob(String[] dataNodes, String sql, 
  11.         SQLJobHandler jobHandler) { 
  12.     for (String dataNode : dataNodes) { 
  13.         SQLJob job = new SQLJob(jobId.incrementAndGet(), sql, dataNode, 
  14.                 jobHandler, this); 
  15.         bachJob.addJob(job, true); 
  16.     } 
  17.  

BatchSQLJob 通過執行中任務列表、待執行任務列表來實現順序/并發執行任務。核心代碼如下:

  1. // BatchSQLJob.java 
  2. /** 
  3. * 執行中任務列表 
  4. */ 
  5. private ConcurrentHashMap<Integer, SQLJob> runningJobs = new ConcurrentHashMap<Integer, SQLJob>(); 
  6. /** 
  7. * 待執行任務列表 
  8. */ 
  9. private ConcurrentLinkedQueue<SQLJob> waitingJobs = new ConcurrentLinkedQueue<SQLJob>(); 
  10.  
  11. public void addJob(SQLJob newJob, boolean parallExecute) { 
  12.    if (parallExecute) { 
  13.        runJob(newJob); 
  14.    } else { 
  15.        waitingJobs.offer(newJob); 
  16.        if (runningJobs.isEmpty()) { // 若無正在執行中的任務,則從等待隊列里獲取任務進行執行。 
  17.            SQLJob job = waitingJobs.poll(); 
  18.            if (job != null) { 
  19.                runJob(job); 
  20.            } 
  21.        } 
  22.    } 
  23.  
  24. public boolean jobFinished(SQLJob sqlJob) { 
  25.     runningJobs.remove(sqlJob.getId()); 
  26.     SQLJob job = waitingJobs.poll(); 
  27.     if (job != null) { 
  28.         runJob(job); 
  29.         return false
  30.     } else { 
  31.         if (noMoreJobInput) { 
  32.             return runningJobs.isEmpty() && waitingJobs.isEmpty(); 
  33.         } else { 
  34.             return false
  35.         } 
  36.     } 
  37.  
  • 順序執行時,當 runningJobs 存在執行中的任務時,#addJob(...) 時,不立即執行,添加到 waitingJobs。當 SQLJob 完成時,順序調用下一個任務。
  • 并發執行時,#addJob(...) 時,立即執行。

SQLJob SQL 異步執行任務。其 jobHandler(SQLJobHandler) 屬性,在 SQL 執行有返回結果時,會進行回調,從而實現異步執行。

在 ShareJoin 里,SQLJobHandler 有兩個實現:ShareDBJoinHandler、ShareRowOutPutDataHandler。前者,左邊的表執行的 SQL 回調;后者,右邊的表執行的 SQL 回調。

 

3.4 ShareDBJoinHandler

ShareDBJoinHandler,左邊的表執行的 SQL 回調。流程如下:

 

  • #fieldEofResponse(...) :接收數據節點返回的 fields,放入內存。
  • #rowResponse(...) :接收數據節點返回的 row,放入內存。
  • #rowEofResponse(...) :接收完一個數據節點返回所有的 row。當所有數據節點都完成 SQL 執行時,提交右邊的表執行的 SQL 任務,并行執行,即圖中#createQryJob(...)。

當 SQL 為 /*!mycat:catlet=io.mycat.catlets.ShareJoin */ SELECT o.id, u.username from t_order o join t_user u on o.uid = u.id; 時, sql = getChildSQL() 的返回結果為 select id, username from t_user where id in (1, 2, 3)。

核心代碼如下:

  1. // ShareJoin.java 
  2. private void createQryJob(int batchSize) { 
  3.    int count = 0; 
  4.    Map<String, byte[]> batchRows = new ConcurrentHashMap<String, byte[]>(); 
  5.    String theId = null
  6.    StringBuilder sb = new StringBuilder().append('('); 
  7.    String svalue = ""
  8.    for (Map.Entry<String, String> e : ids.entrySet()) { 
  9.        theId = e.getKey(); 
  10.        byte[] rowbyte = rows.remove(theId); 
  11.        if (rowbyte != null) { 
  12.            batchRows.put(theId, rowbyte); 
  13.        } 
  14.        if (!svalue.equals(e.getValue())) { 
  15.            if (joinKeyType == Fields.FIELD_TYPE_VAR_STRING 
  16.                    || joinKeyType == Fields.FIELD_TYPE_STRING) { // joinkey 為varchar 
  17.                sb.append("'").append(e.getValue()).append("'").append(','); // ('digdeep','yuanfang'
  18.            } else { // 默認joinkey為int/long 
  19.                sb.append(e.getValue()).append(','); // (1,2,3) 
  20.            } 
  21.        } 
  22.        svalue = e.getValue(); 
  23.        if (count++ > batchSize) { 
  24.            break; 
  25.        } 
  26.    } 
  27.    if (count == 0) { 
  28.        return
  29.    } 
  30.    jointTableIsData = true
  31.    sb.deleteCharAt(sb.length() - 1).append(')'); 
  32.    String sql = String.format(joinParser.getChildSQL(), sb); 
  33.    getRoute(sql); 
  34.    ctx.executeNativeSQLParallJob(getDataNodes(), sql, new ShareRowOutPutDataHandler(this, fields, joinindex, joinParser.getJoinRkey(), batchRows, ctx.getSession())); 
  35.  

3.5 ShareRowOutPutDataHandler

ShareRowOutPutDataHandler,右邊的表執行的 SQL 回調。流程如下:

 

  • #fieldEofResponse(...) :接收數據節點返回的 fields,返回 header 給 MySQL Client。
  • #rowResponse(...) :接收數據節點返回的 row,匹配左表的記錄,返回合并后返回的 row 給 MySQL Client。
  • #rowEofResponse(...) :當所有 row 都返回完后,返回 eof 給 MySQL Client。

核心代碼如下:

  1. // ShareRowOutPutDataHandler.java 
  2. public boolean onRowData(String dataNode, byte[] rowData) { 
  3.    RowDataPacket rowDataPkgold = ResultSetUtil.parseRowData(rowData, bfields); 
  4.    //拷貝一份batchRows 
  5.    Map<String, byte[]> batchRowsCopy = new ConcurrentHashMap<String, byte[]>(); 
  6.    batchRowsCopy.putAll(arows); 
  7.    // 獲取Id字段, 
  8.    String id = ByteUtil.getString(rowDataPkgold.fieldValues.get(joinR)); 
  9.    // 查找ID對應的A表的記錄 
  10.    byte[] arow = getRow(batchRowsCopy, id, joinL); 
  11.    while (arow != null) { 
  12.        RowDataPacket rowDataPkg = ResultSetUtil.parseRowData(arow, afields);//ctx.getAllFields()); 
  13.        for (int i = 1; i < rowDataPkgold.fieldCount; i++) { 
  14.            // 設置b.name 字段 
  15.            byte[] bname = rowDataPkgold.fieldValues.get(i); 
  16.            rowDataPkg.add(bname); 
  17.            rowDataPkg.addFieldCount(1); 
  18.        } 
  19.        // huangyiming add 
  20.        MiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler(); 
  21.        if (null == middlerResultHandler) { 
  22.            ctx.writeRow(rowDataPkg); 
  23.        } else { 
  24.            if (middlerResultHandler instanceof MiddlerQueryResultHandler) { 
  25.                byte[] columnData = rowDataPkg.fieldValues.get(0); 
  26.                if (columnData != null && columnData.length > 0) { 
  27.                    String rowValue = new String(columnData); 
  28.                    middlerResultHandler.add(rowValue); 
  29.                } 
  30.                //} 
  31.            } 
  32.  
  33.        } 
  34.        arow = getRow(batchRowsCopy, id, joinL); 
  35.    } 
  36.    return false
  37.  

4. 彩蛋

如下是本文涉及到的核心類,有興趣的同學可以翻一翻。

 

ShareJoin 另外不支持的功能:

  1. 只支持 inner join,不支持 left join、right join 等等連接。
  2. 不支持 order by。
  3. 不支持 group by 以及 相關聚合函數。
  4. 即使 join 左表的字段未聲明為返回 fields 也會返回。

恩,MyCAT 弱XA 源碼繼續走起!

責任編輯:龐桂玉 來源: 芋艿V的博客
相關推薦

2017-07-26 09:41:28

MyCATSQLMongoDB

2017-07-18 17:35:16

數據庫MyCATPreparedSta

2017-12-01 05:40:56

數據庫中間件join

2017-12-01 05:04:32

數據庫中間件Atlas

2017-11-27 05:36:16

數據庫中間件TDDL

2017-11-27 05:06:42

數據庫中間件cobar

2018-02-24 19:37:33

Java8數據庫中間件

2011-08-10 13:03:58

CJDBC數據庫集群

2017-05-23 18:55:05

mysql-proxy數據庫架構

2020-04-10 17:00:33

Mycat分庫分表SpringBoot

2009-01-20 10:45:55

Oracle數據庫中間件

2024-12-06 08:29:29

2017-11-27 06:01:37

數據庫中間件中間層

2017-12-11 13:30:49

Go語言數據庫中間件

2017-11-03 11:02:08

數據庫中間件

2017-11-30 08:56:14

數據庫中間件架構師

2019-05-13 15:00:14

MySQLMyCat數據庫

2018-11-07 15:30:19

數據庫NewSQLNoSQL

2021-07-27 05:49:59

MySQL數據庫中間件

2020-10-15 08:34:32

數據庫中間件漫談
點贊
收藏

51CTO技術棧公眾號

青青青国产精品一区二区| 免费日韩一区二区| 日韩一区二区三区在线视频| youjizz.com在线观看| 五月婷婷伊人网| 免费在线观看一区二区三区| 欧美精品videosex牲欧美| 精品少妇人妻一区二区黑料社区| 亚洲日本中文| 欧美日韩中文在线| 91色精品视频在线| av永久免费观看| 老司机亚洲精品一区二区| 国产精品免费久久| 国产欧美日韩一区| 在线免费观看一级片| 伊人精品成人久久综合软件| 一区国产精品视频| 中文字幕天堂av| 欧美videos粗暴| 色综合天天天天做夜夜夜夜做| 国产精品亚洲不卡a| 成人h动漫精品一区二区下载| 欧美精品二区| 亚洲欧美国产一本综合首页| 秋霞午夜鲁丝一区二区| 97精品国产99久久久久久免费| 亚洲国产一区视频| 青少年xxxxx性开放hg| 精品无人乱码| 成人ar影院免费观看视频| 成人精品福利视频| 国产情侣呻吟对白高潮| 国产日韩欧美| 亚洲人成在线观看| 精品人妻一区二区免费| 电影一区中文字幕| 欧美乱妇15p| 国产天堂在线播放| 精品国产免费人成网站| 午夜亚洲国产au精品一区二区| 久久精品在线免费视频| 老司机在线视频二区| 日本一区二区成人在线| 欧美一区1区三区3区公司| 性xxxx视频播放免费| 91亚洲精品一区二区乱码| 97在线资源站| 亚洲第一精品网站| 国产一区二区福利视频| 成人免费淫片aa视频免费| 中文字幕福利视频| 美女久久久精品| 国产精品视频1区| 无码久久精品国产亚洲av影片| 日韩二区三区在线观看| 国产精品69久久久久| 天天干,天天干| 青娱乐精品视频| 国产精品一区二区三区毛片淫片| 超碰在线97观看| 免费xxxx性欧美18vr| 国产精品普通话| 在线观看免费中文字幕| 国产综合一区二区| 99三级在线| 日本高清视频www| 91免费版在线看| 欧美日韩精品久久| 东凛在线观看| 国产高清久久久久| 国产99在线|中文| 凹凸精品一区二区三区| 蜜桃久久av一区| 91网站在线看| 高h放荡受浪受bl| 91免费看片在线观看| 色噜噜狠狠色综合网| 欧美日本高清| 亚洲图片一区二区| 毛片av免费在线观看| 2019年精品视频自拍| 欧美日韩aaaaaa| 男人添女人荫蒂国产| 波多野结衣欧美| 亚洲欧美国产日韩天堂区| 极品尤物一区二区| 牛夜精品久久久久久久99黑人| 久久久久久国产| 国产一级一级国产| 精品成人国产| 国产98色在线| a天堂视频在线| 91在线精品一区二区| 亚洲va久久久噜噜噜久久狠狠| 91极品在线| 在线视频一区二区免费| 在线观看中文av| 午夜精品福利影院| 久久精品视频免费播放| 亚洲男人第一av| 另类综合日韩欧美亚洲| 精品日产一区2区三区黄免费| 国产69精品久久app免费版| 亚洲精品免费视频| 黄色av免费在线播放| 亚洲一二av| 中文字幕在线看视频国产欧美| 欧美交换国产一区内射| 日韩国产欧美一区二区三区| 成人在线视频网址| 91社区在线观看播放| 天天影视网天天综合色在线播放| 亚洲乱码日产精品bd在线观看| 亚洲最大网站| 日韩区在线观看| 成年人视频软件| 午夜一级久久| 成人高清在线观看| 国产不卡在线| 欧美日韩国产综合久久| 亚洲国产无码精品| 亚洲视频在线免费| 国产精品一区二区三区成人| 欧美在线一卡| 国产视频亚洲色图| 亚洲人成网站在线观看播放 | 久久久久国产一区二区三区四区 | 欧美日韩夫妻久久| 亚洲av无码国产精品久久| 伊人久久亚洲美女图片| 亚洲精品欧美一区二区三区| 亚洲麻豆精品| 欧美午夜不卡在线观看免费| 亚洲第一成人网站| 国产美女诱惑一区二区| 国产视频一区二区三区四区| 七七久久电影网| 日韩一区二区在线看| 国产老头老太做爰视频| 麻豆91精品视频| 一个色的综合| 农村妇女一区二区| 9999精品| 欧美日韩高清影院| 久久久免费看片| 日本欧美大码aⅴ在线播放| 久久国产精品一区二区三区四区| 国内在线免费视频| 亚洲成年网站在线观看| 国产 日韩 欧美 成人| 欧美福利电影在线观看| 成人黄色影片在线| 黄色小网站在线观看| 欧美日韩国产精选| 亚洲欧美综合7777色婷婷| 老色鬼精品视频在线观看播放| 亚洲.欧美.日本.国产综合在线| 欧美成人精品三级网站| 一区二区欧美久久| 在线观看中文字幕2021| 国产精品久久99| 亚洲成人手机在线观看| 韩国av一区| 国产精品69av| 欧美性猛交xxx乱大交3蜜桃| 欧美区视频在线观看| 美女福利视频在线观看| 成人免费精品视频| 3d动漫一区二区三区| 久久国内精品| 精品国产一区二区三区四区在线观看 | 羞羞色午夜精品一区二区三区| 成人天堂噜噜噜| 成人在线免费观看黄色| 99在线观看精品视频| 亚洲成人av福利| 亚洲黄色免费视频| 国产一区在线精品| 国产3p露脸普通话对白| 国产免费久久| 亚洲影视九九影院在线观看| 爱啪啪综合导航| 国产一区二区三区毛片| 国产叼嘿视频在线观看| 日韩欧美成人区| 欧美色视频一区二区三区在线观看| 国产成人午夜精品影院观看视频| 亚洲熟妇av日韩熟妇在线| 大色综合视频网站在线播放| 91免费版黄色| 性欧美1819sex性高清| 久久亚洲综合国产精品99麻豆精品福利| 人妻妺妺窝人体色www聚色窝| 色综合久久综合网欧美综合网| 欧美视频一区二区在线| 成人动漫一区二区| 伊人成人222| 一区二区精品| 激情五月五月婷婷| 狠狠色丁香婷婷综合影院| 97超碰资源| 黄色精品视频| 77777亚洲午夜久久多人| 精品久久久免费视频| 日韩欧美在线第一页| 欧美日韩精品亚洲精品| 欧美国产精品中文字幕| aaa黄色大片| 麻豆精品一区二区av白丝在线| 国产日韩欧美精品在线观看| 久久综合成人| 欧美美乳视频网站在线观看| 欧美日本三级| 国产色婷婷国产综合在线理论片a| 美女扒开腿让男人桶爽久久软| 精品国产视频在线| av在线收看| 亚洲老头老太hd| 国精产品一品二品国精品69xx| 欧美欧美欧美欧美| 99re热视频| 欧美日韩一区二区免费视频| 欧美 日韩 国产 一区二区三区| 国产日本一区二区| 精品无码在线视频| 丝袜美腿高跟呻吟高潮一区| 欧美做暖暖视频| 久久精品影视| 一区二区三区在线观看www| 欧美激情三区| 国产不卡一区二区在线播放| 黄色污网站在线观看| 欧美激情免费看| 91精选在线| 免费91在线视频| 麻豆影院在线| 日韩在线观看免费全| www.黄在线观看| 中文字幕国产精品| 3p在线观看| 色哟哟亚洲精品一区二区| 国产小视频在线播放| 亚洲男女性事视频| 嫩草精品影院| 亚洲午夜av久久乱码| 免费在线性爱视频| 亚洲图中文字幕| 懂色一区二区三区| www.久久久久| 黄色网址在线免费| 美女精品视频一区| 中文字幕在线观看播放| 欧美激情免费在线| av日韩中文| 欧洲中文字幕国产精品| 国精产品一区一区三区四川| 国产精品久久久久影院日本| 福利视频一区| 亚洲一区亚洲二区亚洲三区| 日本成人精品| 精品综合久久| 自拍自偷一区二区三区| 日本精品一区| 99视频精品全部免费在线视频| 国产高清在线一区二区| 动漫3d精品一区二区三区乱码| 精品久久sese| 精品国产一区二区三区久久久樱花| 神马影院一区二区| 国产精品福利在线观看播放| www.激情网| 国产精品毛片| www.国产视频.com| 国产成人精品一区二| 在线观看av中文字幕| 国产拍欧美日韩视频二区| 亚洲视频重口味| 亚洲国产精品久久人人爱蜜臀| 久久久免费高清视频| 欧美精品成人一区二区三区四区| 成人av手机在线| 亚洲乱码一区av黑人高潮 | 自拍亚洲一区| 成年人免费观看的视频| 亚洲手机在线| 日本成人中文字幕在线| 国产精品资源在线观看| 日本一区二区三区网站| 亚洲欧洲精品一区二区精品久久久 | 黄色不卡一区| 久久久久亚洲av无码专区喷水| 亚洲精品黄色| 亚洲欧洲日本精品| 波多野洁衣一区| 欧美88888| 婷婷久久综合九色综合绿巨人 | 筱崎爱全乳无删减在线观看| 国产精品男女猛烈高潮激情| 99国产精品久久一区二区三区| 人禽交欧美网站免费| 欧美黄色免费| 九一精品在线观看| 免播放器亚洲| 亚洲在线观看网站| 2020国产精品自拍| 欧美成人精品欧美一级| 色婷婷国产精品| 亚洲精品国产suv一区| 中文字幕av一区二区| xxxcom在线观看| 成人av色在线观看| 你懂的一区二区三区| 国产女教师bbwbbwbbw| 日本欧美在线观看| 人妻在线日韩免费视频| 亚洲最大成人网4388xx| 一卡二卡三卡在线观看| 亚洲人成伊人成综合网久久久| 国产桃色电影在线播放| 成人性生交大片免费看视频直播 | 日韩精品在线观看免费| 日韩欧美中文字幕精品| 色三级在线观看| 国产成人精品国内自产拍免费看 | 亚洲国产一区二区三区在线观看| 免费成人黄色| 国产精品流白浆视频| 亚洲桃色综合影院| 亚洲精品蜜桃久久久久久| 欧美国产高清| theporn国产精品| 国产suv精品一区二区6| 一区二区三区影视| 欧美色综合天天久久综合精品| 五月天婷婷在线播放| 久久久欧美精品| 91精品短视频| 日韩一级免费看| 国产激情精品久久久第一区二区 | 尹人成人综合网| 日本少妇一级片| 亚洲综合自拍偷拍| 欧美 日韩 中文字幕| 欧美精品福利在线| 97一区二区国产好的精华液| 成人在线视频一区二区三区| 国产精品系列在线播放| 欧美日韩精品亚洲精品| 日韩视频一区在线观看| www在线观看播放免费视频日本| 国产一区视频在线| 婷婷综合伊人| 精品国产午夜福利在线观看| 成人午夜在线视频| 久久精品视频9| 亚洲精品久久在线| 欧美黄色网页| 亚洲一区二区三区免费看| 麻豆国产欧美日韩综合精品二区| 91久久久久久久久久久久久久 | 亚洲成人动漫av| 五月天婷婷在线播放| 国产成人精品av在线| 99视频精品视频高清免费| 亚洲热在线视频| 亚洲高清视频的网址| 亚欧洲精品视频| 日韩av片免费在线观看| 成人精品影院| 午夜影院免费版| 亚洲成a人v欧美综合天堂下载 | 大奶在线精品| 日本免费黄视频| 中文av一区特黄| 亚洲成人久久精品| 4k岛国日韩精品**专区| 成人激情视频| 2025中文字幕| 日韩欧美中文第一页| 国产系列电影在线播放网址| 91精品国产综合久久久久久蜜臀| 欧美色图首页| 欧美色图亚洲激情| 在线播放91灌醉迷j高跟美女| 欧美家庭影院| 日韩福利二区| 日韩午夜免费| 美国美女黄色片| 欧美zozozo| 成人做爰免费视频免费看| 国产日韩第一页| 2023国产精品| 国产免费无遮挡| 日韩av大片在线| 自拍日韩欧美| 亚洲av熟女国产一区二区性色| 日韩女优电影在线观看| 日韩av首页| 福利视频一二区|