很长一段时间以来,安装LAMP时都是编译安装其数据库(MySQL,MariaDB,Percona Server)的。这是一个非常耗时的过程,而且编译安装的版本,很多插件默认是不会被编译安装的。
偶然间在 MariaDB 的下载列表里,看到有适用于 Linux 下的二进制安装包。于是就研究了一下,发现其二进制包分为 2 个版本:GLIBC_2.14 及 GLIBC_2.14+,解压其安装包后,稍微设置一下就可以直接使用了。
这大大提高了安装效率,安装速度只取决于从服务器下载二进制包的速度。
于是我在脚本里先做判断系统里安装的 GNU_LIBC_VERSION 是多少,2.14 是一道分界线,这也决定着下载链接是怎样的。
一、
MariaDB 5.5, MariaDB 10.0, MariaDB 10.1 一直都是这么安装的,直到 MariaDB 10.2 的出现,让我头疼的一件事出现了。
本来以为只要安装好了 MariaDB 10.2,后面编译 PHP 会非常顺利,结果一开始 configure 的时候就出错了。
checking for mysql_set_server_option in -lmysqlclient... no configure: error: wrong mysql library version or lib not found. Check config.log for more information.
这个错误提示太熟悉了,缺少了 libmysqlclient.so, libmysqlclient.a,于是我就去 MariaDB 安装目录下的 lib 里一看,发现果然跟Percona Server 一样改名了。
看起来 libmariadbclient.a, libmariadb.so.3 这两个文件最相似。
做了 symbolic link 后,将 libmysqlclient.a 指向为 libmariadbclient.a,libmysqlclient.so 指向为 libmariadb.so.3,继续编译 PHP。
果然 configure 没问题了,喜滋滋地等待编译完成。当编译到 mysqli 模块时,异常退出。
mysqli.c: In function 'zm_info_mysqli': mysqli.c:990: error: 'MYSQL_SERVER_VERSION' undeclared (first use in this function) mysqli.c:990: error: (Each undeclared identifier is reported only once mysqli.c:990: error: for each function it appears in.)
What?这难道是 MariaDB 的头文件里未定义 MYSQL_SERVER_VERSION 变量?
去 MariaDB 安装目录下的 include/mysql 里 grep 一下所有的 .h 文件,发现 MYSQL_SERVER_VERSION 被定义在 mysql_version.h 里。
扒了一下 PHP 源代码里的 ext/mysqli 下的 mysqli.c,一路找到 php_mysqli_structs.h 文件里 include 了 my_global.h,又在 my_global.h 里看到 include 了 my_config.h。
最后我在 my_config.h 里确实没有看到 MYSQL_SERVER_VERSION 变量被定义。
然后,我手动修改了一下 my_global.h,在原来 #include 后面加了一行 #include 。然后继续编译 PHP。
果然,不再出现 MYSQL_SERVER_VERSION 变量的问题,然而,结果还是 boom,错误提示如下:
mysqli_nonapi.c: In function 'mysqli_common_connect': mysqli_nonapi.c:266: error: 'MYSQL' has no member named 'reconnect' mysqli_nonapi.c: In function 'zif_mysqli_error_list': mysqli_nonapi.c:448: warning: passing argument 4 of 'add_assoc_string_ex' discards qualifiers from pointer target type
然后,我就发现我搞不定这个问题。
于是我从 LAMP 脚本里去掉了安装 MariaDB 10.2 这个选项。
2017 年 7 月 18 日更新
MariaDB 10.2.7 恢复了 libmysqlclient.so,与 Percona 一样是 symbolic link,同时也在头文件里加入了 MYSQL_VERSION_ID 定义。然而我测试了一下,还是会出现 configure: error: wrong mysql library version or lib not found. Check config.log for more information.
PHP 的编译选项只能从
--with-mysqli=/usr/local/mariadb/bin/mysql_config --with-pdo-mysql=/usr/local/mariadb
改为
--with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd
即 MySQL native driver。
二、
有了二进制安装 MariaDB 的经验,直觉告诉我 MySQL 肯定也有二进制包,去官网一看,果然有的。
Oracle 将 MySQL 的二进制安装包命名为 Linux – Generic。对于 GNU_LIBC_VERSION,只要求 GLIBC_2.5 即可安装(正在开发的 MySQL 8.0 则需 GLIBC_2.12)
安装过程与 MariaDB 类似,唯一的区别是 MySQL 5.7 开始,不再使用 mysql_install_db 来初始化安装数据库,而是使用 mysqld –initialize-insecure 来安装。
与此同时,MySQL 5.7 的默认数据库 mysql 中的 user 表结构也发生了若干变化,去掉了 Password 列,将密码 hash 保存在 authentication_string 列等等。而这又导致了低版本的 phpMyAdmin 如 4.4 无法正确识别数据库里的所有用户。
基本上,Percona Server 遵循着和 MySQL 一样的节奏,比如 5.7 开始使用 mysqld –initialize-insecure 来初始化,除了版本号。
Percona Server 有着自己独特的版本号。在主版本号之外,还有个 rel 版本号。5.5 和 5.6 使用的 rel 版本号带小数点,比如 5.6.36-82.0。而 5.7 则没有,比如 5.7.18-15。
没错,Percona Server 也提供了二进制安装包。但是它也有自己的风格,是不一样的烟火。
Percona Server 对二进制包的 openssl 版本作了严格的区分。比如文件名含 ssl100 适用于所有的 Debian/Ubuntu 版本,而 ssl101 只适用于 CentOS 6 和 CentOS 7。
Percona Server 的个性远不止于此。
你以为解压了这个二进制包就万事大吉了吗?它的安装目录和压缩包名一样长!
安装目录下的 bin/mysqld_safe 和 bin/mysql_config 里也包含了和文件名一样的路径名,而且是 hard path,这就意味着如果直接解压到某个安装文件夹下,是无法初始化和启动的。
必须要先将这两个文件里的路径替换为实际安装路径才可以。
与此同时,安装目录的 lib 目录下,并没有 libmysqlclient.so 和 libmysqlclient.a,而是叫 libperconaserverclient.so 和 libperconaserverclient.a。
因此需要创建 symbolic link 后才能被 PHP 识别和编译。
三、
通过二进制安装数据库后,再通过 phpMyAdmin 登录,你会发现多了一个 Plugins Tab,点进去会显示很多插件,大体上分为 3 类:AUTHENTICATION, INFORMATION SCHEMA, STORAGE ENGINE。而通过编译安装的数据库就没有这些。
这让我想起了一个比喻:微软的 Windows 会直接给你一个蛋糕,你直接吃就好了,没得挑;而 Linux 会给你面粉,鸡蛋,黄油,水,苏打粉等,好了,你去编译一个自定义的蛋糕吧。
事实上,为什么不多提供一个选项,让喜欢吃蛋糕的人,既可以选成品,也可以选用原材料来自己做一个?
二进制包就是这样的一个成品。如果可以的话,其实我也想选择 Apache, PHP 最新版的二进制包,然而并没有,所以只好自己编译了。各个操作系统发行版自带的版本往往都是其中的某个大版本,然后不断更新也不会变,少了很多选择的乐趣。