在一些使用达梦数据库的实际项目场景中,有时候会出现数据库内存池中SQL CACHE MANAGERMENT项使用率较高的情况,我们知道该sql缓冲区主要是用来存储sql,执行计划的。当该项内存池使用比较大时意味着数据库服务端的确接收处理了大量不同种类样式的sql,而应用发出的sql往往存在很多“结构相同,参数值不同”的情况,此时如果将这些相同结构的sql解析一次生成执行计划,在不同参数值传进来是多次重复执行可以节省系统开销,提升sql执行效率。下面我们来讨论一下在进行sql常量参数化过程可能遇到的一些问题:
create table test(id int,name varchar(20));
insert into test values(1,'abc');
insert into test values(2,'def');
commit;
select cache_item,sqlstr from v$cachepln where sqlstr like '%test%';
复制
查看计划池或dmsql中sql是服务端执行的是原封不动的常量sql格式。
SQL> insert into test values(?,?);
请输入参数1的值:3
请输入参数2的值:aaa
影响行数 1
已用时间: 9.595(毫秒). 执行号:12100.
SQL> insert into test values(?,?);
请输入参数1的值:4
请输入参数2的值:bbb
影响行数 1
已用时间: 0.490(毫秒). 执行号:12101.
SQL> commit;
复制
再次查看发现服务端接受的参数化的sql。
但是有时候应用代码短时间无法改造成参数化的sql格式,怎么办呢?达梦提供了
打开系统级sql常量参数化的方法,设置参数
CLT_CONST_TO_PARAM = 1
insert into test values(5,'ccc');
insert into test values(6,'ddd');
commit;
复制
此时再次使用常量sql进行测试发现自动进行了参数化转换。
由于修改参数重启了数据库服务,所以计划缓存都清理了,sql依然进行了常量参数化。
使用jdbc测试程序测试:
可以看到并没有将常量参数化
public void insert(String vId,String vName) throws SQLException {
String vSql = "insert into test values("+ vId + ",'"+ vName + "')";
PreparedStatement pstmt = conn.prepareStatement(vSql);
pstmt.executeUpdate();
pstmt.close();
System.out.println("insert complete!");
}
复制
这里为什么开启了常量参数化后,常量并没有被参数化呢?**其实这里与jdbc中使用的创建句柄类型有关,我们知道prepareStatement表示sql语句已经编译好了,对于需要多次重复执行的 SQL 语句经常才会去使用 PreparedStatement 对象,也就是说数据库“认为要执行的sql已经是参数化之后的sql”,所以并不会再去进行参数化了。
将代码中的语句成createStatement对象
public void insert2(String vId,String vName) throws SQLException {
String vSql = "insert into test values("+ vId + ",'"+ vName + "')";
Statement stmt = conn.createStatement();
stmt.executeUpdate(vSql);
stmt.close();
System.out.println("insert complete!");
}
复制
可以看到使用createStatement对象时系统还是自动进行了常量参数化转换。
那如果应用的代码就是全部使用的prepareStatement,短时间也没法改动怎么办呢?Jdbc中提供了以下参数
我们将改参数加到连接串中jdbc:dm://127.0.0.1?prepareOptimize=true再次测试prepareStatement的常量参数化情况:
此时可以看到开启prepareOptimize=true后即使应用是preparestament也会进行参数化转换。
在数据库应用开发过程中,是否对sql进行常量参数化设置会影响执行效率和执行计划重用。达梦数据库中提供了以上几种参数化的方法,在实际应用过程中,代码开发最好还是搞清楚不同方法的使用场景,不要混为一旦,虽然常量和参数化都可以使用prepareStatement,但是会带来一些隐藏问题并在排查的过程中造成一定的困扰。
文章
阅读量
获赞