简介MySQL 4.1.11 的 .tar.gz 源码包面向需要在 Linux/Unix 环境部署或研究早期版本数据库的运维、开发与教学人员。整个包体积 21.82MB共 4541 个文件c 与 h 覆盖服务端核心实现cpp、hpp、java 对应各类客户端与工具tcl、test、result 等测试文件便于回归验证configure、makefile、am、ac 等构建脚本构成从环境检测到定制安装的完整编译链路。已有 196 人学习。研读这套源码可以系统掌握旧版 MySQL 的目录组织、编译配置、初始化流程和权限设置思路对需要复现 4.1.11 环境、做源码级兼容性测试或梳理数据库从 4.x 到 8.x 演进的读者包体结构清晰、脚本齐全能显著减少查阅文档与试错成本。源码树还包含客户端程序、嵌入式库、测试套件和多种平台适配脚本基本可还原 4.1.11 时代完整的工程组织可直接用于数据库原理课程的教学案例或实验素材。1. MySQL 4.1.11 源码包老系统的最后一根救命稻草mysql-4.1.11.tar.gz 这个名字放到 MySQL 8.x 满天飞的今天很多新入行的开发已经不认识它了。但如果你接到过老系统维护单就知道这种源码包有多关键一套十几年前上线的业务还在跑 MySQL 4.1官方仓库早就把它归档新装环境没有现成 RPM唯一能复现的就是这份 tar.gz。MySQL 4.1 是 4.0 到 5.0 之间的过渡版本引入了 utf8 字符集、子查询、预处理语句以及从那时起一直沿用到 5.7 的 41 位密码哈希。4.1.11 则是 4.1 系列后期的修订版比早期 4.1.0 修了更多安全问题。这篇文章按我实际拆这个包的流程来写从版本定位、源码结构到离线编译、初始化、故障排查再到新环境下的兼容编译技巧。需要给老应用搭测试环境、做迁移前行为对照或者单纯想读读旧 MySQL 源码的人照着走就能落地。2. 4.1.11 是什么版本定位、源码结构和两个决定成败的特性2.1 版本定位为什么选 4.1.11 而不是 4.1.0MySQL 4.1 在官方版本序列里属于承上启下的位置。4.0 时代还只有 MyISAM 和 Heap 这种简单引擎SQL 能力也比较弱4.1 补上了子查询、UNION、预处理语句这些现代开发者习以为常的东西到了 5.0 才加入视图、存储过程和触发器。所以 4.1 是一道分水岭老应用从 4.0 升级上来语法兼容性就卡在这里。而 4.1.11 是 4.1 分支靠后的修订版本官方在 4.1 系列后期集中修了一批安全漏洞和复制相关的 bug。线下选包的时候经常有人问「MySQL 下载哪个版本」我的习惯是既然必须用 4.1 系列就选最后几个修订版别拿 4.1.0 这种早期 GA 包上生产。下表能直观看出 4.1 在整个演进里的位置版本字符集SQL 能力存储引擎认证哈希4.0latin1 为主utf8 支持极弱基本 SQL无子查询MyISAM / Heap / InnoDB16 位旧哈希4.1引入完整字符集系统utf8 可用子查询、UNION、预处理语句MyISAM 默认InnoDB 可选41 位新哈希兼容旧哈希5.0同上增加视图、存储过程、触发器MyISAM / InnoDB41 位哈希适合用这个版本的场景很具体一是老系统还在线上跑你需要一台与生产完全一致的环境做排障二是准备从 4.1 迁移到新版本迁移前要在隔离环境里跑一遍老 SQL确定哪些语法升级后会变三是拿它当源码教材4.1 的代码量比 8.0 小一个数量级读起来没有心理负担。如果你只是想新写业务别碰它去用 8.0。2.2 源码包里的结构configure、sql/、myisam/、innobase/ 各自干什么解压这个 tar.gz 之后顶层目录一眼能看到 configure、configure.in、sql、client、myisam、innobase、heap、scripts、support-files 这些。configure 是 autoconf 生成的脚本configure.in 是它的源文件。想了解这个版本支持哪些编译选项直接看 configure.in 比看文档更准因为 4.1 时代的文档很多已经打不开了。sql/ 目录是服务端主体mysqld 的入口、词法语法分析、SQL 执行逻辑都在这里。遇见认证协议的问题重点看 sql/ 里密码校验相关代码遇到语法不兼容去 sql/sql_yacc.yy 里查它支持哪些语句。myisam/ 是 MyISAM 引擎的实现heap/ 是内存表innobase/ 是 InnoDB 引擎。注意在 4.1 里 InnoDB 还没有进到统一的 storage/ 插件框架那个是 5.1 以后的事所以它是一个独立的 innobase 目录编译时也是单独处理。client/ 目录下是 mysql、mysqldump、mysqladmin 这些命令行工具的源码。scripts/ 目录里有 mysql_install_db 和 mysql_fix_privilege_tables前者用来初始化数据目录后者用于修复权限表。support-files/ 里有 my-*.cnf 配置模板和 mysql.server 启动脚本后面初始化环境时直接用得到。知道这些目录分工排查问题的路径就清楚了建索引逻辑有疑问去 myisam/ 里找服务端连不上看 sql/ 里的协议处理而不是整包翻。2.3 两个决定成败的特性utf8 字符集与 4.1 新认证协议先说字符集。4.1 之前 MySQL 的字符集体系很简陋latin1 打天下。4.1 第一次引入了 character_set 系统默认还是 latin1但你可以用 default-character-setutf8 让服务端和客户端都走 utf8。这个 utf8 是 3 字节的 utf8不是 8.0 里的 utf8mb4存 emoji 会丢。另一个配套语法是 SET NAMES连接建立后执行SET NAMES utf8可以临时切换连接级字符集不用改服务端配置。再说认证。4.1 之前的密码是 16 位短哈希4.1 开始换成 41 位哈希。服务端 mysql.user 表里的 Password 字段变长认证协议也升级了。这带来一个经典问题4.0 的老客户端连 4.1 服务端会直接报 Client does not support authentication protocol requested by server; consider upgrading MySQL client。为了兼容4.1 服务端提供了 old_passwords 变量。把它打开新建账号时用 OLD_PASSWORD() 生成 16 位哈希老客户端就能连但新客户端连这种旧哈希账号又会失败。这个双轨制是 4.1 特有的后面迁移到 5.7、8.0 时还会遇到类似情况理解了它很多连接报错就不用瞎猜了。3. Linux 离线编译安装configure 参数与三步初始化3.1 依赖与用户准备离线编译这套源码依赖必须提前准备好。核心是 gcc、g、make还有 libncurses5-dev、libreadline-dev 和 zlib1g-dev。ncurses 和 readline 是给 mysql 命令行工具用的缺了 configure 会报找不到 readline 头文件zlib 是给客户端压缩协议用的。离线环境下的做法是找一台能上网的机器把对应发行版的 deb 或 rpm 包下载好然后用 dpkg -i 或 rpm -ivh 安装别等到 configure 报错再临时想办法。接下来创建用户和目录。我习惯先建一个专门的 mysql 系统用户再用它跑服务端。目录规划上prefix 用 /usr/local/mysql数据目录默认放在 prefix 下的 var 子目录。groupadd mysql useradd -g mysql mysql mkdir -p /usr/local/mysql chown -R mysql:mysql /usr/local/mysql这里 useradd 和 groupadd 需要 root 权限后面所有 make install 的操作也是 root 执行。用独立用户跑 mysqld 是 4.1 时代就开始的规范做法避免服务进程拥有过高权限。3.2 解压与 configure参数逐个讲解压后进入源码目录配置之前先处理一个 64 位系统的老毛病。4.1 的代码年代太早在 x86_64 上编译不加 -fPIC链接阶段会报 relocation 错误。所以我一般在 configure 前先把 CFLAGS 和 CXXFLAGS 导出。tar zxvf mysql-4.1.11.tar.gz cd mysql-4.1.11 # 64 位系统必须加 PIC否则后面链接会报 relocation 错误 export CFLAGS-O2 -fPIC export CXXFLAGS-O2 -fPIC ./configure \ --prefix/usr/local/mysql \ --with-mysqld-usermysql \ --with-charsetutf8 \ --with-extra-charsetsall \ --with-unix-socket-path/tmp/mysql.sock \ --with-tcp-port3306 \ --without-berkeley-db逐个解释这些参数。--prefix 指定安装根目录编译产物会分散到 bin、lib、include、share 这些子目录--with-mysqld-user 告诉服务端默认以 mysql 用户运行后面写进 my.cnf 时省事--with-charsetutf8 是把 utf8 作为默认服务端字符集编进去这是 4.1 版本最关键的参数不设的话默认 latin1后面乱码会很头疼--with-extra-charsetsall 带上所有附加字符集容量多占一点但兼容性最好--with-unix-socket-path 把 socket 固定在 /tmp/mysql.sock这是 4.1 客户端程序的默认查找路径--with-tcp-port 指定 3306 端口。最后 --without-berkeley-db 一定要加BDB 引擎在那个时代已经边缘化编译它还要额外依赖 Berkeley DB 库纯属给自己挖坑。configure 执行过程中如果报错留意最后几行输出。缺库会提示 checking for ... no权限问题会说 cannot create ...。解决一个重跑一个不要急着改代码。3.3 make 与 make install编译日志里看什么configure 通过后开始编译这一步在旧机器上可能跑十几分钟。不要一上来就开高并行4.1 的 Makefile 依赖关系写得比较原始make -j4 偶尔会踩到文件没生成完就编译的竞态。我的习惯是先不加 -j 跑确认能过再决定要不要加速。make make installmake 失败时输出的最后几行才是真正的错误信息。常见的失败有几种某个 .cc 文件找不到头文件说明之前 configure 缺了依赖语法错误报在 sql_yacc.cc 之类生成文件里说明 bison 版本不一致还有一种是链接阶段的警告被当错误处理这种一般是新 gcc 环境。看到错误先记下文件路径再回头查对应的 configure 选项别盲目清理重来。make install 完成后ls /usr/local/mysql 看一下bin 下有 mysql、mysqldump、mysqladminlibexec 或 bin 下有 mysqld 主程序。mysql_install_db 这个初始化脚本此时也在 bin 目录下它负责创建 mysql 库、权限表和默认的 test 库。3.4 初始化数据目录、配置 my.cnf、启动并验证安装完成后先执行初始化脚本生成数据目录这是在启动 mysqld 之前必须做的。cd /usr/local/mysql bin/mysql_install_db --usermysql --datadir/usr/local/mysql/var chown -R root . chown -R mysql var初始化脚本会往 var 目录写入一堆系统库文件这一步输出的文本里会带 To start mysqld at boot time you have to copy support-files/mysql.server... 这样的提示照着做就行。chown 这里有个讲究把安装目录整体归 root只有 var 数据目录归 mysql 用户这样即使服务端被人利用能写的也只有数据目录这是老 MySQL 部署的标准权限姿势。然后从源码树里拷贝配置模板再启动服务cd /path/to/mysql-4.1.11/support-files cp my-medium.cnf /etc/my.cnf /usr/local/mysql/bin/mysqld_safe --usermysql sleep 3 /usr/local/mysql/bin/mysqladmin pingmysqld_safe 是 4.1 推荐的启动方式它会以守护进程形式拉起 mysqldmysqld 崩了会自动重启日志写到数据目录下的 .err 文件。sleep 3 是等 mysqld 完成初始化随后 mysqladmin ping 如果返回 mysqld is alive说明服务已经起来了。这一步跑通离线环境里的 MySQL 4.1.11 就算立住了。4. 避坑安装、连接与启动里最容易翻车的五个地方4.1 error 2002找不到 /tmp/mysql.sock现象执行mysql -u root报错ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock。原因分三类mysqld 根本没起来客户端指定的 socket 路径和服务端不一致或者 /tmp 目录权限异常。解决先ps aux | grep mysqld确认进程在不在再看 my.cnf 里 [client] 和 [mysqld] 两段的 socket 是否都指向 /tmp/mysql.sock本机手动连接时显式指定mysql -u root -S /tmp/mysql.sock绕过配置干扰。4.2 Client does not support authentication protocol现象用老版本客户端或老语言库连接 4.1 服务端报Client does not support authentication protocol requested by server; consider upgrading MySQL client。原因4.1 服务端把密码存成了新的 41 位哈希而客户端只认 16 位旧哈希。解决在服务端打开 old_passwords 并把账号密码重设为旧格式SET old_passwords1; SET PASSWORD FOR testlocalhost OLD_PASSWORD(secret);。反过来如果你把 4.1 的库迁移到 5.7老账号密码还是旧哈希新版客户端会连不上需要在目标实例里再执行一次SET PASSWORD PASSWORD(...)重置为新格式。新版 MySQL Workbench 连这个老版本也大概率卡在这我一般直接命令行处理。4.3 64 位系统链接时报 relocation 错误现象make 到链接阶段报错里有relocation R_X86_64_32 against ... can not be used when making a shared object; recompile with -fPIC。原因configure 阶段没加 -fPIC生成的代码在 64 位动态链接时不满足重定位要求。解决回到 3.2 节的步骤先export CFLAGS-O2 -fPIC、export CXXFLAGS-O2 -fPIC然后清干净重新 configure。注意要make clean再重来否则旧的 .o 文件还带着错误编译选项。4.4 新版 gcc 环境下编译到一半报 implicit declaration现象在 CentOS 7 以上或新版 Ubuntu 上编译报implicit declaration of function ...有的版本还直接变成 error 中断。原因4.1 的 C 代码是为 gcc 3.x 写的函数声明不规范新 gcc 默认报错。解决最快的路线是换旧发行版容器gcc 4.4 这类环境里编 4.1.11 基本一把过如果必须在当前机器上编configure 时加CFLAGS-O2 -fPIC -Wno-errorimplicit-function-declaration -Wno-implicit-function-declaration先压制错误个别文件报别的语法则要打补丁后面第 6 章展开。4.5 启动脚本兼容没有 systemd只有 LSB init现象在新系统上执行service mysql start报mysqld.service - LSB: start and stop mysql loaded: /etc/rc.d/init.d/mysql之类混淆错误。原因4.1 源码自带的是 support-files/mysql.server 这种 LSB init 脚本它不生成 systemd unit新系统默认用 systemd 解析 init 脚本时行为会有出入。解决最直接的是不用 service 命令手动跑mysqld_safe --usermysql 确实要开机自启就写一个 systemd unitExecStart 指向 mysqld_safeType 用 forking。等 mysqld 稳定跑起来之后再决定要不要做自启别一开始就卡在 init 脚本兼容性上。5. 配置与使用my.cnf、字符集、库表导入导出实操5.1 my.cnf 最小配置一份可以直接落地的参数4.1 的配置写法跟 5.7、8.0 有差异最典型的是字符集参数。8.0 里用 character-set-server4.1 里叫 default-character-set写错了服务端不认直接静默用 latin1。下面这份是我在 4.1.11 上验证过的最小配置[client] port 3306 socket /tmp/mysql.sock default-character-set utf8 [mysqld] basedir /usr/local/mysql datadir /usr/local/mysql/var port 3306 socket /tmp/mysql.sock default-character-set utf8 skip-external-locking max_connections 100 # 需要 InnoDB 就放开下面两行否则 4.1 默认只跑 MyISAM # innodb_data_file_path ibdata1:10M:autoextend # innodb_buffer_pool_size 256M[client] 段的 default-character-set 决定 mysql 命令行客户端用什么字符集发请求[mysqld] 段决定服务端用什么字符集存数据。两端不一致中文很容易乱。max_connections 在老硬件上别调太大4.1 的线程模型是每个连接一个线程连接数 200 就很吃资源。skip-external-locking 是为了避免 NFS 场景下的外部锁问题本地机器加上无害。关于 InnoDB4.1 时代默认引擎是 MyISAM如果你确需行级锁和事务把 innodb 参数放出来。但注意 4.1 的 InnoDB 版本很旧崩溃恢复能力远不如现在数据量大的库我会优先考虑 MyISAM 加定期备份。如果是为了迁移测试建议跟生产保持一致生产用什么引擎这里就用什么。5.2 导入导出与常用 SQL先知道 4.1 的边界在哪连接服务和基础操作都很直接。root 首次登录不需要密码登录后立刻给 root 设一个避免裸奔。/usr/local/mysql/bin/mysql -u root mysql SET PASSWORD FOR rootlocalhost PASSWORD(your-pass);mysqldump 导出和导入是老套路但字符集参数必须显式交代否则导出连接用 latin1中文全变问号# 导出全部库 /usr/local/mysql/bin/mysqldump -u root -p --default-character-setutf8 --all-databases all.sql # 导入 /usr/local/mysql/bin/mysql -u root -p --default-character-setutf8 all.sql常用 SQL 层面4.1 支持 CREATE TABLE IF NOT EXISTS、多表 UPDATE、ORDER BY 排序、CREATE INDEX这些今天最基础的写法都能跑。举个例子CREATE TABLE IF NOT EXISTS user ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT DEFAULT 0, KEY idx_name (name) ); UPDATE user u, score s SET u.age s.age WHERE u.id s.uid; SELECT * FROM user ORDER BY id DESC LIMIT 10; CREATE INDEX idx_age ON user(age);注意边界4.1 没有存储过程、视图和触发器这些是 5.0 才加入的。如果你的旧应用脚本里出现 CREATE PROCEDURE 或 CREATE VIEW那它根本不是 4.1 环境的产物别在 4.1.11 上浪费时间。另外 4.1 的 MyISAM 只有表级锁没有行级锁并发写入高的时候锁等待明显这也是老应用在新硬件上跑不快的原因之一跟 8.0 完全是两套并发模型。5.3 字符集切换latin1 转 utf8 的处理套路老库最常见的问题是把 latin1 数据转成 utf8。直接 ALTER TABLE 修改列的 charset 往往只改了元数据旧数据里的字节并没有转变照样乱码。正确的处理套路是导出、改写、再导入第一步用 latin1 连接导出mysqldump -u root -p --default-character-setlatin1 --databases mydb mydb_latin1.sql第二步把 dump 文件里的表定义批量替换。用 sed 把DEFAULT CHARSETlatin1改成DEFAULT CHARSETutf8同时把文件头部的 SET NAMES latin1 改成 SET NAMES utf8。sed -i s/DEFAULT CHARSETlatin1/DEFAULT CHARSETutf8/g mydb_latin1.sql sed -i s/SET NAMES latin1/SET NAMES utf8/g mydb_latin1.sql第三步用 utf8 连接导回新库mysql -u root -p --default-character-setutf8 mydb_utf8 mydb_latin1.sql这一步的原理是latin1 读取时把字节原样读出替换表定义后再用 utf8 写入等于把纯字节塞进 utf8 字段中文就能正确显示。跑完以后随机抽查几条记录重点看中文、引号、反斜杠这些特殊字符。这个手法在 4.1 和 5.7 迁移中通用我每次做老库迁移都用它兜底。6. 进阶技巧新环境编译老源码的兼容配置与二进制迁移6.1 编译期兼容参数如果你的机器只有 gcc 9 以上的编译器又不想折腾容器先试这组参数export CFLAGS-O2 -fPIC -Wno-errorimplicit-function-declaration -Wno-implicit-function-declaration export CXXFLAGS-O2 -fPIC -fpermissive-fpermissive 是给 C 部分放松语法检查用的-Wno-implicit-function-declaration 压制 C 代码里最常见的隐式声明报错。这样能推进到链接阶段但个别文件仍可能因为 C 标准变化报错比如某些函数从返回 int 变成 void 导致赋值冲突。这种就要看具体报错文件用编辑器把对应的声明补上。6.2 更稳的路线旧发行版容器里编译拷贝二进制补丁打得越多越不放心我后来的习惯是直接开一个旧发行版容器来编译。CentOS 6 或 Debian 7 这一类环境里的 gcc 版本是 4.x编译 4.1.11 非常顺基本不需要额外改代码。在容器里装好依赖按第 3 章的 configure 参数执行编译完把整个 /usr/local/mysql 打包cd /usr/local tar czf mysql-4.1.11-built.tar.gz mysql/把这份打包文件带到目标机器解压到相同路径即可。MySQL 服务端二进制对 glibc 有依赖目标机器的 glibc 版本最好不低于编译环境CentOS 6 里编出来的二进制放到 CentOS 7 上能跑反过来不行。从那以后我每次拿到老源码包都先看 configure 脚本、确认目标机器的 gcc 和 glibc 版本再决定直接编还是进容器编绝不硬顶着新编译器往上冲。这份 mysql-4.1.11.tar.gz 在离线场景下就是老环境的后悔药建议下载一份存到本地哪天遇到老库救急照着这条路走一遍就活了。希望帮到你。本文还有配套的精品资源点击获取
