接下来需要编写HandlerSocket客户端代码,我们提供了C++和Perl客户端库,下面是一个简单的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->open_index(0, 'test', 'user', 'PRIMARY', 'user_name,user_email,created');
die $hs->get_error() if $res != 0;
#3. main logic
#fetching rows by id
#execute_single (index id, cond, cond value, max rows, offset) $res = $hs->execute_single(0, '=', [ '101' ], 1, 0);
die $hs->get_error() if $res->[0] != 0;
shift(@$res);
for (my $row = 0; $row < 1; ++$row)
{
my $user_name= $res->[$row + 0];
my $user_email= $res->[$row + 1];
my $created= $res->[$row + 2];
print "$user_name\t$user_email\t$created\n";
}
#4. closing the connection $hs->close();
上面的代码从user表查询user_name,user_email和created列,查询条件是user_id=101,因此查询结果和前面的SELECT语句一样。
对于大多数Web应用程序而言,保持轻量级的HandlerSocket连接是一个很好的做法(持续连接),让大量的请求可以集中于主要逻辑(上面代码中的#3部分)。
HandlerSocket协议是一个小尺寸的基于文本的协议,和Memcached文本协议类似,你可以使用telnet通过HandlerSocket获取数据。
Trying 192.168.1.2...
Connected to xxx.dena.jp (192.168.1.2).
Escape character is '^]'.
P 0 test user PRIMARY user_name,user_email,created
0 1
0 = 1 101
0 3 Yukari Takeba yukari.takeba@dena.jp 2010-02-03 11:22:33
绿色表示请求数据包,字段必须用Tab键分隔。
基准测试
现在是时候展示我们的基准测试结果了,我使用上面的user表,从多线程远程客户端测试了执行主键查询操作的次数,所有用户数据都装入到内存中(我测试了100万行),我也用类似的数据测试了Memcached(我使用libmemcached和memcached_get()获取用户数据),在MySQL SQL测试中,我使用了一个传统的SELECT语句:“SELECT user_name, user_email, created FROM user WHERE user_id=?”,Memcached和HandlerSocket客户端代码均使用C/C++编写,所有客户端程序都位于远程主机上,通过TCP/IP连接到MySQL/Memcached。最高的吞吐量情况如下:
MySQL via SQL 105,000 %us 60% %sy 28%
memcached 420,000 %us 8% %sy 88%
MySQL via HandlerSocket 750,000 %us 45% %sy 53%
通过HandlerSocket比传统的SQL语句吞吐量要高出7.5倍,这说明了MySQL的SQL层是非常耗资源的,如果能跳过这一层性能肯定会大大提升。有趣的是,MySQL使用HandlerSocket时的速度比使用Memcached快178%,并且Memcached消耗的%system资源也更多,虽然Memcached是一个很好的产品,但仍然有优化的空间。
下面是oprofile输出内容,是在MySQL HandlerSocket测试期间收集到的,在核心操作,如网络数据包处理,获取数据等的CPU资源消耗(bnx2是一个网络设备驱动程序)。
984785 5.9118 bnx2 /bnx2
847486 5.0876 ha_innodb_plugin.so.0.0.0 ut_delay
545303 3.2735 ha_innodb_plugin.so.0.0.0 btr_search_guess_on_hash
317570 1.9064 ha_innodb_plugin.so.0.0.0 row_search_for_mysql
298271 1.7906 vmlinux tcp_ack
291739 1.7513 libc-2.5.so vfprintf
264704 1.5891 vmlinux .text.super_90_sync
248546 1.4921 vmlinux blk_recount_segments
244474 1.4676 libc-2.5.so _int_malloc
226738 1.3611 ha_innodb_plugin.so.0.0.0 _ZL14build_template P19row_prebuilt_structP3THDP8st_tablej
206057 1.2370 HandlerSocket.so dena::hstcpsvr_worker::run_one_ep()
183330 1.1006 ha_innodb_plugin.so.0.0.0 mutex_spin_wait
175738 1.0550 HandlerSocket.so dena::dbcontext:: cmd_find_internal(dena::dbcallback_i&, dena::prep_stmt const&, ha_rkey_function, dena::cmd_exec_args const&)
169967 1.0203 ha_innodb_plugin.so.0.0.0 buf_page_get_known_nowait
165337 0.9925 libc-2.5.so memcpy
149611 0.8981 ha_innodb_plugin.so.0.0.0 row_sel_store_mysql_rec
148967 0.8943 vmlinux generic_make_request
因为HandlerSocket是运行在MySQL内部的,并直接与InnoDB打交道,你可以使用常见的SQL命令,如SHOW GLOBAL STATUS获得统计信息,Innodb_rows_read达到了750000+是值得一看的。
| Innodb_rows_read | 750192 |
| Innodb_rows_read | 751510 |
| Innodb_rows_read | 757558 |
| Innodb_rows_read | 747060 |
| Innodb_rows_read | 748474 |
| Innodb_rows_read | 759344 |
| Innodb_rows_read | 753081 |
| Innodb_rows_read | 754375 | ...
测试用机详细规格如下:
型号:戴尔PowerEdge R710
CPU:Nehalem 8核,E5540@2.53GHz
内存:32GB(所有数据都装入缓冲池)
MySQL版本:5.1.50,InnoDB插件
Memcached/libmemcached版本:1.4.5(Memcached),0.44(libmemcached)
网络:Boradcom NetXtreme II BCM5709 1000Base-T(内建四端口,使用了其中三个)
Memcached和HandlerSocket都做了网络I/O限制,当我测试单个端口时,HandlerSocket的成绩是260000qps,Memcached的成绩是220000qps。