1. 程式人生 > 程式設計 >golng mysql庫連線池分析

golng mysql庫連線池分析

0x1 背景

golang的協程是好用,但是有時候瓶頸並不在語言上,而是在後面的資料來源上面,例如我們常見的mysql,redis等,當一個後端服務很多請求的時候,語言是能hold得住,但是 mysql產生錯誤,比如 too many connection,too many time_wait 等等這些,今天我們就分析一下怎麼解決這種問題

0x2 程式碼範例

請檢視main.go, halokid (有幫忙的話請start或者follow一下哦,謝謝)

0x3 分析

只執行ini函式, 檢查mysql的程式顯示為(原有的mysql是沒有程式在處理的)

沒執行前


mysql> show processlist;
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
| Id | User            | Host             | db   | Command | Time | State                  | Info             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
|  4 | event_scheduler | localhost        | NULL | Daemon  | 2304 | Waiting on empty queue | NULL             |
|  9 | root            | 10.244.1.1:64000 | test
| Sleep | 1315 | | NULL | | 10 | root | 10.244.1.1:64022 | test | Query | 0 | starting | show processlist | +----+-----------------+------------------+------+---------+------+------------------------+------------------+ 3 rows in set (0.01 sec) 複製程式碼

執行後


mysql> show processlist;
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
| Id | User            | Host             | db   | Command | Time | State                  | Info             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
|  4 | event_scheduler | localhost        | NULL | Daemon  | 2284 | Waiting on empty queue | NULL             |
|  9 | root            | 10.244.1.1:64000 | test
| Sleep | 1295 | | NULL | | 10 | root | 10.244.1.1:64022 | test | Query | 0 | starting | show processlist | | 13 | root | 10.244.1.1:52134 | test | Sleep | 20 | | NULL | +----+-----------------+------------------+------+---------+------+------------------------+------------------+ 4 rows in set (0.00 sec) 複製程式碼

可見執行 db.Ping() 之後, process多了一個 Sleep 的連線,就是放了一個連線進 連線池

執行

db.SetMaxOpenConns(10)
db.SetMaxIdleConns(5)
複製程式碼

兩句之後, 連線池並沒有改變, 可見上面的邏輯是在 資料庫處理邏輯真實執行的時候才生效的

執行協程查詢

mysql> show processlist;
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
| Id | User            | Host             | db   | Command | Time | State                  | Info             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
|  4 | event_scheduler | localhost        | NULL | Daemon  | 4397 | Waiting on empty queue | NULL             |
|  9 | root            | 10.244.1.1:64000 | test | Sleep   | 3408 |                        | NULL             |
| 10 | root            | 10.244.1.1:64022 | test | Query   |    0 | starting               | show processlist |
| 19 | root            | 10.244.1.1:54823 | test | Sleep   |  952 |                        | NULL             |
| 20 | root            | 10.244.1.1:54824 | test | Sleep   | 1104 |                        | NULL             |
| 47 | root            | 10.244.1.1:57906 | test | Sleep   |    0 |                        | NULL             |
| 48 | root            | 10.244.1.1:57909 | test | Sleep   |    0 |                        | NULL             |
| 49 | root            | 10.244.1.1:57912 | test | Sleep   |    0 |                        | NULL             |
| 50 | root            | 10.244.1.1:57907 | test | Sleep   |    0 |                        | NULL             |
| 51 | root            | 10.244.1.1:57908 | test | Sleep   |    0 |                        | NULL             |
| 52 | root            | 10.244.1.1:57913 | test | Sleep   |    0 |                        | NULL             |
| 53 | root            | 10.244.1.1:57911 | test | Sleep   |    0 |                        | NULL             |
| 54 | root            | 10.244.1.1:57910 | test | Sleep   |    0 |                        | NULL             |
| 55 | root            | 10.244.1.1:57915 | test | Sleep   |    0 |                        | NULL             |
| 56 | root            | 10.244.1.1:57914 | test | Sleep   |    0 |                        | NULL             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
15 rows in set (0.00 sec)

複製程式碼

執行完在等待

mysql> show processlist;
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
| Id | User            | Host             | db   | Command | Time | State                  | Info             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
|  4 | event_scheduler | localhost        | NULL | Daemon  | 3931 | Waiting on empty queue | NULL             |
|  9 | root            | 10.244.1.1:64000 | test | Sleep   | 2942 |                        | NULL             |
| 10 | root            | 10.244.1.1:64022 | test | Query   |    0 | starting               | show processlist |
| 19 | root            | 10.244.1.1:54823 | test | Sleep   |  486 |                        | NULL             |
| 20 | root            | 10.244.1.1:54824 | test | Sleep   |  638 |                        | NULL             |
| 32 | root            | 10.244.1.1:56588 | test | Sleep   |   22 |                        | NULL             |
| 33 | root            | 10.244.1.1:56591 | test | Sleep   |   22 |                        | NULL             |
| 34 | root            | 10.244.1.1:56589 | test | Sleep   |   22 |                        | NULL             |
| 35 | root            | 10.244.1.1:56590 | test | Sleep   |   22 |                        | NULL             |
| 36 | root            | 10.244.1.1:56592 | test | Sleep   |   22 |                        | NULL             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
10 rows in set (0.00 sec)
複製程式碼

協程執行完之後

mysql> show processlist;
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
| Id | User            | Host             | db   | Command | Time | State                  | Info             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
|  4 | event_scheduler | localhost        | NULL | Daemon  | 3941 | Waiting on empty queue | NULL             |
|  9 | root            | 10.244.1.1:64000 | test | Sleep   | 2952 |                        | NULL             |
| 10 | root            | 10.244.1.1:64022 | test | Query   |    0 | starting               | show processlist |
| 19 | root            | 10.244.1.1:54823 | test | Sleep   |  496 |                        | NULL             |
| 20 | root            | 10.244.1.1:54824 | test | Sleep   |  648 |                        | NULL             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
5 rows in set (0.00 sec)
複製程式碼

我們發現最大連線控制在了10個, 執行完之後還有5個連線在保持著

這裡有一個很重要的問題,就是連線池的過期時間

0x4 深入分析 我們把 db.SetConnMaxLifetime(15 * time.Second), 連線池連線的生命週期設定為 15秒,我們會發現15秒之後,連線池的連線都會斷掉

mysql> show processlist;
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
| Id | User            | Host             | db   | Command | Time | State                  | Info             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
|  4 | event_scheduler | localhost        | NULL | Daemon  | 4987 | Waiting on empty queue | NULL             |
|  9 | root            | 10.244.1.1:64000 | test | Sleep   | 3998 |                        | NULL             |
| 10 | root            | 10.244.1.1:64022 | test | Query   |    0 | starting               | show processlist |
| 19 | root            | 10.244.1.1:54823 | test | Sleep   | 1542 |                        | NULL             |
| 20 | root            | 10.244.1.1:54824 | test | Sleep   | 1694 |                        | NULL             |
+----+-----------------+------------------+------+---------+------+------------------------+------------------+
5 rows in set (0.00 sec)

複製程式碼

30秒之後再次查詢資料庫

time.Sleep(30 * time.Second)
  rows,err := db.Query("select name from users")
  fmt.Println("err -----",err)
  defer rows.Close()
  for rows.Next(){
    var name string
    rows.Scan(&name)
    fmt.Println("name---",name)
  }
複製程式碼

這個時候發現程式會重新發起新的db連線

總結:

mysql服務端的連線生命週期

還有一種請況就是,我們的程式的連線池生命週期設定大於mysql伺服器的生命週期設定, 這個時候就會有一種請況,假如我們重複用連線池的連線,會產生 連線錯誤的問題,解決方法有兩種:

  1. 可以在程式裡面設定生命週期時間小於mysql服務端的連線生命週期時間就可以了
  2. 增加程式的重連(keepalive)機制,就是定時傳送一個連線包服務端 關於第2點我們我們以後可以再發散來說,一般如果允許的話,用第一種方式即可。
mysql> show variables like 'mysqlx_wait_timeout';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| mysqlx_wait_timeout | 28800 |
+---------------------+-------+
1 row in set (0.00 sec)
複製程式碼