如何阻止PHP会话关闭?解决MySQL RDS孤儿表性能问题
解决方案
1. 用MySQL事件调度器定时清理孤儿表(零额外成本)
既然用的是MySQL RDS,直接用它自带的事件调度器就能解决,不用加额外服务:
- 第一步:给每张用户专属表加
last_activity字段,设置自动更新:
这个字段会在用户操作表时自动刷新为当前时间,记录用户最后活跃节点。ALTER TABLE user_xxx_table ADD COLUMN last_activity TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; - 第二步:开启MySQL事件调度器(RDS需先在参数组将
event_scheduler设为ON,重启实例生效),然后创建定时清理事件,比如每小时清理一次超过24小时无活动的表:DELIMITER // CREATE EVENT clean_orphan_tables ON SCHEDULE EVERY 1 HOUR DO BEGIN DECLARE done INT DEFAULT FALSE; DECLARE table_name VARCHAR(255); DECLARE cur CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema = '你的数据库名' AND table_name LIKE 'user_%' AND (SELECT last_activity FROM `table_name`) < DATE_SUB(NOW(), INTERVAL 24 HOUR); DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN cur; read_loop: LOOP FETCH cur INTO table_name; IF done THEN LEAVE read_loop; END IF; SET @drop_sql = CONCAT('DROP TABLE IF EXISTS `', table_name, '`'); PREPARE stmt FROM @drop_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; END LOOP; CLOSE cur; END // DELIMITER ; - 第三步:在CodeIgniter 3的全局钩子(比如
post_controller)里,用户每次请求后手动刷新对应表的last_activity(如果用户没操作表但有页面请求的话):// hooks.php 配置 $hook['post_controller'] = array( 'class' => 'ActivityHook', 'function' => 'update_last_activity', 'filename' => 'ActivityHook.php', 'filepath' => 'hooks', ); // hooks/ActivityHook.php class ActivityHook { public function update_last_activity() { $CI =& get_instance(); $user_id = $CI->session->userdata('user_id'); if ($user_id) { $table = "user_{$user_id}_main"; // 选一张用户核心表即可 $CI->db->query("UPDATE `{$table}` SET last_activity = NOW()"); } } }
优点:零额外成本,完全基于现有RDS;缺点:清理有延迟(最多延迟1小时),需要修改现有表结构。
2. 重构表结构:放弃用户专属表,改用带用户标识的共享表(最优解)
从根源解决表数量爆炸的问题:把6张用户专属表改成6张共享表,每张表加user_id字段,用户数据按user_id区分:
- 比如原来的
user_123_order改成orders表,添加user_id INT NOT NULL字段,给user_id加索引。 - 用户注销时,执行
DELETE FROM orders WHERE user_id = {$user_id};,批量删除该用户的所有数据。 - 同样用定时任务清理过期数据:比如删除30天未登录用户的所有数据:
DELETE FROM orders WHERE user_id IN (SELECT id FROM users WHERE last_login < DATE_SUB(NOW(), INTERVAL 30 DAY)); -- 对其他5张表执行同样操作
优点:彻底避免孤儿表问题,数据库性能更稳定,维护成本低;缺点:需要重构现有代码的查询逻辑(比如原来查user_123_order,现在要加WHERE user_id = 123条件)。
3. 前端监听页面关闭事件,触发异步注销(补充方案)
用浏览器的beforeunload事件配合navigator.sendBeacon发送清理请求,覆盖大部分正常关闭标签页的场景:
- 前端全局JS代码:
window.addEventListener('beforeunload', function() { const userId = document.querySelector('meta[name="user-id"]').content; const formData = new FormData(); formData.append('user_id', userId); // sendBeacon会在页面卸载时异步发送,不会阻塞页面关闭 navigator.sendBeacon('/clean-user-tables', formData); }); - CodeIgniter后端接口:
public function clean_user_tables() { $user_id = $this->input->post('user_id'); if ($user_id) { $tables = [ "user_{$user_id}_table1", "user_{$user_id}_table2", // 其他4张表 ]; foreach ($tables as $table) { $this->db->query("DROP TABLE IF EXISTS `{$table}`"); } } }
优点:能及时清理大部分正常关页用户的表;缺点:无法覆盖浏览器崩溃、网络中断等极端情况,只能作为辅助手段。
内容的提问来源于stack exchange,提问作者richard
相关产品推荐
相关产品推荐

