简介:这是一份 MySQL 4.1.11 的 .tar.gz 源码包,面向需要在 Linux/Unix 环境编译旧版数据库、排查遗留系统或研究数据库早期实现的技术人员。作为 4.x 分支的代表性发布,包体完整收录服务器、客户端及配套工具的源码与构建脚本。包内共有 4541 个文件,不仅包含 829 个 C 文件、441 个头文件、351 个 C++ 文件与 191 个 cc 文件,还收集了 327 个测试结果、326 个测试脚本、236 个 Tcl 脚本以及大量 automake 配置、Java 代码和 shell 工具,构成从源码、构建系统到回归测试的完整工程目录;压缩包整体约 21.82MB,已有 197 人学习。借助该包可还原 MySQL 4.x 的存储引擎、复制协议与优化器代码,也能通过 configure、make、make install 的典型流程,理解开源软件从源码到可运行服务的编译安装链路。无论是维护遗留系统、排查旧环境兼容问题,还是想对照现代版本梳理数据库核心模块演进,这份资料都提供了直接可用的一手代码和脚本素材。
1. 快二十年的源码包:mysql-4.1.11.tar.gz 现在到底还能拿来干什么
mysql-4.1.11.tar.gz 这个名字,对今天的数据库从业者来说已经相当陌生。MySQL 4.1 系列是整个数据库走向「每字段独立字符集、子查询、预处理语句」的关键一代,而 4.1.11 是那个系列的中期修订版。你现在会遇到这个包,大概率不是想考古,而是手里确实攥着一个没法扔的场景:某台旧服务器的业务库还压在这套版本上,或者交接资料里躺着一个没人敢动的源码包,领导只丢给你一句「看能不能把数据弄出来」。
这篇文章不打算把 4.1.11 讲成博物馆展品。它要解决的是三类实际问题:在老机器上把 4.1.11 重新编译跑起来、把历史数据干净地迁到新版、以及搞清楚这套老代码在今天的 Linux 上为什么要跟你对着干。适合的读者是接手旧系统的一线运维和开发,以及需要用老版本做兼容性验证的人。先立一个结论:4.1.11 在现代环境里能编译、能启动、能导数据,但每一步都要按老规矩来,你越熟悉 5.x 之后的新习惯,踩的坑反而越多。
2. 编译前先给 4.1 老树把脉:工具链与三项关键检查
2.1 为什么 4.1 源码包在今天编译会翻车:三条代际差异
MySQL 4.1 的代码是二十年前的工具链环境下写出来的,今天的编译器和系统库跟它之间隔着三代变化。第一道坎是编译器语法。4.1 的源码里有大量老式 C 写法,部分汇编代码段也是按当时 binutils 的语法写的。新版 gcc 默认开了更严格的告警和更激进的优化,很多当年只是 warning 的代码,现在直接升级成 error 中断编译,最典型的是-fno-strict-aliasing这类优化开关与老代码的冲突。
第二道坎是系统库拆分。老版本 MySQL 依赖的不少函数当年直接由 libc 提供,后来这些函数被拆到独立的库里。最常见的两个:crypt()被拆到 libcrypt,某些老的多线程接口在不同 glibc 版本上签名也有变化。表现就是 configure 检查项失败,或者 make 链接阶段报 undefined reference。
第三道坎是安全组件。4.1 的 SSL 支持绑定的还是 OpenSSL 0.9.x 时代的 API,新版 OpenSSL 把大量老函数废弃甚至直接移除。如果你不加选择地打开--with-openssl,编译到 SSL 相关文件时几乎必挂。所以我在编译老版本时有一条原则:默认不启用 SSL,能用 socket 本地访问的就别折腾 TLS。这不是偷懒,是让 4.1 老老实实跑起来的最短路径。
2.2 环境检查:一条命令摸清 gcc、make、bison、ncurses 与 OpenSSL
动手 configure 之前,先把工具链摸一遍。用下面这组命令检查五个关键项,任何一个缺失或不兼容,都能提前预判,而不是等编译跑到一半才翻车:
echo "--- 编译器 ---"; gcc --version 2>/dev/null | head -1 echo "--- make ---"; make --version 2>/dev/null | head -1 echo "--- bison ---"; bison --version 2>/dev/null | head -1 echo "--- ncurses ---"; pkg-config --exists ncurses 2>/dev/null && pkg-config --modversion ncurses echo "--- openssl ---"; openssl version 2>/dev/nullgcc 和 make 是必须存在的,这不用多说。bison 这个要单独讲:4.1 源码包里其实已经带好了由 bison 生成的sql_yacc.cc和sql_lex.cc,理论上不装 bison 也能编。但 configure 脚本里仍然会检查 bison 是否存在,找不到就报错退出。省事的办法是装上,但注意版本别太新,bison 3.x 处理老式 yacc 语法偶尔会出问题,有条件就装 2.x。ncurses 是另一个硬依赖,老版本 MySQL 的终端交互和密码输入依赖 curses 库,缺了它 configure 会卡在 termcap 检查项上。
OpenSSL 这里要反过来看:如果系统里没有装开发头文件,反而是好事,这能帮你杜绝误开 SSL 编译的念头。如果装了,也不要主动去配置--with-openssl。我见过太多人在这一项上浪费时间,最后发现业务场景根本不走 TLS。
2.3 configure 老式选项与现代习惯的差异:prefix 与引擎
MySQL 4.1 的 configure 是 autoconf 老风格,跟 5.7 之后的 CMake 完全是两个物种。它没有-DWITH_XXX这套写法,全部是--with-xxx和--enable-xxx。而且 4.1 的默认安装路径是/usr/local/mysql,默认数据目录是编译前缀下的var而不是data,这一点和 5.x 之后完全不一样,也是最容易埋坑的地方。
另一个容易忽略的是存储引擎。4.1 时代 MyISAM 是绝对主力,InnoDB 虽然已经集成进标准源码树,但源码包里自行编译时,多个发行版的历史经验都建议在 configure 阶段显式确认 InnoDB 是否启用。如果你拿到的是一个裁剪过的二次打包源码,没准 InnoDB 根本没编进去。我一般会在 configure 命令里显式带上--with-innodb,如果手头这个小版本的 configure 不认识这个选项,说明它默认已内置,忽略即可。这样两种场景都能兜住。
配置前缀时还有个习惯值得坚持:不要装到系统默认目录,单独指定一个业务目录,比如/opt/mysql-4.1.11。老版本卸载本来就不干净,装到独立目录,将来整个目录一删就完事,数据目录在它底下也方便打包迁移。
3. 把 ./configure 到 make install 跑通:最小命令与必调参数
3.1 configure 最小可用命令:禁 SSL、定 charset、选引擎
进入正题。假设你已经把 mysql-4.1.11.tar.gz 解压到了/opt/src/mysql-4.1.11,下面这条命令是我在 64 位 Linux 上编译老版本时最常用的配置组合:
cd /opt/src/mysql-4.1.11 ./configure \ --prefix=/opt/mysql-4.1.11 \ --with-mysqld-user=mysql \ --with-charset=utf8 \ --with-collation=utf8_general_ci \ --with-innodb \ --disable-assembler逐个说参数。--prefix指定安装根目录,后续所有二进制、库文件、man 文档都会装到这里。--with-mysqld-user=mysql让 mysqld 在启动时自动 setuid 到 mysql 用户,避免用 root 跑数据库进程,这是底线。--with-charset=utf8和--with-collation=utf8_general_ci是 4.1 时代设置默认字符集的方式,注意 4.1 里的 utf8 是早期实现,不等于后来 5.5 引入的 utf8mb4,但作为老库的默认字符集,utf8 总比默认的 latin1 实用。
--with-innodb上面说过,是给「InnoDB 没默认编进去」的源码包兜底用的。最值得注意的是--disable-assembler。4.1 源码里有一段针对 x86 的汇编优化代码,当年能提升性能,但在新版本 binutils 上经常因为指令写法过时导致编译失败。在 64 位系统上这段汇编本身就没什么收益,直接关掉最干净。
这里我刻意没有加任何--with-openssl相关参数。老版本 MySQL 默认不会强制链接 OpenSSL,你不主动开启它,就能绕开新版 OpenSSL API 不兼容的坑。如果某些小版本 configure 默认探测到了系统里的 OpenSSL,你可以显式加--without-openssl再试一次,个别版本选项名略有出入,报错不认识的话直接忽略即可。
3.2 make 与 make install:链接期报错的两个兜底方案
configure 顺利结束后,进入编译阶段。这里有一个和现代项目很不一样的地方:建议第一次不要开并行编译,老老实实跑默认串行 make。4.1 的 Makefile 并行处理能力远不如当代构建系统,手动加-j4容易触发莫名其妙的头文件依赖竞态,报出来的错误会让你误判是代码问题还是编译环境问题。
make第一次编译如果顺利通过,后续再想重编可以开make -j2。这个过程在普通服务器上大约需要十几分钟到半小时,取决于机器性能。如果中途报错,绝大多数集中在两个地方。
第一个是链接阶段报undefined reference to 'crypt'。原因是新版系统把 crypt 函数从 libc 挪到了独立的 libcrypt 库。处理方式是重新 configure,在环境变量里补上链接库:
export LIBS="-lcrypt" ./configure \ --prefix=/opt/mysql-4.1.11 \ --with-mysqld-user=mysql \ --with-charset=utf8 \ --with-collation=utf8_general_ci \ --with-innodb \ --disable-assembler make clean make注意LIBS环境变量必须写在 configure 之前,同时要make clean清掉之前一半的编译产物,否则残留的 .o 文件会让你白等一轮。
第二个常见中断点是汇编文件报错。如果你没有加--disable-assembler又刚好在 64 位系统上编译,可能会看到形如Error: suffix or operands invalid for 'movq'之类的提示,这是老汇编语法和 binutils 新语法的冲突。解决方案就是回 configure 阶段补上--disable-assembler,然后同样make clean重来。编译通过后执行make install,安装本身基本不会出问题。
4. 初始化与首启:老版本的数据目录在 var 不在 data
4.1 建用户、搭目录、跑 mysql_install_db
安装完成后,别急着启动。4.1 没有 5.7 之后的--initialize,初始化数据目录靠的是一个独立脚本mysql_install_db。在执行前,先要把系统用户和目录结构准备好,这是老版本最容易忽略的一步:
groupadd mysql useradd -g mysql -s /sbin/nologin mysql mkdir -p /opt/mysql-4.1.11/var chown -R mysql:mysql /opt/mysql-4.1.11 /opt/mysql-4.1.11/bin/mysql_install_db --datadir=/opt/mysql-4.1.11/var --user=mysqlmysql_install_db这个脚本在源码树里的位置是scripts/mysql_install_db,安装后位于bin/mysql_install_db,直接用安装后的路径即可。--datadir指定数据目录,4.1 的习惯是放在安装前缀下的var目录,不是 5.x 常用的data。--user=mysql让初始化脚本以 mysql 用户身份创建系统库和权限表,避免后面 mysqld 因目录属主不对而拒绝启动。
需要说明的是,mysql_install_db创建的是mysql系统库,里面包含user、db、host等权限表,以及test测试库。初始化完成后,root 账号默认没有密码,只能通过本地 socket 连接,这是老版本的安全边界,后面要自己补上密码策略。
4.2 my.cnf 配置:字符集、socket、引擎开关
初始化完成后,写一份最小可用的 my.cnf。老版本对配置项的容忍度和新版本不一样,写多了反而容易触发不识别参数导致启动失败。有些参数在 5.7 里还能用,在 4.1 里直接就报错退出,所以配置文件要尽量克制:
[mysqld] user = mysql basedir = /opt/mysql-4.1.11 datadir = /opt/mysql-4.1.11/var socket = /opt/mysql-4.1.11/var/mysql.sock port = 3306 default-character-set = utf8 skip-locking [client] socket = /opt/mysql-4.1.11/var/mysql.sock default-character-set = utf8这里有两个 4.1 特有的关键点。第一是default-character-set,在 4.1 里它是 mysqld 的合法参数,作用是设定服务端默认字符集,这个参数在 5.0 之后被改名成了character-set-server,你要是拿新习惯给 4.1 写character-set-server=utf8,mysqld 会直接拒绝启动。第二是skip-locking,它关闭外部文件锁。老版本的外部锁在 NFS 和某些现代文件系统上会引发诡异的表损坏问题,直接关掉最省心。
[client]段里的socket必须和[mysqld]段一致。前面说过 4.1 默认把 socket 放在 datadir 下,而客户端默认找的是/tmp/mysql.sock,不一致就会出现 2002 连接错误。在配置文件里统一指定是最稳妥的做法,比每次命令行传-S参数要可靠。
4.3 mysqld_safe 启动、看日志、跑通首次自检
启动 4.1 的服务端,标准入口是mysqld_safe,它会负责拉起 mysqld 进程并在 crash 后尝试重启。启动命令就这么简单:
/opt/mysql-4.1.11/bin/mysqld_safe --defaults-file=/opt/mysql-4.1.11/my.cnf &启动之后别急着连,先确认进程真的活下来了。老版本没有 systemd 托管,也没有现成的 status 命令,我一般用两步自检:
sleep 2 /opt/mysql-4.1.11/bin/mysqladmin -uroot ping /opt/mysql-4.1.11/bin/mysql -uroot -e "SELECT VERSION(); SHOW VARIABLES LIKE 'character_set%';"mysqladmin ping能通说明服务端已经响应请求。第二条命令验证版本号和字符集实际生效值。如果 ping 不通,第一步永远是看错误日志:/opt/mysql-4.1.11/var/目录下的*.err文件,老版本把几乎所有启动失败的原因都写在里面。很多新手习惯先翻系统日志,但 4.1 时代很多发行版根本没有把 mysqld 的输出接到系统日志,还是直接看 err 文件最靠谱。
5. 避坑:老版本在当代内核上运行的五条排查记录
5.1 现象:configure 卡在 termcap / bison 检查项
configure 输出里出现checking for termcap functions library... configure: error: No curses/termcap library found,这是缺 ncurses 开发包。原因很简单:新版发行版默认不装 ncurses-devel,而 4.1 的 configure 又是老式探测逻辑,不会自动适配 pkg-config。解决方法是安装对应开发包,然后把头文件路径显式指给 configure:
export C_INCLUDE_PATH=/usr/include/ncursesw export LIBS="-lncursesw" ./configure ...如果系统里只有 ncursesw(带宽字符支持),上面的路径调整就能让 configure 认出来。bison 报缺的话,装一个 bison 2.x 的版本即可,没有 bison 3.x 环境时也不需要单独编译。
5.2 现象:链接阶段报 undefined reference to crypt
make跑到最后,链接 mysqld 时报undefined reference to 'crypt'。这是因为新版系统的 crypt 函数从 libc 拆到了 libcrypt,而 4.1 的链接参数里只有-lc。解决办法就是前面提过的export LIBS="-lcrypt"再重配。注意这个坑只出现在某些较新的发行版上,老系统本身不会有。判断方法很简单:在有问题的系统上执行grep -l crypt /usr/lib/libc.*,如果找不到符号,基本就是这个原因。
5.3 现象:mysqld 启动即退出,报打不开特权表
错误日志里出现Couldn't find table或者Can't open and lock privilege tables: Table 'mysql.user' doesn't exist,这是典型的没初始化数据目录,或者 mysqld 读到的 datadir 是空的。另一种情况是 datadir 路径配置正确,但目录属主不是 mysql,mysqld 没有权限写文件。解决方法是回头执行 4.1 小节的mysql_install_db,并确认chown -R mysql:mysql覆盖到了整个安装目录和数据目录。启动验证时用ls -la /opt/mysql-4.1.11/var看一眼,mysql目录和test目录存在且属主为 mysql,才是正常状态。
5.4 现象:客户端连接报 2002,找不到 socket
mysql -uroot报ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'。原因不是服务端没起来,而是 mysqld 把 socket 建到了 my.cnf 指定的/opt/mysql-4.1.11/var/mysql.sock,客户端却按默认路径/tmp/mysql.sock去找。解决方式有两种:命令行加-S /opt/mysql-4.1.11/var/mysql.sock,或者像我一样在 my.cnf 的[client]段里固定 socket 路径。这个问题在新版本里不常见,因为新版客户端和服务端默认路径基本一致,但 4.1 年代源码包安装的默认路径就不是 /tmp,所以几乎每个人都会撞一次。
5.5 现象:字符集乱码,SET NAMES 也救不回来
数据写入时正常,SELECT 出来是问号或乱码,执行SET NAMES utf8后部分表变正常、部分表还是乱。原因是 4.1 的字符集是分层的:服务端默认字符集、客户端连接字符集、每个字段自己的字符集,三层可以各自独立。如果表在建表时字段指定了 latin1,连接用 utf8,那查询时会发生双向转换,而转换规则遇到特殊字节就会变成乱码。排查时先看SHOW CREATE TABLE,确认字段的 charset 到底是谁,然后让客户端连接字符集和字段字符集保持一致,不要跨字符集硬来。导出数据时同样按源表的实际字符集来,迁移到新版后再统一转成 utf8mb4 也不迟。
6. 把数据从 4.1 里安全搬走:mysqldump 导出与导入新版的最小流程
6.1 导出命令:固定字符集与版本注释
对绝大多数人来说,让 4.1.11 跑起来不是终点,把里面的业务数据救出来才是。导出用 mysqldump,命令如下:
/opt/mysql-4.1.11/bin/mysqldump \ -uroot \ --all-databases \ --default-character-set=utf8 \ --quick \ --add-drop-table \ > mysql-4.1.11-dump.sql--default-character-set=utf8让导出客户端以 utf8 作为连接字符集,避免在导出过程中发生转码。--quick是逐行读取而不是缓存到内存,老库数据量大时能明显降低内存占用。--add-drop-table在每张表前生成 DROP TABLE 语句,导入时不用手动清理旧表。--all-databases会连带 mysql 系统库一起导出,但老版本权限表结构和新版完全不同,所以我实际迁移时通常不迁 mysql 库,而是只导出业务库,权限账号到新库重建。注意这里不要加--single-transaction,它只在 InnoDB 表上有意义,而 4.1 的数据大概率以 MyISAM 为主。
导出文件开头几行会看到一堆/*!40101 SET ... */,这是 MySQL 的版本条件注释。以40101开头的注释表示「客户端版本大于等于 4.1.1 时执行里面的语句」,所以这份文件在高版本客户端上导入时会自动执行这些设置语句,这是 4.1 时代留下的跨版本兼容设计,不要觉得它们碍事。
6.2 新版导入的兼容点:引擎、字符集与权限表
导入前先检查引擎。在 4.1 端执行SHOW TABLE STATUS FROM 你的库名;,看每张表的 Engine 列。如果只有 MyISAM 和 InnoDB,导入新版基本没障碍。如果出现 BDB 或其他老引擎,新版可能已经移除支持,需要提前把这些表转成 MyISAM 或 InnoDB。
mysql -uroot -p < mysql-4.1.11-dump.sql导入本身就是一个重定向,没有太多参数。真正要注意的是导入后的验证。新版导入 utf8 数据时,8.0 会默认把 utf8 当 utf8mb3,能正常导入,但可能产生告警,不影响数据内容。latin1 表导入 utf8mb4 目标库时,要确认连接字符集,否则原本正常的字节会被当作错误的编码重新解释一次。我的习惯是导入前在 4.1 端记录各业务表的行数,导入后在新端重新执行SELECT COUNT(*)对比,一致才收工。
6.3 收尾习惯:双向 dump 验证法与一条土办法
验证数据完整性,我常用一个土办法:把导入新库的数据再反向 dump 一次,导入一台测试用的 4.1 实例,两边行数对上,才算数据真正无损。这不是信任问题,而是 4.1 到新版之间隔了太多大版本,任何一次字符集或引擎转换的小偏差,行数对不上时一眼就能看出来。具体流程是:
# 新端反向导出 mysqldump -uroot -p --all-databases --skip-lock-tables > verify-dump.sql # 导入测试用 4.1 实例 /opt/mysql-4.1.11/bin/mysql -uroot < verify-dump.sql # 两端执行同样的行数统计,逐表对比对照表按下面三种情况判结果:MyISAM 表行数一致,通过;InnoDB 表行数一致但表空间文件大小明显异常,需要继续检查是否发生了索引重建;字符集列出现乱码,回源头查导出的 charset 与表字段是否匹配。4.1 没有 information_schema,统计行数只能在命令行逐条 count。
我自己的收尾习惯是导出文件永远保留两份,一份原样归档,一份作为导入源,绝不直接用原文件反复测试。老版本数据库的备份恢复本身就玄学成分偏多,多留一份原样文件就是给自己留后悔药。这个方向值不值得投入,我的判断很简单:只要还有业务数据压在这个版本上,把 4.1 的实例干净地拉起来并导出数据,就是成本最低的一条迁移路径。希望帮到你。
本文还有配套的精品资源,点击获取