MySQL中文参考手册---18 问题和常见错误
18.1 如果MySQL总是崩溃怎么办所有MySQL在发布它们之前,在许多平台上被测试。这并不意味着在MySQL中没有任何错误,但是如果有错误,它们是很少的并且很能难发现。如果你有一个问题并且如果你尝试找出究竟是什么摧毁了你的系统,它将总是有帮助的,因为你将有一个更好机会使得它被快速修复。 首先你应该试着找出问题mysqld守护进程是否死掉或你的问题是否与你的客户有关。你可以用mysqladmin version检查你的mysqld服务器正常执行了多长时间,如果mysqld死了,你可以在文件“mysql-data-directory/'hostname'.err”中找到其原因。 因为很难知道一些东西为什么崩溃,首先试着检查为其它人工作的东西是否使你崩溃。请尝试下列事情:
18.2 使用MySQL时的一些常见错误18.2.1 MySQL server has gone away错误本小节也涉及有关Lost connection to server during query的错误。 对MySQL server has gone away错误最常见的原因是服务器超时了并且关闭了连接。缺省地,如果没有事情发生,服务器在 8个小时后关闭连接。你可在启动mysqld时通过设置wait_timeout变量改变时间限制。 你可以通过执行mysqladmin version并且检验正常运行的时间来检查MySQL还没死掉。 如果你有一个脚本,你只须再发出查询让客护进行一次自动的重新连接。 在这种请下,你通常能获得下列错误代码(你得到的是OS相关的):
如果你向服务器发送不正确的或太大的查询,你也可能得到这些错误。如果mysqld得到一个太大或不正常的包,它认为客户出错了并关闭连接。如果你需要较大的查询(例如,如果你正在处理较大的BLOB列),你可以使用-O max_allowed_packet=#选项(缺省1M)启动mysqld以增加查询限制。多余的内存按需分配,这样mysqld只有在你发出较大差询时或mysqld必须返回较大的结果行时,才使用更多的内存! 18.2.2 Can't connect to [local] MySQL server错误一个MySQL客户可以两种不同的方式连接mysqld服务器:Unix套接字,它通过在文件系统中的一个文件(缺省“/tmp/mysqld.sock”)进行连接;或TCP/IP,它通过一个端口号连接。Unix套接字比TCP/IP更快,但是只有用在连接同一台计算机上的服务器。如果你不指定主机名或如果你指定特殊的主机名localhost,使用Unix套接字。 错误(2002)Can't connect to ...通常意味着没有一个MySQL服务器运行在系统上或当试图连接mysqld服务器时,你正在使用一个错误的套接字文件或TCP/IP端口。 由检查(使用ps)在你的服务器上有一个名为mysqld的进程启动!如果没有任何mysqld过程,你应该启动一个。见4.15.2 启动MySQL服务器的问题。 如果一个mysqld过程正在运行,你可以通过尝试这些不同的连接来检查服务器(当然,端口号和套接字路径名可能在你的安装中是不同的): shell> mysqladmin versionshell> mysqladmin variablesshell> mysqladmin -h `hostname` version variablesshell> mysqladmin -h `hostname` --port=3306 versionshell> mysqladmin -h 'ip for your host' versionshell> mysqladmin --socket=/tmp/mysql.sock version 注意hostname命令使用反引号“`”而非正引号“'”;这些导致hostname输出(即,当前主机名)被代替进mysqladmin命令中。 这是可能造成Can't connect to local MySQL server错误的一些原因:
如果你得到错误Can't connect to MySQL server on some_hostname,你可以尝试下列步骤找出问题是什么:
18.2.3 Host '...' is blocked错误如果你得到象这样的一个错误: Host 'hostname' is blocked because of many connection errors.Unblock with 'mysqladmin flush-hosts' 这意味着,mysqld已经得到了大量(max_connect_errors)的主机'hostname'的在中途被中断了的连接请求。在max_connect_errors次失败请求后,mysqld认定出错了(象来字一个黑客的攻击),并且阻止该站点进一步的连接,直到某人执行命令mysqladmin flush-hosts。 缺省地,mysqld在10个连接错误后阻塞一台主机。你可以通过象这样启动服务器很容易地调整它: shell> safe_mysqld -O max_connect_errors=10000 & 注意,对给定的主机,如果得到这条错误消息,你应该首先检查该主机的TCP/IP连接有没有问题。如果你的TCP/IP连接不在运行,增加max_connect_errors变量的值对你也不会有帮助! 18.2.4 Too many connections错误如果在你试土连接MySQL时,你得到错误Too many connections,这意味着已经有max_connections个客户连接了mysqld服务器。 如果你需要比缺省(100)更多的连接,那么你应该重启mysqld,用更大的 max_connections 变量值。 注意,mysqld实际上允许(max_connections+1)个客户连接。最后一个连接是为一个用Process权限的用户保留的。通过不把这个权限给一般用户(他们不应该需要它),有这个权限一个管理员可以登录并且使用SHOW PROCESSLIST找出什么可能出错。见7.21 SHOW句法(得到表,列的信息)。 18.2.5 Out of memory错误如果你发出查询并且得到类似于下面的错误: mysql: Out of memory at line 42, 'malloc.c'mysql: needed 8136 byte (8k), memory in use: 12481367 bytes (12189k)ERROR 2008: MySQL client ran out of memory 注意,错误指向了MySQL客户mysql。这个错误的原因很简单,客户没有足够的内存存储全部结果。 为了修正这个问题,首先检查你的查询是否正确。它应该返回这么多的行,这合理吗?如果是这样,你可以使用mysql --quick,它使用mysql_use_result()检索结果集合。这将较少的负担放在了客户端(只是服务器更多)。 18.2.6 Packet too large错误当一个MySQL客户或mysqld服务器得到一个比max_allowed_packet个字节长的包,它发出一个Packet too large错误并终止连接。 如果你正在使用mysql客户,你可以通过用mysql --set-variable=max_allowed_packet=8M指定一个更大的缓冲区来启动客户程序。 如果你正在使用不允许你指定最大包大小的其他客户(例如 DBI),你需要在你启动服务器时设置包大小。你可以使用mysqld的命令行选项设置max_allowed_packet为一个更大的尺寸。例如,如果你正期望将一个全长的BLOB存入一张表中,你将需要用--set-variable=max_allowed_packet=24M选项来启动服务器。
18.2.7 The table is full错误这个错误发生在内存临时表变得比tmp_table_size字节大时。为了避免这个问题,你可以使用mysqld的-O tmp_table_size=#选项来增加临时表的大小,或在你发出有疑问的查询之前使用SQL选项SQL_BIG_TABLES。见7.25 SET OPTION句法。 你也可以使用--big-tables选项启动mysqld。这与为所有查询使用SQL_BIG_TABLES完全相同。 18.2.8 Commands out of sync in client错误如果你在你的客户代码中得到Commands out of sync; You can't run this command now,你正在以错误的次序调用客户函数! 这可能发生,例如,如果你正在使用mysql_use_result()并且在你已经调用了mysql_free_result()之前试图执行新查询。如果你在mysql_use_result()或mysql_store_result()之间试图执行返回数据的2个查询,它也可能发生。 18.2.9 Ignoring user错误如果你得到下列错误: Found wrong password for user: 'some_user@some_host'; Ignoring user 这意味着在mysqld启动时或在它再次装载权限表时,它在user表中找到了一个有一个无效口令的条目。结果,条目简单地被权限系统忽略。 可能导致这个问题的原因和修正:
18.2.10 Table 'xxx' doesn't exist错误如果你得到错误Table 'xxx' doesn't exist或Can't find file: 'xxx' (errno: 2),这意味着在当前数据库中没有名为xxx的表存在。 注意,因为MySQL使用目录和文件存储数据库和表,数据库和表名件是区分大小写的!(在Win32上,数据库和表名不是区分大小写的,但是在查询中对所有表的引用必须使用相同的大小写!) 你可以用SHOW TABLES检查你在当前数据库中有哪个表。见7.21 SHOW句法(得到表、列的信息)。 18.3 MySQL怎样处理一个溢出的磁盘当出现一个磁盘溢出的情况时,MySQL做下列事情:
为了缓和这个问题,你可以采取下列行动:
18.4 如何从一个文本文件运行SQL命令一般地,mysql客户被交互性地使用,象这样: shell> mysql database 然而,也可以把你的SQL命令放在一个文件中并且告诉mysql从该文件读取其输入。要想这样做,创造一个文本文件“text_file”,它包含你想要执行的命令。然后如下那样调用mysql: shell> mysql database < text_file 你也能启动有一个USE db_name语句的文本文件。在这种情况下,在命令行上指定数据库名是不必要的: shell> mysql < text_file 见12.1 不同的MySQL程序概述。 18.5 MySQL在哪儿存储临时文件MySQL使用TMPDIR环境变量的值作为存储临时文件的目录的路径名。如果你没有设置TMPDIR,MySQL使用系统缺省值,它通常是“/tmp”或“/usr/tmp”。如果包含你的临时文件目录的文件系统太小,你应该编辑safe_mysqld设定TMPDIR指向你有足够空间的一个文件系统!你也可以使用mysqld的--tmpdir选项目设置临时目录。 MySQL以“隐含文件”创建所有临时文件。这保证了如果mysqld被终止,临时文件也将被删除。使用隐含文件的缺点是你将看不到一个大的临时文件填满了临时文件目录所在的文件系统。 当排序(ORDER BY或GROUP BY)时,MySQL通常使用一个或两个临时文件。最大磁盘空间需求是: (存储东西的长度 + sizeof (数据库指针))* 匹配的行数* 2 sizeof(数据库指针)通常是4,但是在未来对确实很大的表可能增加。 对一些SELECT查询,MySQL也创建临时SQL表。这些没被隐含且有“SQL_*”格式的名字。 ALTER TABLE和OPTIMIZE TABLE在原数据库表的同一个目录中创建一张临时表。 18.6 怎样保护“/tmp/mysql.sock ”不被删除如果你有这个问题,事实上任何人可以删除MySQL通讯套接字“/tmp/mysql.sock”,在Unix的大多数版本上,你能通过为其设置sticky(t)位来保护你的“/tmp”文件系统。作为root登录并且做下列事情: shell> chmod +t /tmp 这将保护你的“/tmp”文件系统使得文件仅能由他们的所有者或超级用户(root)删除。 你能执行ls -ld /tmp检查sticky位是否被设置,如果最后一位许可位是t,该位被设置了。 18.7 Access denied错误见6.6 权限系统如何工作。并且特别要看6.13 引起Access denied错误的原因。 18.8 怎样作为一个一般用户运行MySQLMySQL服务器mysqld能被任何用户启动并运行。为了将mysqld改由Unix用户user_name来运行,你必须做下列事情:
现在,你的mysqld进程应该正在作为Unix用户user_name运行,并运行完好。尽管有一件事情没有变化:权限表的内容。缺省 地(就在运行了脚本mysql_install_db安装的权限表后),MySQL用户root是唯一有存取mysql数据库或创建或抛弃数据库权限的用户。除非你改变了那些权限,否则他们仍然保持。当你作为一个Unix用户而不是root登录时,这不应该阻止你作为MySQL root用户来存取MySQL;只要为客户程序指定-u root的选项。 注意通过在命令行上提供-u root,作为root存取MySQL,与作为Unix root用户或其他Unix用户运行MySQL没有关系。MySQL的存取权限和用户名与Unix用户名字是完全分开的。唯一与Unix用户名有关的是,如果当你调用一个客户程序时,你不提供一个-u选项,客户将试图使用你的Unix登录名作为你的MySQL用户名进行连接。 如果你的Unix机器本身不安全,你可能应该至少在存取表中为MySQL root用户放上一个口令。否则,在那台机器上有一个帐号的任何用户能运行mysql -u root db_name并且做他喜欢做的任何事情。 18.9 怎样重新设置一个忘记的口令如果你忘记了MySQL的root用户的口令,你可以用下列过程恢复它。
18.10 文件许可权限问题如果你有与文件许可有关的问题,例如,如果当你创建一张表时,mysql发出下列错误消息: ERROR: Can't find file: 'path/with/filename.frm' (Errcode: 13) 那么可能是在mysqld启动时,环境变量UMASK可能设置不正确。缺省的umask值是0660。你可以如下启动safe_mysqld改变其行为: shell> UMASK=384 # = 600 in octalshell> export UMASKshell> /path/to/safe_mysqld & 18.11 文件没找到如果你从MySQL得到ERROR '...' not found (errno: 23), Can't open file: ... (errno: 24)或任何其他有errno 23或errno 24的错误,它意味着,你没有为MySQL分配足够的文件描述符。你能使用perror实用程序得到错误号含义是什么的描述: shell> perror 23File table overflowshell> perror 24Too many open files 这里的问题是mysqld正在试图同时保持打开太多的文件。你也可以告诉mysqld一次不打开那么多的文件,或增加mysqld可得到的文件描述符数量。 为了告诉mysqld一次保持打开更少的文件,你可以通过使用safe_mysqld的-O table_cache=32选项(缺省值是64)使表缓冲更小。减小max_connections值也将减少打开文件的数量(缺省值是90)。 要想改变mysqld可用的文件描述符数量,修改safe_mysqld脚本。脚本中有一条注释了的行ulimit -n 256。你可以删除'#'字符来去掉该行的注释,并且改变数字256改变为mysqld可用的文件描述符的数量。 ulimit能增加文件描述符的数量,但是只能到操作系统强加的限制。如果你需要增加每个进程可用的文件描述符数量的OS限制,参见你的操作系统文档。 注意,如果你运行tcsh外壳,ulimit将不工作!当你请求当前限制时,tcsh也将报告不正确的值!在这种情况下,你应该用sh启动safe_mysqld! 18.12 使用DATE列的问题一个DATE值的格式是'YYYY-MM-DD'。根据ANSI SQL,不允许其他格式。你应该在UPDATE表达式和SELECT语句的WHERE子句中使用这个格式。例如: mysql> SELECT * FROM tbl_name WHERE date >= '1997-05-05'; 为了方便,如果日期用在数字上下文,MySQL自动变换一个日期到一个数字(并且反过来也如此)。当更新时和将一个日期与TIMESTAMP、DATE或DATETIME列比较的一个WHERE子句中,也是足够灵活以允许一种“宽松”的字符串格式。(宽松格式意味着任何标点字符用作在部件之间的分割符。例如,'1998-08-15'和'1998#08#15'是等价的。)MySQL也能变换不包含分割符的一个字符串(例如 '19980815'),如果它作为一个日期说得通。 特殊日期'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,'19970505')=0; STRCMP()是字符串函数,因此它将idate转换为一个字符串并且实施字符串比较。它不将'19970505'转换为一个日期并实施日期比较。 注意,MySQL不检查日期是否正确。如果你存储一个不正确的日期,例如'1998-2-31',错误的日期将被存储。如果日期不能被变换到任何合理的值,在DATE字段中存储一个0。这主要是一个速度问题并且我们认为检查日期是应用程序的责任,而不服务器。 18.13 时区问题如果你有一个问题,SELECT NOW()以GMT时间返回值而不是你的本地时间,你必须设定TZ环境变量为你的当前时区。这应该在服务器运行的环境进行,例如在safe_mysqld或mysql.server中。 18.14 在搜索中的大小写敏感性缺省地,MySQL搜索是大小写不敏感的(尽管有一些字符集从来不是忽略大小写的,例如捷克语)。这意味着,如果你用col_name LIKE 'a%'搜寻,你将得到所有以A或a开始的列值。如果你想要使这个搜索大小写敏感,使用象INDEX(col_name, "A")=0检查一个前缀。或如果列值必须确切是"A",使用STRCMP(col_name, "A") = 0。 简单的比较操作(>=、>、= 、< 、<=、排序和聚合)是基于每个字符的“排序值”。有同样排序值的字符(象E,e和'e)被视为相同的字符! LIKE比较在每个字符的大写值上进行(E==e 但是E<>'e)。 如果你想要一个列总是被当作大小写敏感的方式,声明它为BINARY。见7.7 CREATE TABLE句法。 如果你使用以所谓的big5编码的中文数据,你要使所有的字符列是BINARY,它可行,是因为big5编码字符的排序顺序基于 ASCII代码的顺序。 18.15 NULL值问题NULL值的概念是造成SQL的新手的混淆的普遍原因,他们经常认为NULL是和一个空字符串''的一样的东西。不是这样的!例如,下列语句是完全不同的: mysql> INSERT INTO my_table (phone) VALUES (NULL);mysql> INSERT INTO my_table (phone) VALUES (""); 两个语句把值插入到phone列,但是第一个插入一个NULL值而第二个插入一个空字符串。第一个的含义可以认为是“电话号码不知道”,而第二个则可意味着“她没有电话”。 在SQL中,NULL值在于任何其他值甚至NULL值比较时总是假的(FALSE)。包含NULL的一个表达式总是产生一个NULL值,除非在包含在表达式中的运算符和函数的文档中指出。在下列例子,所有的列返回NULL: mysql> SELECT NULL,1+NULL,CONCAT('Invisible',NULL); 如果你想要寻找值是NULL的列,你不能使用=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 = ""; 在MySQL中,就像很多其他的SQL服务器一样,你不能索引可以有NULL值的列。你必须声明这样的列为NOT NULL,而且,你不能插入NULL到索引的列中。 当用LOAD DATA INFILE读取数据时,空列用''更新。如果你想要在一个列中有NULL值,你应该在文本文件中使用\N。字面上的词'NULL'也可以在某些情形下使用。见7.16 LOAD DATA INFILE句法。 当使用ORDER BY时,首先呈现NULL值。如果你用DESC以降序排序,NULL值最后显示。当使用GROUP BY时,所有的NULL值被认为是相等的。 为了有助于NULL的处理,你能使用IS NULL和IS NOT NULL运算符和IFNULL()函数。 对某些列类型,NULL值被特殊地处理。如果你将NULL插入表的第一个TIMESTAMP列,则插入当前的日期和时间。如果你将NULL插入一个AUTO_INCREMENT列,则插入顺序中的下一个数字。
18.16 alias问题你可以在GROUP BY、ORDER BY或在HAVING部分中使用别名引用列。别名也可以用来为列取一个更好点的名字: SELECT SQRT(a*b) as rt FROM table_name GROUP BY rt HAVING rt > 0;SELECT id,COUNT(*) AS cnt FROM table_name GROUP BY id HAVING cnt > 0;SELECT id AS "Customer identity" FROM table_name; 注意,你的 ANSI SQL 不允许你在一个WHERE子句中引用一个别名。这是因为在WHERE代码被执行时,列值还可能没有终结。例如下列查询是不合法: SELECT id,COUNT(*) AS cnt FROM table_name WHERE cnt > 0 GROUP BY id; WHERE语句被执行以确定哪些行应该包括GROUP BY部分中,而HAVING用来决定应该只用结果集合中的哪些行。 18.17 从关联的表中删除行因为MySQL不支持子选择或在DELETE语句中使用多个表,你应该使用下列方法从2个关联的表中删除行:
如果在related_column查询中的字符的全部数量超过1,048,576(缺省值max_allowed_packet),你应该分成更小的部分并且执行多个DELETE语句。如果related_column是一个索引,你每次只删除100-1000个related_column id将可能使得DELETE最快。如果related_column不是一个索引,速度与IN子句中参数的数量无关。 18.18 解决没有匹配行的问题如果你有一个复杂的查询,涉及多个表,但没有返回任何行,你应该使用下列过程查找你的询问有什么不对:
18.19 与ALTER TABLE有关的问题如果ALTER TABLE死于这样一个错误: Error on rename of './database/name.frm' to './database/B-a.frm' (Errcode: 17) 问题可能是MySQL在前一个ALTER TABLE中已经崩溃并且留下了一个名为“A-xxx”或“B-xxx”的老的数据库表。在这种情况下,到MySQL数据目录中并删除所有名字以A-或B-开始的文件。(你可以把他们移到别的地方而不是删除他们)。 ALTER TABLE工作方式是:
如果某些改名操作出错,MySQL试图还原改变。如果出错严重(当然,这不应该发生。),MySQL可能留下了老表为“B-xxx”但是一个简单改名就应该恢复你的数据。 18.20 怎样改变一张表中列的顺序SQL的要点是中抽象应用程序以避免数据存储格式。你应该总是以你想要检索数据的意愿指定顺序。例如: SELECT col_name1, col_name2, col_name3 FROM tbl_name; 将以col_name1、col_name2、col_name3的顺序返回列,而: SELECT col_name1, col_name3, col_name2 FROM tbl_name; 将以col_name1、col_name3、col_name2的顺序返回列。 在一个应用程序中,你应该决不基于他们的位置使用SELECT * 检索列,因为被返回的列的顺序永远不能保证;对你的数据库的一个简单改变可能导致你的应用程序相当有戏剧性地失败。 不管怎样,如果你想要改变列的顺序,你可以这样做:
|