7月 132010
 

mysql的执行计划:
explain
显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。
使用方法,在select语句前加上explain就可以了:
如:explain select * from test1
EXPLAIN
列的解释:

table
:显示这一行的数据是关于哪张表的
type
:这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为consteq_regrefrangeindexheALL
possible_keys
:显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句

key
实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句中使用USE INDEXindexname)来强制使用一个索引或者用IGNORE INDEXindexname)来强制MYSQL忽略索引
key_len
:使用的索引的长度。在不损失精确性的情况下,长度越短越好
ref
:显示索引的哪一列被使用了,如果可能的话,是一个常数
rows
MYSQL认为必须检查的用来返回请求数据的行数
Extra
:关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporaryUsing filesort,意思MYSQL根本不能使用索引,结果是检索会很慢
extra
列返回的描述的意义
Distinct:
一旦MYSQL找到了与行相联合匹配的行,就不再搜索了
Not exists: MYSQL
优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了
Range checked for each Record
index map:#:没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一
Using filesort:
看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行
Using index:
列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候
Using temporary
看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY
Where used
使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALLindex,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)
system
表只有一行:system表。这是const连接类型的特殊情况
const:
表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它当做常数来对待
eq_ref:
在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用
ref:
这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少越少越好
range:
这个连接类型使用索引返回一个范围中的行,比如使用><查找东西时发生的情况
index:
这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)
ALL:
这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免

 Posted by at 下午 9:49
7月 052010
 

转自: http://machael.blog.51cto.com/829462/239112

Replication 线程

   Mysql的 Replication 是一个异步的复制过程,从一个 Mysql instace(我们称之为 Master)复制到另一个 Mysql instance(我们称之 Slave)。在 Master 与 Slave 之间的实现整个复制过程主要由三个线程来完成,其中两个线程(Sql线程和IO线程)在 Slave 端,另外一个线程(IO线程)在 Master 端。
  要实现 MySQL 的 Replication ,首先必须打开 Master 端的Binary Log(mysql-bin.xxxxxx)功能,否则无法实现。因为整个复制过程实际上就是Slave从Master端获取该日志然后再在自己身上完全 顺序的执行日志中所记录的各种操作。打开 MySQL 的 Binary Log 可以通过在启动 MySQL Server 的过程中使用 “—log-bin” 参数选项,或者在 my.cnf 配置文件中的 mysqld 参数组([mysqld]标识后的参数部分)增加 “log-bin” 参数项。
  MySQL 复制的基本过程如下:
  1. Slave 上面的IO线程连接上 Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;
   2. Master 接收到来自 Slave 的 IO 线程的请求后,通过负责复制的 IO 线程根据请求信息读取指定日志指定位置之后的日志信息,返回给 Slave 端的 IO 线程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息在 Master 端的 Binary Log 文件的名称以及在 Binary Log 中的位置;
  3. Slave 的 IO 线程接收到信息后,将接收到的日志内容依次写入到 Slave 端的Relay Log文件(mysql-relay-bin.xxxxxx)的最末端,并将读取到的Master端的bin-log的文件名和位置记录到master- info文件中,以便在下一次读取的时候能够清楚的高速Master“我需要从某个bin-log的哪个位置开始往后的日志内容,请发给我”
   4. Slave 的 SQL 线程检测到 Relay Log 中新增加了内容后,会马上解析该 Log 文件中的内容成为在 Master 端真实执行时候的那些可执行的 Query 语句,并在自身执行这些 Query。这样,实际上就是在 Master 端和 Slave 端执行了同样的 Query,所以两端的数据是完全一样的。
  实际上,在老版本中,MySQL 的复制实现在 Slave 端并不是由 SQL 线程和 IO 线程这两个线程共同协作而完成的,而是由单独的一个线程来完成所有的工作。但是 MySQL 的工程师们很快发现,这样做存在很大的风险和性能问题,主要如下:
   首先,如果通过一个单一的线程来独立实现这个工作的话,就使复制 Master 端的,Binary Log日志,以及解析这些日志,然后再在自身执行的这个过程成为一个串行的过程,性能自然会受到较大的限制,这种架构下的 Replication 的延迟自然就比较长了。
   其次,Slave 端的这个复制线程从 Master 端获取 Binary Log 过来之后,需要接着解析这些内容,还原成 Master 端所执行的原始 Query,然后在自身执行。在这个过程中,Master端很可能又已经产生了大量的变化并生成了大量的 Binary Log 信息。如果在这个阶段 Master 端的存储系统出现了无法修复的故障,那么在这个阶段所产生的所有变更都将永远的丢失,无法再找回来。这种潜在风险在Slave 端压力比较大的时候尤其突出,因为如果 Slave 压力比较大,解析日志以及应用这些日志所花费的时间自然就会更长一些,可能丢失的数据也就会更多。
   所以,在后期的改造中,新版本的 MySQL 为了尽量减小这个风险,并提高复制的性能,将 Slave 端的复制改为两个线程来完成,也就是前面所提到的 SQL 线程和 IO 线程。最早提出这个改进方案的是Yahoo!的一位工程师“Jeremy Zawodny”。通过这样的改造,这样既在很大程度上解决了性能问题,缩短了异步的延时时间,同时也减少了潜在的数据丢失量。
  当然,即使是换成了现在这样两个线程来协作处理之后,同样也还是存在 Slave 数据延时以及数据丢失的可能性的,毕竟这个复制是异步的。只要数据的更改不是在一个事务中,这些问题都是存在的。
   如果要完全避免这些问题,就只能用 MySQL 的 Cluster 来解决了。不过 MySQL的 Cluster 知道笔者写这部分内容的时候,仍然还是一个内存数 据库的解决方案,也就是需要将所有数据包括索引全部都 Load 到内存中,这样就对内存的要求就非常大的大,对于一般的大众化应用来说可实施性并不是太大。当然,在之前与 MySQL 的 CTO David 交流的时候得知,MySQL 现在正在不断改进其 Cluster 的实现,其中非常大的一个改动就是允许数据不用全部 Load 到内存中,而仅仅只是索引全部 Load 到内存中,我想信在完成该项改造之后的 MySQL Cluster 将会更加受人欢迎,可实施性也会更大。

 Posted by at 上午 7:46
12月 072009
 

这次导数据,有所得,也有所失。
1. 如果可能的话,尽量使用文件处理;
   a. 因为我对业务没有足够的了解,没有使用文件处理
   b. 因为
数据不太干净,没敢使用文件处理

2. 本来只处理正查表就够了,由于我对业务的不熟悉,使得处理时间翻倍了

3. sql语句总是可以优化的:
   a. 把select * 修改为指定的需要的某个或某几个字段,而且尽量让这几个字段出现在使用的索引里,这样本次查询就不需要访问数据文件了
   b. 大数据量的分批查询有两种方法: 根据某字段做hash; 根据某字段排序,注意order by where limit

4. 着实领教了文件系统的cache对mysql的影响。

 Posted by at 下午 9:17
12月 072009
 

t1表结构:
uid   fid  ftype  timestring

primary key(uid,fid);

目前要将t1表中的数据一条一条地写到t2表中:
t2表结构:
uid   fid  ftype  dual

primary key(uid,fid);

1. 我们暂且不考虑通过处理文件的方式来导数据,只研究一条一条地该怎么做

2. 因为t2中需要t1中的ftype,为了不需要访问t1表的数据文件,可以将t1的主键重建为:
primary key(uid,fid,ftype);

3. 因为t1的数据量很大(1000万吧),所以需要分部分读取,然后处理,分部分的方法有两种:
a.
for($i = 0; $i< 127; $i++) {

$sql = "select * from t1 where mod(uid,127) = $i";

}

b.
$i = 10000;
$start = 0;
      while($i == 10000) {

$sql = "select * from t1 where uid > $start order by uid limit 10000";
$result = mysql_query($sql);

$cntDealed = count($result);
$start += $cntDealed;

}

4.
第一种办法计算量大一些,每次要遍历所有的数据,但是不需要排序
第二种办法需要处理的数据量越来越少,速度越来越快,但是做了一些不太必要的排序操作

5. 如果我们的操作90%的情况下需要除主键外的某一个字段,而且这个字段很小,为了提高查询速度,可以考虑将该字段添加到逐渐里面去,尽管他逻辑上可以不是主键。

 Posted by at 下午 8:35
11月 162009
 

1. mysql 的查询速度可能受mysql qcache的影响,更可能受系统cache的影响,而且系统cache影响可能是200和6000的差别,不可小视啊。

2. 如果能批量处理就不要一条一条地处理,批量处理方法:

a.  mysqldump 工具
b.  select * into outfile "myfile" from atable;  // 这样将以tab分隔的方式将记录写到文件里面,对于反斜线会自动加反斜线转义的
c.  用loaddata将b的结果导入到目标表里面

3. mysql的瓶颈一般在磁盘IO上
4. 不要小看sql语句的优化,注意下面两条语句的区别

a.  select * from atable where akey = ‘avalue’;
b.  select count(*) from atable where akey = ‘avalue’;

这里不仅仅是结果集的数据量的差异,a语句除了访问索引文件,还要访问数据文件; 而b语句只访问索引文件就够了; 如果内存小的话,这里的差异就比较大了。

5.  mysql 需要修改表结构时的一些到数据的问题

a. 首先考虑上面提到的使用文件的方式处理
b. 如果需要逐条数据处理则可以将源数据导成文本文件,逐行处理; 注意: 数据要干净,否则就不要使用文件处理
c. 如果需要逐条处理,但是数据量很大,又不能一次查询获取,一般的做法为:
   按照主键排序,每次取一部分(如:1万条,和每条数据量大小有关,既不要频繁获取,又不要每次获取数据量太大,以至于占太多内存)
   这种做法似乎很常用,但是
   写程序并不简单,需要考虑的地方比较多;
   排序只是为了将数据分块,所以做了一些无用功;
   排序是一件很耗时的事情,如果数据量很大,非常不建议使用order by;
   排序还很可能出现[ERROR] /usr/local/mysql/libexec/mysqld: Sort aborted 的错误
   下面介绍一种比较简单有效的做法:
   思路是我们只是想把要取的数据分块儿,很自然就想到了hash,如果主键(的一)部分是数字,则可以根据数量的大小选取一个适当的素数,根据对该素数取模来分块; 将设有900万条数据,每次想取约1万条,则 900万/1万 = 100 ,则取100左右的素数就行,如:127;
   如果主键不是数字,也可以使用hash函数
   


6.  联合主键时的一些查询

mysql> select sql_no_cache count(*) into outfile "/tmp/a" from t_user_friend_list_52 group by user_
id;
Query OK, 878180 rows affected (6.65 sec)

mysql> select sql_no_cache count(*) into outfile "/tmp/b" from t_user_friend_list_52 group by user_
id,friend_id;
Query OK, 9511565 rows affected (10.48 sec)

mysql> select sql_no_cache * into outfile "/tmp/c" from t_user_friend_list_52 order by user_id,frie
nd_id limit 9000000,1000;         
Query OK, 1000 rows affected (31.82 sec)

mysql> select sql_no_cache * into outfile "/tmp/d" from t_user_friend_list_52 order by user_id limi
t 9000000,1000;         
Query OK, 1000 rows affected (31.96 sec)

前两行比较说明根据联合主键的第一列分组还是比根据整个联合主键分组要快一些,毕竟都是只访问索引文件
后两行比较说明根据联合主键的第一列排序还是比根据整个联合主键排序没有明显差别

 Posted by at 下午 8:54
10月 072008
 

低版本的mysql对嵌套查询支持的不够好,在mysql5中,如果要使用嵌套查询,如:

select count(*)  from  (select * from table where 1=1) as tmp_table;

这里要说明的是,对于子select需要as成一个临时表,否则就语法错误。

 Posted by at 上午 7:30
8月 292008
 

从mysql4 到mysql5 一般会遇到这个问题:
Client does not support authentication protocol requested by server; consider upgrading MySQL client

这可以看作是一个客户端的问题,也可以看作是一个服务器端的问题,因为更新客户端可以解决这个问题,修改服务器设置也可以解决这个问题;

1. 在Linux上升级了mysql服务器后,链接mysql服务器就出现了这个问题,以为是多么致命的问题,就升级了客户端,原来用的动态链接库是libmysqlclient.so.10 ,换成libmysqlclient.so.14就可以了

2. 后来发现同样版本的mysql服务器,一个服务器用libmysqlclient.so.10 不能连接,一个用libmysqlclient.so.10 就能连接,感觉不是客户端的问题了,网上查了一下,其实在服务器端简单设置一下就行了,下面是抄人家的,但是我也是验证过了的,我的mysql版本,5.0.14

mysql> SET PASSWORD FOR 'some_user'@'some_host' = OLD_PASSWORD('newpwd');

官方说明:http://dev.mysql.com/doc/refman/5.0/en/old-client.html

 Posted by at 上午 1:19
8月 072008
 

1.复制表结构及数据到新表

CREATE TABLE 新表
SELECT * FROM 旧表

2.只复制表结构到新表

CREATE TABLE 新表
SELECT * FROM 旧表 WHERE 1=2
即:让WHERE条件不成立.
方法二:(低版本的mysql不支持,mysql4.0.25 不支持,mysql5已经支持了)   
CREATE TABLE 新表
LIKE 旧表

3.复制旧表的数据到新表(假设两个表结构一样)

INSERT INTO 新表
SELECT * FROM 旧表

4.复制旧表的数据到新表(假设两个表结构不一样)

INSERT INTO 新表(字段1,字段2,…….)
SELECT 字段1,字段2,…… FROM 旧表

 Posted by at 下午 8:37
7月 242008
 

 

mysql 协议地址:http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol

Mysql 认证步骤

一、建立连接
1、使用系统的socket函数建立一个socket
2、使用这个socket初始化一个vio对象
net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ);
3、对这个socket执行connect操作,连接到远程主机
4、使用vio初始化net对象
my_net_init(net, net->vio)
并设置为keep alive
vio_keepalive(net->vio,TRUE);

二、第一次交换
客户端执行recv,会收到一个来自server的包,其中第一个字节是协议的版本号。
其它的重要信息还有connection id、scramble

41 00 00 00
0A 35 2E 30 2E 32 30 2D 73 74 61 6E 64 61 72 64 2D 6C 6F 67 00 44 8E 4E 00 5A 66 72 2A 79 43 24 27 00 2C A2 08 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 36 7B 29 58 5E 50 56 41 21 7C 73 4C 00
 
 其格式如下:
 1                            协议的版本号 (0×0A)
 n (Null-Terminated String)   服务器版本
 4                            thread_id
 8                            scramble_buff
 1                            (填充) 永远是 0×00
 2                            server_capabilities
 1                            server_language
 2                            server_status
 13                           (填充) 永远是 0×00 …
 13                           scramble_buff剩余的部分 (4.1)
 
三、然后客户端将密码等发送过去
客户端根据服务器发给的scramble加密,并存放在scramble_buff中发给服务器
发送登录数据:
00000000        3A 00 00 01 85 A6 03 00 00 00 00 01 08 00 00 00
00000010        00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000020        00 00 00 00 72 6F 6F 74 00 14 00 00 00 00 00 00
00000030        00 00 00 00 00 00 00 00 00 00 00 00 00 00  

 Bytes                        Name
 —–                        —-
 4                            client_flags
 4                            max_packet_size
 1                            charset_number
 23                           (filler) always 0×00…
 n (Null-Terminated String)   user
 n (Length Coded Binary)      scramble_buff (1 + x bytes)
 1                            (filler) always 0×00
 n (Null-Terminated String)   databasename

databasename字段可有可无。
scramble_buff字段的数据是这样生的:
scramble(end, mysql->scramble, passwd);

四、再度发送scrambled password (可选)
授权信息已经发送过去了,服务器可以会回答说OK(发回一个OK_PACKET),也有可能会要求再度发送scrambled password。
如果要再度发送,服务器会返回一个1字节的包,如果第一个字节是0xFE且mysql.server_capabilities设置了CLIENT_SECURE_CONNECTION,那么
就需要再度发送scrambled password
这个似乎是为了和以前老版本兼容,这次需要使用3.23版的scramble对password进行加密然后发送。
scramble_323(buff, mysql->scramble, passwd);
如:
0×8059000:      0×09    0×00    0×00    0×03    0×4d    0×45    0×46    0×4c
0×8059008:      0×4f    0×44    0×4b    0×4b    0×00
这个包的格式很简单,包头,然后是9个字节的scramble(其中最后一个字节必须是0×00)
不过要注意,此处包头的第4个字节是0×03,因为这是认证过程是双方来回发送的第三个包了。
五、命令
0×20,0×00,0×00,0×00, 包头
0×03 //命令的类型,COM_QUERY
select * from xxx where xxx //arg

========================================================
MYSQL认证漏洞:
1、构造0长度的scramble绕过密码校验
这几乎可以算是mysql目前发现的危害性最严重的安全漏洞了。

出问题的代码:
my_bool
check_scramble_323(const char *scrambled, const char *message,
                   ulong *hash_pass)
{
  struct rand_struct rand_st;
  ulong hash_message[2];
  char buff[16],*to,extra; /* Big enough for check */
  const char *pos;
  hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
  randominit(&rand_st,hash_pass[0] ^ hash_message[0],
             hash_pass[1] ^ hash_message[1]);
  to=buff;
  for (pos=scrambled ; *pos ; pos++)
    *to++=(char) (floor(my_rnd(&rand_st)*31)+64);
  extra=(char) (floor(my_rnd(&rand_st)*31));
  to=buff;
  while (*scrambled)
  {
    if (*scrambled++ != (char) (*to++ ^ extra))
      return 1; /* Wrong password */
  }
  return 0;
}
改正后的 (多加了一句  if (pos-scrambled != SCRAMBLE_LENGTH_323) return 1)

my_bool
check_scramble_323(const char *scrambled, const char *message,
                   ulong *hash_pass)
{
  struct rand_struct rand_st;
  ulong hash_message[2];
  char buff[16],*to,extra;                      /* Big enough for check */
  const char *pos;

  hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
  randominit(&rand_st,hash_pass[0] ^ hash_message[0],
             hash_pass[1] ^ hash_message[1]);
  to=buff;
  DBUG_ASSERT(sizeof(buff) > SCRAMBLE_LENGTH_323);
  for (pos=scrambled ; *pos && to < buff+sizeof(buff) ; pos++)
    *to++=(char) (floor(my_rnd(&rand_st)*31)+64);
  if (pos-scrambled != SCRAMBLE_LENGTH_323)
    return 1;
  extra=(char) (floor(my_rnd(&rand_st)*31));
  to=buff;
  while (*scrambled)
  {
    if (*scrambled++ != (char) (*to++ ^ extra))
      return 1;                                 /* Wrong password */
  }
  return 0;
}

2、构造一个足够长的passwd让strlen溢出。
出问题的代码:
  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
    *passwd++ : strlen(passwd);
  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
    db + passwd_len + 1 : 0;
  uint db_len= db ? strlen(db) : 0;
 
修正后:
  char *passwd= strend(user)+1;
  。。。。
  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
    (uchar)(*passwd++) : strlen(passwd);
  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
    db + passwd_len + 1 : 0;
  /* strlen() can’t be easily deleted without changing protocol */
  uint db_len= db ? strlen(db) : 0;
设想,客户端发来的包中,如果client_capabilities 指定 CLIENT_SECURE_CONNECTION和CLIENT_SECURE_CONNECTION两个位的值
且passwd的第一个字节大于0×80,那么*passwd作为一个char值将是负的,在其再被转为uint的时候,将会是一个很大的uint。然后db这个指针就会被指向别处,从而对一段意外的内存进行strlen。这时可能会引发内存的读错误。从而造成拒绝服务攻击。但是这个不太容易,因为passwd_len的有效范围在[-128,127]之间,所以所能造成的危害很小。
如果passwd_len不等于-1,而是一个更大的负数,那么strlen函数至多会读到username后的那么’\0′就会终止。这样做的好处是可以借助前面那段填充区来设置db name以供后面用,缺点是此处无法引发内存错误。
如果passwd_len恰好设置为-1,且passwd_len后面的那些字节(SCRAMBLE)全部用非0值填充,那么strlen就会一直朝后面读下去直到找到0。但是由于db指针指向的是堆上,而且在很多操作系统下,如Freebsd,都会把在堆上分配的内存在交给用户使用前都初始化成0,所以……想让strlen读越界也很难。

而接下来的一个关于边界的检查
  if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) …
也因为整数溢出而导致失效。
然后程序会一直向下执行到check_user,如果编译的时候定义了NO_EMBEDDED_ACCESS_CHECKS,那么万事大吉,此时的db指针会被立即传递给mysql_change_db函数,哦……………………否则的话,将会有一处关于password_len的检查
  if (passwd_len != 0 &&
      passwd_len != SCRAMBLE_LENGTH &&
      passwd_len != SCRAMBLE_LENGTH_323)
    DBUG_RETURN(ER_HANDSHAKE_ERROR);
server会立刻给客户端报告一个handshare error而终止连接。
==========================================
附:mysql调试策略
1、mysqld
要在mysqld正在运行的时候挂一个gdb进去是很不容易的,我的方法是在编译的configure的时候加一个选项–with-debug,然后修改我感兴趣的部分的代码,用
DBUG_PRINT(”snnn info”,(”passwd_len:%u,db_len:%u”,passwd_len,db_len));
这样的方式去输出调试语句。
然后用mysqld_safe –defaults-file=xxx –debug &的方式启动mysql,然后去/tmp/mysqld.trace中查找我记录的日志。

2、mysql client
要直接调试mysql client也不好办,通常都是利用mysql client库,自己写一些简单的程序,然后用gdb跟踪到mysql client库中去。其中很重要的两个函数是
my_real_read
my_net_write
通过对这两个函数下断点能够很顺利的跟踪服务器、客户端之间的数据交换流程。

 Posted by at 上午 2:43
6月 112008
 

show processlist查看有大量如下信息,造成mysql假死.

| 364 | unauthenticated user | xxx.xxx.xxx.xxx:63249 | NULL | Connect |    | login | NULL          |
| 365 | unauthenticated user | xxx.xxx.xxx.xxx:56768 | NULL | Connect |    | login | NULL          |
| 366 | unauthenticated user | xxx.xxx.xxx.xxx:54127 | NULL | Connect |    | login | NULL          |
| 367 | unauthenticated user | xxx.xxx.xxx.xxx:51060 | NULL | Connect |    | login | NULL          |

看下手册中的解释是:unauthenticated user refers to a thread that has become associated with a client connection but for which authentication of the client user has not yet been done。意即:有一个线程在处理客户端的连接,但是该客户端还没通过用户验证。
原因可能有:
1、 服务器在做DNS反响解析,解决办法有2:
1、) 在 hosts 中添加客户端ip,如
192.168.0.1   yejr
2、) MySQL启动参数增加一个skip-name-resolve,即不启用DNS反响解析;或者在/etc/my.cnf里添加一行skip-name-resolve
2、服务器的线程还处于排队状态,因此可以加大 back_log

然后重启mysql就会发现unauthenticated user 全部消失

[手册]

7.5.10. How MySQL Uses DNS

When a new client connects to mysqld, mysqld spawns a new thread to handle the request. This thread first checks whether the hostname is in the hostname cache. If not, the thread attempts to resolve the hostname:

        

  •     

    If the operating system supports the thread-safe gethostbyaddr_r() and gethostbyname_r() calls, the thread uses them to perform hostname resolution.

        

  •     

  •     

    If the operating system does not support the thread-safe calls, the thread locks a mutex and calls gethostbyaddr() and gethostbyname() instead. In this case, no other thread can resolve hostnames that are not in the hostname cache until the first thread unlocks the mutex.

        

You can disable DNS hostname lookups by starting mysqld with the --skip-name-resolve option. However, in this case, you can use only IP numbers in the MySQL grant tables.

If you have a very slow DNS and many hosts, you can get more performance by either disabling DNS lookups with --skip-name-resolve or by increasing the HOST_CACHE_SIZE define (default value: 128) and recompiling mysqld.

You can disable the hostname cache by starting the server with the --skip-host-cache option. To clear the hostname cache, issue a FLUSH HOSTS statement or execute the mysqladmin flush-hosts command.

To disallow TCP/IP connections entirely, start mysqld with the --skip-networking option.

 Posted by at 上午 7:23