最初发布在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
.