E. 移植到其他系统 / E.1. 调试MySQL服务器

E.1. 调试MySQL服务器

E.1.1. 针对调试编译MySQL
E.1.2. 创建跟踪文件
E.1.3. 在gdb环境下调试mysqld
E.1.4. 使用堆栈跟踪
E.1.5. 使用日志文件找出mysqld出错原因
E.1.6. 如果出现表格崩溃,请生成测试案例

如果你使用MySQL某些非常新的功能,你可以带--skip-new参数(这个选项禁止掉所有新的潜在不安全的功能)或带 --safe-mode参数(它禁止掉很多可能导致问题的优化设置)来运行mysqld 请参阅A.4.2节,“如果MySQL依旧崩溃,应该做什么”

如果 mysqld 不启动,你应该查证有没有干扰你的设置的my.cnf文件。你可以用mysqld --print-defaults...检查my.cnf参量,并用mysqld --no-defaults来启动去避免它们。

如果mysqld 启动耗尽CPU或内存资源,或者它“挂”了起来,你可以使用 mysqladmin processlist status去找出是否有人执行了一个占用很长时间的查询。如果你正面临着性能问题或新客户端不能连 之时的问题,在某些窗口中运行mysqladmin -i10 processlist status可能是一个好主意。

mysqladmin debug 命令把一些有关使用中的锁,使用的内存以及查询使用的信息转储到MySQL日志文件里。这将有助于解决一些问题。即使你没有为调试编译MySQL,这个命令也提供一些有用的信息!

如果问题是一些表变得越来越慢,你应该试着用PTIMIZE TABLE或myisamchk优化表。I请参阅第5章:数据库管理。你也可以用EXPLAIN检查慢 的查询。 

对那些于你的环境是独特的问题,你也应该查阅这个手册里OS规格的部分请参阅 2.12节,“操作系统系统的注意事项”

E.1.1. 针对调试编译MySQL

如果你遇到一些非常明确的问题,你可以总是试着调试MySQL。要调试MySQL,你必须用--with-debug或--with-debug=full选项来配置MySQL。你可以检查MySQL是否是通过mysqld --help来和调试一起编译的。如果--debug标记和选项一起被列出了,你就可以调试了。在这种情况mysqladmin ver下把mysqld版本列成mysql ... --debug

如果你使用gccegcs,推荐的configure 行如下:

CC=gcc CFLAGS="-O2" CXX=gcc CXXFLAGS="-O2 -felide-constructors \
   -fno-exceptions -fno-rtti" ./configure --prefix=/usr/local/mysql \
   --with-debug --with-extra-charsets=complex

这避免了libstdc++库和C++异常(很多编译器在线程代码里有C++异常的问题)的问题,并编译了一个支持所有字符集的MySQL版本。

如果你怀疑内存溢出错误,你可以用--with-debug=full来配置MySQL,这会安装一个内存分配(SAFEMALLOC)检查器。可是,运行SAFEMALLOC是非常慢的,所以如果你遇到性能上的问题,你应该 用--skip-safemalloc选项启动mysqld。这样禁止掉对调用malloc()和free()的内存检查。

当你用--with-debug编译mysqld时,如果它不再崩溃,你大致已经在MySQL内找到一个编译器缺陷或计时缺陷。这种情况下,你可以试着把-g加到上面的CFLAGSCXXFLAGS变量,并且不使用--with-debug。如果mysqld失败,你至少可以gdb用附着上它或使用核心文件上的gdb去找出发生什么问题。

当你为调试配置MySQL时,你就自动允许许多额外的监视mysqld健康的安全检查函数。如果它们发现一些“不期望”的事,会写一个条目到stderr,safe_mysqld指引这个stderr到错误日志!这也意味着如果MySQL发生什么意外的问题,并且你正使用一个源文件发布版本,那么你要做的第一件事就是去为调试配置MySQL!(第二件事是发邮件到MySQL邮件列表请求帮助)。请参阅1.7.1.1节,“MySQL邮件列表”。请根据你使用的MySQL版本对所有缺陷报告或问题使用mysqlbug脚本!

在Windows MySQL发布包里,mysqld.exe默认编译为支持追踪文件。

E.1.2. 创建跟踪文件

如果mysqld 服务器没有启动或者你可以快速地使其崩溃,你可以创建一个跟踪文件来找出问题。

要这么做的话,你必须有一个编译了支持调试的mysqld 你可以通过执行mysqld -V来检查一下。如果版本号后面跟着-debug,它就是被编译成支持跟踪文件。(在 Windows中,调试服务器被命名为mysqld-debug 而不是象MySQL 4.1 那样的mysqld )。

如下命令,启动带跟踪文件的 mysqld 服务器,跟踪文件位于Unix上的/tmp/mysqld.trace目录里,Windows上 的C:\mysqld.trace目录里:

shell> mysqld --debug

在Windows上,你也可以使用--standalone参数,启动mysqld让它不作为服务。在控制台窗口,使用这个命令:

C:\> mysqld-debug --debug --standalone

完毕之后,你可以使用第二个窗口中的 mysql.exe 命令行工具重新制造问题。你可以用mysqladmin shutdown命令停止mysqld服务器。

注意,跟踪文件会变得很大!如果你想生成一个小一点的跟踪文件,你可以使用类似这样的调制选项:

mysqld --debug=d,info,error,query,general,where:O,/tmp/mysqld.trace

这样就仅把带最感兴趣标记的信息写进跟踪文件里.

如果你生成一个有关于此的缺陷报告,请只用把跟踪文件中的相关行发送到恰当的邮件列表去,那里关注你报告出问题的部分。如果你不能找出哪里出问题,你可以ftp上载整个跟踪文件到ftp://ftp.mysql.com/pub/mysql/upload/,并附有完全的缺陷报告,MySQL开发人员会看到它的。

追踪文件是由Fred Fish用DBUG软件包生成的,请参阅E.3节,“DBUG软件包”

E.1.3. 在gdb环境下调试mysqld

如果mysqld崩溃了,在大多数系统上,你也可是从gdb启动mysqld来获取更多信息。

Linux上,有一些老版本的gdb,如果你想要能调试mysqld线程,你必须使用run --one-threadsome。在这种情况下,你可以一次只激活一个线程。我们推荐你升级到gdb 5.1 ASAP ,这个版本上线程调试工作得更好!

NTPL 线程(Linux上的新线程库)可能会在gdb下运行mysqld时遇到问题。一些症状如下:

  • mysqld 在启动过程中挂起(在它写ready for connections之前)。

  • mysqld 在调用pthread_mutex_lock()或pthread_mutex_unlock()过程中崩溃。

在这种情况下你应该在启动gdb之前在外壳上设置如下环境变量:

LD_ASSUME_KERNEL=2.4.1
export LD_ASSUME_KERNEL

gdb下运行mysqld时,你应该用--skip-stack-trace来禁止堆栈跟踪,以便能捕获gdb内的段错误。

在MySQL 4.0.14和以上版本,你应该对mysqld使用--gdb选项。 这会为SIGINT安装一个中断处理器(需要用^C停止mysqld来设置断点),并且禁止堆栈跟踪和核心文件处理。

gdb没有给旧线程释放内存的整个时间里,如果你做了大量的新连接,在gdb下调试MySQL是非常困难的。你可以通过带 -O thread_cache_size= 'max_connections +1' 启动mysqld 来避免这个问题。在多数情况下,只使用-O thread_cache_size=5'就受益无穷了!

如果mysqld带着SIGSEGV信号死掉了,而你想在Linux上转储核心,你可以带--core-file选项启动mysqld。这个核心文件可以被用来生成 向后跟踪,它可以帮你找出mysqld 为何死掉:

shell> gdb mysqld core
gdb>   backtrace full
gdb>   exit

请参阅A.4.2节,“如果MySQL依旧崩溃,该如何去做”

如果你在Linux上使用gdb 4.17.x 或以上版本,你应该安装一个带有如下信息的 .gdb 文件到你当前目录:

set print sevenbit off
handle SIGUSR1 nostop noprint
handle SIGUSR2 nostop noprint
handle SIGWAITING nostop noprint
handle SIGLWP nostop noprint
handle SIGPIPE nostop
handle SIGALRM nostop
handle SIGHUP nostop
handle SIGTERM nostop noprint

如果你用gdb调试线程遇到问题,你应该下载gdb 5.x版本并用它试一下调试。新版本的 gdb 大大改善了线程处理!

下面是如何调试mysqld的例子:

shell> gdb /usr/local/libexec/mysqld
gdb> run
...
backtrace full # Do this when mysqld crashes

把上面的输入写进一个用mysqlbug生成的邮件里,发送到综合MySQL邮件列表。请参阅1.7.1.1节,“MySQL 邮件列表”

如果mysqld 挂起,你可以试着用一些诸如strace 或 /usr/proc/bin/pstack 这样的系统工具连检查mysqld 在哪里挂起。

strace /tmp/log libexec/mysqld

如果你使用 Perl DBI 接口,你可以使用trace方法或设置DBI_TRACE环境变量来打开调试信息。

E.1.4. 使用堆栈跟踪

在一些操作系统上,如果mysqld意外死掉,错误日志包含一个堆栈跟踪。你可以用它来找出mysqld 在哪里(也许可能找出为什么)死掉。请参阅5.11.1节,“错误日志”。要获得堆栈跟踪,你不能用-fomit-frame-pointer 选项编译mysqld 为gcc。 请参阅E.1.1节,“针对调试编译MySQL”

如果错误文件包含类似下面的一些内容:

mysqld got signal 11;
The manual section 'Debugging a MySQL server' tells you how to use a
stack trace and/or the core file to produce a readable backtrace that may
help in finding out why mysqld died
Attempting backtrace. You can use the following information to find out
where mysqld died.  If you see no messages after this, something went
terribly wrong...
stack range sanity check, ok, backtrace follows
0x40077552
0x81281a0
0x8128f47
0x8127be0
0x8127995
0x8104947
0x80ff28f
0x810131b
0x80ee4bc
0x80c3c91
0x80c6b43
0x80c1fd9
0x80c1686

你可以使用如下步骤找出mysqld在什么地方出现问题:

  1. 复制前面的数字到一个文件,如mysqld.stack。

  2. mysqld 服务器生成符号文件:

    nm -n libexec/mysqld > /tmp/mysqld.sym
    

    注意,多数MySQL二进制发布包("debug" 软件包,包含这些信息的地方就在二进制发布包本身之内)带上述文件,在其中这些文件名为mysqld.sym.gz。在这种情况下,你可以简单地解压缩它:

    gunzip < bin/mysqld.sym.gz > /tmp/mysqld.sym
    
  3. 执行 resolve_stack_dump -s /tmp/mysqld.sym -n mysqld.stack.

    这个命令会打印出mysqld死在哪里。如果这个不能帮你找出mysqld为什么死掉,你应该生成一个缺陷报告,并在缺陷报告里包含上述命令的输出结果。

    注意,尽管在多数情况下,仅有一个堆栈跟踪不能帮助我们找出问题的原因。为了定位缺陷或找到一个大致范围,我们在大多数情况下需要知道杀掉mysqld的查询,并最好知道一个测试案例 ,以便我们能重复问题!请参阅1.7.1.3节,“如何报告缺陷和问题”

E.1.5. 使用日志文件找出mysqld中的错误原因

注意,在带--log选项启动 mysqld之前,你应该用myisamchk检查所有的表。请参阅第5章:数据库管理.

如果 mysqld 死了或挂起,你应该用--log启动 mysqld 。当mysqld 再次死掉时,你可以检查日志文件的最后,找出杀掉mysqld的查询。

如果你不带文件名使用 --log ,日志被保存在名为host_name.log的数据库目录里。在多数情况下,日志文件中的最后一个查询杀掉mysqld,但如果有可能,你应该重启mysqld并从mysq命令行工具执行找到的查询来验证一下。如果这个查询杀掉了mysqld,你也应该测试所有没有完成的复杂查询

你也可以在所有占用长时间的SELECT声明上用命令EXPLAIN来确认 mysqld正适当地使用索引。请参阅7.2.1节,“EXPLAIN 语法(获得关于SELECT的信息)”

你可以带 --log-slow-queries启动mysqld来找出占用长时间来执行的查询。请参阅5.11.4节 ,“缓慢查询日志”

如果你在错误日志文件(通常名为hostname.err)中发现 mysqld restarted 字样,你大致已经找到导致mysqld的查询。如果发生这种情况,你应该用myisamchk检查所有表(参阅 第5章:数据库管理),并在MySQL日志文件中测试这些查询看是否有不执行的。如果找到这样一个查询,试着升级到最新的MySQL版本。如果这样不能帮助你,你不能在mysql邮件存档中发现任何相关内容,你应该把缺陷报告给MySQL邮件列表。邮件列表在http://lists.mysql.com/订阅,这个地址上也有连到在线列表存档的链接。

如果你已经用myisam-recover启动了mysqld,MySQL自动检查并试着修复MyISAM 表,看是否它们被标志为“未正常关闭”或“崩溃”。如果发生这种情况,MySQL在文件hostname.err 写一个条目'Warning: Checking table ...',后面跟着警告:如果表需要修复,请修复它。如果你遇上大量的这些错误而mysqld没有意外死掉,那就是有问题了,需要进一步调查。请参阅5.3.1节,“mysqld命令行选项”

如果mysqld意外死掉,这可不是一个好兆头,但在这种情况下不用研究Checking table...信息,而是要找出mysqld为什么死掉。

E.1.6. 如果出现表崩溃,请生成测试案例

如果在一些更新命令之后,mysqld总是当掉,或者如果你遇到被破坏的表,你可以用下面的操作测试看这个缺陷是否是可重复产生的:

  • 卸掉MySQL守护进程(用mysqladmin shutdown)。

  • 给该表做备份(防止修复操作反而搞坏这种很不可能出现的情况)。

  • myisamchk -s database/*.MYI 检查所有的表,用myisamchk -r database/table.MYI修理有错误的表。

  • 对该表做第二次备份。

  • 如果需要更多的空间就从MySQL数据库目录删除(或移走)旧日志文件。

  • 带--log-bin启动Start mysqld 。请参阅5.11.3节,“二进制日志”。如果你想找出搞mysqld的查询,你应该使用use --log --log-bin。

  • 当你已经遭遇一个被破坏的表时,请停止mysqld server 。

  • 还原备份。

  • 不带--log-bin重启动mysqld 服务器。 

  • 重新执行mysqlbinlog update-log-file | mysql命令。更新的日志用名字hostname-bin.#保存在MySQL数据库目录下。

  • 如果该表再次被破坏,或者你可用上诉命令让mysqld 死掉,你就已经找到可重复产生的缺陷,它应该很容易被修复!可以ftp上传表和二进制日志到 ftp://ftp.mysql.com/pub/mysql/upload/ 然后把它输入我们在 http://bugs.mysql.com/上的缺陷系统。(请注意,/pub/mysql/upload/ 在FTP时是不可以列出(内容)的,所以不能在FTP客户端看见你已经上载的东西。)如果你是一个支持客户,你可以使用 MySQL客户支持中心https://support.mysql.com/ 来 提醒MySQL 技术人员这个问题,让这个问题尽快得到解决。

如果你想缩小问题的范围,你也可以使用 mysql_find_rows脚本来只执行一些 更新语句。