1. 项目概述
在CentOS系统上使用宝塔面板搭建HOJ在线判题系统时,数据库配置和脚本部署是最关键的环节之一。作为一名长期维护在线编程竞赛平台的运维工程师,我将在本文详细拆解整个部署过程中的技术要点和避坑指南。
HOJ作为开源的在线判题系统,其稳定运行高度依赖MySQL数据库的正确配置。通过宝塔面板可以大幅简化环境部署流程,但数据库权限管理、字符集设置、备份脚本编写等环节仍需要手动优化。本文将覆盖从基础环境准备到生产级部署的全套方案,特别适合需要快速搭建在线编程评测平台的技术团队参考。
2. 环境准备与依赖安装
2.1 系统基础环境配置
推荐使用CentOS 7.9最小化安装版本,确保系统纯净。首次登录后需要完成以下基础配置:
# 更新系统组件 yum update -y && yum install -y epel-release # 安装常用工具 yum install -y wget curl vim git unzip # 关闭SELinux(需要重启生效) sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config setenforce 0 # 调整文件描述符限制 echo "* soft nofile 65535" >> /etc/security/limits.conf echo "* hard nofile 65535" >> /etc/security/limits.conf注意:生产环境建议保留SELinux,但需要针对HOJ服务配置详细策略。本文为简化部署流程暂时禁用。
2.2 宝塔面板安装与优化
执行官方一键安装命令:
wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh安装完成后,建议立即进行以下安全加固:
- 修改默认8888端口为非常用端口
- 在面板设置中绑定访问IP(仅限管理员IP)
- 开启BasicAuth二次认证
- 安装Fail2ban插件防止暴力破解
进入宝塔面板后,通过"软件商店"安装以下必备组件:
- Nginx 1.20+
- MySQL 5.7(必须5.7版本)
- PHP 7.4(仅用于管理后台)
- Redis 6.2+
3. MySQL数据库深度配置
3.1 数据库初始化设置
在宝塔面板创建MySQL数据库时,必须注意以下参数:
- 字符集选择utf8mb4(完整支持emoji和特殊字符)
- 排序规则选择utf8mb4_general_ci
- 权限设置中勾选"所有权限"
- 连接数限制建议设置为200+
通过phpMyAdmin执行以下SQL初始化HOJ所需数据库:
CREATE DATABASE `hoj` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; GRANT ALL PRIVILEGES ON hoj.* TO 'hoj_user'@'%' IDENTIFIED BY 'StrongPassword123!'; FLUSH PRIVILEGES;3.2 性能优化配置
编辑MySQL配置文件(/etc/my.cnf),在[mysqld]段落后添加:
# 连接配置 max_connections = 500 wait_timeout = 300 interactive_timeout = 300 # 缓冲池配置(根据内存调整) innodb_buffer_pool_size = 2G innodb_buffer_pool_instances = 4 # 日志配置 slow_query_log = 1 slow_query_log_file = /var/log/mysql-slow.log long_query_time = 2 # InnoDB优化 innodb_flush_log_at_trx_commit = 2 innodb_log_file_size = 256M innodb_log_buffer_size = 16M innodb_file_per_table = 1重启MySQL服务使配置生效:
systemctl restart mysqld4. HOJ数据库部署实战
4.1 数据库结构导入
下载HOJ最新发行版并解压:
wget https://github.com/hufangyun/hoj/releases/download/v2.7.4/hoj-2.7.4.tar.gz tar -zxvf hoj-2.7.4.tar.gz cd hoj-2.7.4/sql使用mysql命令导入基础结构:
mysql -u hoj_user -p hoj < hoj.sql关键点:导入前需要确认sql文件中的表引擎是否为InnoDB(MyISAM在高并发下会出现锁问题)
4.2 初始数据配置
导入基础数据后,需要手动修改以下关键配置:
-- 设置默认管理员账号 UPDATE `user` SET `password` = MD5('admin123'), `email` = 'admin@yourdomain.com' WHERE `user_id` = 1; -- 配置系统判题服务器 INSERT INTO `judge_server` (`hostname`, `ip`, `port`, `max_task_number`) VALUES ('judge1', '127.0.0.1', 8080, 10);5. 自动化运维脚本开发
5.1 数据库备份脚本
创建/root/scripts/hoj_backup.sh:
#!/bin/bash # 定义备份目录和保留天数 BACKUP_DIR="/data/backups/hoj" KEEP_DAYS=7 DATE=$(date +%Y%m%d%H%M%S) # 创建备份目录 mkdir -p ${BACKUP_DIR} # MySQL全量备份 mysqldump -uhoj_user -p'StrongPassword123!' --single-transaction --master-data=2 --routines --triggers hoj | gzip > ${BACKUP_DIR}/hoj_db_${DATE}.sql.gz # 清理旧备份 find ${BACKUP_DIR} -name "hoj_db_*.sql.gz" -type f -mtime +${KEEP_DAYS} -delete添加到crontab实现每日自动备份:
0 3 * * * /bin/bash /root/scripts/hoj_backup.sh5.2 日志轮转配置
创建/etc/logrotate.d/hoj:
/var/log/hoj/*.log { daily missingok rotate 30 compress delaycompress notifempty create 640 root root sharedscripts postrotate /usr/bin/systemctl restart hoj-judge >/dev/null 2>&1 || true endscript }6. 高可用架构设计
6.1 主从复制配置
在生产环境中建议配置MySQL主从复制:
- 主库my.cnf添加:
server-id = 1 log_bin = mysql-bin binlog_format = ROW binlog_row_image = FULL- 从库配置:
server-id = 2 relay_log = mysql-relay-bin read_only = 1- 主库创建复制账号:
CREATE USER 'repl'@'%' IDENTIFIED BY 'ReplPassword123!'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';- 从库启动复制:
CHANGE MASTER TO MASTER_HOST='master_ip', MASTER_USER='repl', MASTER_PASSWORD='ReplPassword123!', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=154; START SLAVE;6.2 读写分离实现
使用ProxySQL实现自动读写分离:
-- 添加服务器 INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (10,'master_ip',3306); INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (20,'slave_ip',3306); -- 配置用户 INSERT INTO mysql_users(username,password,default_hostgroup) VALUES ('hoj_user','StrongPassword123!',10); -- 读写分离规则 INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply) VALUES (1,1,'^SELECT.*FOR UPDATE',10,1); INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply) VALUES (2,1,'^SELECT',20,1);7. 性能监控与优化
7.1 监控指标配置
安装Percona Monitoring插件:
yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm yum install -y percona-toolkit创建监控脚本/root/scripts/mysql_monitor.sh:
#!/bin/bash # 连接数监控 CONN=$(mysqladmin -uhoj_user -p'StrongPassword123!' status | awk '{print $4}') echo "MySQL Connections: $CONN" # 慢查询检查 SLOW=$(mysql -uhoj_user -p'StrongPassword123!' -e "SELECT COUNT(*) FROM mysql.slow_log WHERE start_time > NOW() - INTERVAL 1 HOUR;" -s) echo "Slow queries(last hour): $SLOW" # InnoDB缓冲池命中率 HIT_RATE=$(mysql -uhoj_user -p'StrongPassword123!' -e "SHOW ENGINE INNODB STATUS\G" | grep "Buffer pool hit rate" | awk '{print $8}') echo "InnoDB Hit Rate: $HIT_RATE"7.2 常见性能问题处理
- 连接数爆满:
-- 查看活跃连接 SELECT * FROM information_schema.processlist WHERE COMMAND != 'Sleep'; -- 终止特定连接 KILL <process_id>;- 死锁处理:
-- 查看最近死锁 SHOW ENGINE INNODB STATUS\G -- 自动死锁检测配置 SET GLOBAL innodb_deadlock_detect = ON; SET GLOBAL innodb_lock_wait_timeout = 30;- 查询优化:
-- 添加索引示例 ALTER TABLE `submission` ADD INDEX `idx_problem_user` (`problem_id`, `user_id`); -- 优化表结构 OPTIMIZE TABLE `judge_case`;8. 安全加固措施
8.1 数据库访问控制
- 限制远程访问IP:
-- 只允许应用服务器访问 RENAME USER 'hoj_user'@'%' TO 'hoj_user'@'app_server_ip';- 启用SSL连接:
# 生成证书 openssl req -x509 -newkey rsa:4096 -keyout /etc/mysql/ssl/server-key.pem -out /etc/mysql/ssl/server-cert.pem -days 365 -nodesmy.cnf配置:
[mysqld] ssl-ca=/etc/mysql/ssl/ca.pem ssl-cert=/etc/mysql/ssl/server-cert.pem ssl-key=/etc/mysql/ssl/server-key.pem8.2 审计日志配置
安装MySQL企业审计插件(社区版可用替代方案):
wget https://downloads.mariadb.org/interstitial/mariadb-10.5.8/bintar-linux-x86_64/mariadb-10.5.8-linux-x86_64.tar.gz tar -zxvf mariadb-10.5.8-linux-x86_64.tar.gz cp mariadb-10.5.8-linux-x86_64/lib/plugin/server_audit.so /usr/lib64/mysql/plugin/加载插件:
INSTALL PLUGIN server_audit SONAME 'server_audit.so'; SET GLOBAL server_audit_events='CONNECT,QUERY,TABLE'; SET GLOBAL server_audit_logging=ON; SET GLOBAL server_audit_file_rotate_size=100000000;9. 故障排查手册
9.1 常见错误解决方案
- 无法连接数据库:
- 检查防火墙规则:
firewall-cmd --list-all - 验证用户权限:
SHOW GRANTS FOR 'hoj_user'@'app_server_ip'; - 检查MySQL错误日志:
tail -f /var/log/mysqld.log
- 性能突然下降:
-- 查看当前运行查询 SHOW FULL PROCESSLIST; -- 检查锁等待 SELECT * FROM performance_schema.events_waits_current;- 数据不一致修复:
# 使用pt-table-checksum检查数据一致性 pt-table-checksum --user=hoj_user --password='StrongPassword123!' --databases=hoj9.2 灾难恢复演练
- 定期测试备份有效性:
# 随机选择一个备份进行恢复测试 TEST_BACKUP=$(ls /data/backups/hoj | shuf -n 1) gunzip < /data/backups/hoj/${TEST_BACKUP} | mysql -uhoj_user -p'StrongPassword123!' hoj_test- 建立恢复SOP文档:
- 记录关键命令和恢复顺序
- 明确各步骤负责人
- 设置恢复时间目标(RTO)和数据丢失容忍度(RPO)
在实际运维中,我们发现HOJ数据库的判题记录表(submission)增长最快,建议每月归档一次历史数据。可以使用pt-archiver工具实现不影响服务的在线归档:
pt-archiver --source h=localhost,D=hoj,t=submission \ --where 'create_time<DATE_SUB(NOW(), INTERVAL 3 MONTH)' \ --dest h=localhost,D=hoj_archive,t=submission \ --progress 1000 --limit=1000 --txn-size=1000 \ --statistics --no-delete --bulk-insert