JDBC PreparedStatement的Performance问题,setString 和 varchar/nvarchar的关系

近期为了解决一些security的问题,把DAO层JDBC的Statement全部替换为PreparedStatement,然后遇到一个非常奇怪的问题是,一个简单的SQL, 比如

SELECT * FROM MDC_DATA_TYPE WHERE name = 'A' AND number = 1;

改成使用SELECT * FROM MDC_DATA_TYPE WHERE name = ? AND number = ?之后,执行时间几乎慢了100倍,这几乎与业界推荐使用PreparedStatement的事实背道而驰。于是花了很多时间来找原因。

后来找了曾在微软做SQL Server TCS的同事帮忙看,果然是高手,查看了执行计划 (Execution Plan),这里我留一些备忘:

select spid,status,hostname,program_name,text from sys.sysprocesses p
cross apply sys.dm_exec_sql_text(p.sql_handle) t
where spid>50 and program_name like 'JSQL%'
and program_name like 'JSQL%'
order by spid


select * from sys.dm_exec_requests r
cross apply sys.dm_exec_sql_text(r.sql_handle)
where session_id>50



dbcc freeproccache
dbcc freesystemcache('All')
dbcc dropcleanbuffers
dbcc freeproccache

kill 102

select * from sys.dm_exec_quests

 

// 下面这个是找到找到问题的SQL
select * from sys.dm_exec_cached_plans p
cross apply sys.dm_exec_sql_text(p.plan_handle)
cross apply sys.dm_exec_query_plan(p.plan_handle)

可以看到SQLServer缓存的sql:
(@P0 nvarchar(4000),@P1 int,@P2 nvarchar(4000))SELECT * FROM MDC_DATA_TYPE WHERE rmname = @P0 AND numPeriods = @P1 AND periodUnit = @P2 

根本原因:

http://www.newatlanta.com/c/support/servletexec/self_help/faq/detail?faqId=183

PreparedStatement.setString() performs more slowly than PreparedStatement.setBytes(). Why?

  This can occur when the SQL Server data type of your table's column is different than the type being used in your query.

For example, say you have a table in your SQL Server database with a column of type char or varchar, and the query in your Java code looks like this:
"SELECT * from mytable where mytext = ?"
If you use pstmt.setString(1, "99"); in your Java code, then JTurbo inserts the parameter and builds the query using 'N' like this:
... where mytext = N'99'
This forces SQL Server to do a conversion from nvarchar to the type of your table's column (char or varchar).
A conversion like this is expensive.
If you use pstmt.setBytes(1, "99".getBytes("8859_1")); in your Java code, then JTurbo inserts the parameter and builds the query like this:
... where mytext = 0x3939
which does not require a type conversion within SQL Server.
The only way that JTurbo's PreparedStatement could avoid prepending the 'N' would be if it were to first ask SQL Server whether or not the column type was nchar, nvarchar, or ntext. Asking this for every sql query it builds could be very expensive.

Our recommendation is that you change your database column type to be one of the 'n' types:
  • nchar
  • nvarchar
  • ntext

猜你喜欢

转载自dearls.iteye.com/blog/2146654