sql 预编译语句

本文深入探讨MySQL中的预编译语句,包括其优势、工作原理、MySQL的预编译功能以及在Java中的应用。通过分析源码,展示了服务端与客户端预编译的过程,以及缓存对性能的影响。性能测试结果显示,服务端预编译+缓存方案在性能上优于其他配置。
摘要由CSDN通过智能技术生成

sql 预编译语句

1. 背景

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

2. 预编译语句是什么

通常我们的一条sql在db接收到最终执行完毕返回可以分为下面三个过程:

  1. 词法和语义解析
  2. 优化sql语句,制定执行计划
  3. 执行并返回结果

我们把这种普通语句称作Immediate Statements。

但是很多情况,我们的一条sql语句可能会反复执行,或者每次执行的时候只有个别的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。
如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。

所谓预编译语句就是将这类语句中的值用占位符替代,可以视为将sql语句模板化或者说参数化,一般称这类语句叫Prepared Statements或者

Parameterized Statements

预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止sql注入
当然就优化来说,很多时候最优的执行计划不是光靠知道sql语句的模板就能决定了,往往就是需要通过具体值来预估出成本代价。

3. MySQL的预编译功能

注意MySQL的老版本(4.1之前)是不支持服务端预编译的,但基于目前业界生产环境普遍情况,基本可以认为MySQL支持服务端预编译。

下面我们来看一下MySQL中预编译语句的使用。
首先我们有一张测试表t,结构如下所示:

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `a` int(11) DEFAULT NULL,
  `b` varchar(20) DEFAULT NULL,
  UNIQUE KEY `ab` (`a`,`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
3.1 编译

我们接下来通过 PREPARE stmt_name FROM preparable_stm的语法来预编译一条sql语句

mysql> prepare ins from 'insert into t select ?,?';
Query OK, 0 rows affected (0.00 sec)
Statement prepared
3.2 执行

我们通过EXECUTE stmt_name [USING @var_name [, @var_name] …]的语法来执行预编译语句

mysql> set @a=999,@b='hello';
Query OK, 0 rows affected (0.00 sec)

mysql> execute ins using @a,@b;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from t;
+------+-------+
| a    | b     |
+------+-------+
|  999 | hello |
+------+-------+
1 row in set (0.00 sec)

可以看到,数据已经被成功插入表中。

MySQL中的预编译语句作用域是session级,但我们可以通过max_prepared_stmt_count变量来控制全局最大的存储的预编译语句。

mysql> set @@global.max_prepared_stmt_count=1;
Query OK, 0 rows affected (0.00 sec)

mysql> prepare sel from 'select * from t';
ERROR 1461 (42000): Can't create more than max_prepared_stmt_count statements (current value: 1)

当预编译条数已经达到阈值时可以看到MySQL会报如上所示的错误。

3.3 释放

如果我们想要释放一条预编译语句,则可以使用{DEALLOCATE | DROP} PREPARE stmt_name的语法进行操作:

mysql> deallocate prepare ins;
Query OK, 0 rows affected (0.00 sec)

4. 通过MySQL驱动进行预编译

以上介绍了直接在MySQL上通过sql命令进行预编译/缓存sql语句。接下来我们以MySQL Java驱动Connector/J(版本5.1.42)为例来介绍通过MySQL驱动进行预编译。

4.1 客户端预编译

首先,简要提一下JDBC中java.sql.PreparedStatement是java.sql.Statement的子接口,它主要提供了无参数执行方法如executeQuery和executeUpdate等,以及大量形如set{Type}(int, {Type})形式的方法用于设置参数。
在这里插入图片描述
在Connector/J中,java.sql.connection的底层实现类为com.mysql.jdbc.JDBC4Connection,它的类层次结构如下图所示:
在这里插入图片描述
下面是我编写如下测试类,程序中做的事情很简单,就是往test.t表中插入一条记录。
test.t表的结构在上述服务端预编译语句中已经有展示,此处不再赘述。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

/**
 * Test for PreparedStatement.
 *
 * @author Robin Wang
 */
public class PreparedStatementTest {
   
    public static void main(String[] args) throws Throwable {
   
        Class.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haikuotiankongdong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值