Mysql
Codeignitor - 在登錄/會話負載下 MySQL CPU 峰值和致命的 MySQL 死鎖
最初發佈在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
- 請參閱下面的完整錯誤跟踪。因此,我做出了一個可能不正確的假設:
- 增加負載 >> 增加 RDS CPU 使用率
- 高 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;
所以,如果我的假設是正確的,那麼最好的行動計劃是什麼:
- 遷移到 RDS Serverless 並讓 RDS 擴展以處理 CPU 負載?(我在某處讀到 Serverless 可能無法很好地處理鎖,因為它在鎖定時無法正確擴展……我對此的理解顯然是有限的)
- 遷移到更大的固定(非無伺服器)RDS 來處理 CPU 負載?(不理想,因為 95% 的時間網站沒有流量)
- 修改會話以儲存在文件而不是數據庫中- 這對我來說聽起來很合乎邏輯,因為我們將所有會話負載從 MySQL 中移除,但我不完全了解任何其他後果,也不是只是修改
$config['sess_driver']
和設置會話文件文件夾路徑- 別的東西……(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
.