成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

有效解決 MySQL 行鎖等待超時問題

數據庫 MySQL
本文僅介紹如何有效解決行鎖等待超時,因為大多數項目都是此類錯誤,元數據鎖等待超時則不涉及講解。

 [[323417]]

一、背景

 

  1. #### 20191219 10:10:10,234 | com.alibaba.druid.filter.logging.Log4jFilter.statementLogError(Log4jFilter.java:152) | ERROR |  {conn-10593, pstmt-38675} execute error. update operation_service set offlinemark = ? , resourcestatus = ?  where RowGuid = ? 
  2.  
  3. com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction 

上述這個錯誤,接觸 MySQL 的同學或多或少應該都遇到過,專業一點來說,這個報錯我們稱之為鎖等待超時。根據鎖的類型主要細分為:

  • 行鎖等待超時

當 SQL 因為等待行鎖而超時,那么就為行鎖等待超時,常在多并發事務場景下出現。

  • 元數據鎖等待超時

當 SQL 因為等待元數據鎖而超時,那么就為元數據鎖等待超時,常在 DDL 操作期間出現。

本文僅介紹如何有效解決行鎖等待超時,因為大多數項目都是此類錯誤,元數據鎖等待超時則不涉及講解。

二、行鎖的等待

在介紹如何解決行鎖等待問題前,先簡單介紹下這類問題產生的原因。產生原因簡述:當多個事務同時去操作(增刪改)某一行數據的時候,MySQL 為了維護 ACID 特性,就會用鎖的形式來防止多個事務同時操作某一行數據,避免數據不一致。只有分配到行鎖的事務才有權力操作該數據行,直到該事務結束,才釋放行鎖,而其他沒有分配到行鎖的事務就會產生行鎖等待。如果等待時間超過了配置值(也就是 innodb_lock_wait_timeout 參數的值,個人習慣配置成 5s,MySQL 官方默認為 50s),則會拋出行鎖等待超時錯誤。

 

如上圖所示,事務 A 與事務 B 同時會去 Insert 一條主鍵值為 1 的數據,由于事務 A 首先獲取了主鍵值為 1 的行鎖,導致事務 B 因無法獲取行鎖而產生等待,等到事務 A 提交后,事務 B 才獲取該行鎖,完成提交。這里強調的是行鎖的概念,雖然事務 B 重復插入了主鍵,但是在獲取行鎖之前,事務一直是處于行鎖等待的狀態,只有獲取行鎖后,才會報主鍵沖突的錯誤。當然這種 Insert 行鎖沖突的問題比較少見,只有在大量并發插入場景下才會出現,項目上真正常見的是 update&delete 之間行鎖等待,這里只是用于示例,原理都是相同的。

三、產生的原因

根據我之前接觸到的此類問題,大致可以分為以下幾種原因:

1. 程序中非數據庫交互操作導致事務掛起

將接口調用或者文件操作等這一類非數據庫交互操作嵌入在 SQL 事務代碼之中,那么整個事務很有可能因此掛起(接口不通等待超時或是上傳下載大附件)。

2. 事務中包含性能較差的查詢SQL

事務中存在慢查詢,導致同一個事務中的其他 DML 無法及時釋放占用的行鎖,引起行鎖等待。

3. 單個事務中包含大量 SQL

通常是由于在事務代碼中加入 for 循環導致,雖然單個 SQL 運行很快,但是 SQL 數量一大,事務就會很慢。

4. 級聯更新 SQL 執行時間較久

這類 SQL 容易讓人產生錯覺,例如:update A set ... where ...in (select B) 這類級聯更新,不僅會占用 A 表上的行鎖,也會占用 B 表上的行鎖,當 SQL 執行較久時,很容易引起 B 表上的行鎖等待。

5. 磁盤問題導致的事務掛起

極少出現的情形,比如存儲突然離線,SQL 執行會卡在內核調用磁盤的步驟上,一直等待,事務無法提交。綜上可以看出,如果事務長時間未提交,且事務中包含了 DML 操作,那么就有可能產生行鎖等待,引起報錯。

四、定位難點當

web 日志中出現行鎖超時錯誤后,很多開發都會找我來排查問題,這里說下問題定位的難點!

1. MySQL 本身不會主動記錄行鎖等待的相關信息,所以無法有效的進行事后分析。

2. 鎖爭用原因有多種,很難在事后判斷到底是哪一類問題場景,尤其是事后無法復現問題的時候。

3. 找到問題 SQL 后,開發無法有效從代碼中挖掘出完整的事務,這也和公司框架-產品-項目的架構有關,需要靠 DBA 事后采集完整的事務 SQL 才可以進行分析。

五、常用方法

先介紹下個人通常是如何解決此類問題的, 這里問題解決的前提是問題可以復現,只要不是突然出現一次,之后再也不出現,一般都是可以找到問題源頭的。

這里問題復現分為兩種情景:

1. 手動復現

只要按照一定的操作,就可以復現報錯,這種場景較簡單!

2. 隨機復現

不知道何時會突然報錯,無法手動復現,這種場景較難!

下面先寫下統一的模擬場景,用于復現行鎖超時問題,便于大家理解:

 

  1. --表結構 
  2.  
  3. CREATE TABLE `emp` ( 
  4.  
  5.   `id` int(11) NOT NULL
  6.  
  7.   KEY `idx_id` (`id`) 
  8.  
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 
  10.  
  11.  
  12.  
  13. 從1~100w插入100w行記錄。 
  14.  
  15.  
  16.  
  17. --測試過程: 
  18.  
  19. 事務1: 
  20.  
  21. start transaction
  22.  
  23. delete from emp where id = 1; 
  24.  
  25. select * from emp where id in (select id from emp);   -->模擬慢查詢,執行時間很久,事務因此一直不提交,行鎖也不釋放. 
  26.  
  27. commit
  28.  
  29.  
  30.  
  31. 事務2: 
  32.  
  33. start transaction
  34.  
  35. delete from emp where id < 10;   --> 處于等待id=1的行鎖狀態,當達到行鎖超時時間(這里我配置了超時時間為 5s)后,返回行鎖超時報錯 
  36.  
  37. rollback

5.1 手動復現場景

這個場景通常只需要通過 innodb 行鎖等待腳本就可以知道當前 MySQL 的 innodb 行鎖等待情況,例如我們一邊模擬上述報錯場景(模擬頁面操作),另一邊使用腳本查詢(需要在超時之前查詢,否則超時報錯后就看不到了)。

 

  1. /*innodb 行鎖等待腳本*/ 
  2.  
  3. SELECT r.trx_mysql_thread_id waiting_thread,r.trx_query waiting_query, 
  4.  
  5. concat(timestampdiff(SECOND,r.trx_wait_started,CURRENT_TIMESTAMP()),'s'AS duration, 
  6.  
  7. b.trx_mysql_thread_id blocking_thread,t.processlist_command state,b.trx_query blocking_current_query,e.sql_text blocking_last_query 
  8.  
  9. FROM information_schema.innodb_lock_waits w 
  10.  
  11. JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id 
  12.  
  13. JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id 
  14.  
  15. JOIN performance_schema.threads t on t.processlist_id = b.trx_mysql_thread_id 
  16.  
  17. JOIN performance_schema.events_statements_current e USING(thread_id) 

 


如上我們可以看到事務 2 的線程 id 為 76,已經被事務 1,也就是線程 id 為 75 的事務阻塞了 3s,并且可以看到事務 1 當前執行的 SQL 為一個 SELECT。這里也解釋了很多開發經常問我的,為什么 SELECT 也會阻塞其他會話?如果遇到這種情況,那么處理其實非常簡單。需要優化這個 SELECT 就好了,實在優化不了,把這個查詢扔到事務外就可以了,甚至都不需要挖掘出整個事務。上述這個問題模擬,其實就是對應第三節問題產生原因中的第二點(事務中包含性能較差的查詢 SQL),下面我們把第一點(程序中非數據庫交互操作導致事務掛起)也模擬下,對比下現象。我們只需要將事務 1 的過程改成如下即可。

 

 

 

  1. 事務1: 
  2.  
  3. start transaction
  4.  
  5. delete from emp where id = 1; 
  6.  
  7. select * from emp where id in (select id from emp); 
  8.  
  9. 等待60s(什么都不要做)             --> 模擬接口調用超時,事務夯住,隨后再執行commit。 
  10.  
  11. commit

再次用腳本查看,可以看到現象是有所不同的,不同點在于,阻塞事務處于 sleep 狀態,即事務當前并不在跑 SQL。從 DBA 的角度看,這類現象八成就可以斷定是代碼在事務中嵌入了其他的交互操作導致的事務掛起(另外也有可能是網絡問題導致的事務僵死),因為程序并不像人,它不會偷懶,不會出現事務執行到一半,休息一會再提交一說。

 


如果是這類現象的問題,因為本質并不是由于 SQL 慢導致的事務掛起,所以必須要到代碼里去找到對應的點,看下到底是在做什么交互操作卡住了。

 

這里就需要開發去排查代碼才可以找到源頭,但是唯一可用的信息就是該事務最后執行的一條 SQL,也就是上圖中最后一列,從我之前的經驗來看(絕大時候),開發很難單從這一條 SQL 就可以找到代碼里具體位置,尤其是當這條 SQL 是一條很常見的 SQL,就更為困難!

當面對這種情況,就需要 DBA 去挖掘出這個事務執行過的所有 SQL,然后再讓開發去排查代碼,這樣難度應該就小多了。這里就需要用到 MySQL 的 general_log,該日志用于記錄 MySQL 中所有運行過的 SQL。

 

 

  1. --查看general_log是否開啟,及文件名 
  2.  
  3. mysql> show variables like '%general_log%'
  4.  
  5. +------------------+--------------------------------------+ 
  6.  
  7. | Variable_name    | Value                                | 
  8.  
  9. +------------------+--------------------------------------+ 
  10.  
  11. | general_log      | OFF                                  | 
  12.  
  13. | general_log_file | /data/mysql_data/192-168-188-155.log | 
  14.  
  15. +------------------+--------------------------------------+ 
  16.  
  17.  
  18.  
  19. --暫時開啟general_log 
  20.  
  21. mysql> set global general_log = 1; 
  22.  
  23. Query OK, 0 rows affected (0.00 sec) 
  24.  
  25.  
  26.  
  27. --暫時關閉general_log 
  28.  
  29. mysql> set global general_log = 0; 
  30.  
  31. Query OK, 0 rows affected (0.00 sec) 

開啟 general_log 后,手動復現的時候通過 innodb 行鎖等待腳本查詢結果中的線程 ID,去 general_log 找到對應的事務分析即可,如下:


 

 

根據線程 ID 可以很輕易的從 general_log 中找到對應時間點的事務操作(實際場景下可能需要通過管道命令過濾)。如上圖所示,事務 1 與事務 2 的全部 SQL 都可以找到,再通過這些 SQL 去代碼中找到對應的位置即可,比如上圖中線程 ID 為 111 的事務,執行 select * from emp where id in (select id from emp) 后到真正提交,過了 1min 左右,原因要么就是這條 SQL 查詢慢,要么就是代碼在執行其他交互操作。

PS:general_log 由于會記錄所有 SQL,所以對 MySQL 性能影響較大,且容易暴漲,所以只在問題排查時暫時開啟,問題排查后,請及時關閉!

5.2 隨機復現場景

相較于手動復現場景,這種場景因為具有隨機性,所以無法一邊模擬報錯,一邊通過腳本查詢到具體的阻塞情況,因此需要通過其他方式來監控 MySQL 的阻塞情況。我一般是通過在 Linux 上后臺跑監控腳本(innodb_lock_monitor.sh)來記錄 MySQL 阻塞情況,腳本如下:

 

  1. #!/bin/bash 
  2.  
  3.  
  4.  
  5. #賬號、密碼、監控日志 
  6.  
  7. user="root" 
  8.  
  9. password="Gepoint" 
  10.  
  11. logfile="/root/innodb_lock_monitor.log" 
  12.  
  13.  
  14.  
  15. while true 
  16.  
  17. do 
  18.  
  19.         num=`mysql -u${user} -p${password} -e "select count(*) from information_schema.innodb_lock_waits" |grep -v count
  20.  
  21.         if [[ $num -gt 0 ]];then 
  22.  
  23.             date  >> /root/innodb_lock_monitor.log 
  24.  
  25.             mysql -u${user} -p${password} -e  "SELECT r.trx_mysql_thread_id waiting_thread,r.trx_query waiting_query, \ 
  26.  
  27. concat(timestampdiff(SECOND,r.trx_wait_started,CURRENT_TIMESTAMP()),'s'AS duration,\ 
  28.  
  29. b.trx_mysql_thread_id blocking_thread,t.processlist_command state,b.trx_query blocking_query,e.sql_text \ 
  30.  
  31. FROM information_schema.innodb_lock_waits w \ 
  32.  
  33. JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id \ 
  34.  
  35. JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id \ 
  36.  
  37. JOIN performance_schema.threads t on t.processlist_id = b.trx_mysql_thread_id \ 
  38.  
  39. JOIN performance_schema.events_statements_current e USING(thread_id) \G " >> ${logfile} 
  40.  
  41.         fi 
  42.  
  43.         sleep 5 
  44.  
  45. done 

再次查看

 

  1. --使用 nohup 命令后臺運行監控腳本 
  2.  
  3. [root@192-168-188-155 ~]# nohup sh innodb_lock_monitor.sh  & 
  4.  
  5. [2] 31464 
  6.  
  7. nohup: ignoring input and appending output to ‘nohup.out’ 
  8.  
  9.  
  10.  
  11. --查看 nohup.out 是否出現報錯 
  12.  
  13. [root@192-168-188-155 ~]# tail -f nohup.out 
  14.  
  15. mysql: [Warning] Using a password on the command line interface can be insecure. 
  16.  
  17. mysql: [Warning] Using a password on the command line interface can be insecure. 
  18.  
  19. mysql: [Warning] Using a password on the command line interface can be insecure. 
  20.  
  21.  
  22.  
  23. --定時查看監控日志是否有輸出(沒有輸出的話,這個日志也不會生成哦!) 
  24.  
  25. [root@192-168-188-155 ~]# tail -f innodb_lock_monitor.log 
  26.  
  27. Wed Feb  5 11:30:11 CST 2020 
  28.  
  29. *************************** 1. row *************************** 
  30.  
  31.  waiting_thread: 112 
  32.  
  33.   waiting_query: delete from emp where id < 10 
  34.  
  35.        duration: 3s 
  36.  
  37. blocking_thread: 111 
  38.  
  39.           state: Sleep 
  40.  
  41.  blocking_query: NULL 
  42.  
  43.        sql_text: select * from emp where id in (select id from emp) 

當監控日志有輸出阻塞信息時,后續解決方案就和之前的手動復現場景一致。

  • 如果是事務卡在慢 SQL,那么就需要優化 SQL。
  • 如果是事務掛起,那么就通過 general_log 分析事務,然后找到具體的代碼位置。

PS:問題排查完成后,請及時關閉后臺監控進程,通過 kill+pid 的方式直接關閉即可!

六、Performance_Schema

之前的方法感覺不是很方便,因為 general_log 需要訪問服務器,且過濾分析也較難,需要一定的 MySQL 基礎及 Linux 基礎才適用,因此想尋找一種更為簡便的方法。

6.1 方法介紹

個人想法是利用 MySQL 5.5 開始提供的 performance_schema 性能引擎來進行分析,Performance_Schema 是 MySQL 提供的在系統底層監視 MySQL 服務器性能的一個特性,其提供了大量監控項,包括:鎖、IO、事務、內存使用等。

介紹下主要原理:

1. 主要用的表有 2 張 events_transactions_history_long 和 events_statements_history_long。

2. transactions_history_long 會記錄歷史事務信息,events_statements_history_long 則記錄歷史 SQL。

3. 從 transactions_history_long 中得到回滾事務的線程 ID,再根據時間范圍去篩選出可疑的事務,最后從 events_statements_history_long 得到事務對應的 SQL,從中排查哪個為源頭。

優點:

1. 不需要通過 general_log 來獲取事務 SQL。

2. 不需要監控腳本來獲取到行鎖等待情況。

3. 只需要訪問 MySQL 就可以實現,而不需要訪問服務器。

4. 性能開銷較小,且不會暴漲,因為是循環覆蓋寫入的。

5. 可以知道每條 SQL 的運行時長。

缺點:

1. history_long 相關表默認保留記錄有限,可能會把有用的數據刷掉,尤其是在 SQL 運行較多的系統。

2. 如果要加大 history_long 相關表的最大保留行數,需要重啟 MySQL,無法在線修改參數。

3. history_long 相關表記錄中的時間均為相對時間,也就是距離 MySQL 啟動的時長,看起來不是很方便。

4. history_long 相關表不會主動記錄行鎖等待的信息,所以只能通過先根據時間范圍刷選出可疑的事務,再進一步分析,不如腳本監控定位的準。

 

  1. /*開啟performance_schema相關監控項,需要提前開啟performance_schema*/ 
  2.  
  3. UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' where name = 'transaction'
  4.  
  5. UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' where name like '%events_transactions%'
  6.  
  7. UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' where name like '%events_statements%'
  8.  
  9.  
  10.  
  11.  
  12.  
  13. /*查看回滾事務SQL,確認是否是日志里報錯的事務*/ 
  14.  
  15. SELECT a.THREAD_ID 
  16.  
  17.     ,b.EVENT_ID 
  18.  
  19.     ,a.EVENT_NAME 
  20.  
  21.     ,CONCAT (b.TIMER_WAIT / 1000000000000,'s'AS trx_druation 
  22.  
  23.     ,CONCAT (a.TIMER_WAIT / 1000000000000,'s') sql_druation 
  24.  
  25.     ,a.SQL_TEXT,b.STATE,a.MESSAGE_TEXT 
  26.  
  27. FROM performance_schema.events_statements_history_long a 
  28.  
  29. JOIN performance_schema.events_transactions_history_long b ON a.THREAD_ID = b.THREAD_ID 
  30.  
  31.     AND (a.NESTING_EVENT_ID = b.EVENT_ID OR a.EVENT_ID = b.NESTING_EVENT_ID) 
  32.  
  33. WHERE b.autocommit = 'NO' AND a.SQL_TEXT IS NOT NULL AND b.STATE = 'ROLLED BACK' 
  34.  
  35.  
  36.  
  37. /*查看該時間段內可疑事務即超過5s的事務SQL,這里默認innodb_lock_wait_timeout為5s*/ 
  38.  
  39. SELECT a.THREAD_ID 
  40.  
  41.     ,b.EVENT_ID 
  42.  
  43.     ,a.EVENT_NAME 
  44.  
  45.     ,CONCAT (b.TIMER_WAIT / 1000000000000,'s'AS trx_druation 
  46.  
  47.     ,CONCAT (a.TIMER_WAIT / 1000000000000,'s') sql_druation 
  48.  
  49.     ,a.SQL_TEXT,b.STATE,a.MESSAGE_TEXT,a.ROWS_AFFECTED,a.ROWS_EXAMINED,a.ROWS_SENT 
  50.  
  51. FROM performance_schema.events_statements_history_long a 
  52.  
  53. JOIN performance_schema.events_transactions_history_long b ON a.THREAD_ID = b.THREAD_ID 
  54.  
  55.     AND (a.NESTING_EVENT_ID = b.EVENT_ID OR a.EVENT_ID = b.NESTING_EVENT_ID) 
  56.  
  57. WHERE b.autocommit = 'NO' AND SQL_TEXT IS NOT NULL AND b.STATE = 'COMMITTED' 
  58.  
  59.     AND b.TIMER_WAIT / 1000000000000  > 5 
  60.  
  61.     AND b.TIMER_START < (SELECT TIMER_START FROM performance_schema.events_transactions_history_long 
  62.  
  63.         WHERE THREAD_ID = 70402  /*上述SQL查詢結果中的線程ID*/ 
  64.  
  65.         AND EVENT_ID = 518)      /*上述SQL查詢結果中的事件ID*/ 
  66.  
  67.     AND b.TIMER_END > ( SELECT TIMER_END FROM performance_schema.events_transactions_history_long 
  68.  
  69.         WHERE THREAD_ID = 70402  /*上述SQL查詢結果中的線程ID*/ 
  70.  
  71.         AND EVENT_ID = 518)     /*上述SQL查詢結果中的事件ID*/ 
  72.  
  73. ORDER BY a.THREAD_ID 

6.2 測試模擬

如果是用這種方法的話,那么就不需要分手動復現還是隨機復現了,操作方法都是一樣的,下面模擬下如何操作:

1. 首先通過上述方法開啟 performance_schema 相關監控項,會直接生效,無需重啟 MySQL。

2. 然后復現問題,這里最好是手動復現(因為復現后如果沒有及時查看,監控數據可能就會被刷掉),不行的話就只能等待隨機復現了。

3. 問題復現后通過上述腳本查詢是否存在回滾事務(即因為行鎖超時回滾的事務)。

 

4. 然后根據回滾事務的線程 ID 和事件 ID,帶入到最后一個腳本中,查看可疑事務,進行分析。

 

這里由于是測試環境模擬,所以結果非常了然,項目上實際輸出結果可能有很多,需要一一分析事務是否有問題!

七、總結

實際測試后,發現通過 performance_schema 來排查行鎖等待超時問題限制其實也比較多,而且最后的分析也是一門技術活,并不如一開始想象的那么簡單,有點事與愿違了。

通過 performance_schema 排查問題最難處理的有 3 點:

1. 時間問題,相對時間如何轉換為絕對時間,這個目前一直找不到好的方法。

2. 不會主動記錄下行鎖等待的信息,所以只能通過時間節點刷選后進一步分析。

3. 記錄被刷問題,因為是內存表,設置很大容易內存溢出,設置很小就容易被很快刷掉。

責任編輯:華軒 來源: 楊建榮的學習筆記
相關推薦

2021-02-22 17:18:35

MySQLSQL行鎖

2021-03-26 10:40:16

MySQL鎖等待死鎖

2010-11-25 11:15:11

MySQL查詢超時

2022-12-07 10:56:23

線程池監控執行超時

2009-06-10 08:45:31

LinuxWhile文件等待

2009-03-24 10:45:39

MySQL5連接超時數據庫

2024-11-29 07:38:12

MySQL數據庫

2025-02-10 09:58:48

2010-05-19 10:18:23

svn 403 for

2020-10-20 13:50:47

MySQL數據庫

2010-05-24 12:50:59

MySQL表級鎖

2024-03-04 00:01:00

鎖表鎖行MySQL

2011-03-15 15:47:04

MySQL鎖競爭

2011-03-07 09:05:49

鎖競爭MySQL等待時間

2025-06-04 02:55:00

MySQL意向鎖記錄鎖

2024-05-13 12:44:00

InnodbMySQL行級鎖

2022-07-20 08:06:57

MySQL表鎖Innodb

2023-08-31 16:43:46

軟件開發調試

2010-10-14 09:15:20

MySQL查詢

2018-07-31 10:10:06

MySQLInnoDB死鎖
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲永久| 亚洲精品自在在线观看 | 日韩一区二区三区视频 | 免费精品久久久久久中文字幕 | 亚洲精品在线视频 | 国产99久久久国产精品下药 | 国产欧美在线观看 | 日韩精品在线一区 | 国产乱精品一区二区三区 | 在线观看你懂的网站 | 欧美一级www片免费观看 | 亚洲国产精品久久久 | 国产美女精品视频免费观看 | 久久网一区二区 | 91免费视频观看 | 超碰国产在线 | 久久免费高清视频 | 91网在线观看 | 69av在线视频 | 97在线观视频免费观看 | 亚洲在线成人 | 国产精品永久免费视频 | 国产精品美女www爽爽爽 | 国产精品视频一区二区三区不卡 | 国产精品美女久久久久久久网站 | 久久亚洲春色中文字幕久久久 | 97色免费视频 | 日韩电影免费在线观看中文字幕 | 三级视频国产 | 人人草人人干 | 男人的天堂在线视频 | 欧美国产中文 | 一区二区三区中文字幕 | 天天综合国产 | 不卡在线视频 | 国产污视频在线 | 四虎影视免费观看 | 中文字幕不卡在线88 | 亚洲手机视频在线 | 亚洲精品久久久一区二区三区 | 91精品久久久久久久99 |