Mysql

Codeignitor - 在登錄/會話負載下 MySQL CPU 峰值和致命的 MySQL 死鎖

  • November 4, 2020

最初發佈在stackoverflow上,並被推薦 serverfault 可能是更好的地方。

我有一個網站使用:

  • AWS RDS (MySQL Aurora) - 單個 t3.medium 實例
  • 負載均衡器上的 4 個 EC2(固定實例非彈性)
  • CodeIgnitor 3 程式碼庫(3.1.11)(我剛剛根據推薦從 3.1.7 升級,因為新版本中有一些 Session 改進)。

一些規格:

EC2:

PHP Version 7.2.32-1+ubuntu18.04.1+deb.sury.org+1
Linux ip-172-32-19-104 5.4.0-1028-aws #29~18.04.1-Ubuntu SMP Tue Oct 6 17:14:23 UTC 2020 x86_64
Apache/2.4.29 (Ubuntu)

RDS:

5.6.mysql_aurora.1.22.2
Instance class: db.t3.medium
vCPU: 2
RAM: 4 GB

在繁重的負載下(500 人嘗試在十分鐘內登錄),我們會遇到間歇性但重大的問題。很難獲得有關使用者體驗的確切資訊,但事情表明:

  • RDS MySQL Aurora CPU 顯著飆升 (100%)
  • RDS MySQL Aurora Connections 峰值 (30-45) - 根據我的閱讀,RDS Max Connections 是 {DBInstanceClassMemory/12582880},所以大約 340 4GB(1024 4 1024*1024)/12582880
  • 產生的錯誤Deadlock found when trying to get lock; try restarting transaction- 請參閱下面的完整錯誤跟踪。

因此,我做出了一個可能不正確的假設:

  1. 增加負載 >> 增加 RDS CPU 使用率
  2. 高 RDS CPU >> 死鎖 >> 致命的 MySQL 錯誤(我對死鎖不太熟悉,不知道這是否會發生,但聽起來可行)。

錯誤指向libaries\Session\drivers\Session_database_driver.php,具體來說:

    /**
    * Write
    *
    * Writes (create / update) session data
    *
    * @param   string  $session_id Session ID
    * @param   string  $session_data   Serialized session data
    * @return  bool
    */
   public function write($session_id, $session_data)

  ...
  ...
  ...
  if ($this->_db->update($this->_config['save_path'], $update_data))
       {
           $this->_fingerprint = md5($session_data);
           return $this->_success;
       }

因此,我們在嘗試更新 CI 會話時遇到了數據庫死鎖。

它似乎總是在使用者登錄過程中拋出錯誤,我認為這是更新會話繁重。

此會話和數據庫類符合 CI 3.1.7 程式碼庫。

目前的 Code Ignitor Session 配置如下:

$config['sess_driver'] = 'database';
$config['sess_cookie_name'] = 'ci_session';
$config['sess_expiration'] = 7200;
$config['sess_save_path'] = 'ci_sessions';
$config['sess_match_ip'] = FALSE;
$config['sess_time_to_update'] = 300;
$config['sess_regenerate_destroy'] = FALSE;

所以,如果我的假設是正確的,那麼最好的行動計劃是什麼:

  1. 遷移到 RDS Serverless 並讓 RDS 擴展以處理 CPU 負載?(我在某處讀到 Serverless 可能無法很好地處理鎖,因為它在鎖定時無法正確擴展……我對此的理解顯然是有限的)
  2. 遷移到更大的固定(非無伺服器)RDS 來處理 CPU 負載?(不理想,因為 95% 的時間網站沒有流量)
  3. 修改會話以儲存在文件而不是數據庫中- 這對我來說聽起來很合乎邏輯,因為我們將所有會話負載從 MySQL 中移除,但我不完全了解任何其他後果,也不是只是修改$config['sess_driver']和設置會話文件文件夾路徑
  4. 別的東西……(php-fpm?)

對於選項 3),我們使用負載均衡器,所以我擔心如果使用者在中途切換 LB,基於文件的會話將意味著使用者會話的失去。雖然,這可能是一個可以管理的問題,因為使用者將在他們逗留期間留在 LB 上,除非它在中途跌倒。

選項 1 和 2 似乎是一種創可貼的方法,而不是解決無效的問題,但是,這可能只是資源不足的情況。

我在其他地方讀到了一篇關於使用 php-fpm 減少同時 apache 執行緒數量的類似文章的建議,但不確定這是否與此處相關,特別是在 php 7.2 上給出

很難“測試”,因為它只發生在大量使用者登錄負載下,所以一些建議將不勝感激,所以我不必在黑暗中多次刺傷。

謝謝

編輯:

以下完整錯誤的副本:

A Database Error Occurred 

Error Number: 1213 

Deadlock found when trying to get lock; try restarting transaction 

UPDATE `ci_sessions` SET `timestamp` = 1604298368 WHERE `id` = 'fqi83a50dfknbvl9h2r98mtgn2f3j2j6' Filename: libraries/Session/drivers/Session_database_driver.php 

Line Number: 260 

A PHP Error was encountered 

Severity: Warning 

Message: Unknown: Cannot call session save handler in a recursive manner 

Filename: Unknown 

Line Number: 0 
Backtrace: 

A PHP Error was encountered 

Severity: Warning 

Message: Unknown: Failed to write session data using user defined save handler. (session.save_path: /var/lib/php/sessions) 

Filename: Unknown 

Line Number: 0 

Backtrace

編輯: SHOW CREATE TABLE ci_sessions;

'ci_sessions', 'CREATE TABLE `ci_sessions` (
`id` varchar(128) NOT NULL,
`ip_address` varchar(45) NOT NULL,
`timestamp` int(10) unsigned NOT NULL DEFAULT \'0\',
`data` blob NOT NULL,
 KEY `ci_sessions_timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8'

你需要某種索引id

如果id是唯一的,它可能應該是PRIMARY KEY.

引用自:https://serverfault.com/questions/1041044