有人提到使用 HANDLER ,但是因为在解析 SQL时,opening/closing table 还是必须的,所以对于提高吞吐量,它还是只能爱莫能助。
如果只有一小部分数据进入内存,那么 SQL 带来的消耗可以忽略不计。很简单,因为磁盘的 I/0 操作所带来的消耗会要大,这种情况下时,就不需要太过的去考虑 SQL 所带来的消耗。
但是,在大多数的 hot MySQL 服务器中, 大部分的数据都是因为全部载入至内存中而变的只受 CPU 的限制。Profiling 的结果就类似上所述的那样: SQL 层消耗了大量的资源。假设,需要做大量的 PK 查询(i.e. SELECT x FROM t WHERE id=?)或者是做 LIMIT 的范围查询, 即使有 70-80% 都是在同一张表中做 PK 查询(仅仅只是查询条件中给定的值不同,即 value 不同而已), MySQL 还是每次需要去做 parse/open/lock/unlock/close, 这对我们来说,是非常影响效率的。
到底有没有好的方法来减少 MySQL SQL 层的 CPU 资源/争夺呢? 如果使用 MySQL Cluster, NDBAPI 不失为一个很好的解决办法。 在我还是 MySQL/Sun/Oracle 的顾问时,就见到过很多客户对SQL Node + NDB performance 感到非常不爽,但当他们用了 NDBAPI 客户端后,发现性能调提高了 N 倍。当然,在 MySQL Cluster 中是可以同时使用 NDBAPI 和 SQL 的,但在做频繁的访问模式时还是推荐使用 NDBAPI,而在 ad-hoc 或者 查询不频繁的情况下使用 SQL + MySQL + NDB。
以快捷的速度访问 API, 这正是我们需要的,但同时我们也想在 ad_hoc 或者复杂的查询的情况时还是使用 SQL. 像其他的 web service, DeNA 使用的是 InnoDB, 转为 NDB,这并不是一件容易的事情,因为内置InnoDB 即不支持 SQL 也不支持网络层的服务。
最好的办法可以是在 MySQL 的内部,实现一以 MySQL plugin 的形式存在的 NoSQL 的网络服务。它侦听在某端口来接收采用 NoSQL 协议/API 的通讯, 然后通过 MySQL 内部的存储引擎 API 来直接访问 InnoDB。这种方法的理念类似于 NDBAPI, 但是它可以做到与 InnoDB 通讯。
这个理念最初是去年由 Kazuho Oku 在 Cybozu Labs 上提出的,他曾写过采用 Memcached protocols 通讯的MyCached UDF。而我的大学同学实现了另外一个插件 ? HandlerSocket,
是以 MySQL daemaon plugin 形式存在,所以在应用中可把 MySQL 当 NoSQL 使用. 它最大的功能是实现了与存储引擎交互,比如 InnoDB,而这不需要任何的 SQL 方面的开销. 访问 MySQL 的 table 时,当然她也是需要 open/close table 的,但是 它并不是每次都去 open/close table, 因为它会将以前访问过的 table 保存下来以供来是使用,而 opening/closing tables 是最耗资源的,而且很容易引起互斥量的争夺,这样一来,对于提高性能,非常有效。在流量变小时, HandlerSocket 会 close tables, 所以,它不会阻塞 administrative commands (DDL).
它与MySQL + Memcached 的区别在哪呢? 对比图1 和图2 ,可从中看出其不同点。图2 展示了典型的 MySQL + Memecached 的使用. 因为 Memcached 的 get 操作比 MySQL 的内存中/磁盘上的主键查询要快很多,所以 Memcached 用于缓存数据库记录。如果 HandlerSocket 的查询速度能和 Memcached 媲美,我们就不需要使用 Memcached 来缓存记录。
举一个例子,假设有一 user 表,通过 user_id 来获取用户信息:
CREATE TABLE user
(
user_id INT UNSIGNED PRIMARY KEY,
user_name VARCHAR(50),
user_email VARCHAR(255),
created DATETIME
)
ENGINE=InnoDB;
用 SELECT 语句获取用户信息
mysql> SELECT user_name, user_email, created FROM user WHERE user_id=101;
+---------------+-----------------------+---------------------+
| user_name | user_email | created
| +---------------+-----------------------+---------------------+
| Yukari Takeba | yukari.takeba@dena.jp | 2010-02-03 11:22:33
| +---------------+-----------------------+---------------------+
1 row in set (0.00 sec)
下面我们来看看如何使用 HandlerSocket 完成同样的事情.
安装 HandlerSocket
HandlerSocket具体安装步骤请参考这里,基本步骤如下:
1
下载HandlerSocket
2 编译 HandlerSocket(客户端和服务端)
[root@localhost handlersocket]# ./configure --with-mysql-source=mysql-source-dir --with-mysql-bindir=mysql-server-bin-dir
[root@localhost handlersocket]# make
[root@localhost handlersocket]# make install
3 安装 HandlerSocket
mysql> INSTALL PLUGIN \'handlersocket\' soname \'handlersocket.so\';
因为 HandlerSocket是 MySQL 插件,所以可以象使用其它插件,如 InnoDB, Q4M 和 Spider 那样使用它,即不需要修改 MySQL 源代码,MySQL 最好是 5.1 或更高版本,编译 HandlerSocket 时需要 MySQL 源码和 MySQL 库。
书写 HandlerSocket 客户端代码
目前已提供 C++ 和 perl 调用的客户端库,下面是使用 perl 调用的实例代码:
#!/usr/bin/perl
use strict;
use warnings;
use Net::HandlerSocket;
#1. establishing a connection
my $args = { host => \'ip_to_remote_host\', port => 9998 };
my $hs = new Net::HandlerSocket($args);
#2. initializing an index so that we can use in main logics.
# MySQL tables will be opened here (if not opened)
my $res = $hs-