MySQL HandlerSocket in Action

1 Overview

最近一篇关于MySQL HandlerSocket的blog吸引了不少人的注意,甚至MySQL Performance Blog上也有关于HandlerSocket的性能评测。该blog中声称对于一个CPU bound而非IO bound的MySQL Server(即绝大部分的数据可以从缓存中取得,例如InnoDB Buffer Pool有接近100%的命中率),使用HandlerSocket可以将查询性能提升7.5倍。目前HandlerSocket已经被其作者应用到生产环境,效果良好。笔者在昨天抽空安装并试用了一下HandlerSocket Plugin。

 

2 Installation

安装的过程基本顺利,不过可能是由于笔者是在MySQL 5.1.42+InnoDB Plugin 1.0.6上编译安装HandlerSocket的原因,编译HandlerSocket的过程中报如下错误:

configure: error: MySQL source version does not match MySQL binary version

调查一下后发现是configure脚本没有能够找到MySQL source的版本。笔者调整了configure脚本,跳过了该检查。编译安装后会在MySQL的plugin目录中生成一个so文件。然后在MySQL中安装该plugin即可。

Sql代码
  1. mysql>INSTALLPLUGINhandlersocketsoname'handlersocket.so';
  2. mysql>showplugins;
  3. +---------------------+----------+--------------------+---------------------+---------+
  4. |Name|Status|Type|Library|License|
  5. +---------------------+----------+--------------------+---------------------+---------+
  6. |handlersocket|ACTIVE|DAEMON|handlersocket.so|BSD|
  7. 18rowsinset(0.00sec)

 

3 Getting Started

首先创建一个测试用的表。

Sql代码
  1. mysql>CREATETABLE`user`(
  2. `user_id`int(10)unsignedNOTNULL,
  3. `user_name`varchar(50)DEFAULTNULL,
  4. `user_email`varchar(255)DEFAULTNULL,
  5. `created`datetimeDEFAULTNULL,
  6. PRIMARYKEY(`user_id`),
  7. KEY`INDEX_01`(`user_name`)
  8. )ENGINE=InnoDB
  9. mysql>insertintouservalues(1,"John","[email protected]",CURRENT_TIMESTAMP);
  10. mysql>insertintouservalues(2,"Kevin","[email protected]",CURRENT_TIMESTAMP);
  11. mysql>insertintouservalues(3,"Dino","[email protected]",CURRENT_TIMESTAMP);

接下来按照blog中的例子,编写如下perl脚本handlersocket.pl。

Perl代码
  1. #!/usr/bin/perl
  2. usestrict;
  3. usewarnings;
  4. useNet::HandlerSocket;
  5. #1.establishingaconnection
  6. my$args={host=>'localhost',port=>9998};
  7. my$hs=newNet::HandlerSocket($args);
  8. #2.initializinganindexsothatwecanuseinmainlogics.
  9. #MySQLtableswillbeopenedhere(ifnotopened)
  10. my$res=$hs->open_index(0,'test','user','INDEX_01','user_name,user_email,created');
  11. die$hs->get_error()if$res!=0;
  12. #3.mainlogic
  13. #fetchingrowsbyid
  14. #execute_single(indexid,cond,condvalue,maxrows,offset)
  15. $res=$hs->execute_single(0,'=',['kevin'],1,0);
  16. die$hs->get_error()if$res->[0]!=0;
  17. shift(@$res);
  18. for(my$row=0;$row<1;++$row){
  19. my$user_name=$res->[$row+0];
  20. my$user_email=$res->[$row+1];
  21. my$created=$res->[$row+2];
  22. print"$user_name/t$user_email/t$created/n";
  23. }
  24. #4.closingtheconnection
  25. $hs->close();

 

最后执行handlersocket.pl,结果如下:

Shell代码
  1. perlhandlersocket.pl
  2. [email protected]2010-11-1422:35:22

4 Philosophy of the HandlerSocket

通常,MySQL Server可以被看成两层架构:即SQL Layer和Storage Engine Layer,它们之间通过Handler API进行交互。MySQL Server在接收到客户端的Query请求后,通常需要在SQL layer中进行词法分析,语法分析,优化等过程,最终生成一个树型的查询计划,交由执行引擎执行。执行引擎根据查询计划,跟相应的存储引擎通信,得到查询结果。

 

HandlerSocket的作者认为,对于CPU bound的MySQL server来说,SQL layer消耗了过多的资源,以致总体性能不佳。HandlerSocket则绕过了MySQL Server的SQL layer,直接跟存储引擎交互,从而带来了大幅的性能提升。默认情况下HandlerSocket Plugin监听9998和9999两个端口,其中9998只支持读操作,9999支持读写操作,但是性能跟9998端口相比稍慢。

 

HandlerSocket的作者在其blog中列出了一系列HandlerSocket的优点,以下是笔者认为其中比较重要的:

  • Can handle lots of concurrent connections
  • Extremely high performance
  • No duplicate cache
  • No data inconsistency
  • Independent from storage engines
  • Supporting lots of query patterns
  • No need to modify/rebuild MySQL
  • All operational benefits from MySQL

其中No duplicate cache和No data inconsistency这两条,笔者感触颇深。关于MySQL的NoSQL扩展,可能很多项目都在使用memcached,或者自己实现的cache等。这种独立缓存的实现方式有个重要的局限,即如何保证MySQL和cache之间的数据一致性,尽管Memcached Functions for MySQL(基于Trigger和MySQL UDF)从某种程度上提供了一种解决方案。

此外,关于Independent from storage engines和Supporting lots of query patterns。理论上通过Handler API可以和任何存储引擎交互,但是目前HandlerSocket只是在InnoDB Plugin上进行了测试。此外,HandlerSocket的作者在其blog上指出,通过HandlerSocket,不仅可以通过主键查询,也可以通过普通索引,甚至不使用索引进行查询(包括范围查询),甚至还可以进行insert/update/delete等写操作。

关于All operational benefits from MySQL,正如在其blog中提到的,可以比较方便地通过MySQL的既存功能对HandlerSocket进行监控,例如以下几个监控指标:

Sql代码
  1. mysql>showglobalstatuslike'handler_read%';
  2. mysql>showglobalstatuslike'Com_select%';
  3. mysql>showglobalstatuslike'InnoDB_rows_read';

5 Client API

遗憾的是到目前为止,HandlerSocket只有C和Perl的客户端API,Java和Python等版本的客户端API貌似在开发中。在这里简单分析一下Perl版本的客户端API。

 

5.1 Connection

与HandlerSocket Plugin创建连接的方式如下:

Perl代码
  1. useNet::HandlerSocket;
  2. my$args={host=>'localhost',port=>9998};
  3. my$hs=newNet::HandlerSocket($args);

其中Net::HandlerSocket模块存放于HandlerSocket的分发tar包的perl-Net-HandlerSocket目录中,编译安装方式如下:

Shell代码
  1. cdperl-Net-HandlerSocket/
  2. perlMakefile.PL
  3. make
  4. makeinstall

5.2 Opening index

在进行操作之前,首先需要打开一个索引,如下:

Perl代码
  1. my$err=$hs->open_index(3,'database1','table1','PRIMARY','f1,f2');
  2. die$hs->get_error()if$res->[0]!=0;

其中'database1'为schema名,'table1'为表名,'PRIMARY'为索引名,'f1,f2'为查询的列名。关于方法的open_index的第一个参数3,用来在每个Net::HandlerSocket对象中唯一标识一个表名。

 

5.3 Query

通过execute_single方法进行查询,例如:

Perl代码
  1. my$res=$hs->execute_single(3,'=',['foo'],1,0);
  2. die$hs->get_error()if$res->[0]!=0;
  3. shift(@$res);

execute_single方法的第一个参数需要跟之前open_index方法的第一个参数一致。第二个参数'='指定了检索条件,目前支持'=', '>=', '<=', '>'和'<'。第三个参数[ 'foo' ]为一个arrayref,指定了检索的key,其长度必须小于或者等于对应索引的列数。第四个和第五个参数指定了查询的limit和offset。

 

execute_single方法的返回值类型为arrayref,其第一个元素为error code:

  • 0:正常。
  • 负数:I/O 错误,对应的Net::HandlerSocket对象需要被丢弃。
  • 正数:其它错误,但是与HandlerSocket Plugin的连接仍然正常可用,因此对应的Net::HandlerSocket对象可以继续使用。

第一个元素之后的其它元素即查询结果,如果返回的row数大于1,那么也是存放在这个一维数组中。假设查询结果共5行,每行三列,那么对应的代码如下:

Perl代码
  1. die$hs->get_error()if$res->[0]!=0;
  2. shift(@$res);
  3. for(my$row=0;$row<5;++$row){
  4. for(my$col=0;$col<3;++$col){
  5. my$value=$res->[$row*5+$col];
  6. #...
  7. }
  8. }

 

5.4 Insert records

execute_single方法也可以用来插入记录,例如:

Perl代码
  1. my$args={host=>'localhost',port=>9999};
  2. my$hs=newNet::HandlerSocket($args);
  3. my$res=$hs->execute_single(3,'+',['foo','bar','baz']);
  4. die$hs->get_error()if$res->[0]!=0;
  5. my$num_inserted_rows=$res->[1];

需要注意的是,此时连接的是9999端口,因为9998端口只支持读操作。

 

5.5 Update or delete records

对于更新和删除操作,同样使用execute_single方法,例如:

Perl代码
  1. my$args={host=>'localhost',port=>9999};
  2. my$hs=newNet::HandlerSocket($args);
  3. my$res=$hs->execute_single(3,'=',['bar'],1,0,'U',['fubar','hoge']);
  4. die$hs->get_error()if$res->[0]!=0;
  5. my$num_updated_rows=$res->[1];
  6. my$res=$hs->execute_single(3,'=',['baz'],1,0,'D');
  7. die$hs->get_error()if$res->[0]!=0;
  8. my$num_deleted_rows=$res->[1];

execute_single方法的第六个参数指定的操作类型,目前支持'U'和'D'。对于'U'操作,execute_single方法的第七个参数指定了更新后的值;对于'D'操作,第七个参数被忽略。

 

5.6 Multiple operations

可在一次调用中执行多个操作,这样速度更快,例如:

Perl代码
  1. my$rarr=$hs->execute_multi([
  2. [0,'>=',['foo'],5,0],
  3. [2,'=',['bar'],1,0],
  4. [4,'<',['baz'],10,5],
  5. ]);
  6. formy$res(@$rarr){
  7. die$hs->get_error()if$res->[0]!=0;
  8. shift(@$res);
  9. #...
  10. }

6 Reference

http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html (翻)

http://www.mysqlperformanceblog.com/2010/11/02/handlersocket-on-ssd/

 

原文地址:http://whitesock.iteye.com/blog/811339

 

你可能感兴趣的:(handler)