MySQL HandlerSocket

MySQL HandlerSocket 插件说明与安装配置

一、HandlerSocket是什么?
HandlerSocket是akira higuchi写的一个MySQL的插件。以MySQL Daemon Plugin的形式提供类似NoSQL的网络服务,通过这个插件,你可以直接跟MySQL后端的存储引擎做key-value式的交互,省去了MySQL上层的SQL解释、打开关闭表、创建查询计划等CPU开销。
目前使用MySQL的网站,多半同时使用Memcache作为键值缓存。虽然这样的架构极其流行,有众多成功的案例,但过于依赖Memcache,无形中让Memcache成为故障的根源:
Memcache数据一致性的问题:当MySQL数据变化后,如果不能及时有效的清理掉过期的数据,就会造成数据不一致。这在强调即时性的Web2.0时代,不可取。
Memcache崩溃后的雪崩效应:作为缓存的Memcache一旦崩溃,MySQL很可能在短时间内承受高负载而宕机。
注:关于清理过期数据的问题,可以在程序架构上想办法,如果数据操作有统一DAO封装的话,可以利用Observer模式来清理过期数据,非主题内容,资料自查。
面对以上问题,HandlerSocket项目是个不错的解决方案,它通过插件的方式赋予MySQL完整的NoSQL功能,从原理上讲,它跳过MySQL中最耗时的语法解析,查询计划等步骤,直接读取数据,如果内存够大,能装下索引,MySQL的查询效率能提高若干倍!
HandlerSocket:https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
php-handlersocket:http://code.google.com/p/php-handlersocket/

系统信息约定:

系统版本:CentOS 6.3 X86
PHP安装目录:/usr/local/webserver/php5318
MySQL安装目录:/usr/local/webserver/mysql5520
HandlerSocket安装路径:/usr/local/webserver/handlersocket


二、安装配置HandlerSocket
安装之前建议你先安装相关支持及需要的组件包:

yum install gcc gcc-c++ libtool make openssl-devel perl-DBI perl-DBD-MySQL
yum install rpm-build gperf readline-devel ncurses-devel time perl-Time-HiRes


1. 安装
如果使用Percona Server版本的MySQL就简单了,因为它已经内置了HandlerSocket支持,不过考虑到其内置的版本不够新,存在一些早已修复的BUG,所以最好采用源代码编译。HandlerSocket是基于MySQL数据库的,因此在安装HanderSocket前需要先按照常规方式部署MySQL服务,同时需注意HandlerSocket时需要MySQL的源码,因此还需要MySQL源码编译方式安装。

[root@iredmail opt]# git clone https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL.git
[root@iredmail opt]# cd HandlerSocket-Plugin-for-MySQL
[root@iredmail HandlerSocket-Plugin-for-MySQL]# ./autogen.sh
[root@iredmail HandlerSocket-Plugin-for-MySQL]#./configure --prefix=/usr/local/webserver/handlersocket --with-mysql-source=/opt/mysql-5.5.20 --with-mysql-bindir=/usr/local/webserver/mysql5520/bin --with-mysql-plugindir=/usr/local/webserver/mysql5520/lib/mysql/plugin

Tips:
--with-mysql-source 指定MySQL源码所在目录
--with-mysql-bindir 表示MySQL二进制可执行文件目录
--with-mysql-plugindir 指定MySQL插件的存储路径,如果不清楚这个目录在哪,可以按如下方法查询:

mysql> show variables like 'plugin%';
+---------------+-------------------------------------------+
| Variable_name | Value                                     |
+---------------+-------------------------------------------+
| plugin_dir    | /usr/local/webserver/mysql5520/lib/plugin |
+---------------+-------------------------------------------+
1 row in set (0.00 sec)
[root@iredmail HandlerSocket-Plugin-for-MySQL]# make

常见错误:

libtool: link: only absolute run-paths are allowed
make[2]: *** [handlersocket.la] Error 1
make[2]: Leaving directory `/opt/HandlerSocket-Plugin-for-MySQL/handlersocket'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/opt/HandlerSocket-Plugin-for-MySQL'
make: *** [all] Error 2
解决方法:
[root@iredmail HandlerSocket-Plugin-for-MySQL]# vi handlersocket/Makefile
line 301:
$(handlersocket_la_LINK) -rpath $(pkgplugindir) $(handlersocket_la_OBJECTS) $(handlersocket_la_LIBADD) $(LIBS)
-->
$(handlersocket_la_LINK) -rpath /opt/HandlerSocket-Plugin-for-MySQL/handlersocket $( handlersocket_la_OBJECTS) $(handlersocket_la_LIBADD) $(LIBS)


[root@iredmail HandlerSocket-Plugin-for-MySQL]#make install

完成后,mysql-plugindir目录下应有handlersocket相关文件

2、配置MySQL:
修改my.cnf配置文件:

[root@iredmail HandlerSocket-Plugin-for-MySQL]# vi /etc/my.cnf
[mysqld]
plugin-load=handlersocket.so(plugin-load可略过不配)
loose_handlersocket_port = 9998 # 指定读请求端口号
# the port number to bind to (for read requests)
loose_handlersocket_port_wr = 9999 # 指定写请求端口号
# the port number to bind to (for write requests)
loose_handlersocket_threads = 16 # 指定读线程数目
# the number of worker threads (for read requests)
loose_handlersocket_threads_wr = 1 # 指定写线程数目
# the number of worker threads (for write requests)
open_files_limit = 65535
# to allow handlersocket accept many concurren connections, make open_files_limit as large as possible.

Tips:InnoDB的innodb_buffer_pool_size,或MyISAM的key_buffy_size等关系到缓存索引的选项尽可能设置大一些,这样才能发挥HandlerSocket的潜力。

登陆MySQL并激活HandlerSocket插件:

[root@iredmail HandlerSocket-Plugin-for-MySQL]# mysql -uroot -p
mysql> install plugin handlersocket soname 'handlersocket.so';
ERROR 1126 (HY000): Can't open shared library '/usr/local/webserver/mysql5520/lib/plugin/handlersocket.so' (errno: 2 cannot open shared object file: No such file or directory)
说明:这里提示没有找到handlersocket.so扩展文件,请查看扩展文件是否存在。
mysql> install plugin handlersocket soname 'handlersocket.so';
Query OK, 0 rows affected (0.00 sec)

mysql> quit;

至此,HandlerSocket插件安装完毕。

重启mysql服务:

[root@iredmail HandlerSocket-Plugin-for-MySQL]# service mysqld restart


3、HandlerSocket状态测试:

点击在新窗口中浏览此图片

点击在新窗口中浏览此图片

也可以通过查询刚配置的端口是否已经被MySQL占用来确认是否安装成功:

[root@iredmail HandlerSocket-Plugin-for-MySQL]# lsof -i -P | grep mysqld
mysqld    26871 mysql   11u  IPv4  72467      0t0  TCP *:9998 (LISTEN)
mysqld    26871 mysql   29u  IPv4  72469      0t0  TCP *:9999 (LISTEN)
mysqld    26871 mysql   31u  IPv4  72474      0t0  TCP *:3306 (LISTEN)

Tips:If ports 9998 and 9999 don’t show up.  Make sure SELinux is not running.

三、安装配置 php-handlersocket 扩展模块:
1、安装php-handlersocket扩展

[root@iredmail opt]# wget http://php-handlersocket.googlecode.com/files/php-handlersocket-0.3.1.tar.gz
[root@iredmail opt]# tar -zxvf php-handlersocket-0.3.1.tar.gz
[root@iredmail opt]# cd handlersocket/
[root@iredmail handlersocket]# /usr/local/webserver/php5318/bin/phpize
[root@iredmail handlersocket]# ./configure --with-php-config=/usr/local/webserver/php5318/bin/php-config

./configure可加参数:
点击在新窗口中浏览此图片
Tips:If you get error:
configure: error: Can't find hsclient  headers,please install libhsclient first,Or ./configure--disable-handlersocket-hsclient --with-php-config=/usr/local/webserver/php5318/bin/php-config use native type.

[root@iredmail handlersocket]#make && make install

A successful install will have created handlersocket.so and put it into the PHP extensions directory. You'll need to and adjust php.ini and add an extension=handlersocket.so line before you can use the extension.

[root@iredmail handlersocket]# vi /usr/local/webserver/php5318/etc/php.ini
extension=handlersocket.so


至此php扩展安装完成,放问php.info页面,我们可以看到已经成功加载了handlersocket扩展

点击在新窗口中浏览此图片


2、php-handlersocket 使用示例:

  1. /*  
  2.  * String  $host:MySQL ip; 
  3.  * String  $port:handlersocket插件的监听端口,它有两个端口可选:一个用于读、一个用于写  
  4.  */  
  5. $hs = new HandlerSocket($host$port);  
  6. 打开一个数据表:  
  7. /* 
  8.  * Int       $index:这个数字相当于文件操作里的句柄,HandlerSocket的所有其他方法都会依据这个数字来操作由这个   openIndex打开的表, 
  9.  * String  $dbname:库名 
  10.  * String  $table:表名 
  11.  * String  $key:表的“主键”(HandlerSocket::PRIMARY)或“索引名”作为搜索关键字段,这就是说表必须有主键或索引 
  12.  *                 个人理解:要被当做where条件的key字段,这样可以认为handlersocket只有一个where条件 
  13.  * String  $column:'column1,column2' 所打开表的字段(以逗号隔开),就是说$table表的其他字段不会被操作 
  14.  */  
  15. $hs->openIndex($index$dbname$table$key$column);  
  16. 查询:  
  17. /* 
  18.  * Int     $index: openIndex()所用的$index 
  19.  * String  $operation:openIndex方法中指定的$key字段所用的操作符,目前支持'=', '>=', '< =', '>',and '< ';可以理解为where条件 
  20.  * Array   $value 
  21.  * Int       $number(默认是1):获取结果的最大条数;相当于SQL中limit的第二个参数 
  22.  * Int     $skip(默认是0):跳过去几条;相当于SQL中limit的第一个参数 
  23.  */  
  24. $retval = $hs->executeSingle($index$operation$value$number$skip);  
  25. 插入(注意:此处的openIndex要用$port_wr,即读写端口):  
  26. /* 
  27.  * Int     $index: openIndex()所用的$index 
  28.  * Array   $arr:数字元素数与openIndex的$column相同 
  29.  */  
  30. $retval = $hs->executeInsert($index$arr);  
  31. 删除(注意:此处的openIndex要用$port_wr,即读写端口):  
  32. /* 
  33.  * Int     $index: openIndex()所用的$index 
  34.  * String  $operation:openIndex方法中指定的$key字段所用的操作符,目前支持'=', '>=', '< =', '>',and '< ';可以理解为where条件 
  35.  * Array   $value 
  36.  * Int     $number(默认是1):获取结果的最大条数;相当于SQL中limit的第二个参数 
  37.  * Int     $skip(默认是0):跳过去几条;相当于SQL中limit的第一个参数 
  38.  */  
  39. $retval = $hs->executeDelete($index$operation$value$number$skip);  
  40. 更新(注意:此处的openIndex要用$port_wr,即读写端口):  
  41. /* 
  42.  * Int     $index: openIndex()所用的$index 
  43.  * String  $operation:openIndex方法中指定的$key字段所用的操作符,目前支持'=', '>=', '< =', '>',and '< ';可以理解为where条件 
  44.  * Array   $value 
  45.  * Int       $number(默认是1):获取结果的最大条数;相当于SQL中limit的第二个参数 
  46.  * Int     $skip(默认是0):跳过去几条;相当于SQL中limit的第一个参数 
  47.  */  
  48. $retval = $hs->executeUpdate($index$operation$value$number$skip);  


Example:
测试库 hstestdb,测试表hstesttbl:

CREATE TABLE `hstesttbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` char(6) DEFAULT NULL,
  `v` char(6) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_hstesttbl_k` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;


PHP Test Code:

  1. $host       = 'localhost';  
  2. $port       = 9998;  
  3. $port_wr    = 9999;  
  4. $dbname     = 'hstestdb';  
  5. $table      = 'hstesttbl';  
  6.    
  7. //GET  
  8. $hs = new HandlerSocket($host$port);  
  9. if (!($hs->openIndex(1, $dbname$table, HandlerSocket::PRIMARY, 'k,v'))) {  
  10.     echo $hs->getError(), PHP_EOL;  
  11.     die();  
  12. }  
  13.    
  14. $retval = $hs->executeSingle(1, '='array('k1'), 1, 0);  
  15. var_dump($retval);  
  16.    
  17. $retval = $hs->executeMulti(  
  18.     array(  
  19.         array(1, '='array('k1'), 1, 0),  
  20.         array(1, '='array('k2'), 1, 0)  
  21.     )  
  22. );  
  23. var_dump($retval);  
  24. unset($hs);  
  25.    
  26. //UPDATE  
  27. $hs = new HandlerSocket($host$port_wr);  
  28. if (!($hs->openIndex(2, $dbname$table'''v'))) {  
  29.     echo $hs->getError(), PHP_EOL;  
  30.     die();  
  31. }  
  32.    
  33. if ($hs->executeUpdate(2, '='array('k1'), array('V1'), 1, 0) === false) {  
  34.     echo $hs->getError(), PHP_EOL;  
  35.     die();  
  36. }  
  37.    
  38. unset($hs);  
  39.    
  40. //INSERT  
  41. $hs = new HandlerSocket($host$port_wr);  
  42. if (!($hs->openIndex(3, $dbname$table'''k,v'))) {  
  43.     echo $hs->getError(), PHP_EOL;  
  44.     die();  
  45. }  
  46.    
  47. if ($hs->executeInsert(3, array('k2''v2')) === false) {  
  48.     echo $hs->getError(), PHP_EOL;  
  49. }  
  50. if ($hs->executeInsert(3, array('k3''v3')) === false) {  
  51.     echo 'A'$hs->getError(), PHP_EOL;  
  52. }  
  53. if ($hs->executeInsert(3, array('k4''v4')) === false) {  
  54.     echo 'B'$hs->getError(), PHP_EOL;  
  55. }  
  56.    
  57. unset($hs);  
  58.    
  59. //DELETE  
  60. $hs = new HandlerSocket($host$port_wr);  
  61. if (!($hs->openIndex(4, $dbname$table''''))) {  
  62.     echo $hs->getError(), PHP_EOL;  
  63.     die();  
  64. }  
  65.    
  66. if ($hs->executeDelete(4, '='array('k2')) === false) {  
  67.     echo $hs->getError(), PHP_EOL;  
  68.     die();  
  69. }  
  70. ?>  

Tips:理论上HandlerSocket支持MyISAM,InnoDB等各种引擎,不过推荐使用InnoDB。
Tips:To avoid the insert error,Please remember set storage engine:InnoDB.
Tips:对HandlerSocket一个常见的误解是只能执行PRIMARY类型的KV查询,实际上只要支持索引,一般的简单查询它都能胜任,这里就不多说了,官方文档 里有介绍。

HandlerSocket的缺陷:
(1)
写操作并没有淘汰查询缓存——如果执行了写操作通过HandlerSocket,由于没有失效查询缓存, 那么你可能从MySQL读到旧的数据;
(2)不支持自动递增——插入时无法从自增列上自动获得增量值。
鉴于以上问题,扬长避短,使用其合并查询操作,发挥其NoSQL性能获取MySQL的InnoDB类型表数据,具体操作如下:

  1. <?php    
  2. // 通过handlersocket获取数据    
  3. $hs = new HandlerSocket(HS_HOST, HS_PORT);    
  4. if (!($hs->openIndex(1, 'dbname''table', HandlerSocket::PRIMARY, 'id,content,create_uid,create_user,created,state'))){    
  5.    echo $hs->getError(), PHP_EOL;    
  6.    die();    
  7. }    
  8. $dataList = array();    
  9. foreach ($ids as $id) {  
  10.    $dataList[] = array(1, "="array($id));    
  11. }    
  12. $data = $hs->executeMulti($dataList);    


写在最后的:
MySQL5.6提供原生的Memcached API,实际就是KV型NoSQL了,但HandlerSocket并不局限于KV形式,所以仍然有生存空间。

扩展阅读资料:
Install HandlerSocket into CentOS 6.2 MySQL
HandlerSocket的协议
HandlerSocket的原理
HandlerSocket系列(三):性能及其性能优化
MongoDB、HandlerSocket和MySQL性能测试及其结果分析
HandlerSocket的优势和缺陷阐述

相关文章

仅有 1 条评论
  1. 馒头饭madfan

    Mysql也能用上nosql啦,不错哦!

    馒头饭madfan

此处评论已关闭