1、预编译
(1)什么是预编译语句?
先提交SQL语句到服务器,执行预编译,客户端执行SQL时,只需要上传输入参数即可,这样在多次执行同一个语句时,就能省去了多次编译的时间。
(2)为什么要预编译?
「当客户发送一条SQL语句给服务器后,服务器总是需要校验SQL语句的语法格式是否正确,然后把SQL语句编译成可执行的函数,最后才是执行SQL语句。其中校验语法,和编译所花的时间可能比执行SQL语句花的时间还要多。」
但是很多情况,我们的一条sql语句可能会反复执行,或者每次执行的时候只有个别的值不同 (比如query的where子句值不同,update的set子句值不同,insert的values值不同)。
如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。
stmt, err := db.Prepare("SELECT user_answer FROM t_user_problem WHERE openid = $1 and problem_id = $2")
start := time.Now()
for i := 0; i < 100000; i++ {
err = stmt.QueryRow(uid, pid).Scan(&answerStr)
//logs.Debug(answerStr)
}
logs.Debug("预编译:",time.Since(start))
start = time.Now()
for i := 0; i < 100000; i++ {
err = db.QueryRow("SELECT user_answer FROM t_user_problem WHERE openid = $2 and problem_id = $1", pid, uid).Scan(&answerStr)
}
logs.Debug("普通查询:",time.Since(start))
//结果
2019/10/06 16:36:27.457 [D] 预编译: 9.941362172s
2019/10/06 16:36:51.922 [D] 普通查询: 24.465195302s
(3)预编译的优势?
所谓预编译语句就是将这类语句中的值用占位符替代,可以视为将sql语句模板化或者说参数化;
这样类似的语句就省去了编译的时间;
预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;
此外预编译语句能防止sql注入,因为预编译语句将SQL语句和数据分开发送; SQL注入,就是用户的恶意输入,试图通过输入,让服务器拼接出恶意的SQL语句;
❝使用参数化sql,将变量使用占位符代替,也能防注入,并不一定要预编译。
❞
(4)什么时候该使用预编译?
如果不是批量的操作,是没必要使用预编译方法,否则即多了预编译创建和关闭的性能开销,又多写了两行代码,有点得不偿失。
如果是批量的操作,那么毋庸置疑,肯定是db.Prepare拿到Stmt,再由Stmt去执行sql,这样保证批量操作只进行一次预处理。
2、批处理和预编译
golang有预编译好像没有批处理;
java有预编译和批处理:
//采用Statement.addBatch(sql)方式实现批处理:
优点:可以向数据库发送多条不同的SQL语句。
缺点:SQL语句没有预编译。
当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。
//采用PreparedStatement.addBatch()实现批处理
优点:发送的是预编译后的SQL语句,执行效率高。
缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。
当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
本文使用 mdnice 排版