MySQL“自古以来”都有一个神秘的HANDLER命令,而此命令非SQL标准语法,可以降低优化器对于SQL语句的解析与优化开销,从而提升查询性能。看到这里,可能有小伙伴不淡定了,这么好的东西为啥没广泛使用呢?这不是与几年前很夯的handlersocket插件类似吗?

那么,我们先来看看Handler语法说明:

HANDLER tbl_name OPEN [ [AS] alias]
HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,…) [ WHERE where_condition ] [LIMIT … ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST } [ WHERE where_condition ] [LIMIT … ]
HANDLER tbl_name READ { FIRST | NEXT } [ WHERE where_condition ] [LIMIT … ]      //主健,无索引指定
HANDLER tbl_name CLOSE

首先从语法上看,HANDLER可以通过指定的索引去访问数据。但此语法并不支持DML操作。此外,由于减少了SQL解析,Handler命令的性能真的非常不错,根据Inside君的简单主键测试,Handler命令比SQL要快40%~45%。测试脚本如下:

SET @id=FLOOR(RAND()*1000000);
HANDLER sbtest.sbtest1 OPEN AS c;
HANDLER C READ `PRIMARY` = (@id);
HANDLER C CLOSE;

mysql> select * from test.test ;
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2016-07-18 23:44:05 |
| 2 | abc | 2016-07-18 23:44:05 |
| 3 | abd | 2016-07-18 23:44:05 |
| 4 | acd | 2016-07-18 23:44:05 |
| 5 | def | 2016-07-18 23:44:05 |
| 6 | pqr | 2016-07-18 23:44:05 |
| 7 | stu | 2016-07-18 23:44:05 |
| 8 | vwx | 2016-07-18 23:44:05 |
| 9 | yza | 2016-07-18 23:44:05 |
| 10 | def | 2016-07-18 23:44:05 |
+----+------+---------------------+
10 rows in set (0.01 sec)
CREATE TABLE `test` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`data` varchar(32) DEFAULT NULL,
`ts` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `data` (`data`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=gbk
mysql> handler test.test open as c;    //打开
Query OK, 0 rows affected (0.01 sec) mysql> handler c read `PRIMARY`=(5); //查询主健
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 5 | def | 2016-07-18 23:44:05 |
+----+------+---------------------+
1 row in set (0.00 sec) mysql> handler c close; //关闭
Query OK, 0 rows affected (0.00 sec)
mysql> handler test.test open;       //open
Query OK, 0 rows affected (0.00 sec) mysql> handler test read data first; //data索引,第一个记录
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2016-07-18 23:44:05 |
+----+------+---------------------+
1 row in set (0.00 sec) mysql> handler test read data next; //下一个记录
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 2 | abc | 2016-07-18 23:44:05 |
+----+------+---------------------+
1 row in set (0.00 sec) mysql> handler test read data prev; //前一个记录
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2016-07-18 23:44:05 |
+----+------+---------------------+
1 row in set (0.01 sec) mysql> handler test read data last; //最后一条记录
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 9 | yza | 2016-07-18 23:44:05 |
+----+------+---------------------+
1 row in set (0.00 sec)
mysql> handler test read `data`=("yza");
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 9 | yza | 2016-07-18 23:44:05 |
+----+------+---------------------+
1 row in set (0.01 sec)
mysql> handler test read data=("abc") limit 5;
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2016-07-18 23:44:05 |
| 2 | abc | 2016-07-18 23:44:05 |
+----+------+---------------------+
2 rows in set (0.00 sec)
mysql> handler test read first  limit 2;   //头两个记录  ID索引
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2016-07-18 23:44:05 |
| 2 | abc | 2016-07-18 23:44:05 |
+----+------+---------------------+
2 rows in set (0.00 sec) mysql> handler test read first limit 3;
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2016-07-18 23:44:05 |
| 2 | abc | 2016-07-18 23:44:05 |
| 3 | abd | 2016-07-18 23:44:05 |
+----+------+---------------------+
3 rows in set (0.15 sec) mysql> handler test read first ;
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2016-07-18 23:44:05 |
+----+------+---------------------+
1 row in set (0.00 sec)
mysql> handler test read data last limit 5;   //data索引
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 9 | yza | 2016-07-18 23:44:05 |
| 8 | vwx | 2016-07-18 23:44:05 |
| 7 | stu | 2016-07-18 23:44:05 |
| 6 | pqr | 2016-07-18 23:44:05 |
| 10 | def | 2016-07-18 23:44:05 |
+----+------+---------------------+
5 rows in set (0.00 sec)

Handler_read_first :

mysql> flush status;
Query OK, 0 rows affected (0.00 sec) mysql> show session status like "%handler%";
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Handler_commit | 0 |
| Handler_delete | 0 |
| Handler_discover | 0 |
| Handler_external_lock | 0 |
| Handler_mrr_init | 0 |
| Handler_prepare | 0 |
| Handler_read_first | 0 |
| Handler_read_key | 0 |
| Handler_read_last | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
| Handler_rollback | 0 |
| Handler_savepoint | 0 |
| Handler_savepoint_rollback | 0 |
| Handler_update | 0 |
| Handler_write | 0 |
+----------------------------+-------+
18 rows in set (0.03 sec) mysql> handler test read data first;
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2016-07-18 23:44:05 |
+----+------+---------------------+
1 row in set (0.00 sec) mysql> show session status like "%handler%";
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Handler_commit | 1 |
| Handler_delete | 0 |
| Handler_discover | 0 |
| Handler_external_lock | 2 |
| Handler_mrr_init | 0 |
| Handler_prepare | 0 |
| Handler_read_first | 1 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
| Handler_rollback | 0 |
| Handler_savepoint | 0 |
| Handler_savepoint_rollback | 0 |
| Handler_update | 0 |
| Handler_write | 0 |
+----------------------------+-------+
18 rows in set (0.02 sec) mysql> handler test read data first;
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2016-07-18 23:44:05 |
+----+------+---------------------+
1 row in set (0.07 sec) mysql> show session status like "%handler%";
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Handler_commit | 2 |
| Handler_delete | 0 |
| Handler_discover | 0 |
| Handler_external_lock | 4 |
| Handler_mrr_init | 0 |
| Handler_prepare | 0 |
| Handler_read_first | 2 |
| Handler_read_key | 2 |
| Handler_read_last | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
| Handler_rollback | 0 |
| Handler_savepoint | 0 |
| Handler_savepoint_rollback | 0 |
| Handler_update | 0 |
| Handler_write | 0 |
+----------------------------+-------+
18 rows in set (0.03 sec)

在Inside君的24C的测试服务器上,64线程主键查询跑到了近37W QPS,还是非常令人印象深刻的。对比SQL的SELECT查询,整体测试结果如下图所示:

命令HANDLER的主要实现在源码sql_handler.h、sql_handler.cc,设个断点就能观察到具体的流程。MySQL上层及InnoDB存储引擎层主要实现函数入口为:

  • Sql_cmd_handler_open::execute
  • Sql_cmd_handler_read::execute
  • Sql_cmd_handler_close::execute
  • ha_innobase::init_table_handle_for_HANDLER
  • ha_partition::init_table_handle_for_HANDLER()(7版本支持HANDLER操作分区表)

既然性能不错,为什么在生产环境中并不见到命令HANDLER的使用呢?主要是因为HANDLER命令存在以下几个主要问题:

  1. 非一致性读取???
  2. 返回聚集索引中的所有列(即使是二级索引访问),而不能返回某个具体列
  3. 二级索引不使用LIMIT关键字,只能返回1行记录

知道命令HANDLER的同学,可能会认为HANDLER读取存在脏读问题。因为MySQL官方文档对于HANDLER读取的说明就是这么说的:

The handler interface does not have to provide a consistent look of the data (for example, dirty reads are permitted), so the storage engine can use optimizations that SELECT does not normally permit.

然而需要特别注意的是,MySQL文档中准确的说法是可以允许提供不一致的读取。但InnoDB存储引擎的HANDLER实现是支持一致性读取的,Inside君亲测的确不存在脏读问题。当然,源码说明一切,可以发现在函数init_table_handle_for_HANDLER会对READVIEW进行分配,而注释也说明了这点:

/* We let HANDLER always to do the reads as consistent reads, even
if the trx isolation level would have been specified as SERIALIZABLE */
m_prebuilt->select_lock_type = LOCK_NONE;
m_prebuilt->stored_select_lock_type = LOCK_NONE;

貌似用HANDLER命令来做主键的查询是不错的,减少了SQL解析器的开销,性能提升杠杠的。但为此,应用要付出巨大的改动,而SQL最大的优势就在于标准化。相信这也是目前NoSQL数据库遇到的最大的一个问题。比如MongoDB,Inside君每次写查询时都要打开官方的命令对照表……

HANDLER命令与实现的更多相关文章

  1. MySQL中的联合索引学习教程

    MySQL中的联合索引学习教程 这篇文章主要介绍了MySQL中的联合索引学习教程,其中谈到了联合索引对排序的优化等知识点,需要的朋友可以参考下   联合索引又叫复合索引.对于复合索引:Mysql从左到 ...

  2. Exp3

    利用不同免杀方式生成文件 1.msfvenom 使用msfvenom命令查看功能介绍 其中有: -p 选择一个载荷(或者叫模块) -l 载荷列表 -f 生成的文件格式 -e 编码方式 -l 编码次数 ...

  3. Metasploit 读书笔记-持久控制

    Meterpreter的persisitence脚本允许主任meterpreter代理,以确保系统重启之后Meterpreter还能运行。如果是反弹连接方式,可以设置连接攻击机的时间间隔。如果是绑定方 ...

  4. Linux/UNIX之进程环境

    进程环境 进程终止 有8种方式使进程终止,当中5中为正常终止,它们是 1)      从main返回 2)      调用exit 3)      调用_exit或_Exit 4)      最后一个 ...

  5. 浅谈命令查询职责分离(CQRS)模式

    在常用的三层架构中,通常都是通过数据访问层来修改或者查询数据,一般修改和查询使用的是相同的实体.在一些业务逻辑简单的系统中可能没有什么问题,但是随着系统逻辑变得复杂,用户增多,这种设计就会出现一些性能 ...

  6. Event Handler

    在Event Handler中,有一种特殊的Event Handler,称之为Synchronizer或者Denormalizer,其作用就是为了同步“Query Database”.Query Da ...

  7. R自动数据收集第二章HTML笔记1(主要关于handler处理器函数和帮助文档所有示例)

    本文知识点:     1潜在畸形页面使用htmlTreeParse函数 2startElement的用法 3闭包 4handler函数的命令和函数体主要写法 5节点的丢弃,取出,取出标签名称.属性.属 ...

  8. WindowManager massge和handler

    在一个可移动浮动按钮的demo源码学习中,有一些WindowManager的使用,在此做下总结. 1.翻译过来就是窗口管理,是和应用框架层的窗口管理器交互的接口,通过 mWindowManager = ...

  9. Mysql命令show global status求根溯源

    近来,发现好多公司对mysql的性能监控是通过show global status实现的,因此对于这个命令想要探究一番,看他是否是实时更新的. 在此之前,我们必须搞明白mysql对于这个命令的执行过程 ...

随机推荐

  1. WinForms 使用Graphics绘制字体阴影

    C#以两种方法实现文字阴影效果,同时还实现了简单的动画效果: 一种是对文本使用去锯齿的边缘处理,在两个不同的位置绘制文本,形成阴影: 另一个是以阴影为线条,构造影线画刷,先画背景再画前景,使用grap ...

  2. TcxDBVerticalGrid优秀的编辑控件

  3. Codeforces 710 E. Generate a String (dp)

    题目链接:http://codeforces.com/problemset/problem/710/E 加或者减一个字符代价为x,字符数量翻倍代价为y,初始空字符,问你到n个字符的最小代价是多少. d ...

  4. Codeforces 597C. Subsequences (树状数组+dp)

    题目链接:http://codeforces.com/contest/597/problem/C 给你n和数(1~n各不同),问你长为k+1的上升自序列有多少. dp[i][j] 表示末尾数字为i 长 ...

  5. HDU2955Robberies(DP)

    http://acm.hdu.edu.cn/showproblem.php?pid=2955 题目是说一小偷偷东西,第i个物品的价值是M[i],被抓的概率是p[i],现在要使得在被抓的概率在P以下时的 ...

  6. CodeForces 709A Juicer (水题, 模拟)

    题意:给定 n 个桔子的大小,一个杯子的容积,一个最大限度,挨着挤桔子汁,如果大小大于限度,扔掉,如果不杯子满了倒掉,问你要倒掉多少杯. 析:直接按要求模拟就好,满了就清空杯子. 代码如下: #pra ...

  7. mysql中key 、primary key 、unique key 与index区别

    一.key与primary key区别 CREATE TABLE wh_logrecord ( logrecord_id ) NOT NULL auto_increment, ) default NU ...

  8. vs2013修改默认的开发环境

    可能会有朋友和我一样,当安装完VS完成之后,没有选择默认的开发模板,在后面添加新项目时,总是不能选择默认的开发语言,下面给出个简单步骤,记录一下以备用. 看图吧 1.工具>导入和导出设置 2.选 ...

  9. AForge学习笔记(列表)

    AForge学习笔记(11):AForge.Imaging.Textures Clouds texture:具有云彩的纹理效果,示例如下:             CloudsTexture text ...

  10. c#实现word,winWordControl 文档不允许复制、粘贴、隐藏工具栏、快捷保存

    1.隐藏工具栏 //隐藏工具栏 ; i <= winWordControl1.document.CommandBars.Count; i++) { winWordControl1.documen ...