ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL源码编译实战:从CMake配置到初始化启动全指南

MySQL源码编译实战:从CMake配置到初始化启动全指南 1. 为什么我最终选择了源码编译MySQL先说结论如果你只需要一个能跑的MySQL直接下载官方预编译二进制包或者用系统包管理器安装就够了完全不需要折腾源码编译。但如果你像我一样需要定制特定版本、开启特殊功能特性、针对特定CPU架构做优化或者单纯想搞清楚MySQL到底是怎么构建出来的那么源码编译这条路就绕不过去了。我最初接触源码编译是因为一个生产环境的需求业务需要启用INNODB_PAGE_COMPRESSION特性同时要针对我们服务器的CPU指令集做编译优化而官方通用二进制包默认的编译参数比较保守无法发挥硬件全部性能。查了一圈发现只有自己编译才能灵活控制编译选项于是开始研究这条链路。这里要澄清一个概念误区源码编译不等于从零手写代码。MySQL的核心代码是C/C写的所谓编译是把官方开源的C源码通过工具链翻译成当前平台可执行的机器码。整个过程依赖三样东西源码包、编译工具链主要是编译器、构建系统MySQL使用CMake。这三者的版本兼容性决定了编译成功率的高低。另外想提醒一点源码编译不是一次性的工作而是一个需要理解和维护的过程。编译失败、依赖缺失、配置错误都太常见了你甚至会遇到源码版本和CMake版本不兼容这种让人抓狂的问题。这篇文章会把我踩过的坑、最终的完整操作流程、以及每一步为什么这么做的逻辑全部写清楚。2. 编译前的关键准备环境、依赖与源码版本2.1 系统环境与编译工具链的选型我推荐在CentOS 7.9或Ubuntu 20.04/22.04 LTS这类长期支持版本上做源码编译不建议用太新的系统。原因很现实MySQL 8.x系列源码对编译器的版本有下限要求但太新的系统自带的编译器版本可能过高反而会触发一些新编译器的兼容性警告甚至错误。比如Ubuntu 24.04自带的GCC 13在某些情况下会对MySQL 8.0系的源码抛出格式截断相关的告警虽然有些只是warning但一旦配合-Werror参数就会直接编译失败。我实际使用的组合是CentOS 7.9 GCC 9.3这个组合很稳定。如果你的系统自带编译器版本偏低比如CentOS 7自带的GCC 4.8就需要先升级编译器。升级GCC有两种方式用软件集SCL或者直接源码编译GCC。这里我强烈建议优先用软件集或者第三方仓库的方式安装不要自己源码编译GCC因为编译GCC本身又需要GCC这种循环依赖会让人崩溃。在Ubuntu上准备工作相对简单直接执行sudo apt update sudo apt install -y gcc g make cmake pkg-config \ libncurses5-dev libncursesw5-dev libssl-dev \ libaio-dev libnuma-dev libtinfo-dev bison \ flex perl libldap2-dev libsasl2-devCentOS上的对应安装命令是sudo yum install -y gcc gcc-c make cmake bison flex \ ncurses-devel openssl-devel libaio-devel numactl-devel \ perl perl-devel2.2 依赖包的版本与作用我踩过哪些坑先厘清一个关键问题编译MySQL到底需要哪些核心依赖它们各自做什么依赖包作用缺失时的典型错误CMake构建系统核心负责检测环境、生成MakefileCMake Error: Could not find cmakeGCC/GC和C编译器MySQL服务端核心是C写的无法编译源码报错找不到编译器Bison/Flex语法分析器生成工具用于构建SQL解析器致命错误bison: command not foundNCurses终端文本界面库MySQL命令行客户端依赖它找不到ncurses头文件OpenSSLTLS/SSL加密支持MySQL 8.0中必须显式指定Could NOT find OpenSSLlibaio异步IO库InnoDB引擎依赖找不到libaionumactl-develNUMA内存分配策略支持可选依赖但建议安装有一个坑我必须单独拎出来讲在CentOS上编译MySQL 8.0.x时OpenSSL的版本特别讲究。CentOS 7默认的OpenSSL是1.0.2而某些MySQL 8.0的小版本要求OpenSSL 1.1.1或更高。这时候如果不额外编译安装新版OpenSSLCMake阶段就会直接报错Could NOT find OpenSSL。解决方案是手动编译安装OpenSSL 1.1.1然后通过Cmake参数指向它的路径。还有一个很多人会忽略的点磁盘空间和swap配置。源码编译MySQL需要至少10GB可用磁盘空间因为源码包解压、编译的中间文件、安装目录都需要容量。另外编译过程中内存消耗很高如果你用make -j$(nproc)并行编译8G内存的机器可能直接OOM内存溢出。建议在编译前确认swap至少配置了4G或者减少并行编译的进程数。2.3 源码版本怎么选不建议追最新版这里要给一个非常中肯的建议源码编译不要用最新版本用最新版本的前一个或者两个稳定版。为什么因为MySQL每个大版本比如8.0、8.4、9.x的源码结构会有调整而新版本发布初期第三方依赖的兼容性问题还没完全暴露。比如MySQL 8.4发布后官方调整了部分组件如MySQL Shell、MySQL Router的构建方式导致很多人在编译时卡在了组件阶段而编译8.0.36或者8.0.40这些成熟版本就非常顺滑。我实际编译的是MySQL 8.0.40这个版本在源码编译社区里口碑很好兼容性广CMake要求最低3.7GCC要求5.3大部分系统的默认工具链都能满足。选版本的时候建议去MySQL官方下载页的源码归档区这里有所有历史版本的源码包注意区分源码包Source Code和二进制包Binary源码包通常是.tar.gz格式命名含Source字样。下载命令示例wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.40.tar.gz tar -xzvf mysql-8.0.40.tar.gz cd mysql-8.0.403. MySQL源码编译完整实操从CMake到make install3.1 CMake配置阶段——决定编译结果的关键一步CMake是整个编译过程中最核心、最重要的阶段。可以这样理解CMake不编译代码而是根据你给的参数生成编译指令。它做的事情包括检查所有依赖是否存在、版本是否达标、根据参数决定启动哪些功能比如InnoDB的某些特性、是否编译测试组件、确认安装路径、最终生成Makefile文件。这个过程会把检查结果打印在屏幕上如果任何关键依赖缺失直接终止并报错。我使用的CMake配置命令如下直接抄作业的话只需按自己的环境修改安装路径和相关依赖路径cmake .. \ -DCMAKE_INSTALL_PREFIX/usr/local/mysql \ -DMYSQL_DATADIR/usr/local/mysql/data \ -DSYSCONFDIR/etc \ -DWITH_BOOST/usr/local/boost \ -DFORCE_INSOURCE_BUILD1 \ -DDOWNLOAD_BOOST1 \ -DWITH_SSLsystem \ -DWITH_READLINEON \ -DWITH_INNODB_MEMCACHEDOFF \ -DWITH_MYISAM_STORAGE_ENGINE1 \ -DWITH_INNOBASE_STORAGE_ENGINE1 \ -DWITH_PARTITION_STORAGE_ENGINE1 \ -DWITH_ARCHIVE_STORAGE_ENGINE1 \ -DWITH_BLACKHOLE_STORAGE_ENGINE1 \ -DENABLE_DOWNLOADS1 \ -DCMAKE_BUILD_TYPERelease下面把几个关键参数逐一解释清楚这点很重要不要稀里糊涂地复制粘贴-DCMAKE_INSTALL_PREFIX指定安装路径。默认MySQL官方推荐的是/usr/local/mysql如果你安装在别的路径后续所有相关配置比如系统服务脚本、环境变量、配置文件里的路径都要做相应调整。我强烈建议在生产环境固定为/usr/local/mysql这是一个约定俗成的标准路径后续无论是找文件还是排查问题都会方便很多。-DMYSQL_DATADIR数据目录。这是MySQL存储所有数据库文件和日志的目录初始化的时候必须存在并且有正确的权限。-DWITH_BOOST和-DDOWNLOAD_BOOST1MySQL 8.0的源码包不再捆绑Boost C库而源码在编译时需要Boost。第一次配置CMake时会报错提示找不到Boost这时需要在配置命令中加入这两个参数。-DDOWNLOAD_BOOST1表示允许CMake自动下载指定版本的Boost到本地-DWITH_BOOST指定Boost的存放路径。CMake会自动检测系统已安装的Boost版本是否能满足当前MySQL源码的需求如果没必要也可以不指定。这里有一个非常坑的地方MySQL每个小版本对Boost版本有具体要求比如8.0.36要求1.778.0.40要求1.77。如果你系统中已经装了更高版本的BoostCMake可能不会自动识别而是要求你指定路径。最稳妥的做法是按官方要求让CMake自己下载匹配版本。-DWITH_SSLsystem指定使用系统自带的OpenSSL库而不是源码包自带的。如果你在前面环境准备时发现系统OpenSSL版本过旧低于1.1.1就必须先升级OpenSSL或者直接指定-DWITH_SSL/path/to/your/openssl。这里不要心存侥幸跳过因为MySQL 8.0在启动时检查SSL配置如果编译时SSL功能缺失启动后很多加密连接相关的功能都会异常。-DWITH_READLINEON启用readline库支持。它给MySQL命令行工具提供命令行历史记录、自动补全等功能。如果不启用连接数据库后你会发现连不上上下键翻历史命令调试起来极其痛苦。-DCMAKE_BUILD_TYPERelease编译模式设置为发布版。区别于Debug模式Release模式会开启编译器的优化选项比如-O2、-O3生成的二进制文件运行效率更高。调试模式下生成的文件带大量调试符号体积大、运行慢不适合生产。执行CMake之后输出日志会滚动很长时间。你要重点观察这几行-- MySQL 8.0.40-- Packaging: rpm或deb取决于你的系统-- Library ABI: ...-- CMAKE_BUILD_TYPE: Release-- CMAKE_INSTALL_PREFIX: /usr/local/mysql如果中间出现红色的CMake Error照着错误信息逐条排查。最常见的错误基本就是依赖缺失或版本不匹配按照2.2节表格里的对应关系补装就行。3.2 编译与安装make -j参数怎么定耗时多久CMake成功后源码目录下会生成Makefile文件。接下来就是真正吃CPU和内存的编译阶段。编译命令很简单make -j$(nproc)-j参数指定并行编译的进程数。$(nproc)会读取当前CPU核心数理论上每个核心分配一个编译任务是最快的但要注意内存瓶颈。每个编译进程大约消耗1~1.5G内存如果你的机器是4核8G内存跑make -j4有大概率内存耗尽。我的建议是内存16G以上用-j$(nproc)内存8G用-j2或者-j4内存不够就老老实实-j1慢一点总比OOM后重来要好。我实测在一台配置为8核16G内存的云服务器上编译MySQL 8.0.40全程约25~35分钟取决于磁盘IO和CPU主频。在这期间屏幕会疯狂滚动编译日志看到[ 80%]、[ 90%]这样的进度条就可以泡杯茶等着了。编译过程中有几个标志性组件阶段可以特别关注sql/CMakeFiles/sql.dirSQL层核心逻辑C编译最重的一块storage/innobase存储引擎层编译耗时较长mysqld生成最终服务端可执行文件的链接阶段如果内存不足最容易在这里挂编译过程中如果报错日志会直接停止在你需要关注的文件上。此时先不要急着百度先往前翻几十行日志通常真正的报错消息和编译器参数、文件路径、错误类型都在这个位置。比如如果报错cc1plus: out of memory那就意味着真OOM了你需要减少并行编译数而不是去改源码。如果make成功执行完你会看到类似[100%] Built target ...的输出。此时执行安装sudo make installmake install会把编译产物安装到CMAKE_INSTALL_PREFIX指定的目录这里就是/usr/local/mysql。这一步主要是文件复制和基本目录结构创建速度很快基本在1分钟以内完成。安装完成后/usr/local/mysql下会生成bin、lib、include、share、support-files等目录。一个小技巧在make阶段如果因为某个小问题失败修复后可以重新执行make它默认是增量编译不会全部重来只编译上次失败之后的文件。4. 初始化与启动让源码编译的MySQL真正跑起来4.1 修改权限、创建配置文件与数据目录初始化源码编译安装完成的MySQL本质上是“半成品”还需要初始化数据目录、配置系统服务才能使用。一开始/usr/local/mysql/data目录是空的我们第一步要做的是创建MySQL运行时的系统用户并设置目录归属sudo groupadd mysql sudo useradd -r -g mysql -s /bin/false mysql sudo chown -R mysql:mysql /usr/local/mysql这里需要说明一下MySQL官方在初始化时特意要求数据目录的所有者是mysql用户不能是root这样即使mysqld进程被攻破也无法直接以root权限操作系统文件这是基本的安全措施。然后创建配置文件/etc/my.cnf这是控制MySQL运行行为的关键文件。一个最简可用的配置如下[mysqld] user mysql basedir /usr/local/mysql datadir /usr/local/mysql/data port 3306 socket /tmp/mysql.sock pid-file /usr/local/mysql/data/mysql.pid log-error /usr/local/mysql/data/error.log [client] port 3306 socket /tmp/mysql.sock如果你是按我的CMake命令配置的-DSYSCONFDIR/etcMySQL启动时会自动到/etc/my.cnf读取这个配置文件。注意basedir和datadir必须和CMake配置时保持一致否则启动会报路径错误。初始化数据目录MySQL 8.0用mysqld --initialize命令代替了旧版的mysql_install_dbsudo /usr/local/mysql/bin/mysqld --initialize-insecure --usermysql --basedir/usr/local/mysql --datadir/usr/local/mysql/data这里我用的是--initialize-insecure它表示初始化数据目录时不生成随机root密码root账号默认是空密码。这个参数对调试阶段极方便但生产环境建议直接用--initialize默认方式它会生成一个临时随机密码打印到日志中首次登录需要强制改密码。初始化过程很快几秒钟执行完后检查/usr/local/mysql/data目录下是否生成了mysql、sys、performance_schema等系统数据库目录如果生成了说明初始化成功。4.2 启动mysqld与设置环境变量初始化完成后可以尝试手动启动MySQL服务验证一下安装是否成功sudo /usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf --usermysql 启动后用以下命令确认进程和监听状态ps -ef | grep mysqld ss -lntp | grep 3306如果进程存在且3306端口在监听说明服务已经起来了。此时就可以连接测试了/usr/local/mysql/bin/mysql -uroot -p由于初始化时使用--initialize-insecure直接输入空密码回车就能登录。登录成功后执行select version();如果输出版本号如8.0.40恭喜源码编译的MySQL已经跑起来了。这里我强烈建议在登录后立刻做两件事设置root密码ALTER USER rootlocalhost IDENTIFIED BY 你的密码;创建远程访问用户如果业务需要远程连接CREATE USER app% IDENTIFIED BY 密码; GRANT ALL PRIVILEGES ON *.* TO app%; FLUSH PRIVILEGES;然后是环境变量的配置。每次都用全路径/usr/local/mysql/bin/mysql实在太麻烦了把MySQL的bin目录加到PATH里echo export PATH/usr/local/mysql/bin:$PATH ~/.bashrc source ~/.bashrc4.3 注册为systemd服务实现开机自启手动启动只能管当下这一会儿服务器重启之后还得再手动启动太不方便了。我推荐把MySQL注册为systemd服务。在/etc/systemd/system/mysqld.service中创建如下内容[Unit] DescriptionMySQL Server Afternetwork.target [Service] Typeforking Usermysql Groupmysql PIDFile/usr/local/mysql/data/mysql.pid ExecStart/usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf --daemonize ExecReload/bin/kill -s HUP $MAINPID ExecStop/bin/kill -s TERM $MAINPID Restarton-failure [Install] WantedBymulti-user.target这段配置有几个关键点要说明Typeforking因为mysqld --daemonize启动时会先fork一个主进程然后主进程退出真正的MySQL在后台运行。这和mongod、nginx等服务的启动方式一样。--daemonize告诉mysqld启动后转入后台守护进程模式。PIDFile指定PID文件路径systemd通过它来定位主进程做状态监控和停止操作都依赖这个文件。保存后依次执行sudo systemctl daemon-reload sudo systemctl enable mysqld sudo systemctl start mysqld sudo systemctl status mysqld看到Active: active (running)就说明服务已正常运行并且已设置开机自启。此时可以关掉之前手动启动的实例如果还在跑避免端口冲突。5. 编译与运行常见问题排查实录5.1 CMake阶段的高频报错与解决方案源码编译过程中CMake阶段和make阶段遇到的问题几乎占了90%以上。我把碰到的高频问题整理成速查表方便你有针对性地查找错误提示原因解决方法Could NOT find Boost缺少Boost库或版本不匹配在CMake命令中加入-DDOWNLOAD_BOOST1 -DWITH_BOOST/usr/local/boostCould NOT find OpenSSL系统OpenSSL缺失或版本过低安装OpenSSL开发包或编译最新版并用-DWITH_SSL/path指定bison: command not found缺少bison解析器生成工具apt/yum安装bisonNo usable C compiler缺少gcc/g编译器安装build-essential或gcc-c包Found c compiler but is missingGCC版本太低升级GCC推荐使用软件集或源码编译GCC这些错误其实都在CMake的输出日志中有明确提示。我见过很多人不看CMake日志直接在网上搜“MySQL编译失败”那是非常低效的做法。正确姿势是报错后翻日志先看CMake Error后面跟的是哪一行的提示信息再针对性检查。这里分享一个独家的排查方法CMake日志的CMakeCache.txt文件是宝藏。每一次CMake配置结束后源码目录下都会生成CMakeCache.txt文件里面记录了你使用的所有参数、检测到的所有依赖版本。当第二天重新编译时突然报依赖缺失先查看这个文件往往能发现依赖变量的值被上一次配置锁定了比如WITH_SSL_DIR:PATH/usr/local/ssl被写死而你后来把OpenSSL换地方了CMake还在找老的路径。5.2 make阶段内存不足与编译器报错make阶段最常见的坑就是内存不足。之前提到过cc1plus: out of memory这个报错出现时很多人以为代码有问题实际上就是内存不够。遇到这种情况把make -j8改成make -j2同时关掉其他占内存的服务基本都能解决。另一个比较多见的是编译器原生错误比如error: shared_ptr is not a member of std。这种情况通常是GCC版本低于源码要求。排查方式很简单gcc --version看版本号如果低于5.3就需要升级编译器。这里再强调一次升级编译器不要用系统的包管理器默认源因为默认源版本往往还是旧的比如CentOS 7默认源是GCC 4.8要换用软件集或外部源。make阶段编译到100%后如果提示某个动态库链接失败undefined reference to symbol这类错误比较棘手多半是相应功能依赖的库不完整。比如MySQL 8.0默认启用ICU库Unicode支持如果你系统缺少ICU开发包就会在链接阶段报错。安装libicu-devUbuntu或libicu-develCentOS后重新编译即可。5.3 启动阶段的SELinux与端口问题CentOS专项如果你在CentOS上运行源码编译的MySQL启动时还有一个隐藏大坑SELinux。CentOS默认开启了SELinux它限制mysqld读取非标准目录的文件。即使你按照前面步骤做了权限配置启动时依然可能报Permission denied。最简单的处理方式是把SELinux设为permissive宽容模式sudo setenforce 0如果确认是SELinux的锅并且不想全局关闭生产环境不建议全局关闭可以用audit2allow工具生成放行策略但操作复杂度高一些这里不展开。除此之外启动阶段还可能遇到bind: Address already in use错误说明3306端口被占用通常是残留的mysqld进程或其它MySQL实例。用ss -lntp | grep 3306找到进程并处理即可。还有一个特别容易被忽略的问题磁盘空间不足。InnoDB引擎初始化时会预分配表空间文件如果你的磁盘剩余空间不足2G初始化必失败错误信息是No space left on device排查时先看df -h。5.4 编译安装后的性能问题与调优建议很多人以为源码编译本身就是“性能优化”其实不然。如果你只是默认参数编译性能和不加参数下载二进制包没有本质区别。编译阶段真正影响性能的是Cmake配置时的-DCMAKE_BUILD_TYPERelease开启编译器优化以及后续的C编译优化选项如-O3、-marchnative。其中-marchnative是一个很值得关注的参数它让编译器针对当前机器CPU的指令集进行优化能在运行时获得一定的性能提升。但这个参数有一个严重的约束条件——编译出的二进制只能在相同或更高指令集版本的CPU上运行。如果你把编译好的MySQL迁移到更老CPU的机器上会直接报“非法指令”错误。因此生产环境不建议启用-marchnative应该明确指定指令集级别比如-marchx86-64-v2保证可移植性同时兼顾优化。运行时配置也直接影响性能。默认my.cnf里innodb_buffer_pool_size是128MB这对于一台专门跑MySQL的服务器是远远不够的。根据经验建议将它设为你物理内存的60%~70%比如16G内存设置innodb_buffer_pool_size 10G。另外max_connections默认151在很多并发高的场景也不够用可适当调高。有一点需要额外注意源码编译出来的默认my.cnf并没有包含这些优化项你需要自己手动写入这也是很多人在“源码编译安装”之后觉得“还不如直接用二进制包”的一个重要原因。不是编译本身的问题是你没把配置补上。最后再分享几个我个人的实战体会源码编译这条路上我踩过的坑比经验多。最难受的其实不是编译报错而是编译成功之后系统一切正常你却不知道它到底比二进制包好在哪里。所以如果你打算折腾源码编译建议想清楚自己的目标如果是为了性能优化要能把编译参数的变化量化成可观测的指标比如TPCC、sysbench压测结果提升了多少如果是为了学习那就完整走一遍流程把CMake的每个参数吃透。最后分享一个小技巧当你编译多个版本或反复编译时每个版本的工作目录建议单独建一个比如/opt/build/mysql-8.0.40/、/opt/build/mysql-8.4.11/。这样每次编译都是在干净目录中进行CMakeCache不会串排查问题也容易定位。我见过太多人习惯把不同版本源码解压到同一目录反复折腾结果CMakeCache文件互相污染报错信息千奇百怪却查不出根源。目录隔离这个习惯能让你的源码编译体验顺畅一大半。
返回列表