前言
大家好,今天我们来聊聊COM_QUERY
。
什么是COM_QUERY
从MySQL的官方网站上可以知道,COM_QUERY
用于向服务器发送立即执行的基于文本的查询,我们最常见的(insert、select、update、delete)等SQL都属于COM_QUERY
。
对于TiDB来说,兼容了MySQL的协议,所以也会对COM_QUERY
进行处理。
在TiDB中处理这部分的函数叫dispatch,这里匹配到信息如果是mysql.ComQuery
。就会调用handleQuery()进行处理。
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220212_dcd178b0-8bea-11ec-a990-38f9d3cd240d.png)
TiDB处理的协议可以对照parser/mysql/const.go
来知道具体是做什么的。
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220212_dd84c3f2-8bea-11ec-a990-38f9d3cd240d.png)
抓包
接下来我们用抓包工具,对ComQuery进行简单分析一下。
在TiDB-Server服务器上使用tcpdump抓取4000端口的数据,并输出到文件。
tcpdump -i eth0 port 4000 -s 65535 -w a.out
然后登陆TiDB执行一条查询语句。
mysql> select 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.08 sec)
执行完成后把a.out文件下载下来,使用Wireshark
软件打开。
这里建议Wireshark的配置把MySQL的端口改成4000。
Preferences - > Protocols -> MySQL
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220212_ddf954ec-8bea-11ec-a990-38f9d3cd240d.png)
改成4000它会自动把你的信息识别成MySQL。
然后注意观察,MySQL的包,其中有一条是Request Query的信息。
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220212_dea542c0-8bea-11ec-a990-38f9d3cd240d.png)
这里和MySQL协议一样,识别出数据包中的0x03,代表了COM_QUERY
。而后面73 65 6c 65 63 74 20 31
解析出来是SQL执行文本select 1
。
Prepared语句
Prepared
的语句有点特殊,它不属于ComQuery。
我用mysql客户端测试了一下,发现竟然还是ComQuery
。这到是让人挺绝的意外的。
mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> PREPARE mystmt FROM 'SELECT * FROM a1 where id=?';
Query OK, 0 rows affected (0.08 sec)
mysql> SET @id=3;
Query OK, 0 rows affected (0.08 sec)
mysql> EXECUTE mystmt USING @id;
+------+------+
| id | name |
+------+------+
| 3 | aaa |
+------+------+
1 row in set (0.08 sec)
通过tcpdump抓包如下:
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220212_df8c1e98-8bea-11ec-a990-38f9d3cd240d.png)
一个同事提醒了我,程序执行Prepared
和使用MySQL客户端不一样,于是我决定再用golang
测一下。
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
DB, _ := sql.Open("mysql", "root:@tcp(172.xx.xxx.xxx:4000)/test")
if err := DB.Ping(); err != nil {
fmt.Println("open database fail")
return
}
fmt.Println("connnect success")
stmt, _ := DB.Prepare("SELECT * FROM a1 where id=?")
query, _ := stmt.Query(3)
query.Scan()
}
运行上述代码,然后通过tcpdump抓包,可以看到结果和使用mysql客户端有明显的区别。
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220212_e0b46a28-8bea-11ec-a990-38f9d3cd240d.png)
程序上的执行属于Command Prepare Statement
。
后记
学习让人快乐,每天都折腾一点有意思的事。
Reference
1.https://github.com/pingcap/tidb/blob/aa7ad03bcd4ae85ef3a31cfccf2c908df9521d6f/parser/mysql/const.go
2.https://www.cnblogs.com/rickiyang/p/11074180.html