
简介这份 MySQL 5.7.40 Linux 离线安装包面向需要在无外网或受限网络环境中部署关系型数据库的运维、DBA 与后端开发人员基于 glibc2.12、x86_64 架构打包可直接用于内网服务器、测试环境及旧系统兼容场景。压缩包共 379 个文件体积约 646.59MB以 104 个 C 头文件、89 个 so 共享库、25 个 xml 配置/描述文件、9 个 sql 脚本及多组 bin 可执行程序为主覆盖服务端、客户端、备份恢复、初始化与诊断等组件目录层级保留官方二进制发行版结构便于按用途查找和整体迁移。已有约 750 人学习下载说明其在离线部署与版本适配方面具有一定参考价值。对于需要固定 MySQL 5.7.40 版本、减少在线依赖的读者这份包能提供较完整的运行组件与配套脚本可用于搭建实验环境、验证兼容性或作为内网安装的基础文件集合。1. 离线内网里的 MySQL 5.7一个 tar.gz 为什么还能当主角一台不联网的机器一份 mysql-5.7.40-linux-glibc2.12-x86-64.tar.gzMySQL 5.7 离线安装这件事熟练的人十几分钟收工不熟的人能从上午卡到下班。卡点几乎从来不在tar -xzf本身而是三件事系统的 glibc 够不够 2.12、libaio 有没有装、初始化那一刻的参数有没有想清楚。第一件决定 mysqld 能不能被加载第二件决定它能不能起来第三件决定你半年后要不要推倒重装。下面按我在内网机房里的真实顺序走一遍命令可以直接抄参数会说清为什么这么给。2. 先验证这个包能不能在你的机器上跑glibc 2.12、libaio 与 CPU 架构拿到一个离线包最忌讳的动作是直接解压到/usr/local然后./mysqld试一下。二进制包是编译产物它对你机器的运行库有硬性下限达不到的时候报错信息往往只有一行version GLIBC_2.14 not found看着像玄学其实全是可查的确定项。2.1 三条命令摸清系统底子在动手之前把发行版、glibc、架构、内存磁盘一次问清楚。这三条命令加起来不到五秒能省掉后面半小时的猜。# 1) 发行版与内核架构x86_64 是硬要求 cat /etc/os-release uname -m # 2) glibc 版本包名里的 glibc2.12 就是它要求的下限 getconf GNU_LIBC_VERSION ldd --version | head -n 1 # 3) 资源盘点内存决定 buffer pool 给多大磁盘决定 datadir 挂哪儿 free -g df -h /data nprocgetconf GNU_LIBC_VERSION输出的那个数字必须大于等于 2.12。低于这个值没有任何绕过手段唯一的办法是换一个针对更低 glibc 编译的包或者干脆用源码编译。常见的发行版自带的 glibc 大致如下但一定要以机器上的实测为准别信记忆。发行版自带 glibc能否直接跑某企业版 6 系2.12刚好卡线可以某企业版 7 系2.17可以某长期支持版 16.042.23可以某长期支持版 18.042.27可以裁剪过的嵌入式系统常低于 2.12不行换包或重编free -g和df -h不是走过场。innodb_buffer_pool_size要给到物理内存的一半以上机器只有 4G 内存却照着网上的模板抄 8G初始化阶段就会被 OOM Killer 直接干掉而且日志里看不到任何 MySQL 自己的报错只有系统日志里一条 killed process这种翻车最难查。磁盘同理datadir 所在的盘至少留出业务数据量三倍的空间redo、binlog、临时表都在这儿。2.2 libaio 与 ncurses离线环境最容易漏的两个运行库mysqld主程序依赖 libaio 做异步 IOmysql客户端依赖 ncurses 和 tinfo。这两组库在联网机器上一个yum install就完事在内网里就是拦路虎。# 一次性查全缺失项比一条条试错快得多 ldd /usr/local/mysql/bin/mysqld | grep not found ldd /usr/local/mysql/bin/mysql | grep not found # 系统里已有哪些版本 ldconfig -p | grep -E libaio|libncurses|libtinfo如果mysqld那行报libaio.so.1 not found就去同版本的离线源里取对应的包。某企业版 7 系用rpm -ivh libaio-*.rpm某长期支持版用dpkg -i libaio1_*.deb装完执行一次ldconfig再查一遍。客户端的坑更隐蔽一点。较新的系统默认只带libncurses.so.6而 5.7 的客户端是按libncurses.so.5链接的直接跑会报error while loading shared libraries: libncurses.so.5。应急做法是建兼容软链先让命令行工具能用# 应急兼容新版系统只有 .so.6建软链给 5.7 客户端用 ln -s /usr/lib64/libncurses.so.6 /usr/lib64/libncurses.so.5 ln -s /usr/lib64/libtinfo.so.6 /usr/lib64/libtinfo.so.5 ldconfig提示软链只是让客户端能起来长期跑生产建议装系统提供的兼容包别把兼容层当成常态。2.3 解包位置、mysql 用户与目录规划程序目录和数据目录必须分开这是后面能平滑升级的前提。程序放/usr/local下带版本号的目录再用软链指过去数据放独立的/data分区。将来换 5.7 的小版本只要解新包、切软链、重启datadir 一个字节都不用动。# 1) 专用的低权限账号不给登录 shell groupadd -r mysql useradd -r -g mysql -s /sbin/nologin -M mysql # 2) 解包目录名带版本号方便并存多版本 tar -xzf mysql-5.7.40-linux-glibc2.12-x86-64.tar.gz -C /usr/local/ ln -s /usr/local/mysql-5.7.40-linux-glibc2.12-x86_64 /usr/local/mysql # 3) 数据目录规划data/log/tmp/binlog 分开便于单独清理和挂盘 mkdir -p /data/mysql/3306/{data,log,tmp,binlog} chown -R mysql:mysql /data/mysql chmod 750 /data/mysql/3306 # 4) 命令进 PATH echo export PATH/usr/local/mysql/bin:$PATH /etc/profile.d/mysql.sh source /etc/profile.d/mysql.sh这里有两个细节值得强调。第一chown -R mysql:mysql /data/mysql必须覆盖到每一层包括/data本身要有执行权限否则 mysqld 以 mysql 身份运行时连目录都进不去报错是Cant create/write to file看着像文件权限实际是父目录少了x位。第二不要把 datadir 放在/usr/local/mysql/data下面这个目录属于软链目标升级时切换软链极易误删血泪经验。3. 写 my.cnf5.7 的初始化参数一旦落盘就难改配置文件的时机比内容更关键。字符集、表名大小写、redo 大小这几项如果在--initialize之后才想起来改代价是清库重来。所以顺序永远是先定 my.cnf再初始化。3.1 一份可直接改的 my.cnf 模板下面这份是按 16G 内存、纯 OLTP 业务给的中位配置改几个数字就能用。注意[client]段里的 socket 必须和[mysqld]段完全一致这是后面少踩一个坑的关键。[client] port 3306 socket /data/mysql/3306/mysql.sock default-character-set utf8mb4 [mysql] prompt \u\h [\d] no-auto-rehash [mysqld] user mysql basedir /usr/local/mysql datadir /data/mysql/3306/data tmpdir /data/mysql/3306/tmp socket /data/mysql/3306/mysql.sock pid-file /data/mysql/3306/mysqld.pid port 3306 bind-address 0.0.0.0 skip-name-resolve 1 # 字符集初始化前定死之后再改只对新库生效 character-set-server utf8mb4 collation-server utf8mb4_general_ci # InnoDB按内存和磁盘调整 default-storage-engine INNODB innodb_buffer_pool_size 8G innodb_buffer_pool_instances 4 innodb_log_file_size 1G innodb_log_files_in_group 2 innodb_flush_log_at_trx_commit 1 innodb_flush_method O_DIRECT innodb_file_per_table 1 innodb_io_capacity 2000 innodb_io_capacity_max 4000 # 连接层 max_connections 800 max_connect_errors 100000 thread_cache_size 64 table_open_cache 2000 # binlog 与复制 server_id 101 log_bin /data/mysql/3306/binlog/mysql-bin binlog_format ROW sync_binlog 1 expire_logs_days 7 max_binlog_size 512M # 慢日志先开着上线后再调阈值 slow_query_log 1 slow_query_log_file /data/mysql/3306/log/slow.log long_query_time 1 # 错误日志位置必须可写临时密码就写在这里 log_error /data/mysql/3306/log/error.log log_error_verbosity 2 # 其他 lower_case_table_names 1 explicit_defaults_for_timestamp 1 default-time-zone 08:00 sql_mode STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION把这份文件写到/etc/my.cnf权限设成 644、属主 root因为里面暂时没有明文密码。sql_mode这一行是刻意去掉了NO_AUTO_CREATE_USER原因是很多从 5.6 迁过来的老代码依赖GRANT ... IDENTIFIED BY顺带建账号的写法保留这个模式会直接报错。删掉它的副作用是GRANT可以隐式建用户权限收紧要靠流程而不是靠参数这个取舍要心里有数。3.2 innodb_buffer_pool_size、log_file_size 与字符集的取值逻辑参数不是抄来的是算出来的。下面这张表是我实际调参时的判断依据附上给错的后果方便对照自己机器。参数建议取值依据给错的后果innodb_buffer_pool_size物理内存的 50%~70%专用库机器剩下留给连接线程和系统缓存太小磁盘读放大太大触发 OOM 被系统杀innodb_log_file_size512M~2G写入越密集给越大减少 checkpoint 频率太小写性能抖动改大要重启且怕脏关闭innodb_flush_log_at_trx_commit1事务提交必须落盘不丢数据改成 0 或 2 会丢最近一秒事务sync_binlog1与上一条配合保证 binlog 不丢主从切换时从库可能缺事务max_connections应用连接池总和 × 1.3每个连接都要占线程栈和会话 buffer给太大高峰直接内存耗尽table_open_cache2000 起步与 open_files_limit 联动给太大报 too many open filesinnodb_log_file_size有个特别容易被忽略的约束改这个值之后如果上一次是正常关闭重启会自动重建 redo 文件但如果上一次是宕机或强杀mysqld 会拒绝启动并报log file ./ib_logfile0 is of different size。所以我的习惯是初始化前就把这个值定好运行期不再碰它。真要调整先mysqladmin shutdown干净停一次再改配置启动。lower_case_table_names 1是另一个必须在初始化前定的参数。开发同学在本地 Windows 或 Mac 上写 SQL表名大小写混着来到了 Linux 默认值 0 的环境里SELECT * FROM User会直接报Table app_db.User doesnt exist而show tables明明能看到user。5.7 不会在启动时校验这个参数和已有数据是否匹配改了照样能起但已有的大写表名会全部解析不到等于半瘫。字符集则要看业务真实需求。utf8mb4才能存下四字节的字符utf8在 5.7 里其实是 utf8mb3遇到四字节字符会直接截断或报错。新库一律utf8mb4别为了省那点索引空间留隐患。3.3 执行 --initialize临时密码去哪了初始化只做一次做之前先确认 datadir 是空的。--defaults-file必须放在命令的第一个参数位置放在后面 mysqld 会当没看见直接按默认路径找配置然后你会在/var/lib/mysql下面发现一堆莫名其妙生成的文件。# 1) 确认 datadir 为空有残留必须先清掉 ls -A /data/mysql/3306/data # 2) 生成随机临时密码写进 error log生产推荐这种方式 /usr/local/mysql/bin/mysqld \ --defaults-file/etc/my.cnf \ --initialize \ --usermysql # 3) 自测环境可以用空密码方式省一步改密码 # /usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf --initialize-insecure --usermysql # 4) 检查结果没有 ERROR 就算成功然后把临时密码捞出来 tail -n 30 /data/mysql/3306/log/error.log grep -i temporary password /data/mysql/3306/log/error.log--initialize干三件事建 InnoDB 系统表空间和 redo 日志、建mysql系统库里的权限表、创建rootlocalhost并生成临时密码。5.7 默认不再创建test库所以初始化完只有mysql、sys、performance_schema三个库看到库少不要以为出问题了。耗时跟磁盘强相关机械盘上五到十分钟都算正常别因为看着没输出就 CtrlC。一旦中途打断datadir 里就是一堆不完整的文件下次初始化会报data directory has files in it只能清空重来。如果tail出来日志是空的八成是log_error指向的目录 mysqld 没权限写此时它会退回到 stderr 输出日志文件根本不会生成。这种情况加--console前台跑一次所有输出直接打到终端上一眼就能看出真实原因。4. 把实例拉起来systemd 托管、改密码与开远程初始化成功只是把地基打了实例还没跑起来。这一章要解决的是让它作为系统服务稳定运行、把临时密码换成正式密码、让应用能连上。4.1 systemd 单元文件的写法与 Typenotify 的坑很多网上流传的 5.7 unit 文件写的是Typenotify那是给官方 RPM 包用的因为它的 mysqld 编译时带了 systemd 支持。二进制 tar.gz 包不一定带这个特性用Typenotify启动会一直等信号等到TimeoutStartSec超时默认 90 秒然后报 failed而 mysqld 进程其实已经起来了日志里一切正常。这种翻车最费时间。# /etc/systemd/system/mysqld.service [Unit] DescriptionMySQL Server 5.7 Afternetwork.target [Service] Usermysql Groupmysql Typesimple ExecStart/usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf Restarton-failure RestartSec5 LimitNOFILE65535 LimitNPROC65535 PrivateTmpfalse KillSignalSIGTERM TimeoutStartSec300 TimeoutStopSec300 [Install] WantedBymulti-user.targetsystemctl daemon-reload systemctl enable --now mysqld systemctl status mysqld --no-pager几个参数的用意Typesimple表示进程起来就算启动完成最稳PrivateTmpfalse必须显式给否则 systemd 会挂一个私有/tmpsocket 或临时表路径落在那儿时客户端会连不上LimitNOFILE65535决定 mysqld 能打开多少文件句柄table_open_cache设了 2000 但句柄只有 1024启动日志里会警告并悄悄把缓存降下来性能就这样被削掉一截。不写ExecStop是有意的。mysqld 收到 SIGTERM 会自己走干净的关闭流程刷脏页、写 binlog、正常退出。硬写一条mysqladmin -p密码 shutdown等于把明文密码钉在 unit 文件里还多出一个失败点。4.2 拿到临时密码后必须做的四件事用临时密码登录然后逐项确认这四步做完这个实例才算可控。# 用 socket 登录注意 --defaults-file 同样要放第一位 mysql --defaults-file/etc/my.cnf -uroot -p-- 1) 先把临时密码换掉否则什么都干不了 ALTER USER rootlocalhost IDENTIFIED BY StR0ng#Pass_9x2K; -- 2) 看一眼密码强度策略别被复杂度规则反复卡住 SHOW VARIABLES LIKE validate_password%; SET GLOBAL validate_password_policy MEDIUM; SET GLOBAL validate_password_length 12; -- 3) 关掉密码有效期防止半年后应用突然连不上 SHOW VARIABLES LIKE default_password_lifetime; SET GLOBAL default_password_lifetime 0; -- 4) 确认字符集、sql_mode、表名大小写真的按预期生效了 SHOW VARIABLES LIKE character_set_server; SHOW VARIABLES LIKE sql_mode; SHOW VARIABLES LIKE lower_case_table_names;第 3 条值得多说两句。某些老配置模板里带着default_password_lifetime 360含义是账号密码 360 天后过期。到期那天应用侧的报错是ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement连接池里的连接全部失效业务方第一反应是数据库挂了实际只是密码过期。凡是从别处继承来的 my.cnf务必把这一项查出来。用SET GLOBAL改是临时的重启就回退要持久化还是得写回 my.cnf 并重启。validate_password插件同理plugin-load-add validate_password.so要写进[mysqld]段。4.3 应用账号、远程访问与连接串自检生产环境不要用 root 连业务。按最小权限建账号授权范围锁到网段和库级别。CREATE DATABASE app_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 只给业务网段不用 % CREATE USER app_rw10.20.30.% IDENTIFIED BY App#Rw_9x2K; -- 只给 DMLDDL 走变更流程 GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO app_rw10.20.30.%; FLUSH PRIVILEGES;# 在应用机上实测别只在数据库本机验证 mysql -h 10.20.30.11 -P 3306 -u app_rw -p -e SELECT version, hostname, character_set_server; # 防火墙放行若启用 firewall-cmd --permanent --add-port3306/tcp firewall-cmd --reload这里有个和skip-name-resolve直接相关的硬约束开启之后 MySQL 不再做反向 DNS 解析user表里的 host 字段必须是纯 IP 或含%的网段写成主机名app-srv-01会永远匹配不上报Access denied但账号密码明明是对的。这是新人最容易怀疑人生的一类问题。另外bind-address 0.0.0.0只是让 mysqld 监听所有网卡能不能连上还取决于防火墙和云上的安全组两处都要放行。5. 避坑与排查五条真出过事的记录下面这些不是想象出来的场景是内网交付时反复遇到的。每条按现象、原因、解决三段写遇到同类问题可以直接对号入座。5.1 初始化与首次启动阶段的三类硬报错第一条mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file现象是执行 mysqld 任何子命令都直接退出连--version都打不出来看起来像二进制包损坏。原因是系统没装 libaio动态链接器在加载阶段就失败了根本没进到 MySQL 自己的代码里。解决方式是按 2.2 节的做法装对应版本的 libaio 包装完ldconfig刷新缓存再用ldd /usr/local/mysql/bin/mysqld | grep not found复查一遍确保输出为空。第二条[ERROR] --initialize specified but the data directory has files in it. Aborting.现象是初始化直接中止datadir 里确实有东西。原因通常有两种一是这个目录之前装过库没清干净二是上一次初始化跑到一半被 CtrlC 或者被 OOM 杀掉留下了残缺文件。注意不要只rm -rf /data/mysql/3306/data/*就完事隐藏文件比如auto.cnf、.ibd残留不会被通配符匹配到必须用rm -rf /data/mysql/3306/data整个删掉再重建目录然后重新chown mysql:mysql。养成习惯初始化前先ls -A看一眼比事后排查省事。第三条ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock (2)现象是服务明明systemctl status显示 active本地mysql -uroot -p就是连不上报的 socket 路径还是/tmp/mysql.sock。原因是客户端编译时的默认 socket 是/tmp/mysql.sock而服务端在 my.cnf 里配的是/data/mysql/3306/mysql.sock两边对不上。解决方式是在 my.cnf 的[client]段里把socket写成和服务端完全一致的路径或者登录时显式加--socket/data/mysql/3306/mysql.sock。这个坑在写 3.1 节模板时就已经规避掉了但接手别人配好的环境时仍然天天见。5.2 跑起来之后才暴露的两个问题第四条systemd 启动卡满 90 秒然后报 failed但 mysqld 进程其实活着现象是systemctl start mysqld一直挂着最后超时失败可ps -ef | grep mysqld能看到进程手动连也能连上error log 里干干净净。原因是 unit 文件写成了Typenotify而这个二进制包没编译 systemd 的 sd_notify 支持systemd 永远等不到那个我起来了的信号。解决办法是把Type改成simpledaemon-reload之后重启。判断依据很简单如果日志里没有任何报错但服务状态是 failed先怀疑类型声明而不是数据库本身。第五条Table app_db.User doesnt exist但show tables里明明有user现象是某几条 SQL 在生产上报表不存在开发本地完全正常。原因是开发环境是大小写不敏感的Linux 上lower_case_table_names默认是 0表名严格区分大小写。这个问题在初始化之后无法优雅修复只能改 SQL 或者重建库。所以我的做法是初始化前就把lower_case_table_names 1写进 my.cnf从源头上抹平跨平台差异。已经上线的库遇到这个问题短期靠改 SQL 兜住长期排期做一次逻辑导出再导入导入前把参数定好。6. 交付前的一次自检压测、备份和一次真恢复装完能连上不等于能交付。我给自己定的规矩是任何一套新库交给业务之前必须跑一次压测拿基线、做一次备份、再做一次真恢复。三件事加起来不到一小时能挡掉后面 80% 的上线就出事。压测用 sysbench重点不是跑出多高的 TPS而是看基线稳不稳、有没有莫名其妙的抖动。建一个独立的压测库别在业务库上做。# 1) 建压测库与账号 mysql -uroot -p -e CREATE DATABASE sbtest DEFAULT CHARSET utf8mb4; # 2) 准备数据8 张表每张 10 万行 sysbench /usr/share/sysbench/oltp_read_write.lua \ --mysql-host10.20.30.11 --mysql-port3306 \ --mysql-userapp_rw --mysql-passwordApp#Rw_9x2K \ --mysql-dbsbtest --tables8 --table-size100000 prepare # 3) 跑 60 秒8 并发每 10 秒报一次 sysbench /usr/share/sysbench/oltp_read_write.lua \ --mysql-host10.20.30.11 --mysql-port3306 \ --mysql-userapp_rw --mysql-passwordApp#Rw_9x2K \ --mysql-dbsbtest --tables8 --threads8 --time60 \ --report-interval10 run # 4) 收尾清理别把压测数据留在生产库 sysbench /usr/share/sysbench/oltp_read_write.lua \ --mysql-host10.20.30.11 --mysql-port3306 \ --mysql-userapp_rw --mysql-passwordApp#Rw_9x2K \ --mysql-dbsbtest cleanup跑的时候同时开另一个窗口盯SHOW GLOBAL STATUS LIKE Threads_running和慢日志。如果 QPS 波动很大先去看innodb_io_capacity是不是给低了、磁盘是不是机械盘、innodb_buffer_pool_size是不是小于数据集大小。这比对着参数表空想有用得多。备份用 mysqldump几个参数不能省# --single-transaction 给 InnoDB 一致性快照不锁表 # --master-data2 把 binlog 位点以注释形式写进备份做增量恢复的锚点 mysqldump --defaults-file/etc/my.cnf -uroot -p \ --single-transaction --master-data2 \ --routines --triggers --events \ --databases app_db /backup/app_db_$(date %F).sql导出来之后一定要在另一台机器上真恢复一次。我见过太多次备份文件大小正常、恢复时才发现字符集不对或者触发器丢失。--routines和--triggers漏了的话恢复出来的库看着表都在存储过程全没了业务跑到某个定时任务才炸。最后一句掏心窝的话这套流程我前后在十几个内网环境里重复过最容易出问题的从来不是 MySQL 本身而是我以为它已经好了——没验证的软链、没检查的权限、没清干净的 datadir。把 2.1 节那三条命令和 6 章这三步当成肌肉记忆剩下的都是体力活。希望帮到你。本文还有配套的精品资源点击获取