拿到一台装了BCLinux的机器,现场同事说照着CentOS的文档装MySQL就行,结果光是初始化那一步就卡了半个多小时。BCLinux这类面向政企场景的Linux发行版,和CentOS 7确实同源,但默认软件仓库精简很多,很多在CentOS上理所当然的包在这里根本没得装。如果你也正在BCLinux上装MySQL 5.7.34,或者马上要面对类似环境,这篇文章就是给你准备的。我梳理了一套完整的实操流程,覆盖环境确认、依赖处理、二进制包部署、初始化配置、远程连接和故障排查,跟着走就能把MySQL 5.7.34稳稳跑起来。
1. 安装前的关键决策
1.1 BCLinux与CentOS 7的兼容关系,先确认版本再说
BCLinux本质上基于RHEL 7生态构建,和CentOS 7的血缘关系非常近。内核版本是3.10,glibc是2.17,所以CentOS 7上能跑的预编译软件,理论上在BCLinux上也能跑。这句话是理论,实际会有一些细微差异,比如某个依赖包在BCLinux的yum源里没有,或者版本对不上。
动手之前先确认系统版本和架构,命令很简单:
cat /etc/redhat-release cat /etc/os-release uname -m如果输出显示是7.x版本,下面这套流程直接可用。如果发现是8.x系列,那底层的包管理、systemd版本、glibc版本都不一样,很多命令和路径要重新适配。还有一个容易忽略的点:很多BCLinux是精简安装,基础工具都可能不全,比如tar、vim、wget、net-tools。装MySQL之前先把这些补齐,省得到时候解压都没法解:
yum install -y tar gzip vim wget net-tools1.2 为什么偏偏选5.7.34这个版本
说句实在话,MySQL 5.7从功能上已经是上个时代的产物了,但很多业务系统就是跑在5.7上。尤其是那些从老环境迁移过来的系统,JDBC驱动版本、ORM框架、存储过程写法,全部是按5.7调校过的,贸然升到8.0会出现一堆兼容性问题。某个内部已经验证过的版本是5.7.34,那就钉死用5.7.34,不要为了追新去换5.7.44,功能差异对于实际业务几乎无感,反而容易引入新的变量。
另外,5.7.34这个版本在离线软件源里也比较容易找到验证过的副本。MySQL官方提供的Linux通用二进制包大小在600MB左右,内网拷贝、离线部署都很方便。对于BCLinux这种无法随时访问外网的环境来说,能离线装完是硬需求。
1.3 安装方式选型:yum、源码编译、二进制包怎么选
BCLinux的默认yum源里通常没有mysql-community仓库,只有mariadb。想用yum装MySQL,必须先手动导入官方仓库,但很多现场环境根本没有外网,这一步直接卡死。就算把MySQL的rpm包下载下来手动装,你也面临依赖地狱:mysql-community-common、mysql-community-client、mysql-community-libs、mysql-community-server,每个包版本要严格对齐,少一个依赖就装不上,装完还容易和系统自带的mariadb-libs冲突。
源码编译就更不推荐了。MySQL 5.7源码编译需要cmake、boost、gcc全家桶,编译一次一个多小时,中间任何一步报错排查起来都相当痛苦。生产环境图啥?图可控、图快、图好回滚。
二进制包是三者里最可控的方案。官方已经用glibc预编译好了,我们要做的只是解压、配置、初始化、启动。真正的依赖只有libaio这类极少数共享库,处理方式明确。回滚也简单,停服务、删目录、恢复软链接指向,几分钟的事。所以我在这类环境里统统用二进制包。
2. 环境准备与依赖处理
2.1 下载安装包与完整性校验
下载路径是MySQL官方archive页面,选择Linux通用二进制包,具体文件名是mysql-5.7.34-linux-glibc2.12-x86_64.tar.gz。注意选glibc2.12这个版本,它对应的就是CentOS 7/BCLinux 7这一代的系统环境。
下载完一定要做完整性校验,别嫌麻烦。MD5值在官网下载页旁边会给出,执行:
md5sum mysql-5.7.34-linux-glibc2.12-x86_64.tar.gz把输出结果和官方的MD5比对,一致再继续。我在现场见过一次下载了一半就断掉的包,解压倒是能解,但里面的二进制文件损坏,mysqld跑起来各种诡异报错。后来养成习惯,所有离线部署的软件包先校验再解压。
内网环境拷贝时,建议打包成zip或者tar.gz分卷传输,大文件容易传输中断,重新下载成本高。
2.2 用ldd提前排查缺失的动态库
解压之后先别急着配置,用ldd查一下mysqld还缺哪些动态库:
tar -xzf mysql-5.7.34-linux-glibc2.12-x86_64.tar.gz -C /opt/ cd /opt/mysql-5.7.34 ldd bin/mysqld | grep "not found"这一步能让你提前发现问题,而不是等在初始化的时候才报错。最常缺的就是libaio.so.1,对应包名是libaio;偶尔还会缺libnuma.so.1,对应numactl-libs。两个都装上:
yum install -y libaio numactl-libs还有一个小坑:MySQL自带的mysql客户端和mysqldump这类工具还会依赖libncurses.so.5,但BCLinux上往往只有更高版本的ncurses。mysqld本体其实不依赖它,但当你执行mysql命令行工具时会报错。处理方式是做一个软链接:
ln -s /usr/lib64/libncurses.so.6 /usr/lib64/libncurses.so.5注意别在有多个ncurses版本的机器上乱链,先ls一下确认库文件实际路径。
2.3 SELinux和防火墙的处理分寸
很多教程上来就让人禁用SELinux,我这个人的习惯是先搞清楚它是不是真的在捣乱。查看状态:
getenforce如果输出是Disabled,那就不用管。如果是Enforcing,先临时放宽到Permissive做测试:
setenforce 0如果mysqld能正常启动、3306端口能正常访问,说明之前确实是SELinux造成的限制。这时候你再决定要不要永久改成permissive:修改/etc/selinux/config,把SELINUX=enforcing改成SELINUX=permissive,然后重启。注意,生产环境直接把SELinux整个关掉不是一个好习惯,permissive已经是比较折中的做法。
防火墙方面,能只放行端口就不要整机停防火墙。BCLinux上最常见的是firewalld:
systemctl status firewalld firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --reload有些变体是iptables service管理,那就用iptables命令加规则。关键是搞清楚这台机器到底用的哪种,别拿firewalld的命令去操作一个根本没用firewalld的系统。
3. MySQL 5.7.34安装实操全流程
3.1 目录规划:软件和数据分离
安装前先把目录规划好。我的习惯是软件目录和数据目录彻底分开,这样版本升级、数据备份、权限管理都清晰:
- 软件放 /opt/mysql-5.7.34
- 数据放 /data/mysql/data
- 日志放 /data/mysql/log
解压之后,在/usr/local下做一个软链接,指向当前的版本目录:
ln -s /opt/mysql-5.7.34 /usr/local/mysql为什么要加这一层软链接?因为升级的时候只需要解压新版本到/opt,然后把软链接指向新目录即可,my.cnf里的basedir不用动,系统服务也不用改。下次升级你就知道这个设计有多香了。
创建数据目录和日志目录:
mkdir -p /data/mysql/data /data/mysql/log此时先别急着chown,目录权限放到创建用户之后再统一处理。
3.2 创建专用运行用户,别用root跑数据库
MySQL的官方建议是使用专用的系统用户运行,禁止登录,最小权限。创建命令:
useradd -r -s /sbin/nologin mysql-r表示系统用户,不会出现在登录界面;-s /sbin/nologin表示禁止交互式登录。然后给数据目录和日志目录授权:
chown -R mysql:mysql /data/mysql这里有个细节很多人会做错:安装包解压出来的/opt/mysql-5.7.34目录保持root所有就行了,千万不要chown -R mysql。数据库进程只需要对数据目录和日志目录有写权限,代码目录保持root只读反而更安全。如果整个目录都给mysql,一方面存在被篡改的风险,另一方面某些异常情况下mysqld反而会因为其他文件权限问题起不来。
3.3 my.cnf配置文件:哪些参数必须写,哪些是坑
配置文件放在/etc/my.cnf,直接给一份我常用的模板:
[mysqld] basedir=/usr/local/mysql datadir=/data/mysql/data socket=/data/mysql/mysql.sock pid-file=/data/mysql/mysqld.pid log-error=/data/mysql/log/error.log character_set_server=utf8mb4 collation_server=utf8mb4_general_ci lower_case_table_names=1 max_connections=1000 innodb_buffer_pool_size=1G sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION default-time-zone='+8:00' skip-name-resolve [client] socket=/data/mysql/mysql.sock逐个说说关键参数:
innodb_buffer_pool_size是InnoDB的缓存池大小,直接影响数据库性能。经验值是物理内存的60%到70%,但小内存机器别硬套,给个1G到2G先用着,后面看命中率再调。
lower_case_table_names这个参数特别关键。如果业务是从Windows迁过来的,或者表名存在大小写混用的情况,必须设置lower_case_table_names=1,让MySQL把所有表名转为小写存储再比较。这个参数必须在初始化之前就定好,一旦数据目录初始化完成再改,会出现“Table doesn't exist”这种看着完全摸不着头脑的报错。
sql_mode我特意去掉了ONLY_FULL_GROUP_BY。老业务系统里写group by的SQL经常不遵守严格分组规则,在默认sql_mode下会直接报错,而业务代码又是改不动的,所以只能从数据库配置层面妥协。新项目我倒是建议保留。
socket路径写在了/data/mysql目录下,这是为了避免后面systemd服务里PrivateTmp=true带来的socket路径不一致问题,也方便管理。相关坑我后面单独讲。
3.4 初始化数据库:mysqld --initialize与临时密码
初始化是整个安装过程最关键的一步,很多安装失败都发生在这里。执行:
/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --initialize --user=mysql注意,MySQL 5.7和5.6的初始化方式完全不一样。5.6用mysql_install_db脚本,5.7改成了mysqld --initialize。如果你翻到旧文章还在用mysql_install_db,请换成上面这条命令。
--initialize参数会生成一个随机的root临时密码,打印在错误日志里。初始化成功的标志是/data/mysql/log/error.log里出现了类似这样的一行:
[Note] A temporary password is generated for root@localhost: xxxxxxx初始化失败最常见的原因有两个:datadir非空,或者目录属主不是mysql。报错信息都会写进error.log,所以出问题第一步是看日志,不要瞎猜。
测试环境想直接用空密码初始化,可以加--initialize-insecure参数,但生产环境千万不要这么干,临时密码虽然烦人,但那是安全底线。
初始化过程中如果看到“--initialize specified but data directory exists”这类错误,就是datadir目录下有东西了,自己确认一下是不是之前装过或者目录路径写错。
3.5 通过systemd托管服务并设置开机自启
很多博主还让人用mysqld_safe启动,然后自己写rc.local,这套玩法在systemd时代已经过时了。我自己用systemd统一管理,写一个unit文件,路径/etc/systemd/system/mysqld.service:
[Unit] Description=MySQL Server 5.7.34 After=network.target [Service] Type=simple User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf LimitNOFILE=65535 LimitNPROC=65535 Restart=on-failure PrivateTmp=true [Install] WantedBy=multi-user.targetType=simple是因为mysqld本身是前台进程,不需要mysqld_safe包装。LimitNOFILE和LimitNPROC是为了防止高并发下文件描述符不够用,直接写死比较省心。Restart=on-failure让MySQL崩溃时自动拉起,比裸跑mysqld_safe不知道高到哪里去了。
然后加载并启动:
systemctl daemon-reload systemctl enable mysqld systemctl start mysqld systemctl status mysqld ss -lntp | grep 3306这里有一个隐蔽的坑:PrivateTmp=true会把服务进程的/tmp隔离成私有目录。如果my.cnf里socket路径配置的是/tmp/mysql.sock,而客户端去连/tmp/mysql.sock会连不上,两边看到的根本不是同一个文件。所以我在3.3里特意把socket放到/data/mysql/mysql.sock,就是为了不踩这个坑。
3.6 首次登录与修改root密码
拿到临时密码后登录:
mysql -uroot -p临时密码里如果有特殊字符,建议用单引号包起来,尤其是$这类字符在shell里会被当成变量替换,导致密码错误。登录进去之后必须立刻改密码:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourNewPass@123';如果你设置的密码强度不够,会报ERROR 1819,提示不符合validate_password的策略要求。这是5.7默认的密码校验插件在起作用,默认策略是MEDIUM,要求密码长度至少8位、包含大小写字母、数字和特殊字符。测试环境临时调低策略可以:
SET GLOBAL validate_password_policy=0; SET GLOBAL validate_password_length=4;但生产环境建议还是老老实实按MEDIUM策略来,密码复杂点没坏处。
4. 初始化后必做的收尾工作
4.1 业务账号与远程访问授权
root账号只保留本机管理用途就够了。业务连数据库,单独创建一个账号,权限按最小化原则给:
CREATE USER 'app'@'%' IDENTIFIED BY 'AppPass@123'; GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'%'; FLUSH PRIVILEGES;%在这里表示允许从任意主机连接,如果知道业务服务器的IP段,建议精确到网段,比如'192.168.1.%',风险面小很多。
mysql.user表里host字段的匹配逻辑值得说一下:MySQL匹配账号的时候,是从最具体的host到最宽泛的host,localhost和127.0.0.1优先级高于%。所以即使你创建了root@'%',本机通过socket连接时走的还是root@'localhost'这条记录。这也意味着删掉localhost账号是极其危险的操作,管理入口都进不去。远程给root开权限我是明确不建议的,真有需求就限制来源IP:
GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.1.%' IDENTIFIED BY '密码' WITH GRANT OPTION;4.2 字符集与排序规则验证
安装完第一步验证字符集,避免后面插入中文乱码了才回头看:
SHOW VARIABLES LIKE 'character_set_%'; SHOW VARIABLES LIKE 'collation_%';重点看character_set_server是不是utf8mb4,collation_server是不是utf8mb4_general_ci。5.7里utf8mb4的排序规则主要是general_ci和unicode_ci,中文环境下差别不大,unicode_ci排序更精确但性能略低。对多数业务来说general_ci就够了,关键是统一。
建库建表时也明确指定:
CREATE DATABASE appdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;如果发现中文插入后变成乱码,先检查连接层:JDBC连接串里加characterEncoding=utf8mb4,命令行连接时执行SET NAMES utf8mb4。连接层、库表编码、客户端编码三层一致,才能彻底解决乱码。
4.3 时区、binlog与慢查询日志
时区问题属于那种不碰到没事、碰到就查半天的坑。默认情况下MySQL使用的time_zone可能和业务时区对不上,时间字段结果差8小时。直接在my.cnf里写死:
default-time-zone='+8:00'比在每条连接里执行SET time_zone靠谱得多。
binlog是另一个必开项。就算现在不做主从复制,以后做数据恢复、增量备份、搭建从库都离不开它。在my.cnf的[mysqld]段加上:
server-id=1 log-bin=mysql-bin binlog_format=ROW expire_logs_days=7 max_binlog_size=1Gexpire_logs_days=7表示binlog只保留7天,防止日志无限增长把磁盘写满。binlog_format推荐ROW模式,虽然日志文件比STATEMENT模式大,但数据一致性最好,对于需要做时间点恢复的场景尤其重要。
慢查询日志也顺手开上:
slow_query_log=1 slow_query_log_file=/data/mysql/log/slow.log long_query_time=2long_query_time=2表示执行时间超过2秒的SQL会被记录。早发现慢SQL,比业务方找上门来说系统卡了要体面得多。
4.4 定时备份与基础巡检
部署完成后的第一件事,把备份安排上。写一个简单的mysqldump脚本,放在/usr/local/bin/mysql_backup.sh:
#!/bin/bash BACKUP_DIR=/data/backup/mysql DATE=$(date +%F) mkdir -p $BACKUP_DIR mysqldump --single-transaction --routines --triggers --events -uapp_backup -p备份密码 --all-databases | gzip > $BACKUP_DIR/all_$DATE.sql.gz find $BACKUP_DIR -type f -mtime +7 -delete--single-transaction参数告诉mysqldump通过InnoDB的一致性快照来备份,不锁表,生产环境必加。--routines、--triggers、--events保证存储过程、触发器、事件调度器这些对象不会被漏掉。
crontab里加入:
0 2 * * * /bin/bash /usr/local/bin/mysql_backup.sh注意一个安全问题:脚本里有数据库密码明文,这个文件的权限要收一下,chmod 600,至少别让普通用户可读。更讲究的做法是用mysql_config_editor把登录凭据加密存储。
基础巡检我一般就看三个指标:连接数、慢查询、错误日志。连接数用SHOW STATUS LIKE 'Threads_connected';看是否逼近max_connections;错误日志用tail -f /data/mysql/log/error.log看有没有持续的warning;慢查询看slow.log的增量变化。每天花两分钟扫一眼,很多问题都能在发酵之前被掐灭。
5. 常见问题与排查技巧实录
5.1 mysqld报错缺少libaio.so.1
这个错误在BCLinux上非常经典。执行bin/mysqld时直接报:
./mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory原因就是系统缺libaio库。解决方案上面已经给了,yum install -y libaio。补充一个排查思路:不知道缺什么库的时候,用ldd命令把mysqld的依赖全部列出来,然后grep not found,缺哪个装哪个。这个方法对所有二进制软件都适用。
5.2 mysqld启动卡住,error.log却看不到有效内容
遇到不少次这种情况:systemctl start mysqld卡了很久,然后报超时;去查error.log,发现里面啥都没有,或者只有几行无关紧要的启动信息。这时候大概率是数据目录权限出了问题。
典型场景:初始化的时候用了root用户执行mysqld --initialize,导致数据目录里的文件属主全是root。后面用mysql用户启动,mysql进程对数据文件没有写权限,直接卡死。排查命令:
ls -ld /data/mysql/data ls -l /data/mysql/data | head -20看到文件属主是root,就执行chown -R mysql:mysql /data/mysql,然后重启服务。
还有个教训:有一次error.log里确实有报错,但我看的日志路径和实际配置的路径不是同一个文件。检查一下my.cnf里log-error配置的路径,再看看进程实际打开的文件:
ps -ef | grep mysqld | grep -v grep用ls -l /proc/进程号/fd/看进程实际持有的日志文件描述符指向哪里,一查一个准。
5.3 Can't connect to local MySQL server through socket
客户端报这个错,最直接的意思是它没能在socket路径下找到MySQL服务。最常见的两个原因:socket路径不一致、服务其实没起来。
先确认服务状态:
systemctl status mysqld ss -lntp | grep 3306 ss -lx | grep mysql.sock如果服务确实起来了,那基本就是socket路径不匹配。对照my.cnf里面socket配置和客户端默认的socket路径。客户端连接时显式指定socket:
mysql -uroot -p -S /data/mysql/mysql.sock如果是JDBC连接,不会走socket,是用TCP连接的,不受这个影响,但命令行工具很容易踩。
另外别忘了前面讲的PrivateTmp=true的坑。socket配置在/tmp下时会因为systemd的私有目录而被隔离开,客户端连不上,解决办法就是socket别放/tmp,或者去掉PrivateTmp。
5.4 远程能通3306但登录失败:skip-name-resolve的副作用
为了提升连接速度,我在my.cnf里开了skip-name-resolve。这个参数的副作用是:MySQL不会再对客户端IP做反解析成主机名,所以授权账号必须用IP形式表达。
比如业务服务器IP是192.168.1.50,如果你建账号的时候写的host是'app'@'db-server'这种主机名,开启skip-name-resolve之后所有远程连接都会失败,报错是Access denied。解决方式是把授权账号的表达改成IP:
CREATE USER 'app'@'192.168.1.50' IDENTIFIED BY '密码';或者干脆用网段'192.168.1.%'。记住一个原则:开了skip-name-resolve,授权就统一用IP,别再用主机名。
5.5 忘记root密码怎么重置
忘了root密码在测试环境里挺常见的。有个安全风险较高的传统方案是skip-grant-tables,但确实管用。步骤:
- 在/etc/my.cnf的[mysqld]段最后加一行:
skip-grant-tables- 重启MySQL:
systemctl restart mysqld- 此时不需要密码就能登录:
mysql -uroot- 执行更新:
UPDATE mysql.user SET authentication_string=PASSWORD('新密码') WHERE User='root' AND Host='localhost'; FLUSH PRIVILEGES;注意5.7的user表里密码字段是authentication_string,不是5.6时代的password。这一步用的是PASSWORD()函数,8.0里已经废弃了,但5.7没问题。
- 删掉my.cnf里的skip-grant-tables行,重启MySQL。
这一步要特别提醒:skip-grant-tables状态下MySQL完全没有认证,任何人都能免密连进来。这个状态下必须保证3306端口不对公网开放,不然等于裸奔。我在现场重置密码时,会顺手先把业务防火墙策略收紧,只允许本机连MySQL,重置完再恢复。
5.6 问题速查表,建议截图保存
| 问题现象 | 可能原因 | 处理方式 |
|---|---|---|
| ./mysqld: error while loading shared libraries: libaio.so.1 | 系统缺少libaio | yum install -y libaio |
| 初始化时报datadir exists | 数据目录非空 | 清空datadir或改用新目录 |
| 启动后立刻退出,error.log无内容 | 数据目录属主不对 | chown -R mysql:mysql /data/mysql |
| 本机mysql命令连不上socket | socket路径不一致 | 统一socket路径,或用-S指定 |
| 远程连接Access denied | skip-name-resolve下授权用了主机名 | 改成IP网段授权 |
| 中文插入乱码 | 客户端连接字符集与库表不一致 | 连接串加characterEncoding=utf8mb4 |
| 临时密码查不到 | log-error路径和实际不符 | 检查my.cnf,启动时确认日志位置 |
| 密码报错1819 | 不符合validate_password策略 | 调到LOW或改用更强密码 |
最后说点实在的。我在多个BCLinux环境装完MySQL 5.7.34之后最大的感受是:这套系统和CentOS 7真的很亲近,但千万别把它当成CentOS来用。yum源不一样、默认包不全、偶发依赖缺失,每一步都得亲自验证,不能拿别人的文档直接套。我的习惯是把二进制包、依赖rpm、my.cnf模板甚至systemd脚本都收进一个离线部署包里,到现场最多半小时搞定。
再分享一个让我少踩几次坑的小习惯:装完之后,把mysqld --version的输出、show variables like 'character_set_server';的结果、error.log开头几行初始化记录,一起存到部署文档里。MySQL出问题回查的时候,有这份快照能省掉很多瞎猜的时间。