1.概述

prepared statement在MySQL4.1中引进并且增加了一些新的命令:

  • COM_STMT_PREPARE
  • COM_STMT_EXECUTE
  • COM_STMT_CLOSE
  • COM_STMT_RESET
  • COM_STMT_SEND_LONG_DATA

它还定义了一个更紧凑简洁的结果集格式代替ProtocolText::Resultset来返回结果集。

记住不是所有的语句都是可以预处理的:

1.1 预处理说明

sql预处理首先要求客户端提交需要执行的sql,这时提交的sql不传递真实的参数值,参数以问号的形式传递过去。

eg:

insert into user(id, name) values(?, ?);
select * from user where id = ?;

在mysql服务端完成预编译解析,这里的预编译解析包括解析参数的个数、类型等,然后响应给客户端。接下来客户端第二次交互只需传递参数过去就可以完成一个完整的sql的执行。相比传统的sql执行,预处理需要两次交互,才能完成一次sql执行。

预处理的优势:
(1)预处理sql能一定程度上防止sql注入

(2)sql预编译效率更高

(3)二进制包协议让sql预处理更加高效。

mysql预处理命令参数的封装以及结果集的返回,均采用二进制格式封装数据,体积更小,面向底层,能直接被mysql服务端利用。相比普通sql文本协议传输的数据,二进制协议传输数据更加高效。

2.二进制协议结果集

二进制协议结果集类似ProtocolText::Resultset.它仅包含二进制协议结果集行格式。

ProtocolBinary::Resultset:

Packet:

  • lenenc_int column_cout > 0
  • column_count * Protocol::ColumnDefinition
  • 没有或者很多ProtocolBinary::ResultsetRow
  • EOF_Packet

注意:如果CLIENT_DEPRECATE_EOF客户端性能标志被设置,发送OK_Packet,否则发送EOF_Packet。

例如:

3. 二进制协议结果集行

3.1 NULL-Bitmap

二进制协议结果集行由NULL位图组成,该位图包含与结果集+ 2中的列一样多的位以及二进制协议值格式中非NULL的列的值。

ProtocolBinary::ResultsetRow:

3.2 二进制结果集的行(COM_STMT_EXECUTE)

payload:

1      packet header[00]

string[$len]      NULL-bitmap,length: (列数 + 7 + 2 )/8

string[$len]      values

例子:

3.2.1 NULL-Bitmap

二进制协议将NULL值作为位发送到位图内而不是像ProtocolText :: ResultsetRow那样发送完整字节。 如果发送了许多NULL值,则它比旧方法更有效。

警告:

对于二进制协议结果集行,num-fields和field-pos需要添加2的偏移量。对于COM_STMT_EXECUTE,此偏移量为0。

NULL位图需要足够的空间来为发送的每个列存储可能的NULL位。 其空间计算如下:

NULL-bitmap-bytes = (num-fields + 7 + offset) / 8

导致:

4. 预处理命令说明

4.1 COM_STMT_PREPARE

COM_STMT_PREPARE命令用于客户端往服务端提交一个预处理的sql,如上面提到的:

 insert into user(id, name) values(?, ?);

4.2 COM_STMT_EXECUTE

COM_STMT_EXECUTE用于执行预处理sql,正如前面说到的,如果预处理sql需要传递参数,这个命令会发送预处理语句所需要的参数到服务端。如上面的例子,需要传递两个参数id和user的具体值到服务端。

4.3 COM_STMT_CLOSE

COM_STMT_CLOSE用于关闭服务端预处理sql,每一个预处理预处理的sql提交后都保存在mysql服务端的内存当中,每个预处理sql都有一个唯一的id标识,这个命令将发送需要关闭的sql的id,通知服务端可以将所有该预处理sql的资源释放掉(过多的预处理sql保留在服务端会占用较多的内存,因此有必要执行该命令清理无用的预处理sql)。

4.4 COM_STMT_RESET

COM_STMT_RESET命令用于重置COM_STMT_SEND_LONG_DATA命令发送的blob数据。

4.5 COM_STMT_SEND_LONG_DATA

COM_STMT_SEND_LONG_DATA用于往服务端发送字节流数据,通常来说只有在发送blob字段数据才用到该命令。可以多次调用该命令连续传同一个字段的字节的数据,这个命令必须在COM_STMT_EXECUTE命令发送之前执行。

5. 预处理协议结果包说明

mysql预处理结果集采用了二进制协议包进行封装,与普通的查询结果集格式不同。(普通的结果集包采用文本协议包进行封装)。

5.1 普通查询结果集协议包

普通sql查询(相比预处理sql查询)返回的结果集包用文本协议(官方称为Text Protocol)封装。文本协议的结果集包格式根据官网的一个图来说明:

一个结果集包主要包括以下部分(顺序传输):

  • one pakcet show field count(第一个packet用于表示返回结果集列数)
  • column defines packets(一个列就是一个packet, 格式参考Column Define Pakcet)
  • EOF Packet
  • row packets(一行数据就是一个packet, 格式参考ResultsetRow Packet)
  • EOF Packet

5.2 预处理结果集协议包

预处理结果集包的组成和普通协议包类似,区别只在于row packet(数据以二进制协议格式存放)。

  • one packet show field count(第一个packet用于表示返回结果集列数)
  • column define packets(一列就是一个packet,格式参考普通协议包的Column Define Packet)
  • EOF Packet
  • binary row packets(一行数据一个packet,格式参考Binary Row Pakcet)
  • EOF Packet

说明:

Binary Row Packet的第一个字节恒为0, 表示paket header, 接下来,由NULL-Bitmap标识那些值为NULL的列,NULL-Bitmap的长度计算方式为(column-count +7 + 2)/8,其中column-count表示列数,而非空的列值以二进制协议格式(协议格式参考Binary Protocol Value)顺序存储在NULL-Bitmap的后面。

提示:
返回相同结果行,预处理协议包所占字节比普通协议包小,在列数越多,列越长的情况下,相差的大小越明显。

5.3 mysql jdbc 预处理

java.sql.preparestatement可以执行预处理sql,mysql jdbc实现了该接口,并且将预处理分为客户端和服务端预处理

5.3.1 jdbc客户端预处理

mysql jdbc默认情况下采用的就是客户端预处理。客户端预处理的意思是,所有预处理参数都将被缓存在mysql jdbc层,而不是缓存在mysql server。在PrepareStatement执行的时候,在jdbc端完成sql语句的拼接(主要是使用缓存的参数对sql中问号?进行替换, 最终发送到mysql的就是完整的sql语句)。客户端预处理走得是普通的查询协议,而不是真正的mysql预处理协议。

6 Mycat预处理实现机制

Mycat也实现了mysql的预处理协议,可以接收预处理命令的处理。当使用预处理查询,也可以返回正确的二进制结果集包。Mycat预处理的实现是一种取巧的设计,查询走到后端mysql实际上不是发送了预处理命令,而是普通的COM_QUERY命令,后端mysql返回给Mycat的结果集包也是文本协议包,只是在Mycat将结果集包发送往客户端的中间过程,将普通的文本协议结果集包包装成为二进制协议结果集包,然后再返回给客户端。

Mycat预处理的处理流程:

(1)Mycat接收到客户端发送的COM_STMT_PREPARE命令后,解析协议包的内容得到预处理sql语句,eg:insert into user(id, name)value(?, ?),将这些预处理语句缓存在Mycat里面;

(2)当Mycat再次接收到客户端发送的COM_STMT_EXECUTE命令,就把相应的问号替换为实际传递过来的参数值,这时候已经得到了完整的sql语句。

(3)接下来,直接把这个语句丢给Mycat sql查询处理器去执行,中间会经过sql解析模块,路由解析模块以及最后的执行。

(4)最后,当收到后端mysql传递给Mycat的数据准备发往客户端的时候,做一个协议转换,将普通文本结构集协议包转换为二进制结果集协议包并发往客户端。

MySQL之Prepared Statements的更多相关文章

  1. 预编译语句(Prepared Statements)介绍,以MySQL为例

    背景 本文重点讲述MySQL中的预编译语句并从MySQL的Connector/J源码出发讲述其在Java语言中相关使用. 注意:文中的描述与结论基于MySQL 5.7.16以及Connect/J 5. ...

  2. What is the difference between parameterized queries and prepared statements?

    Both parameterized queries and prepared statements are exactly the same thing. Prepared statement se ...

  3. Raising Error Conditions with MySQL SIGNAL / RESIGNAL Statements

    http://www.mysqltutorial.org/mysql-signal-resignal/ Summary: in this tutorial, you will learn how to ...

  4. iOS开发之SQLite--C语言接口规范(三)——Binding Values To Prepared Statements

    在前面的博客中已经介绍了如何连接SQLite数据库,并且简单的查询和遍历结果集.在前面用到了sqlite3_stmt *stmt,也就是预编译后的SQL语句.在本篇博客中会了解一下sqlite3_st ...

  5. Prepared statements(mysqli & pdo)

    参考: http://php.net/manual/en/mysqli.quickstart.prepared-statements.php http://www.ultramegatech.com/ ...

  6. PHP MySQLi Prepared Statements Tutorial to Prevent SQL Injection

    https://websitebeaver.com/prepared-statements-in-php-mysqli-to-prevent-sql-injection#introduction On ...

  7. MySQL与Oracle 差异比较之七其它

    其它 编号 类别 ORACLE MYSQL 注释 1 内连接的更改 1.select a.*, b.*, c.*, d.*  from a, b, c, d where a.id = b.id   a ...

  8. MYSQL COST optimizer

    http://blog.chinaunix.net/uid-26896862-id-3326400.html https://www.slideshare.net/olavsa/mysql-optim ...

  9. mysql与oracle的语法对比

    数据类型 编号 ORACLE MYSQL 注释 1 NUMBER int / DECIMAL DECIMAL就是NUMBER(10,2)这样的结构INT就是是NUMBER(10),表示整型:MYSQL ...

随机推荐

  1. Ubuntu下root账户无法使用xshell远程连接解决方法

    以前使用xshell连接服务器跑程序都很顺利,但这次整了一个root账户,发现居然连不上.百度一下,发现ubuntu系统本身是拒绝root账户远程连接的. 解决办法:参考点击传送门

  2. 初进python世界之数据类型

    文章来源: https://www.cnblogs.com/seagullunix/articles/7297946.html 基本运算符 常用数据类型: 字符串(Str) 数字(Digit) 列表( ...

  3. piecewise_construct存在的意义

    C++11中大部分的容器对于添加元素除了传统的 insert 或者 pusb_back/push_front 之外都提供一个新的函数叫做 emplace. 比如如果你想要向 std::vector 的 ...

  4. opengl读取灰度图生成三维地形并添加光照

    转自:https://www.cnblogs.com/gucheng/p/10152889.html 准备第三方库 glew.freeglut.glm.opencv 准备一张灰度图 最终效果 代码如下 ...

  5. vs2010+ARX2012向导添加mfc支持类出现Error in default.htm PopulateDialogIDs():

    初步判断为ARX2012默认的编译器平台集是v90,如果你只安装了vs2010,没有安装vs2008sp1或者vs2008sp1的编译器,以及对应的Windows MFC SDK,就可能会出现这样的问 ...

  6. 十、Spring的@Profile注解

    首先我们来看看spring官方文档对这个注解的解释: The @Profile annotation allows you to indicate that a component is eligib ...

  7. LeetCode 227. 基本计算器 II(Basic Calculator II)

    227. 基本计算器 II 227. Basic Calculator II 题目描述 实现一个基本的计算器来计算一个简单的字符串表达式的值. 字符串表达式仅包含非负整数,+,-,*,/ 四种运算符和 ...

  8. Windows中使用 Python 调用 Matlab 程序

    https://ww2.mathworks.cn/help/matlab/matlab_external/system-and-configuration-requirements.html http ...

  9. 01_Android入门

    Android系统文件目录结构 / 代表系统的根目录 /data/app/ 存放着第三方的apk文件 /system/app/ 其中是系统中的应用安装文件 /data/data/packagename ...

  10. 13 JSP、MVC开发模式、EL表达式和JSPL标签+软件设计架构---学习笔记

    1.JSP (1)JSP概念:Java Server Pages 即java服务器端页面可以理解为:一个特殊的页面,其中既可以指定定义html标签,又可以定义java代码用于简化书写!!! (2)原理 ...