🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 附录A:问题和常见错误 ** 目录** [ A.1. 如何确定导致问题的原因](#)[ A.2. 使用MySQL程序时的常见错误](#)[A.2.1. 拒绝访问](#)[A.2.2. 无法连接到[local] MySQL服务器](#)[A.2.3. 客户端不支持鉴定协议](#)[ A.2.4. 输入密码时出现密码错误](#)[A.2.5. 主机的hos*t_name被屏蔽*](#)[A.2.6. 连接数过多](#)[A.2.7. 内存溢出](#)[A.2.8. MySQL服务器不可用](#)[A.2.9. 信息包过大](#)[ A.2.10. 通信错误和失效连接](#)[A.2.11. 表已满](#)[A.2.12. 无法创建文件/写入文件](#)[A.2.13. 命令不同步](#)[A.2.14. 忽略用户](#)[A.2.15. 表tbl_na*me不存在*](#)[A.2.16. 无法初始化字符集](#)[ A.2.17. 文件未找到](#)[A.3. 与安装有关的事宜](#)[ A.3.1. 与MySQL客户端库的链接问题](#)[ A.3.2. 如何以普通用户身份运行MySQL](#)[ A.3.3. 与文件许可有关的问题](#)[ A.4. 与管理有关的事宜](#)[ A.4.1. 如何复位根用户密码](#)[ A.4.2. 如果MySQL依然崩溃,应作些什么](#)[ A.4.3. MySQL处理磁盘满的方式](#)[ A.4.4. MySQL将临时文件储存在哪里](#)[ A.4.5. 如何保护或更改MySQL套接字文件/tmp/mysql.sock``](#)[ A.4.6. 时区问题](#)[ A.5. 与查询有关的事宜](#)[ A.5.1. 搜索中的大小写敏感性](#)[ A.5.2. 使用DATE列方面的问题``](#)[ A.5.3. 与NULL值有关的问题``](#)[ A.5.4. 与列别名有关的问题](#)[ A.5.5. 非事务表回滚失败](#)[ A.5.6. 从相关表删除行](#)[ A.5.7. 解决与不匹配行有关的问题](#)[ A.5.8. 与浮点比较有关的问题](#)[ A.6. 与优化器有关的事宜](#)[ A.7. 与表定义有关的事宜](#)[ A.7.1. 与ALTER TABLE有关的问题](#)[ A.7.2. 如何更改表中的列顺序](#)[A.7.3. TEMPORARY TABLE问题](#)[ A.8. MySQL中的已知事宜](#)[ A.8.1. MySQL中的打开事宜](#) 在本附录中,列出了一些你可能会遇到的常见问题和错误消息。并介绍了确定故障原因的方法,以及为解决问题所应采取的措施。 ### A.1. 如何确定导致问题的原因 当你遇到问题时,首先要做的是找出导致问题的程序和设备部件: ·         如果遇到下述征兆之一,或许是因为硬件问题(如内存、主板、CPU或硬盘)或内核问题: 1.    键盘不工作。正常情况下可通过按Caps Lock建进行检查。如果Caps Lock的点亮状态未改变,就需要更换键盘(在此之前,应尝试重启计算机,并检查与键盘相连的所有电缆)。 2.    鼠标指针不移动。 3.    机器未对远程机器的Ping命令做出应答。 4.    与MySQL无关的其他程序工作不正常。 5.    系统意外重启(有问题的用户级程序应不能使系统出现严重问题)。 在该情况下,应检查所有的电缆并运行某些诊断工具,对你的硬件进行检查!此外,还应检查是否有能够解决问题的、适用于你的操作系统的补丁、更新或服务包。请检查所有的库(如glibc)是否是最新的。 使用配备ECC内存的机器以便尽早发现问题总是个好主意。 ·         如果键盘已锁定,可从另一台机器登录到你的机器,并执行kbd_mode –a,或许能恢复。 ·         请检查系统的日志文件(/var/log/messages或类似物)以找出问题的原因。如果你认为问题出在MySQL中,应检查MySQL的日志文件。请参见[5.11节,“MySQL日志文件”](# "5.11. The MySQL Log Files")。 ·         如果你不认为存在硬件问题,应尝试找出导致问题的原因。请使用**top**、**ps**、任务管理器或类似程序,以检查哪个程序占用了所有CPU时间或锁定了机器。 ·         使用**top**、**df**或类似程序检查是否内存不够、磁盘空间不足、文件描述符缺乏、或其他关键资源缺少。 ·         如果问题是失去控制的进程,应尝试杀死它。如果杀不死进程,或许是因为操作系统中存在缺陷。 如果在检查了所有其他可能性之后,并得出结论问题是由MySQL服务器或MySQL客户端导致的,应创建提供给我方的邮件列表或支持团队的缺陷报告。在缺陷报告中,请详细描述系统的行为,以及你认为发生了什么情况。+3. 还应阐明为什么你认为是MySQL导致了问题。请考虑本章介绍的所有情况。准确阐明当你检查系统时问题是如何出现的。对于程序和日志文件的任何输出和错误消息,请使用“复制和粘贴”方法。 尽量详细描述不工作的程序,以及你所见到的所有征兆。我们过去收到过很多仅说明“系统不工作”的缺陷报告。这不会为我们提供有助于解决问题的信息。 如果程序失败,了解下述信息总是有用的: ·         有嫌疑的程序是否出现了分段故障(是否转储内核)? ·         程序是否占用了所有可用的CPU时间?用**top**.进行检查。让程序运行一段时间,或许能简单地评估某些事是否是计算密集性的。 ·         如果问题是因**mysqld**服务器导致的,使用**mysqladmin -u root ping**或**mysqladmin -u root processlist**是否能获得服务器的响应? ·         当你尝试连接到MySQL服务器(例如,mysql)时,客户端程序给出的信息是什么?客户端是否堵塞?是否获得了来自程序的任何输出? 发送缺陷报告时,请遵循[1.7.1.2节,“请教问题或通报缺陷”](# "1.7.1.2. Asking Questions or Reporting Bugs")中给出的说明。 ### A.2. 使用MySQL程序时的常见错误 [A.2.1. 拒绝访问](#)[A.2.2. 无法连接到[local] MySQL服务器](#)[A.2.3. 客户端不支持鉴定协议](#)[ A.2.4. 输入密码时出现密码错误](#)[A.2.5. 主机的hos*t_name被屏蔽*](#)[A.2.6. 连接数过多](#)[A.2.7. 内存溢出](#)[A.2.8. MySQL服务器不可用](#)[A.2.9. 信息包过大](#)[ A.2.10. 通信错误和失效连接](#)[A.2.11. 表已满](#)[A.2.12. 无法创建文件/写入文件](#)[A.2.13. 命令不同步](#)[A.2.14. 忽略用户](#)[A.2.15. 表tbl_na*me不存在*](#)[A.2.16. 无法初始化字符集](#)[ A.2.17. 文件未找到](#) 本节列出了用户运行MySQL服务器时常会遇到的一些错误。尽管问题是在你尝试运行客户端时出现的,但对很多问题的解决方案来说,需要更改MySQL服务器的配置。 ### A.2.1. 拒绝访问 导致拒绝访问错误的原因很多。该错误常与连接时服务器允许客户端使用的MySQL账户有关。请参见[5.7.8节,“拒绝访问错误的原因``”](# "5.7.8. Causes of Access denied Errors")。请参见[5.7.2节,“权限系统工作原理”](# "5.7.2. How the Privilege System Works")。 ### A.2.2. 无法连接到[local] MySQL服务器 [A.2.2.1. 在Windows上与MySQL服务器的连接失败](#) Unix平台上的MySQL客户端能够以两种不同的方式连接到**mysqld**服务器:通过文件系统中的文件(默认为/tmp/mysql.sock)使用Unix套接字进行连接,或通过端口号使用TCP/IP进行连接。Unix套接字文件的连接速度比TCP/IP快,但仅能在与相同计算机上的服务器相连时使用。如果未指定指定主机名或指定了特殊的主机名localhost,将使用Unix套接字。 如果MySQL服务器运行在Windows 9x或Me上,仅能通过TCP/IP进行连接。如服务器运行在Windows NT、2000、XP或2003上,而且使用--enable-named-pipe选项启动,如果在运行服务器的机器上运行客户端,也能使用命名管道进行连接。默认情况下,命名管道的名称为MySQL。如果在连接到mysqld时未给定主机名,MySQL客户端首先会尝试连接到命名管道。如果不能工作,将连接到TCP/IP端口。使用“.”作为主机名,可在Windows平台上强制使用命名管道。 错误(2002)“无法连接到…”通常意味着在系统没有运行的MySQL服务器,或在连接到服务器时使用了不正确的Unix套接字文件名或TCP/IP端口号。 首先检查服务器主机上是否有名为mysqld的进程(在Unix平台上使用**ps xa | grep mysqld**,或在Windows平台上使用任务管理器)。如果没有这类进程,应启动服务器。请参见[2.9.2.3节,“启动MySQL服务器以及其故障诊断和排除”](# "2.9.2.3. Starting and Troubleshooting the MySQL Server")。 如果**mysqld**进程正在运行,可使用下述命令检查。在你的具体设置中,端口号或Unix套接字文件名可能会有所不同。host_ip代表运行服务器的机器的IP编号。 ~~~ shell> mysqladmin version ~~~ ~~~ shell> mysqladmin variables ~~~ ~~~ shell> mysqladmin -h `hostname` version variables ~~~ ~~~ shell> mysqladmin -h `hostname` --port=3306 version ~~~ ~~~ shell> mysqladmin -h host_ip version ~~~ ~~~ shell> mysqladmin --protocol=socket --socket=/tmp/mysql.sock version ~~~ 注意,应与主机名命令一起使用“`”而不是“’”,这会使主机名输出(当前主机名)被代入**mysqladmin**命令。如果没有主机名命令或正运行在Windows平台上,应以手动方式输入机器的主机名(无“`”符号),后跟-h选项。也可以使用TCP/IP协议用-h 127.0.0.1连接到本地主机。 下面给出了一些“无法连接到本地MySQL服务器”错误的可能原因: 1.    **Mysqld**未运行。请检查操作系统的进程列表以确保**mysqld**进程正在运行。 2.    你正在具有很多TCP/IP连接的Windows平台上运行MySQL服务器。如果你的客户端经常出现错误,请参见[A.2.2.1节,“在Windows上与MySQL服务器的连接失败”](# "A.2.2.1. Connection to MySQL Server Failing on Windows"),以找出规避方法。 3.    你正在使用MIT-pthreads的系统上运行。如果你正在运行不具有固有线程的操作系统,**mysqld**将使用MIT-pthreads软件包。请参见[2.1.1节,“MySQL支持的操作系统”](# "2.1.1. Operating Systems Supported by MySQL")。但是,并非所有的MIT-pthreads版本均支持Unix套接字文件。在不支持套接字文件的系统上,连接到服务器时,必须明确指定主机名。请使用下述命令来检查是否连接到了服务器: ~~~ 4.           shell> mysqladmin -h `hostname` version ~~~ 5.    某人移动了**mysqld使用的**Unix套接字文件(默认为/tmp/mysql.sock)。例如,你可能执行了将旧文件从/tmp目录删除的**cron**任务。你总能执行**mysqladmin version**来检查**mysqladmin试图**使用的Unix套接字文件是否的确存在。在该情况下,更正方式是更改**cron**任务,不删除mysql.sock文件,或将套接字文件置于其他地方。请参见[A.4.5节,“如何保护或更改MySQL套接字文件/tmp/mysql.sock``”](# "A.4.5. How to Protect or Change the MySQL Socket File /tmp/mysql.sock")。 6.    你使用--socket=/path/to/socket选项启动了**mysqld**服务器,当忘记将套接字文件的新名称通知客户端程序。如果更改了关于服务器的套接字路径,也必须通知MySQL客户端。可在运行客户端程序时使用相同的—socket选项来完成该任务。此外,你还应确保客户端具有访问文件mysql.sock的权限。要想找出套接字文件的位置,可使用: ~~~ 7.           shell> netstat -ln | grep mysql ~~~ 请参见[A.4.5节,“如何保护或更改MySQL套接字文件/tmp/mysql.sock``”](# "A.4.5. How to Protect or Change the MySQL Socket File /tmp/mysql.sock")。 8.    你正在使用Linux而且1个服务器线程已死亡(内核已清除)。在此情况下,在重启MySQL服务器之前,必须杀死其他**mysqld**线程(例如,使用kill或mysql_zap脚本)。请参见[A.4.2节,“如果MySQL依然崩溃,应作些什么”](# "A.4.2. What to Do If MySQL Keeps Crashing")。 9.    服务器或客户端程序不具有访问包含Unix套接字文件的目录或套接字文件本身的恰当权限。在该情况下,必须更改目录或套接字文件的访问权限,以便服务器或客户端程序能够访问它们,或用–socket选项重启**mysqld**,在该选项中指定服务器能创建、而且客户端可访问的目录下的套接字文件名。 如果遇到错误消息“无法连接到some_host上的MySQL服务器”,可尝试采取下述步骤以找出问题所在: ·         执行“telnet some_host 3306”并按两次回车键,检查服务器是否运行在该主机上(3306是默认的MySQL端口号。如果你的服务器正在监听不同的端口,请更改该值)。如果有1个MySQL服务器正在运行并监听该端口,你应收到包含服务器版本号的回应。如果遇到错误,如“telnet:无法连接到远程主机:拒绝连接”,表示在该定端口上没有运行的服务器。 ·         如果服务器正运行在本地主机上,请使用Unix套接字文件,并使用**mysqladmin -h localhost variables**进行连接。验证服务器监听的TCP/IP端口号(它是port变量的值)。 ·         确保你的**mysqld**服务器未用--skip-networking选项启动。如果使用了该选项,将无法使用TCP/IP连接到它。 ·         检查并确认不存在屏蔽了对MySQL访问的防火墙。需要配置诸如ZoneAlarm和Windows XP个人防火墙等应用程序,以允许对MySQL服务器的外部访问。 #### A.2.2.1. 在Windows上与MySQL服务器的连接失败 当你在具有很多TCP/IP连接的Windows上运行MySQL服务器,并经常在客户端上遇到“无法连接到MySQL服务器”错误时,可能是因为Windows不允许足够的临时(短命)端口用于这类连接。 默认情况下,Windows允许用于使用5000个临时(短命)TCP端口。任何端口关闭后,它将在TIME_WAIT状态保持120秒。与重新初始化全新的连接相比,该状态允许以更低的开销重新使用连接。但是,在该时间逝去前,无法再次使用该端口。 对于小的可用TCP端口堆栈(5000),以及具有TIME_WAIT状态的大量在短时间内打开和关闭的TCP端口,你很可能遇到端口耗尽问题。处理该问题的方法有两种: ·         通过调查连接池以及可能的持久连接,减少快速消耗的TCP端口数。 ·         调整Windows注册表中的某些设置(请参见下面)。 **要点:下述步骤涉及更改Windows 注册表。更改注册表之前,请备份注册表,并确认你已掌握在出现问题时恢复注册表的方法。关于备份年、恢复和编辑注册表的更多信息,请请参见Microsoft知识库中的下述文献:[http://support.microsoft.com/kb/256986/EN-US/](http://support.microsoft.com/kb/256986/EN-US/)。** ·         启动注册表编辑器(Regedt32.exe)。 ·         在注册表中确定下述键值的位置: ~~~ ·                HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters ~~~ ·         在“编辑”菜单上点击“添加值”,然后增加下述注册值: ~~~ ·                Value Name: MaxUserPort ~~~ ~~~ ·                Data Type: REG_DWORD ~~~ ~~~ ·                Value: 65534 ~~~ 它用于设置为任何用户提供的临时端口数。有效范围介于5000和65534之间(十进制)。默认值为0x1388(5000,十进制)。 ·         在“编辑”菜单上点击“添加值”,然后增加下述注册值: ~~~ ·                Value Name: TcpTimedWaitDelay ~~~ ~~~ ·                Data Type: REG_DWORD ~~~ ~~~ ·                Value: 30 ~~~ 它用于设置关闭之前将TCP端口连接保持在TIME_WAIT状态的秒数。有效范围介于0秒和300秒之间。默认值为0x78(120秒)。 ·         退出注册表编辑器。 ·         重新引导机器。 注释:撤销上述设置十分简单,就像删除你创建的注册表一样。 ### A.2.3. 客户端不支持鉴定协议 MySQL 5.1采用了基于密码混编算法的鉴定协议,它与早期客户端(4.1之前)使用的协议不兼容。如果你将服务器升级到4.1之上,用早期的客户端进行连接可能失败,并给出下述消息: ~~~ shell> mysql ~~~ ~~~ 客户端不支持服务器请求的鉴定协议:请考虑升级MySQL客户端。 ~~~ 要想解决该问题,应使用下述方法之一: ·         升级所有的客户端程序,以使用4.1.1或更新的客户端库。 ·         用4.1版之前的客户端连接到服务器时,请使用仍具有4.1版之前风格密码的账户。 ·         对于需要使用4.1版之前的客户端的每位用户,将密码恢复为4.1版之前的风格。可以使用SET PASSWORD语句和OLD_PASSWORD()函数完成该任务: ~~~ ·                mysql> SET PASSWORD FOR ~~~ ~~~ ·                    -> 'some_user'@'some_host' = OLD_PASSWORD('newpwd'); ~~~ 也可以使用UPDATE和FLUSH PRIVILEGES: ~~~ mysql> UPDATE mysql.user SET Password = OLD_PASSWORD('newpwd') ~~~ ~~~     -> WHERE Host = 'some_host' AND User = 'some_user'; ~~~ ~~~ mysql> FLUSH PRIVILEGES; ~~~ 用你打算使用的密码替换前例中的“*newpwd*”。MySQL不能告诉你原来的密码是什么,因此,你需要选择新的密码。 ·         通知服务器使用旧的密码混编算法: 1.    使用“--old-passwords”选项启动**mysqld**。 2.    对于已将密码更新为较长4.1格式的每个账户,为其指定具有旧格式的密码。可以使用下述查询确定这些账户: ~~~ 3.                 mysql> SELECT Host, User, Password FROM mysql.user ~~~ ~~~ 4.                     -> WHERE LENGTH(Password) > 16; ~~~ 对于查询显示的每个账户记录,请使用Host和User值,并使用OLD_PASSWORD()函数以及SET PASSWORD或UPDATE之一指定密码,如前面所介绍的那样。 **注释:**在早期的PHP版本中,mysql扩展不支持MySQL 4.1.1和更高版中的鉴定协议。无论使用的PHP版本是什么,它均是正确的。如果你打算与MySQL 4.1或更高版本一起使用mysql扩展,需要使用前面介绍的选项之一,配置MySQL,以便与较早的客户端一起使用。mysqli扩展(支持“改进的MySQL”,在PHP 5中增加)与MySQL 4.1和更高版本中使用的改进的密码混编算法兼容,不需要对MySQL进行特殊配置就能使用该MySQL客户端库。关于mysqli扩展的更多信息,请参见[http://php.net/mysqli](http://php.net/mysqli)。 关于密码混编和鉴定功能的额外背景知识,请参见[5.7.9节,“MySQL 4.1中的密码哈希处理”](# "5.7.9. Password Hashing in MySQL 4.1")。 ### A.2.4. 输入密码时出现密码错误 使用无下述密码值的“—password”或“-p”选项调用时,MySQL客户端程序将提示输入密码: ~~~ shell> mysql -u user_name -p ~~~ ~~~ Enter password: ~~~ 在某些系统上,当你在选项文件或命令行上指定时,你可能会发现密码能够工作,但是当你在“Enter password:”提示下以交互方式输入密码时,你可能会发现输入的密码不工作。当系统所提供的用于读取密码的库将密码值限定在少数字符时(典型情况下为8个),就会出现该问题。这是与系统库有关的问题,与MySQL无关。要想处理该问题,可将MySQL密码更改为由8个字符或更少字符构成的值,或将密码置于选项文件中。 ### A.2.5. 主机的hos*t_name被屏蔽* 如果遇到下述错误,表示**mysqld**已收到来来自主机“*host_name*”的很多连接请求,但该主机却在中途中断。 ~~~ 由于出现很多连接错误,主机'host_name'被屏蔽。 ~~~ ~~~ 可使用'mysqladmin flush-hosts'解除屏蔽。 ~~~ 允许的中断连接请求的数目由max_connect_errors系统变量的值决定。当超出max_connect_errors规定的连接请求时,**mysqld**将认为某处出错(例如,某人正试图插入),并屏蔽主机的进一步连接请求,直至执行了**mysqladmin flush-hosts**命令,或发出了FLUSH HOSTS语句为止。请参见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。 在默认情况下,**mysqld**会在10次连接错误后屏蔽主机。你可以通过下述方式启动服务器来调整该值: ~~~ shell> mysqld_safe --max_connect_errors=10000 & ~~~ 如果在给定主机上遇到该错误,首先应核实该主机的TCP/IP连接是否正确。如果存在网络问题,增加max_connect_errors变量的值不会有任何好处。 ### A.2.6. 连接数过多 当你试图连接到**mysqld**服务器时遇到“过多连接”错误,这表示所有可用的连接均已被其他客户端使用。 允许的连接数由max_connections系统变量控制。默认值为100。如果需要支持更多的连接,应使用该变量的较大值重启**mysqld**。 **mysqld**实际上允许max_connections+1个客户端进行连接。额外的连接保留给具有SUPER权限的账户。通过为系统管理员而不是普通用户授予SUPER权限(普通用户不应具有该权限),系统管理员能够连接到服务器,并使用SHOW PROCESSLIST来诊断问题,即使已连接的无特权客户端数已达到最大值也同样。请参见[13.5.4.16节,“SHOW PROCESSLIST语法”](# "13.5.4.16. SHOW PROCESSLIST Syntax")。 MySQL能支持的最大连接数取决于给定平台上线程库的质量。Linux或Solaris应能支持500-1000个并发连接,具体情况取决于RAM容量,以及客户端正在作什么。MySQL AB提供的静态Linux库能支持高达4000个连接。 ### A.2.7. 内存溢出 如果使用**mysql**客户端程序发出了查询,并收到下述错误之一,则表示**mysql**没有足够内存来保存全部查询结果: ~~~ mysql: Out of memory at line 42, 'malloc.c' ~~~ ~~~ mysql: needed 8136 byte (8k), memory in use: 12481367 bytes (12189k) ~~~ ~~~ 错误2008: MySQL client ran out of memory ~~~ 要想更正该问题,首先应检查查询是否正确。返回这么多行是否合理?如果不合理,更正查询并再次尝试。否则,应使用“--quick”选项调用**mysql**。这样,将使用mysql_use_result() C API函数来检索结果集,这类函数能够降低客户端上的负载(但会加重服务器上的负载)。 ### A.2.8. MySQL服务器不可用 在本节中,还介绍了出现查询错误期间,与丢失了服务器连接有关的事宜。 MySQL服务器不可用错误的最常见原因是服务器超时以及连接已关闭。在该情况下,通常能见到下述错误代码之一(具体的错误代码与操作系统有关): <table border="1" cellpadding="0" id="table1"><tr><td> <p><strong><span> 错误代码</span></strong></p></td> <td> <p><strong><span> 描述</span></strong></p></td> </tr><tr><td> <p> <span>CR_SERVER_GONE_ERROR</span></p></td> <td> <p>客户端无法将问题发送至服务器。</p></td> </tr><tr><td> <p> <span>CR_SERVER_LOST</span></p></td> <td> <p>写入服务器时客户端未收到错误,但也未获得问题的完整答案(或任何答案)。</p></td> </tr></table> 在默认情况下,如果未发生任何事,8小时后服务器将关闭连接。也可以在启动**mysqld**时,通过设置wait_timeout变量更改时间限制。请参见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables"). 如果有1个脚本,你仅需要再次发出查询,让客户端再次进行自动连接即可。其中,假定在客户端中启用了自动再连接功能(对于mysql命令行客户端,这是默认设置)。 MySQL服务器不可用错误的一些其他常见原因如下: ·         你(或db系统管理员)使用KILL语句或**mysqladmin kill**命令杀死了正在运行的线程。 ·         你试图在关闭了与服务器的连接后运行查询。这表明应更正应用程序中的逻辑错误。 ·         你在客户端一侧遇到TCP/IP连接超时错误。如果你使用了命令:mysql_options(..., MYSQL_OPT_READ_TIMEOUT,...)或mysql_options(..., MYSQL_OPT_WRITE_TIMEOUT,...),就可能出现该问题。在该情况下,增加超时值可能有助于问题的解决。 ·         你在服务器端遇到超时错误,而且禁止了客户端中的自动再连接功能(MYSQL结构中的再连接标志等于0)。 ·         你正在使用Windows客户端,而且在发出命令之前服务器撤销了连接(或许是因为已超过wait_timeout)。 在Windows平台上出现问题的原因,在某些情况下,将TCP/IP连接写入服务器时,MySQL未收到来自操作系统的错误,但当试图从连接读取答案时出现错误。 在该情况下,即使MYSQL结构中的再连接标志等于1,MySQL也不会执行自动再连接并再次发出查询,这是因为它不知道服务器是否收到原始查询。 对此的解决方式是:如果自上一次查询以来经过了较长时间,在连接上执行mysql_ping(正是MyODBC所作的);或在**mysqld**服务器上将wait_timeout设置得很高,使之实际上不存在超时。 ·         如果你向服务器发出了不正确或过大的查询,也会遇到这类问题。如果**mysqld**收到过大或无序的信息包,它会认为客户端出错,并关闭连接。如果需要执行较大的查询(例如,正在处理大的BLOB列),可通过设置服务器的max_allowed_packet变量,增加查询限制值,该变量的默认值为1MB。或许,你还需增加客户端上的最大信息包大小。关于设置信息包大小的更多信息,请参见[A.2.9节,“信息包过大”](# "A.2.9. Packet too large")。 ·         如果你的客户端低于4.0.8而且你的服务器高于4.0.8,当你接收16MB或更大的信息包时,可能会丢失连接。 ·         如果MySQL是用“--skip-networking”选项启动的,也会见到MySQL服务器不可用错误。 ·         你遇到了执行查询时服务器宕机的缺陷。 通过执行**mysqladmin version**并检查服务器的正常工作时间,可检查服务器是否宕机并重启。如果客户端连接是因**mysqld**崩溃和重启而断开的,应将重点放在查找崩溃你方面。首先应再次检查发出的查询是否再次杀死了服务器。请参见[A.4.2节,“如果MySQL依然崩溃,应作些什么”](# "A.4.2. What to Do If MySQL Keeps Crashing")。 用“--log-warnings=2”选项启动mysqld,可获得关于连接的更多信息。这样,就能将某些断开连接错误记录到hostname.err文件中。请参见[5.11.1节,“错误日志”](# "5.11.1. The Error Log")。 如果你打算创建与该问题有关的缺陷报告,务必包含下述信息: 1.    指明MySQL服务器是否宕机。通过服务器错误日志可发现这方面的信息。请参见[A.4.2节,“如果MySQL依然崩溃,应作些什么”](# "A.4.2. What to Do If MySQL Keeps Crashing")。 2.    如果特定查询杀死了**mysqld**,而且在运行查询前用CHECK TABLE检查了涉及的表,你是否能提供可重复的测试范例?请参见[E.1.6节,“如果出现表崩溃,请生成测试案例”](# "E.1.6. Making a Test Case If You Experience Table Corruption")。 3.    在MySQL服务器中,系统变量wait_timeout的值是什么?(**mysqladmin variables**给出了该变量的值)。 4.    你是否尝试使用“--log”选项来运行**mysqld**,以确定是否在日志中出现问题? 另请参见[A.2.10节,“通信错误和失效连接”](# "A.2.10. Communication Errors and Aborted Connections")。 请参见[1.7.1.2节,“请教问题或通报缺陷”](# "1.7.1.2. Asking Questions or Reporting Bugs")。 ### A.2.9. 信息包过大 通信信息包是发送至MySQL服务器的单个SQL语句,或发送至客户端的单一行。 在MySQL 5.1服务器和客户端之间最大能发送的可能信息包为1GB。 当MySQL客户端或**mysqld**服务器收到大于max_allowed_packet字节的信息包时,将发出“信息包过大”错误,并关闭连接。对于某些客户端,如果通信信息包过大,在执行查询期间,了能回遇到“丢失与MySQL服务器的连接”错误。 客户端和服务器均有自己的max_allowed_packet变量,因此,如你打算处理大的信息包,必须增加客户端和服务器上的该变量。 如果你正在使用**mysql**客户端程序,其max_allowed_packet变量的默认值为16MB。要想设置较大的值,可用下述方式启动**mysql**: ~~~ mysql> mysql --max_allowed_packet=32M ~~~ 它将信息包的大小设置为32MB。 服务器的默认max_allowed_packet值为1MB。如果服务器需要处理大的查询,可增加该值(例如,如果准备处理大的BLOB列)。例如,要想将该设置为16MB,可采用下述方式启动服务器: ~~~ mysql> mysqld --max_allowed_packet=16M ~~~ 也能使用选项文件来设置max_allowed_packet。要想将服务器的该变量设置为16MB,可在选项文件中增加下行内容: ~~~ [mysqld] ~~~ ~~~ max_allowed_packet=16M ~~~ 增加该变量的值十分安全,这是因为仅当需要时才会分配额外内存。例如,仅当你发出长查询或**mysqld**必须返回大的结果行时**mysqld**才会分配更多内存。该变量之所以取较小默认值是一种预防措施,以捕获客户端和服务器之间的错误信息包,并确保不会因偶然使用大的信息包而导致内存溢出。 如果你正是用大的BLOB值,而且未为**mysqld**授予为处理查询而访问足够内存的权限,也会遇到与大信息包有关的奇怪问题。如果怀疑出现了该情况,请尝试在**mysqld_safe**脚本开始增加**ulimit -d 256000**,并重启**mysqld**。 ### A.2.10. 通信错误和失效连接 对于连接问题,服务器错误日志是有用的信息源。请参见[5.11.1节,“错误日志”](# "5.11.1. The Error Log")。如果服务器是用“--log-warnings”选项启动的,在错误日志中可能会发现下述消息: ~~~ 010301 14:38:23  Aborted connection 854 to db: 'users' user: 'josh' ~~~ 如果“Aborted connections”(放弃连接)消息出现在错误日志中,可能的原因是: 1.    客户端程序在退出之前未调用mysql_close()。 2.    客户端的空闲时间超过wait_timeout或interactive_timeout秒,未向服务器发出任何请求。请参见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。 3.    客户端在数据传输中途突然结束。 出现这类情况时,服务器将增加“Aborted_clients”(放弃客户端)状态变量。 出现下述情况时,服务器将增加“Aborted_clients”(放弃客户端)状态变量。 ·         客户端不具有连接至数据库的权限。 ·         客户端采用了不正确的密码。 ·         连接信息包不含正确信息。 ·         获取连接信息包的时间超过connect_timeout秒。请参见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。 如果出现这类情况,可能表明某人正试图侵入你的服务器! 对于放弃客户端或放弃连接问题,其他可能的源应包括: ·         与Linux一起使用以太网协议,半双工或全双工。很多Linux以太网驱动均存在该缺陷。应通过FTP在客户端和服务器机器之间传输大文件来测试该缺陷。如果传输处于burst-pause-burst-pause(爆发-暂停-爆发-暂停)模式,表明你遇到了Linux双工故障。唯一的解决方法是,将网卡和Hub/交换器的双工模式切换为全双工或半双工,并对结果进行测试以确定最佳设置。 ·         与线程库有关的某些问题导致读取中断。 ·         配置不良的TCP/IP。 ·         有问题的以太网、Hub、交换器、电缆等。仅能通过更换硬件才能恰当诊断。 ·         变量max_allowed_packet过小或查询要求的内存超过为**mysqld**分配的内存。请参见[A.2.9节,“信息包过大”](# "A.2.9. Packet too large")。 另请参见[A.2.8节,“MySQL服务器不可用”](# "A.2.8. MySQL server has gone away")。 ### A.2.11. 表已满 表已满错误出现的方式有数种: ·         你正在使用低于3.23版的MySQL服务器,而且“内存中”临时表超过了tmp_table_size字节。要想避免该问题,可使用“-O tmp_table_size=*val*”选项以便**mysqld**增加临时表的大小,或在发出有问题的查询之前,使用SQL选项SQL_BIG_TABLES。请参见[13.5.3节,“SET语法”](# "13.5.3. SET Syntax")。 也可以使用“--big-tables”选项启动**mysqld**。它与使用针对所有查询的SQL_BIG_TABLES完全相同。 自MySQL 3.23起,该问题应不再出现。如果“内存中”临时表超过tmp_table_size,服务器会自动将其转换为基于磁盘的MyISAM表。 ·         你正在使用InnoDB表,并超出了InnoDB表空间。在该情况下,解决方法是增加InnoDB表空间。请参见[15.2.7节,“添加和删除InnoDB数据和日志文件``”](# "15.2.7. Adding and Removing InnoDB Data and Log Files")。 ·         你正在仅支持2GB文件的操作系统上使用ISAM或MyISAM表,数据文件或索引文件达到了该限制值。 ·         你正在使用MyISAM表,而且表所需的空间超过内部指针允许的大小。如果在创建表时未指定MAX_ROWS表,MySQL将使用myisam_data_pointer_size系统变量。默认值为6字节,它足以容纳65536TB数据。请参见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。 使用该语句,可检查最大数据/索引大小: ~~~ SHOW TABLE STATUS FROM database LIKE 'tbl_name'; ~~~ 也可以使用**myisamchk -dv /path/to/table-index-file**。 如果指针大小过小,可使用ALTER TABLE更正该问题: ~~~ ALTER TABLE tbl_name MAX_ROWS=1000000000 AVG_ROW_LENGTH=nnn; ~~~ 仅应为具有BLOB或TEXT列的表指定AVG_ROW_LENGTH。在该情况下,MySQL不能仅根据行数优化所需的空间。 ### A.2.12. 无法创建文件/写入文件 如果对某些查询遇到下述类型的错误,它意味着MySQL不能为临时目录下的结果集创建临时文件: ~~~ 无法创建/写入文件'\\sqla3fe_0.ism'。 ~~~ 前述错误是Windows平台上的典型消息,Unix平台上的消息与之类似。 一种更正方式是使用“--tmpdir”选项启动**mysqld**,或在选项文件的[mysqld]部分增加该选项。例如,要想指定目录C:\temp,可使用: ~~~ [mysqld] ~~~ ~~~ tmpdir=C:/temp ~~~ 目录C:\temp必须存在,并有足够的空间允许MySQL写入它。请参见[4.3.2节,“使用选项文件”](# "4.3.2. Using Option Files")。 该错误的另一个原因可能是许可事宜。请确认MySQL服务器能够写入tmpdir目录。 此外,还用使用**perror**检查错误代码。服务器无法写入表的一个原因是文件系统已满。 ~~~ shell> perror 28 ~~~ ~~~ 错误代码28:磁盘上无剩余空间。 ~~~ ### A.2.13. 命令不同步 如果遇到“命令不同步”错误,将无法在你的客户端代码中运行该命令,你正在以错误顺序调用客户端函数。 例如,如果你正使用mysql_use_result(),并打算在调用mysql_free_result()之前执行新查询,就会出现该问题。如果你试图执行两次查询,但并未在两次查询之间调用mysql_use_result()或mysql_store_result(),也会出现该问题。 ### A.2.14. 忽略用户 如果遇到下述错误,表示当启动**mysqld**时或重新加载授权表时,在用户表中发现具有非法密码的账户。 发现用户'*some_user*'@'*some_host*'密码错误:忽略用户。 作为其结果,许可系统将简单忽略账户。 在下面的介绍中,指明了可能的原因和问题的更正措施: 1.    或许,你正打算用旧的用户表运行新版本的**mysqld**。执行**mysqlshow mysql user**检查Password(密码)列是否短于16个字符,通过该方式可检查该问题。如果结果是肯定的,可运行脚本/add_long_password脚本更正该问题。 2.    账户具有旧的密码(8字符长),而且未使用“--old-protocol”选项启动**mysqld**。更新用户表中的账户,使之具有新的密码,或使用“--old-protocol”选项重启**mysqld**。 3.    在用户表中未使用PASSWORD()函数指定了密码。使用**mysql**用新密码更新用户表中的账户,务必使用PASSWORD()函数: ~~~ 4.           mysql> UPDATE user SET Password=PASSWORD('newpwd') ~~~ ~~~ 5.               -> WHERE User='some_user' AND Host='some_host'; ~~~ ### A.2.15. 表tbl_na*me不存在* 如果遇到下述错误之一,通常意味着当前数据库中不存在具有给定名称的表: ~~~ 表'tbl_name'不存在 ~~~ ~~~ 无法找到文件:'tbl_name' (errno: 2) ~~~ 在某些情况下,表或许存在,但未正确引用它: ·         由于MySQL使用目录和文件来保存数据库和表,如果它们位于区分文件名大小写的文件系统上,数据库和表名也区分文件大小写。 ·         即使对于不区分大小写的文件系统,如Windows,在查询内对给定表的所有引用必须使用相同的大小写。 可以使用SHOW TABLES检查位于当前数据库中的表。请参见[13.5.4节,“SHOW语法”](# "13.5.4. SHOW Syntax")。 ### A.2.16. 无法初始化字符集 如果存在字符集问题,可能会遇到下述错误: ~~~ MySQL连接失败:无法初始化字符集charset_name。 ~~~ 导致该错误的原因: ·         字符集为多字节字符集,但客户端不支持该字符集。在该情况下,需要使用“--with-charset=*charset_name*”或“--with-extra-charsets=*charset_name*”选项运行**configure**以重新编译客户端。请参见[2.8.2节,“典型配置选项****”](# "2.8.2. Typical configure Options")。 所有的标准MySQL二进制文件均是采用“--with-extra-character-sets=complex”编译的,能够支持所有的多字节字符集。请参见[5.10.1节,“数据和排序用字符集”](# "5.10.1. The Character Set Used for Data and Sorting")。 ·         字符集是未编译到**mysqld**中的简单字符集,而且字符集定义文件不在客户端预期的位置。 在该情况下,需要采取下述方法之一解决问题: 1.    重新编译客户端,使之支持字符集。请参见[2.8.2节,“典型配置选项****”](# "2.8.2. Typical configure Options")。 2.    为客户端指定字符集定义文件所在的目录。对于很多客户端,可使用“--character-sets-dir”选项完成该任务。 3.    将字符集定义文件复制到客户端预期的位置。 ### A.2.17. 文件未找到 如果遇到“ERROR '...'未发现(errno: 23)”,“无法打开文件:... (errno: 24)”,或来自MySQL的具有errno 23或errno 24的其它错误,它表示未为MySQL服务器分配足够的文件描述符。你可以使用**perror**实用工具来了解错误编号的含义: ~~~ shell> perror 23 ~~~ ~~~ 错误代码23:文件表溢出 ~~~ ~~~ shell> perror 24 ~~~ ~~~ 错误代码24:打开文件过多 ~~~ ~~~ shell> perror 11 ~~~ ~~~ 错误代码11:资源暂时不可用 ~~~ 这里的问题是,**mysqld**正试图同时打开过多的文件。你可以通知**mysqld**不要一次打开过多文件,或增加**mysqld**可用文件描述符的数目。 要想通知**mysqld**将一次打开的文件控制在较小的数目上,可降低table_cache系统变量的值(),从而减少表高速缓冲(默认值为64)。降低max_connections的值也能降低打开文件的数目(默认值为100)。 要想更改**mysqld**可用的文件描述符的数目,可在**mysqld_safe**上使用“--open-files-limit”选项或设置(自MySQL 3.23.30开始)open_files_limit系统变量。请参见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。设置这些值的最简单方式是在选项文件中增加1个选项。请参见[4.3.2节,“使用选项文件”](# "4.3.2. Using Option Files")。如果**mysqld**的版本较低,不支持设置打开文件的数目,可编辑**mysqld_safe**脚本。在脚本中有1个注释掉的行**ulimit -n 256。你可以删除**‘#’字符取消对该行的注释,更改数值256,以设置**mysqld**可用的文件描述符数目。 “--open-files-limit”和**ulimit**能够增加文件描述符的数目,但最高不能超过操作系统限制的数目。此外还有1个“硬”限制,仅当以根用户身份启动**mysqld_safe**或**mysqld**时才能覆盖它(请记住,在该情况下,还需使用“--user”选项启动服务器,以便在启动后不再以根用户身份继续运行)。如果需要增加操作系统限制的对各进程可用文件描述符的数目,请参阅系统文档。 **注释:**如果运行**tcsh** shell,**ulimit**不工作!请求当前限制值时,**tcsh**还能通报不正确的值。在该情况下,应使用**sh**启动**mysqld_safe。** ### A.3. 与安装有关的事宜 [ A.3.1. 与MySQL客户端库的链接问题](#)[ A.3.2. 如何以普通用户身份运行MySQL](#)[ A.3.3. 与文件许可有关的问题](#) ### A.3.1. 与MySQL客户端库的链接问题 当你链接到应用程序以使用MySQL客户端库时,可能会遇到以mysql_开始的未定义引用错误,如下所示: ~~~ /tmp/ccFKsdPa.o: 在函数`main'中: ~~~ ~~~ /tmp/ccFKsdPa.o(.text+0xb): 对`mysql_init'的未定义引用。 ~~~ ~~~ /tmp/ccFKsdPa.o(.text+0x31): 对`mysql_real_connect'的未定义引用。 ~~~ ~~~ /tmp/ccFKsdPa.o(.text+0x57): 对`mysql_real_connect'的未定义引用。 ~~~ ~~~ /tmp/ccFKsdPa.o(.text+0x69): 对`mysql_error'的未定义引用。 ~~~ ~~~ /tmp/ccFKsdPa.o(.text+0x9a): 对`mysql_close'的未定义引用。 ~~~ 通过在链接命令后增加“-Ldir_path -lmysqlclient”选项,应能解决该问题,其中,dir_path代表客户端库所在目录的路径名。要想确定正确的目录,可尝试下述命令: ~~~ shell> mysql_config --libs ~~~ 来自**mysql_config**的输出可能会指明应在链接命令上指定的其他库。 对于非压缩或压缩函数,如果遇到未定义引用错误,可在链接命令后添加-lz,并再次尝试。 对于应在系统上存在的函数(如connect),如果遇到未定义引用错误,请检查相关函数的手册页,以便确定应在链接命令上增加哪些库。 对于系统上不存在的函数,可能会遇到未定义引用错误,如下所示: ~~~ mf_format.o(.text+0x201): 对`__lxstat'的未定义引用。 ~~~ 它通常意味着你的MySQL客户端库是在与你的系统不100%兼容的系统上编译的。在该情况下,应下载最新的MySQL源码分发版,并自己编译MySQL。请参见[2.8节,“使用源码分发版安装MySQL”](# "2.8. MySQL Installation Using a Source Distribution")。 当你试图执行MySQL程序时,可能会遇到运行时未定义引用错误。如果这类错误指明了以mysql_开始的符号,或指明未发现mysqlclient库,这意味着你的系统无法找到共享的libmysqlclient.so库。对其的更正方式是,通知系统在库所在位置搜索共享库。请使用与系统相适应的下述方法: ·         将libmysqlclient.so所在目录的路径添加到LD_LIBRARY_PATH环境变量中。 ·         将libmysqlclient.so所在目录的路径添加到LD_LIBRARY环境变量中。 ·         将libmysqlclient.so拷贝到可被系统搜索的目录下,如/lib,然后通过执行ldconfig更新共享库信息。 解决该问题的另一种方法是,以静态方式将你的程序与“-static”选项链接在一起,或在链接代码之前删除动态MySQL库。使用第2种方法之前,应确保没有使用动态库的其它程序。 ### A.3.2. 如何以普通用户身份运行MySQL 在Windows平台上,能够使用普通用户账户以Windows服务方式运行服务器。 在Unix平台上,不是任何用户都能启动并运行MySQL服务器**mysqld的**。但是,处于安全方面的原因,应避免以Unix根用户身份运行服务器。要想更改**mysqld**,使之能以正常的无特权Unix用户*user_name*身份运行,必须采取下述步骤: 如果服务器正在运行,停止它(使用**mysqladmin shutdown**)。 更改数据库目录和文件,允许*user_name*读写其中的文件(可能需要以Unix根用户身份完成这类设置): ~~~ shell> chown -R user_name /path/to/mysql/datadir ~~~ 如果未这样做,当以*user_name*身份运行时,服务器无法访问数据库或表。 如果MySQL数据目录下的目录或文件采用的是符号链接,还需跟踪这些链接,并更改它们指向的目录和文件。chown –R可帮助你跟踪符号链接。 以*user_name*身份启动服务器。如果你正在使用MySQL 3.22或更高版本,另一种可选方式是,以Unix根用户身份启动**mysqld**,并使用--user=*user_name*选项。**mysqld**启动,然后在接受任何连接前,切换至Unix用户*user_name*并以该用户身份运行。 要想在系统启动时自动以给定的用户身份启动服务器,可在服务器数据目录下的/etc/my.cnf选项文件或my.cnf选项文件的[mysqld]组中,通过增加用户选项来指定用户名。。例如: ~~~ [mysqld] ~~~ ~~~ user=user_name ~~~ 如果你的Unix机器本身并不安全,应在授权表中为MySQL根账户指定密码。如不然,任何在该机器上具有登录账户的用户都能使用“--user=root”选项运行**mysql**客户端,并执行任何操作。在任何情况下均应为MySQL账户指定密码,这是个好主意,尤其是在服务器主机上存在其他登录账户时,更是如此。请参见[2.9节,“安装后的设置和测试”](# "2.9. Post-Installation Setup and Testing")。 ### A.3.3. 与文件许可有关的问题 如果遇到与文件许可有关的问题,可能数启动mysqld时UMASK环境变量设置得不正确。例如,当你创建表时,MySQL可能会发出下述错误消息: ~~~ ERROR: 无法找到文件:'path/with/filename.frm' (Errcode: 13) ~~~ UMASK的默认值是0660。通过下述方式启动**mysqld_safe**,可改变该情况: ~~~ shell> UMASK=384  # = 600 in octal ~~~ ~~~ shell> export UMASK ~~~ ~~~ shell> mysqld_safe & ~~~ 在默认情况下,MySQL用0700的许可创建数据库和RAID目录。你可以通过设置UMASK_DIR变量更改该行为。如果你设置了它的值,将使用组合的UMASK和UMASK_DIR值创建新目录。例如,如果你打算为所有新的目录授予组访问权限,可: ~~~ shell> UMASK_DIR=504  # = 770 in octal ~~~ ~~~ shell> export UMASK_DIR ~~~ ~~~ shell> mysqld_safe & ~~~ 在MySQL 3.23.25和更高版本中,如果是以0开始的,MySQL将认为UMASK和UMASK_DIR的值均采用八进制形式。 请参见[附录F:](#)[*环境变量*](# "Appendix F. Environment Variables")。 ### A.4. 与管理有关的事宜 [A.4.1. 如何复位根用户密码](#)[A.4.2. 如果MySQL依然崩溃,应作些什么](#)[A.4.3. MySQL处理磁盘满的方式](#)[A.4.4. MySQL将临时文件储存在哪里](#)[A.4.5. 如何保护或更改MySQL套接字文件/tmp/mysql.sock``](#)[A.4.6. 时区问题](#) ### A.4.1. 如何复位根用户密码 如果你从未为MySQL设置根用户密码,服务器在以根用户身份进行连接时不需要密码。但是,建议你为每个账户设置密码。请参见[5.6.1节,“通用安全指南”](# "5.6.1. General Security Guidelines")。 如果你以前设置了根用户密码,但却忘记了该密码,可设置新的密码。下述步骤是针对Windows平台的。在本节后面的内容中,介绍了针对Unix平台的步骤。 在Windows平台下,该步骤是: 以系统管理员身份登录到系统。 如果MySQL服务器正在运行,停止它。对于作为Windows服务运行的服务器,进入服务管理器: ~~~ 开始菜单->控制面板->管理工具->服务 ~~~ 然后在列表中找出MySQL服务器,并停止它。 如果服务器不是作为服务而运行的,可能需要使用任务管理器来强制停止它。 创建1个文本文件,并将下述命令置于单一行中: ~~~ SET PASSWORD FOR 'root'@'localhost' = PASSWORD('MyNewPassword'); ~~~ 用任意名称保存该文件。在本例中,该文件为C:\mysql-init.txt。 打开控制台窗口,进入DOS命令提示: ~~~ 开始菜单->运行-> cmd ~~~ 假定你已将MySQL安装到C:\mysql。如果你将MySQL安装到了另一位置,请对下述命令进行相应的调整。 在DOS命令提示符下,执行命令: ~~~ C:\> C:\mysql\bin\mysqld-nt --init-file=C:\mysql-init.txt ~~~ 在服务器启动时,执行由“--init-file”选项命名的文件的内容,更改根用户密码。当服务器成功启动后,应删除C:\mysql-init.txt。 如果你使用MySQL安装向导安装了MySQL,或许需要指定“--defaults-file”选项: ~~~ C:\> C:\Program Files\MySQL\MySQL Server 5.1\bin\mysqld-nt.exe ~~~ ~~~          --defaults-file="C:\Program Files\MySQL\MySQL Server 5.1\my.ini" ~~~ ~~~          --init-file=C:\mysql-init.txt ~~~ 使用服务管理器,可找到恰当的“--defaults-file”设置: ~~~ 开始菜单->控制面板->管理工具->服务 ~~~ 在列表中找出MySQL服务,右击,并选择“属性”选项。在可执行字段的Path(路径)中包含“--defaults-file”设置。 停止MySQL服务器,然后在正常模式下重启它。如果以服务方式运行服务器,应从Windows服务窗口启动它。如果以手动方式启动了服务器,能够像正常情形下一样使用命令。 应能使用新密码进行连接。 在Unix环境下,重置根用户密码的步骤如下: 以Unix根用户身份、或以运行mysqld服务器的相同身份登录到系统。 找到包含服务器进程ID的.pid文件。该文件的准确位置和名称取决于你的分发版、主机名和配置。常见位置是/var/lib/mysql/、/var/run/mysqld/和/usr/local/mysql/data/。一般情况下,文件名的扩展名为.pid,并以mysqld或系统的主机名开始。 在下述命令中使用.pid文件的路径名,向**mysqld**进程发出正常的kill(而不是kill -9),可停止MySQL服务器: ~~~ shell> kill `cat /mysql-data-directory/host_name.pid` ~~~ 注意,cat命令使用符号“`”而不是“’”:这会使cat的输出代入到kill命令中。 创建文本文件,并将下述命令放在文件内的1行上: ~~~ SET PASSWORD FOR 'root'@'localhost' = PASSWORD('MyNewPassword'); ~~~ 用任意名称保存文件。对于本例,文件为~/mysql-init。 用特殊的“--init-file=~/mysql-init”选项重启MySQL服务器: ~~~ shell> mysqld_safe --init-file=~/mysql-init & ~~~ 文件init-file的内容在服务器启动时执行,更改根用户密码。服务器成功启动后,应删除~/mysql-init。 应能使用新密码进行连接。 作为可选方式,在任何平台上,可使用**mysql**客户端设置新密码(但该方法不够安全): 停止**mysqld**,并用“--skip-grant-tables --user=root”选项重启它(Windows用户可省略--user=root部分)。 使用下述命令连接到**mysqld**服务器: ~~~ shell> mysql -u root ~~~ 在**mysql**客户端发出下述语句: ~~~ mysql> UPDATE mysql.user SET Password=PASSWORD('newpwd') ~~~ ~~~     ->                   WHERE User='root'; ~~~ ~~~ mysql> FLUSH PRIVILEGES; ~~~ 用打算使用的实际根用户密码替换“*newpwd*”。 应能使用新密码进行连接。 ### A.4.2. 如果MySQL依然崩溃,应作些什么 正式发布之前,每个MySQL版本均在很多平台上进行了测试。这不表示MySQL中不存在缺陷,但是,如果存在缺陷,它们应很少,而且很难发现。如果你遇到问题,如果你尝试找出导致系统崩溃的准确原因,这始终很有帮助,这是因为,如果这样的话,快速解决问题的机会很大。 首先,应尝试找出问题是否与**mysqld**服务器有关,或是否与客户端有关。通过执行**mysqladmin version**,可检查**mysqld**服务器运行了多长时间。如果**mysqld**宕机并重启,应查看服务器的错误日志以找出原因。请参见[5.11.1节,“错误日志”](# "5.11.1. The Error Log")。 在某些系统上,在错误日志中,可发现**mysqld**宕机的堆栈跟踪,可使用resolve_stack_dump程序解决它。请参见[E.1.4节,“使用堆栈跟踪”](# "E.1.4. Using a Stack Trace")。注意,错误日志中的变量值并非始终是100%正确的。 很多服务器崩溃是因损坏的数据文件或索引文件而导致的,每次执行完SQL语句之后并在向客户端通告结果之前,MySQL将使用write()系统调用更新磁盘上的文件(如果你使用了“--delay-key-write”选项,情况并非如此,此时将写入数据文件而不是索引文件)。这意味着,即使mysqld崩溃,数据文件的内容也是安全的,这是因为操作系统能保证将未刷新的数据写入磁盘。使用“--flush”选项启动**mysqld**,这样,每次执行完SQL语句后,可强制MySQL将所有内容写入磁盘。 前述介绍表明,在正常情况下不会出现损坏的表,除非出现了下述情况之一: 在更新过程中,MySQL服务器或服务器主机被停止。 你发现了**mysqld**中存在的1个缺陷,该缺陷导致**mysqld**在更新中途中止。 在**mysqld**操作的同时,某些外部程序正在操控数据文件或索引文件,未恰当锁定表。 你正使用系统上的相同数据目录运行很多**mysqld**服务器,该系统不支持良好的文件系统锁定(通常是由lockd锁定管理器负责的),或使用“--skip-external-locking”选项运行了多个服务器。 崩溃的数据文件或索引文件,其中包含导致mysqld混乱的损坏很严重的数据。 在数据存储节点发现缺陷。这种可能性不大,但至少是可能的。在该情况下,可在修复的表副本上,通过使用ALTER TABLE,尝试将表类型更改为另一种存储引擎。 由于很难得知为什么某事会出现崩溃,首先请检查用于其他方面的事项是否崩溃。请尝试采取下述措施: 用**mysqladmin shutdown**停止**mysqld**服务器,从数据目录运行**myisamchk --silent --force */*.MYI**,检查所有的MyISAM表,并重启**mysqld**。这样,就能确保从干净的状态运行服务器。请参见[第5章:数据库管理](# "Chapter 5. Database Administration")。 使用“--log”选项启动**mysqld**,并根据写入日志的信息确定是否某些特殊的查询杀死了服务器。约95%的缺陷与特定的查询有关。正常情况下,这是服务器重启前日志文件中最够数个查询中的1个。请参见[5.11.2节,“通用查询日志”](# "5.11.2. The General Query Log")。如果能够用特殊查询重复杀死MySQL,即使在发出查询前检查了所有表的情况下也同样,那么你就应能确定缺陷,并应提交关于该缺陷的缺陷报告。请参见[1.7.1.3节,“如何通报缺陷和问题”](# "1.7.1.3. How to Report Bugs or Problems")。 尝试提供一个测试范例,我们应能利用该范例重复问题。请参见[E.1.6节,“如果出现表崩溃,请生成测试案例”](# "E.1.6. Making a Test Case If You Experience Table Corruption")。 请在mysql-test目录下并根据MySQL基准进行测试。请参见[27.1.2节,“MySQL测试套件”](# "27.1.2. MySQL Test Suite")。它们能相当良好地测试MySQL。你也可以为基准测试增加代码,以模拟你的应用程序。基准测试可在源码分发版的sql-bench目录下找到,对于二进制分发版,可在MySQL安装目录下的sql-bench目录下找到。 尝试使用fork_big.pl脚本(它位于源码分发版的测试目录下)。 如果你将MySQL配置为调试模式,如果某事出错,可更为容易地搜集关于可能错误的信息。如果将MySQL配置为调试模式,可生成1个安全的内存分配程序,可使用它发现某些错误。此外,它还提供了很多输出,这类输出与出现的问题相关。在**configure**上使用“--with-debug”或“--with-debug=full”选项重新配置MySQL,然后再编译它。请参见[E.1节,“调试MySQL服务器”](# "E.1. Debugging a MySQL Server")。 确保为你的操作系统应用了最新的补丁。 对**mysqld**使用“--skip-external-locking”选项。在某些系统上,lockd锁定管理器不能正确工作,“--skip-external-locking”选项通知**mysqld**不使用外部锁定。(这意味着,你不能在相同的数据目录上运行2个**mysqld**服务器,如果使用**myisamchk**,必须谨慎。然而,尝试将该选项用作测试也是有益的)。 当mysqld看上去正在运行但并未响应时,是否运行了**mysqladmin -u root processlist**?某些时候,即使你认为**mysqld**处于闲置状态时,实际情况并非如此。问题可能是因为所有连接均已使用,或存在某些内部锁定问题。即使在该情况下,**mysqladmin -u root processlist**通常能够进行连接,并能提供关于当前连接数以及其状态的有用信息。 在运行其他查询的同时,在单独的窗口中运行命令**mysqladmin -i 5 status**或**mysqladmin -i 5 -r status**,以生成统计信息。 尝试采用下述方法: 从gdb(或另一个调试器)启动**mysqld**。请参见[E.1.3节,“在gdb环境下调试m**ysqld**”](# "E.1.3. Debugging mysqld under gdb")。 运行测试脚本。 在3个较低层面上输出backtrace(向后跟踪)和局部变量。在**gdb**中,当mysqld在gdb内崩溃时,可使用下述命令完成该任务: ~~~ backtrace ~~~ ~~~ info local ~~~ ~~~ up ~~~ ~~~ info local ~~~ ~~~ up ~~~ ~~~ info local ~~~ 使用**gdb**,你还能检查与info线程共存的线程,并切换至特定的线程N,其中,N是线程ID。 尝试用Perl脚本模拟你的应用程序,强制MySQL崩溃或行为异常。 发送正常的缺陷报告。请参见[1.7.1.3节,“如何通报缺陷和问题”](# "1.7.1.3. How to Report Bugs or Problems")。应比通常的报告更详细。由于MySQL是为很多人提供服务的,它可能因仅存在于你的计算机上的某事崩溃(例如,与你的特定系统库有关的错误)。 如果你遇到与包含动态长度行的表有关的问题,而且你仅使用VARCHAR列(而不是BLOB或TEXT列),可尝试用ALTER TABLE将所有VARCHAR列更改为CHAR列。这样,就会强制MySQL使用固定大小的行。固定大小的行占用的空间略多,但对损坏的容忍度更高。 目前的动态行代码在MySQL AB已使用多年,很少遇到问题,但从本质上看,动态长度行更倾向于出现错误,因此,不妨尝试采用该策略以查看它是否有帮助,这不失为一个好主意。 诊断问题时不要将你的服务器硬件排除在外。有缺陷的硬件能够导致数据损坏。对硬件进行故障诊断与排除操作时,尤其应注意RAM和硬盘驱动器。 ### A.4.3. MySQL处理磁盘满的方式 在本节中,介绍了MySQL响应磁盘满错误的方式(如“设备上无剩余空间”),以及响应超配额错误的方式(如“写入失败”或“达到了用户屏蔽限制”)。 本节介绍的内容与写入MyISAM表有关。它也适用于写入二进制日志文件和二进制索引文件,但对“row”和“record”的应用应被视为“event”。 出现磁盘满状况时,MySQL将: 每分钟检查一次,查看是否有足够空间写入当前行。如果有足够空间,将继续,就像什么也未发生一样。 每10分钟将1个条目写入日志文件,提醒磁盘满状况。 为了减轻问题,可采取下述措施: 要想继续,仅需有足够的磁盘空间以插入所有记录。 要想放弃线程,必须使用**mysqladmin kill**。下次检查磁盘时将放弃线程(1分钟)。 其他线程可能会正在等待导致磁盘满状况的表。如果有数个“已锁定”的线程,杀死正在磁盘满状况下等待的某一线程,以便允许其他线程继续。 对前述行为的例外是,当你使用REPAIR TABLE或OPTIMIZE TABLE时,或当索引是在LOAD DATA INFILE或ALTER TABLE语句后、在批操作中创建的。所有这些语句能创建大的临时文件,如果保留这些文件,会导致系统其他部分出现大问题。如果在MySQL执行这类操作的同时磁盘已满,它将删除大的临时文件,并将表标注为崩溃。但对于ALTER TABLE例外,旧表保持不变。 ### A.4.4. MySQL将临时文件储存在哪里 MySQL使用环境变量TMPDIR的值作为保存临时文件的目录的路径名。如果未设置TMPDIR,MySQL将使用系统的默认值,通常为/tmp、/var/tmp或/usr/tmp。如果包含临时文件目录的文件系统过小,可对mysqld使用“—tmpdir”选项,在具有足够空间的文件系统内指定1个目录。 在MySQL 5.1中,“—tmpdir”选项可被设置为数个路径的列表,以循环方式使用。在Unix平台上,路径用冒号字符“:”隔开,在Windows、NetWare和OS/2平台上,路径用分号字符“;”隔开。注意,为了有效分布负载,这些路径应位于不同的物理磁盘上,而不是位于相同磁盘的不同分区中。 如果MySQL服务器正作为复制从服务器使用,不应将“--tmpdir”设置为指向基于内存的文件系统的目录,或当服务器主机重启时将清空的目录。对于复制从服务器,需要在机器重启时仍保留一些临时文件,以便能够复制临时表或执行LOAD DATA INFILE操作。如果在服务器重启时丢失了临时文件目录下的文件,复制将失败。 MySQL会以隐含方式创建所有的临时文件。这样,就能确保中止**mysqld**时会删除所有临时文件。使用隐含文件的缺点在于,在临时文件目录所在的位置中,看不到占用了文件系统的大临时文件。 进行排序时(ORDER BY或GROUP BY),MySQL通常会使用1个或多个临时文件。所需的最大磁盘空间由下述表达式决定: ~~~ (length of what is sorted + sizeof(row pointer)) ~~~ ~~~ * number of matched rows ~~~ ~~~ * 2 ~~~ “row pointer”(行指针)的大小通常是4字节,但在以后,对于大的表,该值可能会增加。 对于某些SELECT查询,MySQL还会创建临时SQL表。它们不是隐含表,并具有SQL_*形式的名称。 ALTER TABLE会在与原始表目录相同的目录下创建临时表。 ### A.4.5. 如何保护或更改MySQL套接字文件/tmp/mysql.sock`` 对于服务器用来与本地客户端进行通信的Unix套接字文件,其默认位置是/tmp/mysql.sock。这有可能导致问题,原因在于,在某些版本的Unix上,任何人都能删除/tmp目录下的文件。 在大多数Unix版本中,可对/tmp目录进行保护,使得文件只能被其所有这或超级用户(根用户)删除。为此,以根用户身份登录,并使用下述命令在/tmp目录上设置粘着位: ~~~ shell> chmod +t /tmp ~~~ 通过执行ls -ld /tmp,可检查是否设置了粘着位。如果最后一个许可字符是“t”,表明设置了粘着位。 另一种方法是改变服务器创建Unix套接字文件的位置。如果进行了这类操作,还应让客户端程序知道文件的位置。能够以多种不同方式指定文件位置: 在全局或局部选项文件中指定路径。例如,将下述行置于文件/etc/my.cnf中: ~~~ [mysqld] ~~~ ~~~ socket=/path/to/socket ~~~ ~~~   ~~~ ~~~ [client] ~~~ ~~~ socket=/path/to/socket ~~~ 请参见[4.3.2节,“使用选项文件”](# "4.3.2. Using Option Files")。 在运行客户端程序时,在命令行上为**mysqld_safe指定**“--socket”选项。 将MYSQL_UNIX_PORT环境变量设置为Unix套接字文件的路径。 重新从源码编译MySQL,以使用不同的默认Unix套接字文件位置。运行**configure**时,用“--with-unix-socket-path”选项定义文件路径。请参见[2.8.2节,“典型配置选项****”](# "2.8.2. Typical configure Options")。 用下述命令连接服务器,能够测试新的套接字位置是否工作: ~~~ shell> mysqladmin --socket=/path/to/socket version ~~~ ### A.4.6. 时区问题 如果遇到与SELECT NOW()有关的问题,它返回GMT值而不是当地时间,就应通知服务器你的当前失去。如果UNIX_TIMESTAMP()返回错误值,上述方式同样适用。应为服务器所运行的环境进行这类设置,例如,在**mysqld_safe**或**mysql.server**中。请参见[附录F:](#)[*环境变量*](# "Appendix F. Environment Variables")。 也可以对**mysqld_safe**使用“--timezone=*timezone_name*”选项,为服务器设置失去。也可以在启动mysqld之前,通过设置TZ环境变量完成该设置。 “--timezone”或TZ的允许值与系统有关。关于可接受的值,请参见操作系统文档。 ### A.5. 与查询有关的事宜 [ A.5.1. 搜索中的大小写敏感性](#)[ A.5.2. 使用DATE列方面的问题``](#)[ A.5.3. 与NULL值有关的问题``](#)[ A.5.4. 与列别名有关的问题](#)[ A.5.5. 非事务表回滚失败](#)[ A.5.6. 从相关表删除行](#)[ A.5.7. 解决与不匹配行有关的问题](#)[ A.5.8. 与浮点比较有关的问题](#) ### A.5.1. 搜索中的大小写敏感性 在默认情况下,MySQL搜索不区分大小写(但某些字符集始终区分大小写,如czech)。这意味着,如果你使用*col_name* LIKE 'a%'进行搜索,你将获得以A或a开始的所有列。如果打算使搜索区分大小写,请确保操作数之一具有区分大小写的或二进制校对。例如,如果你正在比较均适用latin1字符集的列和字符串,可使用COLLATE操作符,使1个操作数具有latin1_general_cs或latin1_bin校对特性。例如: ~~~ col_name COLLATE latin1_general_cs LIKE 'a%' ~~~ ~~~ col_name LIKE 'a%' COLLATE latin1_general_cs ~~~ ~~~ col_name COLLATE latin1_bin LIKE 'a%' ~~~ ~~~ col_name LIKE 'a%' COLLATE latin1_bin ~~~ 如果希望总是以区分大小写的方式处理列,可使用区分大小写的或二进制校对声明它。请参见[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")。 简单的比较操作(>=, >, =, <, <=, 排序和分组)基于每个字符的“排序值”。具有相同排序值的字符(如‘E’, ‘e’,和‘é’)将被当作相同的写字符。 ### A.5.2. 使用DATE列方面的问题`` DATE值的格式是'YYYY-MM-DD'。按照标准的SQL,不允许其他格式。在UPDATE表达式以及SELECT语句的WHERE子句中应使用该格式。例如: ~~~ mysql> SELECT * FROM tbl_name WHERE date >= '2003-05-05'; ~~~ 为了方便,如果日期是在数值环境下使用的,MySQL会自动将日期转换为数值(反之亦然)。它还具有相当的智能,在更新时或在与TIMESTAMP、DATE或DATETIME列比较日期的WHERE子句中,允许“宽松的”字符串形式(“宽松形式”表示,任何标点字符均能用作各部分之间的分隔符。例如,'2004-08-15'和'2004#08#15'是等同的)。MySQL还能转换不含任何分隔符的字符串(如'20040815'),前体是它必须是有意义的日期。 使用<、<=、=、>=、>、或BETWEEN操作符将DATE、TIME、DATETIME或TIMESTAMP与常量字符串进行比较时,MySQL通常会将字符串转换为内部长整数,以便进行快速比较(以及略为“宽松”的字符串检查)。但是,该转换具有下述例外: 比较两列时 将DATE、TIME、DATETIME或TIMESTAMP列与表达式进行比较时 使用其他比较方法时,如IN或STRCMP()。 对于这些例外情形,会将对象转换为字符串并执行字符串比较,采用该方式进行比较。 为了保持安全,假定按字符串比较字符串,如果你打算比较临时值和字符串,将使用恰当的字符串函数。 对于特殊日期'0000-00-00',能够以'0000-00-00'形式保存和检索。在MyODBC中使用'0000-00-00'日期时,对于MyODBC 2.50.12或更高版本,该日期将被自动转换为NULL,这是因为ODBC不能处理这类日期。 由于MySQL能够执行前面所介绍的转换,下述语句均能正常工作: ~~~ mysql> INSERT INTO tbl_name (idate) VALUES (19970505); ~~~ ~~~ mysql> INSERT INTO tbl_name (idate) VALUES ('19970505'); ~~~ ~~~ mysql> INSERT INTO tbl_name (idate) VALUES ('97-05-05'); ~~~ ~~~ mysql> INSERT INTO tbl_name (idate) VALUES ('1997.05.05'); ~~~ ~~~ mysql> INSERT INTO tbl_name (idate) VALUES ('1997 05 05'); ~~~ ~~~ mysql> INSERT INTO tbl_name (idate) VALUES ('0000-00-00'); ~~~ ~~~   ~~~ ~~~ mysql> SELECT idate FROM tbl_name WHERE idate >= '1997-05-05'; ~~~ ~~~ mysql> SELECT idate FROM tbl_name WHERE idate >= 19970505; ~~~ ~~~ mysql> SELECT MOD(idate,100) FROM tbl_name WHERE idate >= 19970505; ~~~ ~~~ mysql> SELECT idate FROM tbl_name WHERE idate >= '19970505'; ~~~ 但是,下述语句不能正常工作: ~~~ mysql> SELECT idate FROM tbl_name WHERE STRCMP(idate,'20030505')=0; ~~~ STRCMP()是一种字符串函数,它能将idate转换为'YYYY-MM-DD'格式的字符串,并执行字符串比较。它不能将'20030505'转换为日期'2003-05-05'并进行日期比较。 如果你正在使用ALLOW_INVALID_DATES SQL模式,MySQL允许以仅执行给定的有限检查方式保存日期:MySQL仅保证天位于1~31的范围内,月位于1~12的范围内。 这样就使得MySQL很适合于Web应用程序,其中,你能获得三个不同字段中的年、月、日值,也能准确保存用户插入的值(无日期验证)。 如果未使用NO_ZERO_IN_DATE SQL模式,“天”和“月”部分可能为0。如果你打算将生日保存在DATE列而且仅知道部分日期,它十分方便。 如果未使用NO_ZERO_DATE SQL模式,MySQL也允许你将'0000-00-00'保存为“伪日期”。在某些情况下,它比使用NULL值更方便。 如果无法将日期转换为任何合理值,“0”将保存在DATE列中,并被检索为'0000-00-00'。这是兼顾速度和便利性的事宜。我们认为,数据库服务器的职责是检索与你保存的日期相同的日期(即使在任何情况下,数据在逻辑上不正确也同样)。我们认为,对日期的检查应由应用程序而不是服务器负责。 如果你希望MySQL检查所有日期并仅接受合法日期(除非由IGNORE覆盖),应将sql_mode设置为"NO_ZERO_IN_DATE,NO_ZERO_DATE"。 ### A.5.3. 与NULL值有关的问题`` 对于SQL的新手,NULL值的概念常常会造成混淆,他们常认为NULL是与空字符串''相同的事。情况并非如此。例如,下述语句是完全不同的: ~~~ mysql> INSERT INTO my_table (phone) VALUES (NULL); ~~~ ~~~ mysql> INSERT INTO my_table (phone) VALUES (''); ~~~ 这两条语句均会将值插入phone(电话)列,但第1条语句插入的是NULL值,第2条语句插入的是空字符串。第1种情况的含义可被解释为“电话号码未知”,而第2种情况的含义可被解释为“该人员没有电话,因此没有电话号码”。 为了进行NULL处理,可使用IS NULL和IS NOT NULL操作符以及IFNULL()函数。 在SQL中,NULL值与任何其它值的比较(即使是NULL)永远不会为“真”。包含NULL的表达式总是会导出NULL值,除非在关于操作符的文档中以及表达式的函数中作了其他规定。下述示例中的所有列均返回NULL: ~~~ mysql> SELECT NULL, 1+NULL, CONCAT('Invisible',NULL); ~~~ 如果打算搜索列值为NULL的列,不能使用expr = NULL测试。下述语句不返回任何行,这是因为,对于任何表达式,expr = NULL永远不为“真”: ~~~ mysql> SELECT * FROM my_table WHERE phone = NULL; ~~~ 要想查找NULL值,必须使用IS NULL测试。在下面的语句中,介绍了查找NULL电话号码和空电话号码的方式: ~~~ mysql> SELECT * FROM my_table WHERE phone IS NULL; ~~~ ~~~ mysql> SELECT * FROM my_table WHERE phone = ''; ~~~ 更多信息和示例,请参见[3.3.4.6节,“使用NULL值”](# "3.3.4.6. Working with NULL Values")。 如果你正在使用MyISAM、InnoDB、BDB、或MEMORY存储引擎,能够在可能具有NULL值的列上增加1条索引。如不然,必须声明索引列为NOT NULL,而且不能将NULL插入到列中。 用LOAD DATA INFILE读取数据时,对于空的或丢失的列,将用''更新它们。如果希望在列中具有NULL值,应在数据文件中使用\N。在某些情况下,也可以使用文字性单词“NULL”。请参见[13.2.5 “LOAD DATA INFILE语法”](# "13.2.5. LOAD DATA INFILE Syntax")。 使用DISTINCT、GROUP BY或ORDER BY时,所有NULL值将被视为等同的。 使用ORDER BY时,首先将显示NULL值,如果指定了DESC按降序排列,NULL值将最后显示。 对于聚合(累计)函数,如COUNT()、MIN()和SUM(),将忽略NULL值。对此的例外是COUNT(*),它将计数行而不是单独的列值。例如,下述语句产生两个计数。首先计数表中的行数,其次计数age列中的非NULL值数目: ~~~ mysql> SELECT COUNT(*), COUNT(age) FROM person; ~~~ 对于某些列类型,MySQL将对NULL值进行特殊处理。如果将NULL插入TIMESTAMP列,将插入当前日期和时间。如果将NULL插入具有AUTO_INCREMENT属性的整数列,将插入序列中的下一个编号。 ### A.5.4. 与列别名有关的问题 可以使用别名来引用GROUP BY、ORDER BY或HAVING子句中的列。别名也能用于为列提供更好的名称: ~~~ SELECT SQRT(a*b) AS root FROM tbl_name GROUP BY root HAVING root > 0; ~~~ ~~~ SELECT id, COUNT(*) AS cnt FROM tbl_name GROUP BY id HAVING cnt > 0; ~~~ ~~~ SELECT id AS 'Customer identity' FROM tbl_name; ~~~ 标准SQL不允许在WHERE子句中已用列别名。这是因为,执行WHERE代码时,可能尚未确定列值。例如,下述查询是非法的: ~~~ SELECT id, COUNT(*) AS cnt FROM tbl_name WHERE cnt > 0 GROUP BY id; ~~~ 执行WHERE语句以确定哪些行应被包含在GROUP BY部分中,而HAVING用于确定应使用结果集中的哪些行。 ### A.5.5. 非事务表回滚失败 执行ROLLBACK(回滚)时,如果收到下述消息,表示事务中使用的1个或多个表不支持事务: ~~~ 警告:某些更改的非事务性表不能被回滚。 ~~~ 这些非事务性表不受ROLLBACK语句的影响。 如果在事务中意外地混合了事务性表和非事务性表,导致该消息的最可能原因是,你认为本应是事务性的表实际上不是。如你试图使用mysqld服务器不支持的事务性存储引擎(或用启动选项禁止了它)创建表,就可能出现该情况。如果**mysqld**不支持存储引擎,它将以MyISAM表创建表,这是非事务性表。 可使用下述语句之一检查表的标类型: ~~~ SHOW TABLE STATUS LIKE 'tbl_name'; ~~~ ~~~ SHOW CREATE TABLE tbl_name; ~~~ 请参见[13.5.4.18节,“SHOW TABLE STATUS语法](# "13.5.4.18. SHOW TABLE STATUS Syntax")以及[13.5.4.5节,“SHOW CREATE TABLE语法”](# "13.5.4.5. SHOW CREATE TABLE Syntax")。 使用下述语句,可检查mysqld服务器支持的存储引擎: ~~~ SHOW ENGINES; ~~~ 也可以使用下述语句,检查与你感兴趣的存储引擎有关的变量值: ~~~ SHOW VARIABLES LIKE 'have_%'; ~~~ 例如,要想确定InnoDB存储引擎是否可用,可检查have_innodb变量的值。 请参见[13.5.4.8节,“SHOW ENGINES语法”](# "13.5.4.8. SHOW ENGINES Syntax")和[13.5.4.21节,“SHOW VARIABLES语法”](# "13.5.4.21. SHOW VARIABLES Syntax")。 ### A.5.6. 从相关表删除行 如果针对related_table的DELETE语句的总长度超过1MB(系统变量max_allowed_packet的默认值),应将其分为较小的部分,并执行多个DELETE语句。如果related_column是索引列,为每条语句指定100~1000个related_column值,或许能获得更快的DELETE速度。如果related_column不是索引列,速度与IN子句中的参量数目无关。 ### A.5.7. 解决与不匹配行有关的问题 如果有使用了很多表的复杂查询,但未返回任何行,应采用下述步骤找出什么出错: 用EXPLAIN测试查询,以检查是否发现某事显然出错。请参见[7.2.1节,“EXPLAIN语法(获取关于SELECT的信息)``”](# "7.2.1. EXPLAIN Syntax (Get Information About a SELECT)")。 仅选择在WHERE子句中使用的列。 从查询中1次删除1个表,直至返回了某些行为止。如果表很大,较好的主意是在查询中使用LIMIT 10。 对于具有与上次从查询中删除的表匹配的行的列,发出SELECT查询。 如果将FLOAT或DOUBLE列与具有数值类型的数值进行比较,不能使用等式(=)比较。在大多数计算机语言中,该问题很常见,这是因为,并非所有的浮点值均能以准确的精度保存。在某些情况下,将FLOAT更改为DOUBLE可更正该问题。请参见[A.5.8节,“与浮点比较有关的问题”](# "A.5.8. Problems with Floating-Point Comparisons")。 如果仍不能找出问题之所在,请创建能与显示问题的“mysql test < query.sql”一起运行的最小测试。通过使用**mysqldump --quick db_name *****tbl_name_1***** ... *****tbl_name_n***** > query.sql**转储表,可创建测试文件。在编辑器中打开文件,删除某些插入的行(如果有超出演示问题所需的行),并在文件末尾添加SELECT语句。 通过执行下述命令,验证测试文件能演示问题: ~~~ shell> mysqladmin create test2 ~~~ ~~~ shell> mysql test2 < query.sql ~~~ 使用**mysqlbug**将测试文件张贴到哟娜通用MySQL邮件列表。请参见[1.7.1.1节,“The MySQL邮件列表”](# "1.7.1.1. The MySQL Mailing Lists")。 ### A.5.8. 与浮点比较有关的问题 注意,下述部分主要与DOUBLE和FLOAT列相关,原因在于浮点数的不准确本质。MySQL使用64位十进制数值的精度执行DECIMAL操作,当处理DECIMAL列时,应能解决大多数常见的不准确问题。 浮点数有时会导致混淆,这是因为它们无法以准确值保存在计算机体系结构中。你在屏幕上所看到的值通常不是数值的准确值。对于FLOAT和DOUBLE列类型,情况就是如此。DECIMAL列能保存具有准确精度的值,这是因为它们是由字符串表示的。 在下面的示例中,介绍了使用DOUBLE时的问题: ~~~ mysql> CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE); ~~~ ~~~ mysql> INSERT INTO t1 VALUES (1, 101.40, 21.40), (1, -80.00, 0.00), ~~~ ~~~     -> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40), ~~~ ~~~     -> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00), ~~~ ~~~     -> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00), ~~~ ~~~     -> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20), ~~~ ~~~     -> (6, 0.00, 0.00), (6, -51.40, 0.00); ~~~ ~~~   ~~~ ~~~ mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b ~~~ ~~~     -> FROM t1 GROUP BY i HAVING a <> b; ~~~ ~~~   ~~~ ~~~ +------+-------+------+ ~~~ ~~~ | i    | a     | b    | ~~~ ~~~ +------+-------+------+ ~~~ ~~~ |    1 |  21.4 | 21.4 | ~~~ ~~~ |    2 |  76.8 | 76.8 | ~~~ ~~~ |    3 |   7.4 |  7.4 | ~~~ ~~~ |    4 |  15.4 | 15.4 | ~~~ ~~~ |    5 |   7.2 |  7.2 | ~~~ ~~~ |    6 | -51.4 |    0 | ~~~ ~~~ +------+-------+------+ ~~~ 结果是正确的。尽管前5个记录看上去不应能进行比较测试(a和b的值看上去没有什么不同),但它们能进行比较,这是因为显示的数值间的差异在十分位左右,具体情况取决于计算机的体系结构。 如果列d1和d2定义为DECIMAL而不是DOUBLE,SELECT查询的结果仅包含1行,即上面显示的最后1行。 ### A.6. 与优化器有关的事宜 MySQL采用了基于开销的优化器,以确定处理查询的最解方式。在很多情况下,MySQL能够计算最佳的可能查询计划,但在某些情况下,MySQL没有关于数据的足够信息,不得不就数据进行“有教养”的估测。 当MySQL未能做“正确的”事时,可使用下述工具来帮助MySQL: 使用EXPLAIN语句获取关于MySQL如何处理查询的信息。要想使用它,可在SELECT语句前添加关键字EXPLAIN: ~~~ mysql> EXPLAIN SELECT * FROM t1, t2 WHERE t1.i = t2.i; ~~~ 关于EXPLAIN的详细讨论,请参见[7.2.1节,“EXPLAIN语法(获取关于SELECT的信息)``”](# "7.2.1. EXPLAIN Syntax (Get Information About a SELECT)")。 使用ANALYZE TABLE * tbl_name*,为已扫描的表更新键分配。请参见[13.5.2.1节,“ANALYZE TABLE语法”](# "13.5.2.1. ANALYZE TABLE Syntax")。 为已扫描的表使用FORCE INDEX,通知MySQL:与使用给定的索引相比,表扫描开销昂贵。请参见[13.2.7节,“SELECT语法”](# "13.2.7. SELECT Syntax")。 ~~~ SELECT * FROM t1, t2 FORCE INDEX (index_for_column) ~~~ ~~~ WHERE t1.col_name=t2.col_name; ~~~ USE INDEX和IGNORE INDEX也有一定的帮助。 关于全局和表级别的STRAIGHT_JOIN。请参见[13.2.7节,“SELECT语法”](# "13.2.7. SELECT Syntax")。 你可以调节全局或线程类系统变量。例如,用“--max-seeks-for-key=1000”选项启动**mysqld**,或使用“SET max_seeks_for_key=1000”来通知优化器:假定任何表扫描均不会导致1000个以上的键搜索。请参见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。 ### A.7. 与表定义有关的事宜 [ A.7.1. 与ALTER TABLE有关的问题](#)[ A.7.2. 如何更改表中的列顺序](#)[A.7.3. TEMPORARY TABLE问题](#) ### A.7.1. 与ALTER TABLE有关的问题 ALTER TABLE将表更改为当前字符集。如果在执行ALTER TABLE操作期间遇到重复键错误,原因在于新的字符集将2个键映射到了相同值,或是表已损坏。在后一种情况下,应在表上运行REPAIR TABLE。 如果ALTER TABLE失败并给出下述错误,问题可能是因为在ALTER TABLE操作的早期阶段出现MySQL崩溃,没有名为A-*xxx*或B-*xxx*的旧表: ~~~ Error on rename of './database/name.frm' ~~~ ~~~ to './database/B-xxx.frm' (Errcode: 17) ~~~ 在该情况下,进入MySQL数据目录,并删除其名称为以A-或B-开始的所有文件(或许你希望将它们移动到其他地方而不是删除它们)。 ALTER TABLE的工作方式如下: - 用请求的结构变化创建名为A-*xxx*的新表。 - 将所有行从原始表拷贝到A-*xxx*。 - 将原始表重命名为B-*xxx*。 - 将A-*xxx*重命名为原始表的名称。 - 删除B-*xxx*。 如果在重命名操作中出错,MySQL将尝试撤销更改。如果错误很严重(尽管这不应出现),MySQL会将旧表保留为B-*xxx*。简单地在系统级别上重命名表文件,应能使数据复原。 如果在事务性表上使用ALTER TABLE,或正在使用Windows或OS/2操作系统,如果已在表上执行了LOCK TABLE操作,ALTER TABLE将对表执行解锁操作。这是因为InnoDB和这类操作系统不能撤销正在使用的表。 ### A.7.2. 如何更改表中的列顺序 首先,请考虑是否的确需要更改表中的列顺序。SQL的核心要点是从数据存储格式获取应用。总应指定检索数据的顺序。在下面的第1条语句中,以*col_name1*、*col_name2*、*col_name3*顺序返回列;在第2条语句中,以*col_name1*、*col_name3*、*col_name2*顺序返回列: ~~~ mysql> SELECT col_name1, col_name2, col_name3 FROM tbl_name; ~~~ ~~~ mysql> SELECT col_name1, col_name3, col_name2 FROM tbl_name; ~~~ 如果决定更改表列的顺序,可执行下述操作: 1. 用具有新顺序的列创建新表。 1. 执行该语句: ~~~ mysql> INSERT INTO new_table ~~~ ~~~     -> SELECT columns-in-new-order FROM old_table; ~~~ 1. 撤销或重命名old_table。 1. 将新表重命名为原始名称: ~~~ mysql> ALTER TABLE new_table RENAME old_table; ~~~ SELECT *十分适合于测试查询。但是,在应用程序中,永远不要依赖SELECT *的使用,不要依赖根据其位置检索列。如果添加、移动或删除了列,所返回的列的顺序和位置不会保持相同。对表结构的简单更改也会导致应用程序失败。 ### A.7.3. TEMPORARY TABLE问题 下面介绍了对使用TEMPORARY表的限制: - TEMPORARY表只能是HEAP、ISAM、MyISAM、MERGE、或InnoDB类型。 - 在相同的查询中,不能引用TEMPORARY表1次以上。例如,下例不能正常工作: ~~~ mysql> SELECT * FROM temp_table, temp_table AS t2; ~~~ ~~~ 错误1137:不能再次打开表:'temp_table' ~~~ - SHOW TABLES语句不会列出TEMPORARY表。 - 不能使用RENAME重命名TEMPORARY表。但能使用ALTER TABLE取而代之: ~~~ mysql> ALTER TABLE orig_name RENAME new_name; ~~~ ### A.8. MySQL中的已知事宜 [ A.8.1. MySQL中的打开事宜](#) 在本节中,列出了当前MySQL版本中的已知事宜。 关于平台相关事宜的更多信息,请参见[2.12节,“具体操作系统相关的注意事项”](# "2.12. Operating System-Specific Notes")和[附录E:](#)[*移植到其他系统*](# "Appendix E. Porting to Other Systems")中的安装和移植说明。 ### A.8.1. MySQL中的打开事宜 下面列出了已知问题,更正它们具有较高的优先级: - 如果将NULL值与使用ALL/ANY/SOME的子查询进行比较,而且子查询返回空的结果,比较操作会评估NULL的非标准结果而不是TRUE或FALSE。在MySQL 5.1中将更正该问题。 - 对于IN的线子查询优化不像“=”那样有效。 - 即使使用了lower_case_table_names=2(允许MySQL记住数据库名和表名使用的大小写),对于函数DATABASE()或在各种日志内(在不区分大小写的系统上),MySQL也不会记住数据库名使用的大小写情况。 - 在复制操作中,撤销FOREIGN KEY约束不工作,这是因为约束可能在从服务器上有另一个名称。 - REPLACE(以及具有REPLACE选项的LOAD DATA)不会触发ON DELETE CASCADE。 - 如果未使用所有列而且仅使用DISTINCT列表中的列,在GROUP_CONCAT()中,DISTINCT不能与ORDER BY一起工作。 - 如果1位用户拥有长时间运行的事务,而且另1位用户撤销了在事务中更新的某1表,那么在表用于事务本身之前,存在较小的机会,会在二进制日志中包含DROP TABLE命令。我们计划更正该问题,方法是让DROP TABLE命令等待,直至表未在任何事务中使用为止。 - 将大的整数值(介于263和264–1之间)插入数值或字符串列时,它将作为负值插入,这是因为该数值是在有符号整数环境下评估的。 - 如果服务器运行在不具备二进制日志功能的条件下,FLUSH TABLES WITH READ LOCK不能屏蔽COMMIT,执行完整备份时这可能会导致问题(表间的一致性问题)。 - 在某些情况下,作用在BDB表上的ANALYZE TABLE会导致表不可用,直至重启**mysqld**为止。如果出现该情况,请在MySQL错误文件中查找下述形式的错误: ~~~ 001207 22:07:56  bdb:  log_flush: LSN past current end-of-log ~~~ - 在所有事务完成之前,不要在BDB表(正在其上运行多语句事务)上执行ALTER TABLE(可能会忽略事务)。 - 对于正在使用INSERT DELAYED的表,在其上执行ANALYZE TABLE、OPTIMIZE TABLE和REPAIR TABLE时,可能会导致问题。 - 在表上执行LOCK TABLE ...和FLUSH TABLES ...时,不保证没有完成一半的事务。 - BDB表打开的速度相对较慢。如果你在数据库上有很多BDB表,如果未使用“-A”选项或正使用再混编功能,要想在数据库上使用mysql客户端,需要花费较长的时间。当你有大的表高速缓冲时,这点尤其明显。 - 复制功能采用了查询级日志功能:主服务器将已执行的查询写入二进制日志。这是一种速度很快、简洁和有效的记录方法,在大多数情况下工作良好。 如果以特定的方式设计查询,使得数据更改是非决定性(通常不推荐,即使在复制之外也同样),主服务器和从服务器上的数据将变得不同。 例如: > - 将0或NULL值插入AUTO_INCREMENT列中的CREATE ... SELECT或INSERT ... SELECT语句。 > - DELETE,如果从具有ON DELETE CASCADE属性的外键的表中删除行。 > - REPLACE ... SELECT、INSERT IGNORE ... SELECT,如果在插入的数据中具有重复键。 **当且仅当前述查询没有保证决定行顺序的****ORDER BY子句时。** 例如,对于不具有ORDER BY的INSERT ... SELECT,SELECT可能会以不同的顺序返回行(它会导致具有不同等级的行,从而导致AUTO_INCREMENT列中的不同数值),具体情况取决于优化器在主服务器和从服务器上所作的选择。 在主服务器和从服务器上,查询将进行不同的优化,仅当: > - 使用不同的存储引擎在主服务器上而不是从服务器上保存表。(能够在主服务器和从服务器上使用不同的存储引擎。例如,如果从服务器具有较少的可用磁盘空间,可以在主服务器上使用InnoDB,但在 从服务器桑使用MyISAM)。 > - 在主服务器和从服务器上,MySQL缓冲区大小是不同的(key_buffer_size等)。 > - 在主服务器和从服务器上运行不同的MySQL版本,版本间的优化器代码也不同。 该问题也会影响使用**mysqlbinlog|mysql**的数据库恢复。 避免该问题的最简单方法是,为前述的非决定性查询增加ORDER BY子句,以确保总是以相同的顺序保存或更改行。 在将来的MySQL版本中,需要时,我们将自动增加ORDER BY子句。 下面列出了已知的事宜,这些事宜将在恰当的时候更正: - 日志文件名基于服务器主机名(如果未使用启动选项指定文件名的话)。如果更改了主机名,你将不得不使用诸如“--log-bin=*old_host_name*-bin”等选下美国。另一种选择是重命名旧文件,以反映主机名变更情况(如果是二进制日志,需要编辑二进制日志索引文件,并更正binlog名称)。请参见[5.3.1节,“**mysqld**命令行选项”](# "5.3.1. mysqld Command-Line Options")。 - **Mysqlbinlog**不删除执行LOAD DATA INFILE命令后遗留的临时文件。请参见[8.6节,“mysqlbinlog:用于处理二进制日志文件的实用工具”](# "8.6. mysqlbinlog — Utility for Processing Binary Log Files")。 - RENAME不能与TEMPORARY表一起工作,也不能与MERGE表中使用的表一起工作。 - 由于表定义文件的保存方式,不能在表名、列名或枚举中使用字符255(CHAR(255))。按照安排,当我们实施了新的表定义格式文件时,将在5.1版中更正该问题。 - 使用SET CHARACTER SET时,不能在数据库、表和列名中使用转换的字符。 - 不能在LIKE ... ESCAPE中与ESCAPE一起使用‘_’或‘%’。 - 如果你有1个DECIMAL列,其中,相同的数值以不同的格式保存(例如,+01.00、1.00、01.00),GROUP BY可能会将每个值当作不同的值。 - 使用MIT-pthreads时,不能在另一个目录下创建服务器。这是因为它需要更改MIT-pthreads,我们不太会更正该问题。请参见[2.8.5 “MIT-pthreads注意事项”](# "2.8.5. MIT-pthreads Notes")。 - 在GROUP BY、ORDER BY或DISTINCT中,不能可靠地使用BLOB和TEXT值。在这类情况下,与BLOB值进行比较时,仅使用最前的max_sort_length字节。max_sort_length的默认值是1024,可在服务器启动时或运行时更改它。 - 数值计算是使用BIGINT或DOUBLE(正常情况下均为64位长)进行的。你所能获得的精度取决于函数。通用规则是位函数是按BIGINT精度执行的,IF和ELT()是按BIGINT或DOUBLE精度执行的,其余的函数是按DOUBLE精度执行的。对于除位字段外的其他数,如果大于63位(9223372036854775807),应避免使用无符号长long值。 - 在1个表中,最多能有255个ENUM和SET列。 - 在MIN()、MAX()以及其他聚合函数中,MySQL目前会根据其字符串值比较ENUM和SET列,而不是根据字符串在集合中的相对位置。 - **mysqld_safe**会将来自**mysqld**的所有消息再定向到**mysqld**日志。与之相关的1个问题是,如果你执行**mysqladmin refresh**关闭并再次打开日志,stdout和stderr仍会被重定向到旧的日志。如果你以广义方式使用“--log”,应编辑**mysqld_safe**以记录到*host_name*.err而不是*host_name*.log,以便通过删除它并执行**mysqladmin refresh**,方便地收回为旧日志分配的空间。 - 在UPDATE语句中,列从左向右更新。如果引用了已更新的列,你将得到更新值而不是原始值。例如,下述语句会将KEY增加2,而不是1: ~~~ mysql> UPDATE tbl_name SET KEY=KEY+1,KEY=KEY+1; ~~~ - 你可以在相同查询中引用多个临时表,但不能引用任何给定的临时表1次以上。例如,下述语句不能正常工作: ~~~ mysql> SELECT * FROM temp_table, temp_table AS t2; ~~~ ~~~ 错误1137:不能再次打开表:'temp_table' ~~~ - 当你在联合操作中使用“隐含”列时,与未使用隐含列相比,优化器将以不同的方式处理DISTINCT。在联合操作中,隐含列将作为结果的组成部份计数(即使未显示),但在正常查询中,隐含列不参与DISTINCT比较。在以后,我们可能会更改该情况,在执行DISTINCT时不比较隐含列。 例如: ~~~ SELECT DISTINCT mp3id FROM band_downloads ~~~ ~~~        WHERE userid = 9 ORDER BY id DESC; ~~~ 以及 ~~~ SELECT DISTINCT band_downloads.mp3id ~~~ ~~~        FROM band_downloads,band_mp3 ~~~ ~~~        WHERE band_downloads.userid = 9 ~~~ ~~~        AND band_mp3.id = band_downloads.mp3id ~~~ ~~~        ORDER BY band_downloads.id DESC; ~~~ 在第2种情况下,使用MySQL服务器3.23.x,可在结果集中获得2个等同行(这是因为,隐藏ID列中的值可能不同)。 注意,在结果集中,仅对不含ORDER BY列的查询才会出现该情况。 - 如果在返回空集的查询上执行PROCEDURE,在某些情况下,PROCEDURE不转换列。 - 创建具有MERGE类型的表时,不检查基本表是否具有兼容的类型。 - 如果使用ALTER TABLE为MERGE表中使用的表增加了UNIQUE索引,然后在MERGE表上增加了正常索引,如果在表中存在旧的、非UNIQUE键,对于这些表,键顺序是不同的。这是因为,ALTER TABLE会将UNIQUE索引放在正常索引之前,以便能尽早检测到重复的键。   这是MySQL参考手册的翻译版本,关于MySQL参考手册,请访问[dev.mysql.com](http://dev.mysql.com/doc/mysql/en)。原始参考手册为英文版,与英文版参考手册相比,本翻译版可能不是最新的。