问题起因
oj有一个给contest匹配namelist的功能:选择已经insert的名单,添加到contest的管理界面。
其中有两个更新名单的按钮不会自动去重,即 选择名单后,点击两次会插入两次相同的95人名单,共计190条记录。
为了暂时解决这个问题,我直接在MySQL中删除了后添加的95条数据。
然后通过浏览器查看名单,OJ就崩了!!但是如果不看contest相关的功能,oj可以正常work。
重启apache依旧只能正常访问非contest模块。
这个时候我发现MySQL连不上了,显示Connect failed: Too many connections,修改/etc/my.cnf
max_connections = 1500
重启MySQL,MySQL可以正常连接了,开头出现的问题也解决了。
分析原因
1.MySQL最大连接数
MySQL 5.7默认的最大连接数是151 ,文档在此。
既然修改最大连接数解决之前oj卡的问题,大概率是因为之前MySQL的最大连接数不够造成的。
2.MySQL隔离级别
既然在修改最大连接数之前,oj除contest相关功能,其他功能都可以正常访问。
尝试在本地重现,删除几条多余的记录,刷新网页,并没有卡。
查看本地数据库,在information_schema数据库-GLOBAL_VARIABLES表中查看max_connections=151。
说明是访问非本地数据时才有的缓存问题,已知MySQL的默认隔离级别是可重复读,确认当前会话和系统的隔离级别
mysql> use information_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
均为可重复读,即可能出现幻读错误。
3.MySQL锁
已知oj的MySQL选取InnoDB索引,即默认使用行级锁。但是当我一下子删除95条数据的时候,且使用索引操作数据,所以并没有升级为表锁。进而开销大,加锁慢;会出现死锁。
4.web端缓存问题
已知使用Bootstrap前端框架,在加载表格数据时使用的是client模式,即一次性加载全部表格内容,根据设置自动分页,无需再次请求服务器。
5.在线上重现问题
将最大连接数还原回默认,重启MySQL,删除没用比赛中的某几条记录,刷新网页,oj并没有卡,怀疑是在线人数较少的原因。
6.查阅相关资料,以及apache的MPM模式
event模式解决了在keep-alive场景下,长期被占用的线程的资源浪费问题,在现在版本里的已经是稳定可用的模式。event MPM中,会有一个专门的线程来管理这些keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场景下的请求处理能力。
结论
昨晚oj卡的原因是,删除数据时请求人数过多(当时上公选课 100人左右,都怀疑自己网的问题,一个劲的刷新,导致火上浇油),每个client都持有访问MySQL的句柄,并且由于当时删除数据contest相关表格出现死锁,导致当前连接超过最大连接数。
update:之前没卡是因为 apache可能是维护了一个MySQL连接池的东西,所以,只要不是【同时】需要MySQL连接,就没有问题,即使考试or比赛有几百人登录。