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

MariaDB線程池源碼分析

數據庫 MySQL MariaDB
MySQL5.5的Enterprise版本以plugin的方式引入了thread pool,在并發請求數達到一定 數量的時候,性能相比社區版貌似有不少提高, 可以看下這個性能對比。

 在引入線程池之前,MySQL支持的線程處理方式(thread_handling參數控制)有no-threads和one-thread-per-connection兩種方式,no-threads方式是指任一時刻最多只有一個連接可以連接到server,一般用于實驗性質。 one-thread-per-connection是指針對每個連接創建一個線程來處理這個連接的所有請求,直到連接斷開,線程 結束。是thread_handling的默認方式。

one-thread-per-connection存在的問題就是需要為每個連接創建一個新的thread,當并發連接數達到一定 程度,性能會有明顯下降,因為過多的線程會導致頻繁的上下文切換,CPU cache命中率降低和鎖的競爭 更加激烈。

解決one-thread-per-connection的方法就是降低線程數,這樣就需要多個連接共用線程,這便引入了線程 池的概念。線程池中的線程是針對請求的,而不是針對連接的,也就是說幾個連接可能使用相同的線程處理 各自的請求。

MariaDB在5.5引入了一個動態的線程池方案,可以根據當前請求的并發情況自動增加或減少線程數,還好 MariaDB完全開源,本文結合MariaDB的代碼來介紹下thread pool的實現。這里使用的MariaDB 10.0的 代碼樹。

1  相關參數

MySQL的參數都寫在sys_vars.cc文件下。

  1. static Sys_var_uint Sys_threadpool_idle_thread_timeout( 
  2.   "thread_pool_idle_timeout"
  3.   "Timeout in seconds for an idle thread in the thread pool." 
  4.   "Worker thread will be shut down after timeout"
  5.   GLOBAL_VAR(threadpool_idle_timeout), CMD_LINE(REQUIRED_ARG), 
  6.   VALID_RANGE(1, UINT_MAX), DEFAULT(60), BLOCK_SIZE(1) 
  7. ); 
  8. static Sys_var_uint Sys_threadpool_oversubscribe( 
  9.   "thread_pool_oversubscribe"
  10.   "How many additional active worker threads in a group are allowed."
  11.   GLOBAL_VAR(threadpool_oversubscribe), CMD_LINE(REQUIRED_ARG), 
  12.   VALID_RANGE(1, 1000), DEFAULT(3), BLOCK_SIZE(1) 
  13. ); 
  14. static Sys_var_uint Sys_threadpool_size( 
  15.  "thread_pool_size"
  16.  "Number of thread groups in the pool. " 
  17.  "This parameter is roughly equivalent to maximum number of concurrently " 
  18.  "executing threads (threads in a waiting state do not count as executing)."
  19.   GLOBAL_VAR(threadpool_size), CMD_LINE(REQUIRED_ARG), 
  20.   VALID_RANGE(1, MAX_THREAD_GROUPS), DEFAULT(my_getncpus()), BLOCK_SIZE(1), 
  21.   NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), 
  22.   ON_UPDATE(fix_threadpool_size) 
  23. ); 
  24. static Sys_var_uint Sys_threadpool_stall_limit( 
  25.  "thread_pool_stall_limit"
  26.  "Maximum query execution time in milliseconds," 
  27.  "before an executing non-yielding thread is considered stalled." 
  28.  "If a worker thread is stalled, additional worker thread " 
  29.  "may be created to handle remaining clients."
  30.   GLOBAL_VAR(threadpool_stall_limit), CMD_LINE(REQUIRED_ARG), 
  31.   VALID_RANGE(10, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1), 
  32.   NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),  
  33.   ON_UPDATE(fix_threadpool_stall_limit) 
  34. ); 

這幾個參數都有相應的描述,這里再稍微具體介紹一下。

thread_pool_size: 線程池的分組(group)個數。MariaDB的線程池并不是說一整個 大池子,而是分成了不同的group,而且是按照到來connection的順序進行分組的,如 第一個connection分配到group[0],那么第二個connection就分配到group[1],是一種 Round Robin的輪詢分配方式。默認值是CPU core個數。

thread_pool_idle_timeout: 線程最大空閑時間,如果某個線程空閑的時間大于這個 參數,則線程退出。

thread_pool_stall_limit: 監控間隔時間,thread pool有個監控線程,每隔這個時間, 會檢查每個group的線程可用數等狀態,然后進行相應的處理,如wake up或者create thread。

thread_pool_oversubscribe: 允許的每個group上的活躍的線程數,注意這并不是每個group上的 最大線程數,而只是可以處理請求的線程數。

2  thread handling設置

thread pool模式其實是新增了一種thread_handling的方式,即在配置文件中設置:

  1. [mysqld] 
  2. thread_handling=pool-of-threads. 
  3. .... 

 MySQL內部是有一個scheduler_functions結構體,不論thread_handling是哪種方式,都是通過設置這個 結構體中的函數來進行不同的調度。

  1. /** scheduler_functions結構體 */ 
  2. struct scheduler_functions 
  3.   uint max_threads, *connection_count; 
  4.   ulong *max_connections; 
  5.   bool (*init)(void); 
  6.   bool (*init_new_connection_thread)(void); 
  7.   void (*add_connection)(THD *thd); 
  8.   void (*thd_wait_begin)(THD *thd, int wait_type); 
  9.   void (*thd_wait_end)(THD *thd); 
  10.   void (*post_kill_notification)(THD *thd); 
  11.   bool (*end_thread)(THD *thd, bool cache_thread); 
  12.   void (*end)(void); 
  13. }; 
  14. static int get_options(int *argc_ptr, char ***argv_ptr) 
  15.   ... 
  16.   /** 根據thread_handling選項的設置,選擇不同的處理方式*/ 
  17. if (thread_handling <= SCHEDULER_ONE_THREAD_PER_CONNECTION) 
  18.     /**one thread per connection 方式 */ 
  19.     one_thread_per_connection_scheduler(thread_scheduler, &max_connections, 
  20.                                         &connection_count); 
  21.   else if (thread_handling == SCHEDULER_NO_THREADS) 
  22.     /** no thread 方式 */ 
  23.     one_thread_scheduler(thread_scheduler); 
  24.   else 
  25.     /** thread pool 方式 */ 
  26.     pool_of_threads_scheduler(thread_scheduler,  &max_connections, 
  27.                                         &connection_count);  
  28.   ...                                         
  29. static scheduler_functions tp_scheduler_functions
  30.   0,                                  // max_threads 
  31.   NULL, 
  32.   NULL, 
  33.   tp_init,                            // init 
  34.   NULL,                               // init_new_connection_thread 
  35.   tp_add_connection,                  // add_connection 
  36.   tp_wait_begin,                      // thd_wait_begin 
  37.   tp_wait_end,                        // thd_wait_end 
  38.   post_kill_notification,             // post_kill_notification 
  39.   NULL,                               // end_thread 
  40.   tp_end                              // end 
  41. }; 
  42. void pool_of_threads_scheduler(struct scheduler_functions *func, 
  43.     ulong *arg_max_connections, 
  44.     uint *arg_connection_count) 
  45.   /** 設置scheduler_functions結構體為tp_scheduler_functions */ 
  46.   *func = tp_scheduler_functions
  47.   func->max_threadsthreadpool_max_threads
  48.   func->max_connectionsarg_max_connections
  49.   func->connection_countarg_connection_count
  50.   scheduler_init(); 

上面可以看到設置了thread_scheduler的處理函數為tp_scheduler_functions,即 為thread pool方式,這種方式對應的初始函數為tp_init, 創建新連接的函數為 tp_add_connection,等待開始函數為tp_wait_begin,等待結束函數為tp_wait_end. 這里說明下等待函數的意義,等待函數一般是在等待磁盤I/O,等待鎖資源,SLEEP,或者等待 網絡消息的時候,調用wait_begin,在等待結束后調用wait_end,那么為什么要等待的時候 調用等待函數呢?這個在后面進行介紹。

上面講的其實和thread pool關系不是很大,下面開始thread pool流程的介紹。thread pool涉及 到的源碼在emphsql/threadpool_common.cc和emphsql/threadpool_unix.cc, 對于windows而言,還有emphsql/threadpool_win.cc.

3  線程池初始化——tp_init

  1. >tp_init 
  2. >thread_group_init 
  3. >start_timer 

tp_init非常簡單,首先是調用了thread_group_init進行組的初始化, 然后調用的start_timer開啟了監控線程timer_thread。 至此為止,thread pool里面只有一個監控線程啟動,而沒有任何工作線程, 直到有新的連接到來。

4  添加新連接——tp_add_connection

  1. void tp_add_connection(THD *thd) 
  2.   DBUG_ENTER("tp_add_connection"); 
  3.   threads.append(thd); 
  4.   mysql_mutex_unlock(&LOCK_thread_count); 
  5.   connection_t *connectionalloc_connection(thd); 
  6.   if (connection) 
  7.   { 
  8.     thd->event_scheduler.dataconnection
  9.     /* Assign connection to a group. */ 
  10.     thread_group_t *group=  
  11.       &all_groups[thd->thread_id%group_count]; 
  12.     connection->thread_group=group; 
  13.     mysql_mutex_lock(&group->mutex); 
  14.     group->connection_count++; 
  15.     mysql_mutex_unlock(&group->mutex); 
  16.     /* 
  17.        Add connection to the work queue.Actual logon  
  18.        will be done by a worker thread. 
  19.     */ 
  20.    queue_put(group, connection); 
  21.   } 
  22.   else 
  23.   { 
  24.     /* Allocation failed */ 
  25.     threadpool_remove_connection(thd); 
  26.   }  
  27.   DBUG_VOID_RETURN; 

但server的主監聽線程監聽到有客戶端的connect時,會調用tp_add_connection函數進行處理。 首先根據thread_id對group_count取模,找到其所屬的group,然后調用queue_put將此connection 放入到group中的queue中。這里涉及到兩個新的結構體,connection_t和thread_group_t。

  1. struct connection_t 
  2.   THD *thd; 
  3.   thread_group_t *thread_group; 
  4.   connection_t *next_in_queue; 
  5.   connection_t **prev_in_queue; 
  6.   ulonglong abs_wait_timeout; //等待超時時間 
  7.   bool logged_in; //是否進行了登錄驗證 
  8.   bool bound_to_poll_descriptor; //是否添加到了epoll進行監聽 
  9.   bool waiting; //是否在等待狀態,如I/O, sleep 
  10. }; 
  11. struct thread_group_t  
  12.   mysql_mutex_t mutex; 
  13.   connection_queue_t queue;  //connection請求鏈表 
  14.   worker_list_t waiting_threads; //group中正在等待被喚醒的thread 
  15.   worker_thread_t *listener;  //當前group中用于監聽的線程 
  16.   pthread_attr_t *pthread_attr; 
  17.   int  pollfd;  //epoll 文件描述符,用于綁定group中的所有連接 
  18.   int  thread_count;  //線程數 
  19.   int  active_thread_count;//活躍線程數 
  20.   int  connection_count; //連接數 
  21.   /* Stats for the deadlock detection timer routine.*/ 
  22.   int io_event_count;  //epoll產生的事件數 
  23.   int queue_event_count; //工作線程消化的事件數 
  24.   ulonglong last_thread_creation_time; 
  25.   int  shutdown_pipe[2]; 
  26.   bool shutdown; 
  27.   bool stalled; // 工作線程是否處于停滯狀態 
  28. } MY_ALIGNED(512); 

上面對這些參數進行了說明,理解這些參數的意義,才能了解這個動態thread pool的管理機制, 因為每個參數都會影響到thread pool的增長或收縮。

介紹完結構體,繼續回到新的連接到來,這時會調用queue_put函數,將此connection放到 group的隊列queue中。

  1. static void queue_put(thread_group_t *thread_group, connection_t *connection) 
  2.   DBUG_ENTER("queue_put"); 
  3.   mysql_mutex_lock(&thread_group->mutex); 
  4.   thread_group->queue.push_back(connection); 
  5.   if (thread_group->active_thread_count == 0) 
  6.     wake_or_create_thread(thread_group); 
  7.   mysql_mutex_unlock(&thread_group->mutex); 
  8.   DBUG_VOID_RETURN; 

注意,這時候有個active_thread_count的判斷,如果沒有活躍的線程,那么就無法處理 這個新到的請求啊,這時就需要調用wake_or_create_thread,這個函數首先會嘗試喚醒group 等待線程鏈表waiting_threads中的線程,如果沒有等待中的線程,則需要創建一個線程。 至此,新到的connection被掛到了group的queue上,這樣一個連接算是add進隊列了,那么如何 處理這個連接呢?我們繼續往下看。

5  工作線程——worker_main

由于是第一個連接到來,那么肯定沒有waiting_threads,此時會調用create_worker 函數創建一個工作線程。我們直接來看下工作線程。

  1. static void *worker_main(void *param) 
  2.  ... 
  3.   DBUG_ENTER("worker_main"); 
  4.   thread_group_t *thread_group = (thread_group_t *)param; 
  5.   /* Run event loop */ 
  6.   for(;;) 
  7.   { 
  8.     connection_t *connection; 
  9.     struct timespec ts; 
  10.     set_timespec(ts,threadpool_idle_timeout); 
  11.     connection = get_event(&this_thread, thread_group, &ts); 
  12.     if (!connection) 
  13.       break; 
  14.     this_thread.event_count++; 
  15.     handle_event(connection); 
  16.   } 
  17.   .... 
  18.   my_thread_end(); 
  19.   return NULL; 

上面是整個工作線程的邏輯,可以看到是一個循環,get_event用來獲取新的需要處理的 connection,然后調用handle_event進行處理相應的connection。one thread per connection 中每個線程也是一個循環體,這兩者之間的區別就是,thread pool的循環等待的是一個可用的event, 并不局限于某個固定的connection的event,而one thread per connection的循環等待是等待固定的 connection上的event,這就是兩者最大的區別。

6  事件獲取——get_event

工作線程通過get_event獲取需要處理的connection,

  1. connection_t *get_event(worker_thread_t *current_thread,  
  2.   thread_group_t *thread_group,  struct timespec *abstime) 
  3. {  
  4.   ... 
  5.   for(;;)  
  6.   { 
  7.   ... 
  8.       /** 從QUEUE中獲取connection */ 
  9.       connection = queue_get(thread_group); 
  10.       if(connection) { 
  11.         fprintf(stderr, "Thread %x get a new connection.\n", (unsigned int)pthread_self()); 
  12.         break; 
  13.       } 
  14.      ... 
  15.       /**監聽epoll */ 
  16.     if(!thread_group->listener) 
  17.     { 
  18.       thread_group->listenercurrent_thread
  19.       thread_group->active_thread_count--; 
  20.       mysql_mutex_unlock(&thread_group->mutex); 
  21.       fprintf(stderr, "Thread %x waiting for a new event.\n", (unsigned int)pthread_self()); 
  22.       connection = listener(current_thread, thread_group); 
  23.       fprintf(stderr, "Thread %x get a new event for connection %p.\n", 
  24.               (unsigned int)pthread_self(), connection); 
  25.       mysql_mutex_lock(&thread_group->mutex); 
  26.       thread_group->active_thread_count++; 
  27.       /* There is no listener anymore, it just returned. */ 
  28.       thread_group->listenerNULL
  29.       break; 
  30.     } 
  31.     ... 

這個get_event的函數邏輯稍微有點多,這里只抽取了獲取事件的兩個點, 我們接著按照第一個連接到來是的情形進行說明, 第一個連接到來,queue中有了一個connection,這是get_event便會從queue中獲取到一個 connection,返回給worker_main線程。worker_main接著調用handle_event進行事件處理。

每個新的connection連接到服務器后,其socket會綁定到group的epoll中,所以,如果queue中 沒有connection,需要從epool中獲取,每個group的所有連接的socket都綁定在group的epool 中,所以任何一個時刻,最多只有一個線程能夠監聽epoll,如果epoll監聽到有event的話,也會返回 相應的connection,然后再調用handle_event進行處理。

7  事件處理——handle_event

handle_event的邏輯比較簡單,就是根據connection_t上是否登錄過,進行分支,如果沒 登錄過,說明是新到的連接,則進行驗證,否則直接進行請求處理。

  1. static void handle_event(connection_t *connection) 
  2.   DBUG_ENTER("handle_event"); 
  3.   int err; 
  4.   if (!connection->logged_in) //處理登錄 
  5.   { 
  6.     errthreadpool_add_connection(connection->thd); 
  7.     connection->logged_intrue
  8.   } 
  9.   else  //處理請求 
  10.   { 
  11.     errthreadpool_process_request(connection->thd); 
  12.   } 
  13.   if(err) 
  14.     goto end; 
  15.   set_wait_timeout(connection); 
  16.   /** 設置socket到epoll的監聽 */ 
  17.   errstart_io(connection); 
  18. end: 
  19.   if (err) 
  20.     connection_abort(connection); 
  21.   DBUG_VOID_RETURN; 
  22. static int start_io(connection_t *connection) 
  23. {  
  24.   int fd = mysql_socket_getfd(connection->thd->net.vio->mysql_socket); 
  25.   ... 
  26.   /* 綁定到epoll *。 
  27.   if (!connection->bound_to_poll_descriptor) 
  28.   { 
  29.     connection->bound_to_poll_descriptortrue
  30.     return io_poll_associate_fd(group->pollfd, fd, connection); 
  31.   } 
  32.   return io_poll_start_read(group->pollfd, fd, connection); 

注意,在handle_event之后,會調用start_io,這個函數很重要,這個函數會將新 到的connection的socket綁定到group的epoll上進行監聽。

8  線程等待

當group中的線程沒有任務執行時,所有線程都會在get_event處等待,但是有兩種等待方式, 一種是在epoll上等待事件,每個group中只有一個線程會做這個事情,且這個會一直等待,直到有新 的事件到來。另一種就是等待一定的時間, 即參數thread_pool_idle_time這個時間,如果超過了這個時間,那么當前的線程的get_event就會 返回空,然后worker_main線程就會退出。如果在線程等待的過程被喚醒的話,那么就會繼續在 get_event中進行循環,等待新的事件。

9  喚醒等待線程

有兩種方式會喚醒等待的線程,一種是監控線程timer_thread,另一種就是一些active的線程碰到 需要等待的時候,會調用tp_wait_begin,這個函數如果判斷當前沒有active的thread且沒有thread監聽 epoll,則會調用wake_or_create_thread。

監控線程timer_thread用于定期監控group中的thread使用情況,具體的檢查函數是check_stall.

  1. void check_stall(thread_group_t *thread_group) 
  2.   ... 
  3.   /** 如果沒有線程監聽epoll且自上次檢查到現在沒有新的event事件產生,說明所有的 
  4.   活躍線程都在 忙于執行長任務,則需要喚醒或創建工作線程 */ 
  5.   if (!thread_group->listener && !thread_group->io_event_count) 
  6.   { 
  7.     wake_or_create_thread(thread_group); 
  8.     mysql_mutex_unlock(&thread_group->mutex); 
  9.     return; 
  10.   } 
  11.   /*  Reset io event count */ 
  12.   thread_group->io_event_count0
  13.   /** 如果隊列queue中有請求,且自上次檢查到現在queue中的請求沒有被消化, 
  14.   則說明所有活躍線程忙于執行長任務,需要喚醒或創建工作線程*/ 
  15.   if (!thread_group->queue.is_empty() && !thread_group->queue_event_count) 
  16.   { 
  17.     thread_group->stalledtrue
  18.     wake_or_create_thread(thread_group); 
  19.   } 
  20.   /* Reset queue event count */ 
  21.   thread_group->queue_event_count0
  22.   mysql_mutex_unlock(&thread_group->mutex); 

10  小結

MariaDB的thread pool的實現相對比較簡單,總體上就是將group中所有的connection的socket掛在 group的epoll_fd上進行事件監聽,監聽到的事件或被當前線程執行,或者被push到group的queue上 被其他線程執行。

監控線程timer_thread定期的根據需要去喚醒等待線程或創建新的線程,來達到動態增加的thread的 目的。而thread的收縮則是通過線程等待事件超時來完成的。

btw,在跟蹤代碼的過程中,也發現了使用thread pool時導致server crash的情況,提交了個 bug給MariaDB,發現當天就有回復, 并立刻修復push到source tree上了,看來MariaDB的團隊反映夠迅速的,贊一個。

原文鏈接:http://www.cnblogs.com/nocode/archive/2013/05/25/3098317.html

責任編輯:彭凡 來源: 博客園
相關推薦

2018-10-31 15:54:47

Java線程池源碼

2011-08-16 09:34:34

Nginx

2022-12-16 08:31:37

調度線程池源碼

2025-09-24 18:39:45

2022-11-09 09:01:08

并發編程線程池

2017-02-08 13:03:40

Java線程池框架

2015-10-10 09:39:42

Java線程池源碼解析

2013-06-08 10:11:31

Java線程池架構

2023-12-28 07:49:11

線程池源碼應用場景

2021-05-26 11:30:24

Java線程池代碼

2024-01-29 15:54:41

Java線程池公平鎖

2023-05-19 08:01:24

Key消費場景

2024-02-04 08:43:20

源碼線程池緩沖

2025-04-16 08:50:00

信號量隔離線程池隔離并發控制

2024-07-15 08:20:24

2012-05-15 02:18:31

Java線程池

2020-12-10 08:24:40

線程池線程方法

2023-10-13 08:20:02

Spring線程池id

2017-03-31 21:15:36

進程線程池Binde

2025-06-23 00:00:02

線程池Java任務隊列
點贊
收藏

51CTO技術棧公眾號

一级黄色片在线免费观看| 国产在线一区二区三区欧美| 亚洲一二三四视频| 美女久久精品| 欧美日韩亚洲一区二区| 亚洲一区二区三区涩| 亚洲国产精品国自产拍久久| 久久一二三四| 久久福利视频导航| www.狠狠爱| 成人免费直播在线| 欧美日韩一区二区三区视频 | 日日噜噜夜夜狠狠| 国内老司机av在线| 欧美国产一区视频在线观看| 国产精品免费一区二区三区观看 | 欧产日产国产v| 国产欧美日韩在线观看视频| 日韩欧美一级片| 婷婷六月天在线| 丁香花视频在线观看| 国产精品网友自拍| 精品乱码一区二区三区| 国产又粗又长又大视频| 美女日韩在线中文字幕| 色综合久久久久久中文网| 精品无码人妻一区二区免费蜜桃| 成人知道污网站| 欧美一区二区久久| 九九九在线观看视频| 国产美女高潮在线观看| 亚洲蜜臀av乱码久久精品| 天堂va久久久噜噜噜久久va| 欧洲成人一区二区三区| 国产精品资源在线看| 国产欧美精品一区二区三区-老狼| 青青视频在线免费观看| 亚洲深夜av| 韩国欧美亚洲国产| 九九热精品免费视频| 亚洲精品极品少妇16p| 亚洲欧美在线一区| 丰满少妇一区二区| 欧美亚洲tv| 亚洲黄色免费三级| 国产一卡二卡三卡四卡| 中文字幕亚洲在线观看| 欧美成人高清电影在线| 91免费视频污| 国产精品一站二站| 91精品婷婷国产综合久久| 粉色视频免费看| 亚洲黑人在线| 欧美猛男超大videosgay| 国产精品自拍视频在线| 色综合一区二区日本韩国亚洲| 欧美三级中文字幕在线观看| 亚洲视频一二三四| 国产高清日韩| 精品国产麻豆免费人成网站| 无码人妻一区二区三区一| 91精品尤物| 亚洲精品国产电影| 97人妻精品一区二区三区免| 日韩有码一区| 亚洲一区999| 欧美a级片免费看| 亚洲成人tv| 久久免费国产精品1| 日韩三级视频在线| 久久经典综合| 国产欧美 在线欧美| 97久久人国产精品婷婷| 国产精品1区2区| 久久99精品久久久久久青青日本 | 国产精品久久久久久久久动漫 | 视频一区视频二区中文| 国产精品揄拍500视频| 国产毛片在线视频| 成人国产免费视频| 奇米视频888战线精品播放| √天堂资源地址在线官网| 亚洲日穴在线视频| 浮妇高潮喷白浆视频| av激情成人网| 日韩欧美电影在线| 亚洲黄色在线网站| 91日韩在线| 久久久久久久久久久国产| 伊人中文字幕在线观看 | 国产精品久久久久久亚洲色| 国产中文精品久高清在线不| 久久成人精品视频| www.国产高清| 国产伦精品一区二区三区免费迷| 激情视频在线观看一区二区三区| av中文字幕一区二区三区| 1000部国产精品成人观看| 国产老熟妇精品观看| 日韩有码欧美| 亚洲天堂影视av| 欧美人妻精品一区二区三区| 日韩高清一级片| 国产免费一区二区| 中文字幕在线播放| 午夜av区久久| 制服下的诱惑暮生| 大色综合视频网站在线播放| 国内成人精品视频| 成人黄色三级视频| 99久久综合色| 国产亚洲精品久久久久久久| 新片速递亚洲合集欧美合集| 亚洲国产成人精品久久久国产成人一区 | 日韩午夜电影免费看| 亚洲精品黄网在线观看| 91在线播放观看| 毛片一区二区三区| 美日韩免费视频| 成人av影院在线观看| 欧美日韩夫妻久久| 国产成人无码精品久久二区三| 国产综合自拍| 亚洲一区二区三区乱码aⅴ蜜桃女| 久久国产精品高清一区二区三区| 亚洲国产欧美日韩另类综合| 992tv人人草| 91综合久久| 国产精品日日做人人爱| 黄色免费在线播放| 欧美日韩激情网| 岛国精品资源网站| 欧美日韩1区2区3区| 91天堂在线视频| 免费观看成人高潮| 欧美日韩一本到| 亚洲精品成人无码| 久久精品综合| 欧美在线视频一区二区三区| 免费亚洲电影| 亚洲日本成人网| 国产性生活视频| 2021久久国产精品不只是精品| 免费看的黄色大片| 久久a爱视频| 4p变态网欧美系列| 神马电影在线观看| 色综合一个色综合亚洲| 少妇按摩一区二区三区| 久久在线精品| 视频在线精品一区| 国模私拍国内精品国内av| 中文字幕亚洲综合| 国产一区二区女内射| 国产精品乱码妇女bbbb| 久久久久久久高清| 欧美成人高清| 国模精品娜娜一二三区| 超碰一区二区| 91久久国产综合久久91精品网站 | 性生生活大片免费看视频| 第九色区aⅴ天堂久久香| 国产精品自拍视频| 国内精品不卡| 日本一区免费观看| 99在线观看免费| 亚洲精品中文在线观看| 无码人妻一区二区三区免费n鬼沢| 欧美欧美全黄| 久久国产精品高清| 成人日韩精品| 米奇精品一区二区三区在线观看| www.亚洲天堂.com| 亚洲成av人在线观看| 粉嫩av蜜桃av蜜臀av| 久久精品久久99精品久久| 91嫩草国产丨精品入口麻豆| 中文字幕一区二区三区四区久久| 4438全国成人免费| 婷婷五月在线视频| 日韩视频在线永久播放| 国偷自拍第113页| 欧美激情资源网| 久久久久亚洲av无码麻豆| 99国产精品| 亚洲不卡1区| 欧美一级做一级爱a做片性| 欧美激情一区二区三区高清视频| 色视频在线观看| 91精品欧美一区二区三区综合在 | 成人久久18免费网站图片| 日本一本在线免费福利| 亚洲性av网站| 亚洲第一黄色片| 欧美视频一区二| 精品无码人妻一区二区三区| 久久久国产综合精品女国产盗摄| 欧美激情第四页| 日韩福利电影在线观看| 菠萝蜜视频在线观看入口| 精品久久久久久久| 国产精品一区二区欧美| 国产一区精品福利| 91av在线免费观看| а√天堂官网中文在线| 亚洲欧美中文日韩在线v日本| 国产精品久久婷婷| 色就色 综合激情| 国产一级特黄aaa大片| 最新热久久免费视频| 日韩中文字幕有码| 99精品一区二区| 性色av浪潮av| 日韩在线卡一卡二| 欧美精品99久久| 亚洲激情欧美| avav在线播放| 欧美国产免费| 一本色道久久99精品综合| 天堂日韩电影| 国产精品一区二区三区在线| 中文字幕日本一区| 国产精品久久久久久久app| 一本大道色婷婷在线| 欧美激情高清视频| 国产美女福利在线| 色诱女教师一区二区三区| 国产一级网站视频在线| 日韩精品一二三四区| 欧美一级做性受免费大片免费| 91精品婷婷国产综合久久| 亚洲中文字幕在线观看| 91福利资源站| 中文字幕国产在线观看| 色综合色狠狠天天综合色| 一区二区三区福利视频| 日韩欧美成人区| 国产在线观看黄色| 欧美色道久久88综合亚洲精品| 国产区在线观看视频| 午夜伊人狠狠久久| 久久久久久久久久综合| 亚洲韩国精品一区| 久久久久久福利| 亚洲一区二区在线视频| 国产亚洲精品久久777777| 亚洲伊人伊色伊影伊综合网| 久久国产在线观看| 亚洲va欧美va天堂v国产综合| 久久精品女人毛片国产| 婷婷综合在线观看| aaa人片在线| 色噜噜狠狠一区二区三区果冻| 欧美性猛交xxxx乱大交hd | 国产农村老头老太视频| 日韩午夜在线影院| 日韩一区免费视频| 国产丝袜一区二区三区| 福利成人在线观看| 日韩中文字幕在线| av观看在线| 国内揄拍国内精品少妇国语| 中文一区一区三区高中清不卡免费| 国产精品69精品一区二区三区| 国产欧美自拍| 成人高清在线观看| 日本亚洲不卡| 亚洲黄色成人久久久| 亚洲国产精品久久久天堂| 久草视频这里只有精品| 噜噜爱69成人精品| 色乱码一区二区三区在线| 国产一区二区三区免费看| 免费啪视频在线观看| 99精品视频在线免费观看| 日韩免费成人av| 一区二区在线观看视频| 日韩精品一区二区亚洲av| 欧美日韩一区 二区 三区 久久精品| 国产精品久久久久久69| 亚洲国产精品网站| 午夜小视频在线| 欧美劲爆第一页| 日韩av福利| 91av免费看| 欧美女优在线视频| 在线观看18视频网站| 久久狠狠婷婷| 久久无码专区国产精品s| 久久精品一区二区| 久久精品黄色片| 一本色道亚洲精品aⅴ| 国产白浆在线观看| 亚洲免费视频一区二区| av免费在线观看网站| 国产成人精品久久| 蜜桃精品视频| 少妇特黄a一区二区三区| 午夜国产精品视频免费体验区| 北条麻妃av高潮尖叫在线观看| 国产成人综合网站| 美国黄色特级片| 大荫蒂欧美视频另类xxxx| 国产精品无码在线播放| 国产亚洲视频在线观看| 高清在线视频不卡| 亚洲在线视频福利| 日韩精品欧美| 欧美综合在线观看视频| 成人综合在线视频| 欧美丰满熟妇bbbbbb| 欧美性受xxxx| 色视频在线观看福利| 韩国三级日本三级少妇99| 欧州一区二区三区| 夜夜春亚洲嫩草影视日日摸夜夜添夜| 噜噜噜91成人网| 成人网站免费观看| 亚洲一二三区不卡| 国产夫绿帽单男3p精品视频| 中文字幕在线观看日韩| 日韩和的一区二在线| 久久亚洲国产精品日日av夜夜| 国产精品大片免费观看| 国产精品无码乱伦| 国产精品日本一区二区三区在线| 精品视频高清无人区区二区三区| 亚洲欧洲日韩| 亚洲午夜激情影院| 国产欧美一区二区在线| 毛片毛片女人毛片毛片| 亚洲福利小视频| 五月花成人网| 91久久偷偷做嫩草影院| 国产精品99在线观看| 欧美婷婷精品激情| 国产清纯美女被跳蛋高潮一区二区久久w | 国产亚洲综合视频| 欧美精品成人| 激情文学亚洲色图| 国产精品久久精品日日| 中文字幕在线网站| 中文字幕欧美视频在线| 中文另类视频| 91精品论坛| 国产精品香蕉在线观看| 俺要去色综合狠狠| 可以免费观看av毛片| 久久综合九色综合97婷婷女人| 国产精品美女久久久久av爽| 日韩av最新在线观看| 亚洲天堂导航| 欧美日韩一区在线播放| 丝袜亚洲精品中文字幕一区| 一区二区三区伦理片| 精品视频免费在线| 亚洲精华液一区二区三区| 欧美大码xxxx| 国产美女亚洲精品7777| 99热都是精品| 成人午夜视频网站| 自拍偷拍欧美亚洲| 亚洲欧美国产精品久久久久久久| 国产一级理论片| 日韩电影大片中文字幕| 韩国成人漫画| 在线视频亚洲自拍| 国产成人一级电影| 在线观看免费国产视频| 亚洲日韩中文字幕| 亚洲电影二区| 欧日韩免费视频| 精品国产va久久久久久久| xvideos亚洲| 哺乳一区二区三区中文视频| 91好吊色国产欧美日韩在线| 久久精品视频免费| 91麻豆一区二区| 99精品久久99久久久久| 免费观看黄色大片| 国产一区在线观看视频| 国产精品日日夜夜| 亚洲社区在线观看| 91精品亚洲一区在线观看| 久久久久久免费看| 国产精品你懂的在线欣赏| 国产 日韩 欧美 精品| 国产精品狠色婷| 亚洲福利专区| 欧美性生给视频| 日韩成人xxxx| 精品视频成人| 国产熟人av一二三区| 亚洲精品午夜久久久| 裸体xxxx视频在线| 99中文字幕| 老色鬼精品视频在线观看播放| 好吊操这里只有精品| 久久久精品久久久久| 亚洲人成精品久久久|