当你试着联接MySQL服务器时,如果碰到问题,下面各项可以帮助你纠正问题:
· 确保服务器在运行。如果服务器没有运行,则你不能连接服务器。如果你视图连接服务器并看到下述消息,可能是服务器没有运行:
· shell> mysql
· ERROR 2003: Can't connect to MySQL server on 'host_name' (111)
· shell> mysql
· ERROR 2002: Can't connect to local MySQL server through socket
· '/tmp/mysql.sock' (111)
也可能服务器正在运行,但你可能使用与服务器上侦听的不一样的TCP/IP端口、命名管道或Unix套接字文件。你可以调用客户端程序,指定端口选项来指示正确的端口或套接字选项来指示正确的命名管道或Unix套接字文件。要找出套接字文件的地点,应:
shell> netstat -ln | grep mysql
- 必须正确设置授权表,以便服务器可以使用它们进行访问控制。对于某些分发版类型(例如Windows中的二进制分发版或Linux中的RPM分发版),安装过程初始化包含 授权表的mysql数据库。如果分发版没有这样做,你必须运行mysql_install_db脚本来手动初始化授权表。详细内容参见2.9.2节,“Unix下安装后的过程”。
确定是否要初始化授权表的一个方法是寻找数据目录下的mysql目录(数据目录名通常为data或var,位于MySQL安装目录下)。应保证MySQL数据库目录有文件“user.MYD”。否则,执行mysql_install_db脚本。运行并重启服务器后,执行该命令来测试初始权限:
shell> mysql -u root test
服务器应该让你无误地连接。
- 在新的安装以后,你应该连接服务器并且设置你的用户及其访问许可:
· shell> mysql -u root mysql
服务器应该让你连接,因为MySQL root用户初始时没有密码。那也是安全风险,当你正在设置其他MySQL用户时,也应设定root密码是一件重要的事请。关于设置初始密码的说明,参见2.9.3节,“使初始MySQL账户安全”。
- 如果你将一个现存的MySQL安装升级到较新的版本,运行了mysql_fix_privilege_tables脚本吗?如果没有,运行它。增加新功能后,授权表的结构可能会改变,因此更新后应确保表的结构随之更新。相关说明参见2.10.2节,“升级授权表”。
· 如果客户端程序试图连接时收到以下错误信息,说明服务器需要新格式的密码,而客户端不能生成:
· shell> mysql
· Client does not support authentication protocol requested
· by server; consider upgrading MySQL client
关于如何处理的详细信息,参见5.7.9节,“MySQL 4.1中的密码哈希处理”和A.2.3节,“客户端不支持鉴定协议”。
- 如果你作为root试试连接并且得到这个错误,这意味着,你没有行在user表中的User列值为'root'并且mysqld不能为你的客户端解析主机名:
- Access denied for user ''@'unknown' to database mysql
在这种情况下,你必须用--skip-grant-tables选项重启服务器并且编辑“/etc/hosts”或“\windows\hosts”文件为你的主机增加行。
- 如果你从3.22.11以前的版本更新现存的MySQL安装到3.22.11版或以后版本,你运行了mysql_fix_privilege_tables脚本吗?如果没有,运行它。在GRANT语句变得能工作时,授权表的结构用MySQL 3.22.11修改 。
· 记住客户端程序使用选项文件或环境变量中指定的连接参数。如果客户端程序发送不正确的默认连接参数,而你没有在命令行中指定,检查环境变量和适用的选项文件。例如,当你不用任何选项运行客户端程序,得到Access denied错误,确保你没有在选项文件中指定旧密码!
你可以通过使用--no-defaults选项调用客户端程序来禁用选项文件。例如:
shell> mysqladmin --no-defaults -u root version
客户端使用的选项文件见4.3.2节,“使用选项文件”。环境变量列于附录F:环境变量。
· 如果遇到下述错误,说明root密码错误:
· shell> mysqladmin -u root -pxxxx ver
· Access denied for user 'root'@'localhost' (using password: YES)
如果你未指定密码时出现前面的错误,说明某个选项文件中的密码不正确。试试前面所说的--no-defaults选项。
关于密码更改的信息参见5.8.5节,“设置账户密码”。
如果你丢失或忘记root密码,你可以用--skip-grant-tables重启 mysqld来更改密码。参见A.4.1节,“如何复位根用户密码”.
· 如果你使用SET PASSWORD、INSERT或UPDATE更改密码,你必须使用 PASSWORD()函数加密密码。如果你不使用PASSWORD()函数,密码不工作。例如,下面的语句设置密码,但没能加密,因此用户后面不能连接:
· mysql> SET PASSWORD FOR 'abe'@'host_name' = 'eagle';
相反,应这样设置密码:
mysql> SET PASSWORD FOR 'abe'@'host_name' = PASSWORD('eagle');
当你使用GRANT或CREATE USER语句或mysqladmin password命令指定密码时,不需要PASSWORD()函数,它们会自动使用PASSWORD()来加密密码。参见5.8.5节,“设置账户密码”和13.5.1.1节,“CREATE USER语法”。
· localhost是你本地主机名的一个同义词,并且也是如果你不明确地指定主机而客户端尝试连接的默认主机。
要想在这种系统上避免该问题,你可以使用--host=127.0.0.1选项来明确命名服务器主机。这样将通过TCP/IP协议来连接本地mysqld服务器。你还可以指定--host选项使用TCP/IP,使用实际的本机主机名。在这种情况下,主机名必须指定为服务器主机上的user表行,即使你在服务器上运行客户端程序。
· 当尝试用mysql -u user_name与数据库连接时,如果你得到一个Access denied错误,可能会遇到与user表有关的问题,通过执行mysql -u root mysql并且执行下面的SQL语句进行检查:
· mysql> SELECT * FROM user;
结果应该包含一个有Host和User列的行匹配你的计算机主机名和你的MySQL用户名。
- Access denied错误消息将告诉你,你正在用哪个用户尝试登录,你正在试图连接哪个主机,是否使用了密码。通常,你应该在user表中有一行,正确地匹配在错误消息给出的主机名和用户名。例如,如果遇到包含using password: NO的错误信息,说明你登录时没有密码。
· 如果当你试着从一个不是MySQL服务器正在运行的主机上连接时,遇到下列错误,那么在user表中没有匹配那台主机的行:
· Host ... is not allowed to connect to this MySQL server
可以通过组合你正在试图连接的用户/主机名设置一个账户来修正它。如果你不知道正连接的机器的IP号或主机名,应该把一个'%'行作为Host列值放在user表中。在试图从客户端器连接以后,通过SELECT USER()查询显示你如何真正进行连接。(然后用在日志文件上面显示出的实际的主机名代替user表中的'%'行。否则,你将得到一个不安全的系统,因为它允许从任何主机上以任何用户名连接。)
在Linux中,发生该错误的另一个原因可能是你正使用于你所使用版本的glibc库不同版本的库编译的二进制MySQL版本。在这种情况下,你应升级操作系统或glibc,或下载MySQL版本的源码分发版并自己编译。源码RPM一般很容易编译并安装,因此不是大问题。
· 如果你连接时指定主机名,但得到错误消息主机名未显示或为IP号,表示当MySQL服务器将IP号解析为客户端来名时遇到错误:
· shell> mysqladmin -u root -pxxxx -h some-hostname ver
· Access denied for user 'root'@'' (using password: YES)
这表示DNS问题。要想修复,执行mysqladmin flush-hosts来重设内部 DNS主机名缓存。参见7.5.6节,“MySQL如何使用DNS”。
一些常用的解决方案包括:
o 试试找出DNS服务器的错误并修复。
o 在MySQL授权表中指定IP号而不是主机名。
o 在/etc/hosts中放入客户端名。
o 用--skip-name-resolve选项启动mysqld。
o 用--skip-host-cache选项启动mysqld。
o 在Unix中,如果你在同一台机器上运行服务器和客户端,连接到localhost。连接到的localhost的Unix连接使用Unix套接字文件而不是TCP/IP。
o 在Windows中,你在同一台机器上运行服务器和客户端并且服务器支持命名管道连接,连接主机名(周期)。连接使用命名管道而不是TCP/IP。
- 如果mysql -u root test工作但是mysql -h your_hostname -u root test导致Access denied(your_hostname是本地机的实际主机名),那么在user表中可能没有你的主机的正确名字。这里的一个普遍的问题是在user表行中的Host值指定一个唯一的主机名,但是你系统的名字解析例程返回一个完全正规的域名(或相反)。例如,如果你在user表中有一个主机是'tcx'的行,但是你的DNS告诉MySQL你的主机名是'tcx.subnet.se',行将不工作。尝试把一个行加到user表中,它包含你主机的IP号作为Host列的值。(另外,你可以把一个行加到user表中,它有包含一个通配符如'tcx.%'的Host值。然而,使用以“%”结尾的主机名是不安全的并且不推荐!)
- 如果mysql -u user_name test工作但是mysql -u user_name other_db_name不工作,你没有为给定的用户授予other_db_name数据库的访问权限。
- 当在服务器上执行mysql -u user_name时,它工作,但是在其它远程客户端上执mysql -h host_name -u user_name时,它却不工作,你没有为给定的用户授予从远程主机访问服务器的权限。
- 如果你不能弄明白你为什么得到Access denied,从user表中删除所有Host包含通配符值的行(包含“%”或“_”的条目)。一个很普遍的错误是用Host='%'和User='some_user'插入一个新行,认为这将允许你指定localhost从同一台机器进行连接。它不工作的原因是 默认权限包括一个有Host='localhost'和User=''的行,因为那个行的Host值'localhost'比'%'更具体,当从localhost连接时,它用于指向新行!正确的步骤是插入Host='localhost'和User='some_user'的第2个行,或删除Host='localhost'和User=''行。删除条目后,记住用FLUSH PRIVILEGES语句重载授权表。
· 如果你得到下列错误,可以与db或host表有关:
· Access to database denied
如果从db表中选择了在Host列有空值的条目,保证在host表中有一个或多个相应的条目,指定db表中的条目适用哪些主机。
· 如果你能够连接MySQL服务器,但如果在使用命令SELECT ... INTO OUTFILE或LOAD DATA INFILE语句时,你得到Access denied错误,在user表中的条目可能没有启用FILE权限。
· 如果你直接更改授权表(例如,使用INSERT、UPDATE或DELETE语句)并且你的更改好像被忽略了,记住你必须执行FLUSH PRIVILEGES语句或mysqladmin flush-privileges命令让服务器来重读授权表。否则,直到服务器下次重启,你的更改方有效。记住用UPDATE命令更改root密码后,在清空权限前,你不需要指定新密码,因为服务器还不知道你已经更改了密码!
· 如果你的权限似乎在一个会话过程中改变了,可能是一个超级用户改变了他们。再次装入授权表会影响新客户端连接,但是它也影响现存的连接,如5.7.7节,“权限更改何时生效”小节所述。
· 如果你有Perl、Python或ODBC程序的存取问题,试着用mysql -u user_name db_name或mysql -u user_name -pyour_pass db_name与服务器连接。如果你能用mysql客户端进行连接,这是程序的一个问题而不是访问权限的问题。(注意在-p和密码之间没有空格;也可以使用--password=your_pass语法指定密码。如果使用-p选项,MySQL提示你输入密码。)
· 为了测试,用--skip-grant-tables选项启动mysqld守护进程,然后你可以改变MySQL授权表并且使用mysqlaccess脚本检查你的修改是否有如期的效果。当你对你的改变满意时,执行mysqladmin flush-privileges告诉mysqld服务器开始使用新的 授权表。(再次装入授权表覆盖了--skip-grant-tables选项。这允许你告诉服务器开始使用授权表,而不用停掉并重启它)。
· 如果任何其它事情失败,用调试选项(例如,--debug=d,general,query)启动mysqld服务器。这将打印有关尝试连接的主机和用户信息,和发出的每个命令的信息。请参见E.1.2节,“创建跟踪文件”。
· 如果你有任何与MySQL授权表的其它问题,而且觉得你必须将这个问题发送到邮件表,一定要提供一个MySQL授权表的倾倒副本(dump)。你可用mysqldump mysql命令复制数据库表。象平时一样,用mysqlbug脚本邮寄你的问题。参见1.7.1.3节,“如何通报缺陷和问题”。在一些情况下可以用--skip-grant-tables重启mysqld以便能运行mysqldump。
MySQL用户账户列于mysql数据库中的user表内。每个MySQL账户指定一个密码,尽管保存在user表Password列的密码不是明文,但哈希值是从表中的记录计算的。用PASSWORD()函数来计算密码的哈希值。
MySQL在客户端/服务器通信的两个阶段使用密码:
· 如果客户端试图连接服务器,有一个初始鉴定步骤,客户必须提供一个密码,并且必须与客户想要使用的账户在user表保存的哈希值匹配。
· 客户端连接后,它可以(如果有充分的权限) 设置或更改user表内所列的账户的密码哈希值值。客户端可以通过PASSWORD()函数来生成密码哈希值,或使用GRANT或SET PASSWORD语句。
换句话说,当客户端首次试图连接时,服务器使用哈希值进行鉴定。如果连接的客户端调用PASSWORD()函数或使用GRANT或SET语句来设置或更改密码,则服务器产生哈希值。
在MySQL 4.1中密码哈希算法已经更新,提供了更好的安全性并降低了密码被截取的风险。但是,该新机制只能在MySQL 4.1(和更新版本的)服务器和客户端中使用,会产生一些兼容性问题。4.1或新客户端可以连接4.1之前的服务器,因为客户端可以同时理解旧的和新的密码哈希机制。但是,4.1之前的客户端试图连接4.1版或更新版的服务器时会遇到困难。例如,3.23版mysql客户端试图连接5.1服务器时会失败并出现下面的错误消息:
shell> mysql -h localhost -u root
Client does not support authentication protocol requested
by server; consider upgrading MySQL client
出现该问题的另一个普通例子是在升级到MySQL 4.1或更新版后,试图使用旧版本的PHP mysql扩展名。(参见25.3.1节,“使用MySQL和PHP的常见问题”)。
下面讨论了新、旧密码机制之间的差别,以及如果你升级了服务器但需要为4.1版以前的客户端保持向后兼容性该怎样做。A.2.3节,“客户端不支持鉴定协议”中有更详细的信息。该信息将MySQL数据库从4.0版本或更低版升级到4.1版或更高版的PHP编程人员特别重要。
注释:该讨论对比了4.1版的行为和4.1前的行为,这儿描述的4.1中的行为实际上从4.1.1开始。MySQL 4.1.0是一个“旧”的发布,因为它的实施机制与4.1.1版和更新版中的稍有不同。在MySQL 5.0 参考手册中详细描述了4.1.0和最新版之间的差别。
在MySQL 4.1之前,用PASSWORD()函数计算的密码哈希值有16个字节长。应为:
mysql> SELECT PASSWORD('mypass');
+--------------------+
| PASSWORD('mypass') |
+--------------------+
| 6f8c114b58f2ce9e |
+--------------------+
在MySQL 4.1之前,user表的Password列(保存了哈希值)也是16字节长。
在MySQL 4.1中,已经对PASSWORD()函数进行了修改,可以生成41字节的哈希值:
mysql> SELECT PASSWORD('mypass');
+-------------------------------------------+
| PASSWORD('mypass') |
+-------------------------------------------+
| *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
+-------------------------------------------+
同样,user表的Password列必须有41字节长来保存这些值:
· 如果你新安装MySQL 5.1, Password列自动为41字节长。
· 从MySQL 4.1(4.1.1或4.1系列的更新版)升级到MySQL 5.1,应不会出现相关问题,因为两个版本使用相同的密码哈希机制。如果你想要将更早版本的MySQL升级到MySQL5.1,你应先升级到4.1,然后将4.1升级到5.1。
加宽的Password列可以同时保存新、旧格式的密码哈希值。可以有两种方式确定任何给定格式的密码哈希值:
· 明显的不同之处是长度(16字节和41字节)。
· 第2个不同之处是新格式的密码哈希值都以‘*’字符开头,而旧格式的密码绝对不是。
长密码哈希值具有更好的加密属性,并且客户端根据长哈希值进行鉴定比旧的短哈希值更加安全。
短密码哈希值和长密码哈希值之间的不同之处与服务器如何使用密码进行鉴定以及如何为执行密码更改操作的连接的客户端生成密码哈希值都有关。
服务器使用密码哈希值进行鉴定的方式受Password列的宽度影响:
· 如果列较短,只用短哈希鉴定。
· 如果列较长,可以有短或长哈希值,并且服务器可以使用任何一种格式:
o 4.1之前的客户端可以连接,它们只可以使用旧的哈希机制,它们可以只鉴定有短哈希的账户。
o 4.1及以后版本的客户端可以鉴定有短哈希或长哈希的账户。
对于短哈希账户的鉴定过程,4.1和以后版本的客户端比为旧版本的客户端实际要安全得多。从安全性角度,从最低安全到最安全的梯度为:
· 4.1之前的客户端用短密码哈希值进行鉴定
· 4.1或以后版本的客户端用短密码哈希值进行鉴定
· 4.1或以后版本的客户端用长密码哈希值进行鉴定
服务器为连接的客户端生成密码哈希值的方式受Password列宽度和--old-passwords选项的影响。4.1或更新版本的服务器只有满足某个条件才生成长哈希:Password列必须足够宽以容纳长哈希值并且未给定--old-passwords选项。这些条件适合:
· Password列必须足够宽以容纳长哈希(41字节)值。如果列没有更新,仍然为4.1之前的16字节宽,当客户端使用PASSWORD()、GRANT或SET PASSWORD执行密码更改操作时,服务器注意到长哈希不适合,只生成短哈希。如果你已经升级到4.1但还没有运行 mysql_fix_privilege_tables脚本来扩宽Password列时会出现这种行为。
· 如果Password列足够宽,则可以保存短或长密码哈希值。在这种情况下,PASSWORD()、GRANT或SET PASSWORD生成长哈希,除非 用--old-passwords选项启动服务器。该选项强制服务器生成短密码哈希值。
--old-passwords选项的目的是当服务器生成长密码哈希值时,允许你维持同4.1之前的客户端的向后兼容性。该选项不影响鉴定(4.1和以后版本的客户端仍然可以使用有长密码哈希值的账户),但它防止在密码更改操作中在user表中创建长密码哈希值。在这种情况下,该账户不能再用于4.1之前的客户端。没有--old-passwords选项,可能会出现下面的不期望的情况:
· 旧客户端连接有短密码哈希值的账户。
· 客户更改自己的密码。没有--old-passwords,可以为该账户生成长密码哈希值。
· 下次旧客户试图连接账户时不能连接上,因为账户有长密码哈希值,需要新的哈希机制进行鉴定。(一旦账户user表中为长密码哈希值,只有4.1和以后版本的客户端可以鉴定它,因为4.1之前的客户端不理解长哈希)。
该场景说明,如果你必须支持旧的4.1之前的客户端,不使用--old-passwords选项运行4.1或更新版本的服务器很危险。用--old-passwords运行服务器,密码更改操作不会生成长密码哈希值,这样旧客户端也可以访问账户。(这些客户端不能意外地因更改了密码将自己锁出去,并留下长密码哈希值)。
--old-passwords选项的不利之处是你创建或更改的密码使用短哈希,甚至对于4.1客户端也如此。这样,你丢失了长密码哈希值提供的安全性。如果你想要创建有长哈希的账户(例如,为4.1客户端),你必须不使用--old-passwords来运行服务器。
下面的场景可用于运行4.1或以后的服务器,包括MySQL 5.1:
场景1:user表中的短Password列:
· 只有短哈希可以保存到Password列。
· 服务器只使用短哈希进行客户端鉴定。
· 对于连接的客户端,调用PASSWORD()、GRANT或SET PASSWORD的密码哈希生成操作专使用短哈希。对账户的任何更改均会生成短密码哈希值。
· --old-passwords选项可以使用但是多余,因为Password列较短,服务器只生成短密码哈希值。
场景2:长Password列;没有用--old-passwords选项启动服务器:
· 短或长哈希可以保存到Password列。
· 4.1和以后版本的客户端(包括5.1客户端)可以鉴定有短或长哈希的账户。
· 4.1之前的客户端只能鉴定有短哈希的账户。
· 对于连接的客户端,调用PASSWORD()、GRANT或SET PASSWORD的密码哈希生成操作专使用短哈希。对账户的任何更改均会生成短密码哈希值。
如前面所示,该场景的危险性在于4.1之前的客户端可能不能访问有短密码哈希值的账户。通过PASSWORD()、GRANT或SET PASSWORDA对这些账户密码的更改会产生长的密码哈希值。从该点看,4.1之前的客户端升级到4.1之前不能鉴定该账户。
要处理该问题,可以用特殊方法更改密码。例如,一般情况你可以使用SET PASSWORD按照下面的方法更改账户密码:
mysql> SET PASSWORD FOR 'some_user'@'some_host' = PASSWORD('mypass');
要想更改密码但创建短哈希,使用OLD_PASSWORD()函数:
mysql> SET PASSWORD FOR 'some_user'@'some_host' = OLD_PASSWORD('mypass');
当你想明显生成短哈希时,OLD_PASSWORD()很有用。
场景3:长Password列;用--old-passwords选项启动4.1或新版本的服务器:
· 短或长哈希可以保存到Password列。
· 4.1和以后版本的客户端可以鉴定有短或长哈希的账户(请注意只有不使用--old-passwords选项启动服务器,方可以创建长哈希)。
· 4.1之前的客户端只可以鉴定短哈希账户。
· 对于连接的客户端,调用PASSWORD()、GRANT或SET PASSWORD的密码哈希生成操作专使用短哈希。对账户的任何更改均会生成短密码哈希值。
在该场景中,你不能创建长密码哈希值的账户,因为--old-passwords选项防止生成长哈希。并且,如果你在使用--old-passwords选项前创建长哈希账户,当--old-passwords有时更改账户密码,结果会使账户的密码为短密码,安全性较长哈希要降低。
这些场景的不利之处可以概括为:
在场景1中,你不能利用长哈希提供更安全的鉴定。
在场景2中, 如果你没有显式使用OLD_PASSWORD()来更改密码,则4.1之前的客户端不能再访问短哈希账户。
在场景3中,--old-passwords防止短哈希账户不可访问,但密码更改操作使账户的长哈希转换为短哈希,当--old-passwords有效时不能将它改回长哈希。
升级到MySQL4.1或更新版本后,使用PASSWORD()为自己的目的生成密码的应用程序会出现兼容性问题。应用程序实际不应这样做,因为PASSWORD()只应用来管理MySQL账户的密码。但一些应用程序使用PASSWORD()用于自己的目的。
如果你从MySQL 4.1之前的版本升级到4.1或以后版本,并在生成长密码哈希值的条件下运行服务器,应用程序使用PASSWORD()破解自己的密码。这种情况下推荐的方法是修改应用程序,使用其它函数,例如SHA1()或MD5(),来产生哈希值。如果不行,你可以使用OLD_PASSWORD()函数,该函数用来提供旧格式的短哈希。但是,请注意OLD_PASSWORD()可能有一天不再被支持。
如果服务器运行在生成短哈希的条件下,可以使用 OLD_PASSWORD()但与PASSWORD()等同。
将MySQL数据库从4.0或更低版本移植到4.1或更高版本的PHP编程人员应参阅旧客户端。