Codeignitor - 登录/会话负载下 MySQL CPU 峰值和致命 MySQL 死锁

Codeignitor - 登录/会话负载下 MySQL CPU 峰值和致命 MySQL 死锁

最初发布于堆栈溢出,并建议 serverfault 可能是更好的地方。

我有一个网站使用:

  • AWS RDS (MySQL Aurora) - 单个 t3.medium 实例
  • 负载均衡器上的 4 x 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

在高负载下(10 分钟内有 500 人尝试登录),我们会遇到间歇性但严重的问题。很难获得有关用户具体遇到什么情况的信息,但情况表明:

  • RDS MySQL Aurora CPU 大幅飙升(100%)
  • RDS MySQL Aurora 连接数激增(30-45)-据我所知,RDS 最大连接数为 {DBInstanceClassMemory/12582880},因此大约 340 个 4GB(102441024*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),我们使用负载均衡器,因此我担心基于文件的会话可能意味着如果用户中途切换负载均衡器,则用户的会话会丢失。不过,这可能是一个可控的问题,因为用户将一直停留在负载均衡器上,除非负载均衡器中途发生故障。

选项 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'

答案1

您需要某种索引id

如果id是唯一的,它应该是PRIMARY KEY

相关内容