-
2021-01-25 18:31:13
根据自己有限的java数据库编程的经验,对sql做预处理可以提高执行的效率,所以对mysql的c API做了些探索。从好坏两方面来总结mysql提供的这些接口:
好处是,他提供的是一种buffer的机制,我对它做了优化,就是为所有的参数buffer数据创建了一个大的静态的或者全局缓冲区,这样每次改变buffer内容的时候只需要对相应的字段赋值即可,不需要每次重新设置buffer指向的内存,因为这块内存是不变的,参数字段名字和这个内存地址做一个map,赋值的时候可以直接查找到参数的地址。如果有多个prepare statement,且是一个线程内,这块数据缓存可以共同利用,互不干扰的。如果用的hash_map,效率很高,每次参数赋值的消耗是很少的,几乎等同于变量赋值的。
需要注意的是sql字段类型,buffer_type枚举,c ++数据类型之间的映射关系,必须保证不会内存越界。buffer_length可以不指定的,那样就会直接使用length了,当然这是update/insert,如果是select还是要有的。读了mysql的driver的源码才确定了在bind param的时候是如何赋值内存的,如果不是blob 或者buffer长度。在调试时,如果发现写到数据库的和程序中的值不一致,这是可能的问题根源。
我不确定prepare statement在执行时究竟是走的拼sql的流程还是有一个专门的二进制协议来只传递参数值,源代码中这段没找到。我测出来的是执行prepare statement和直接执行sql语句的效率基本上是一致的,有很小很小的提高。
因此,我确定,使用prepare statement的最大节省的是拼sql语句的消耗,如果拼的话往往要使用字符串流的机制,效率比较低,这部分的时间消耗等于执行sql的消耗。理想的情况,不适用prepare statement,弄一个sql语句,拼的时候在参数的地方预留缓冲区,然后赋值的时候就直接把值放到缓冲里,然后把sql直接执行就好了,难题在于变长的char*和blob的缓冲区如何分配,分配的一个足够大的缓冲,如果实际值不够长的话就填充空格。这个还在瞎想阶段,实现起来很难。
使用了prepare statement也省去了escape特殊字符的麻烦,提高了安全性。
问题在于我对一个prepare statement的各个参数每次用到的可能不同,这次用这个参数,不用另一个参数,这样prepare statement就不同了,只好重新建一个。如果有十几个字段,每次不确定用哪几个字段,这在只修改需要修改的内容时是必须的,为了减少不必要的存档生成,当然这个情景很特殊。。。却是我所面对的。如果能在param的buffer增加一个字段来表示这个字段是否有效就好了,可惜没有。
prepare statement需要长期占用数据库连接,这个对于有限的连接数也是一个问题,又、需要有一个类似于连接池的prepare statement池机制啊。。。
这就是我的总结了,具体代码网上搜一大堆。读了源代码收获比较大,它能让我完全确认它是如何工作的。
Great open source!
阅读(5292) | 评论(0) | 转发(0) |
更多相关内容 -
PHP防止sql注入小技巧之sql预处理原理与实现方法分析
2021-01-20 01:26:37我们可以把sql预处理看作是想要运行的 SQL 的一种编译过的模板,它可以使用变量参数进行定制。 我们来看下它有什么好处: 预处理语句大大减少了分析时间,只做了一次查询(虽然语句多次执行)。 绑定参数减少了... -
20. go之sql预处理使用及SQL注入问题
2021-12-30 15:00:411. 使用场景:批量执行sql语句,可以提高查询速度 package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "time" ) var db *sql.DB func initMySQL()(err error){ dsn := "root:root...1. SQL预处理的使用
使用场景:批量执行sql语句,可以提高查询速度
为什么要预处理 ?
1)优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本
2)避免SQL注入问题
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "time" ) var db *sql.DB func initMySQL()(err error){ dsn := "root:root@tcp(127.0.0.1:13306)/go_test" //去初始化全局的db对象,而不是新声明一个变量 db,err = sql.Open("mysql",dsn) if err != nil { panic(err) } //做完错误检查后,确保db不为nil //尝试与数据库建立连接(校验dsn是否正确) err = db.Ping() if err != nil { fmt.Printf("connect to db failed,err: %v\n",err) return err } //根据实际业务设置数值 db.SetConnMaxLifetime(time.Second*10) // 设置连接可以重复使用的最长时间 db.SetMaxOpenConns(500) // 最大连接数 db.SetMaxIdleConns(200) // 最大空闲连接数 return } type user struct { id int age int name string } //预处理查询示例 func prepareQueryDemo() { // Prepare创建一个准备好的用于之后的查询和命令。返回值可以同时执行多个查询和命令 sqlStr := "SELECT id,name,age FROM user WHERE id>?" stmt, err := db.Prepare(sqlStr) if err != nil { fmt.Printf("prepare failed, err:%v\n",err) return } defer stmt.Close() rows, err := stmt.Query(0) if err != nil { fmt.Printf("query failed, err:%v\n",err) return } defer rows.Close() // 循环读取结果集中的数据 for rows.Next() { var u user //需要调用Scan去释放数据库链接 err := rows.Scan(&u.id,&u.name,&u.age) if err != nil { fmt.Printf("scan failed,err:%v\n",err) return } fmt.Printf("id:%d,name:%s,age:%d\n",u.id,u.name,u.age) } } func main() { if err := initMySQL();err != nil { fmt.Printf("connect to db failed,err:%v\n",err) } else { fmt.Println("db connect to db success") } // Close() 用来释放数据库连接的相关资源 defer db.Close() prepareQueryDemo() }
2.SQL注入问题
注意:我们任何时候都不应该自己拼接SQL语句
#符号为注释符,忽略后面的sql语句,#为mysql特有的注释符
应该用 --
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "time" ) var db *sql.DB func initMySQL()(err error){ dsn := "root:root@tcp(127.0.0.1:13306)/go_test" //去初始化全局的db对象,而不是新声明一个变量 db,err = sql.Open("mysql",dsn) if err != nil { panic(err) } //做完错误检查后,确保db不为nil //尝试与数据库建立连接(校验dsn是否正确) err = db.Ping() if err != nil { fmt.Printf("connect to db failed,err: %v\n",err) return err } //根据实际业务设置数值 db.SetConnMaxLifetime(time.Second*10) // 设置连接可以重复使用的最长时间 db.SetMaxOpenConns(500) // 最大连接数 db.SetMaxIdleConns(200) // 最大空闲连接数 return } type user struct { id int age int name string } // sql注入示例 func sqlInjectDemo(name string) { sqlStr := fmt.Sprintf("select id, name, age from user where name='%s'", name) fmt.Printf("SQL:%s\n", sqlStr) var u user err := db.QueryRow(sqlStr).Scan(&u.id, &u.name, &u.age) if err != nil { fmt.Printf("exec failed, err:%v\n", err) return } fmt.Printf("user:%#v\n", u) } func main() { if err := initMySQL();err != nil { fmt.Printf("connect to db failed,err:%v\n",err) } else { fmt.Println("db connect to db success") } // Close() 用来释放数据库连接的相关资源 defer db.Close() sqlInjectDemo("xxxx' or 1=2#") }
-
Java sql 预处理的封装
2021-02-12 20:43:28除了select其他的基本都和insert一致. 因为select得到的结果集需要在处理完结果集后关闭数据库的连接.所以用到监听回调的...DBUtils.select(sql, new DBUtils.Listener() {@Overridepublic void callback(ResultS...除了select其他的基本都和insert一致. 因为select得到的结果集需要在处理完结果集后关闭数据库的连接.所以用到监听回调的办法.
String sql = "select * from comment where fid=?";
DBUtils.select(sql, new DBUtils.Listener() {
@Override
public void callback(ResultSet resultSet) {
try {
while(resultSet.next()){
System.out.println(resultSet.getInt(1));
System.out.println(resultSet.getString(5));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
},1);
我试想的是我在实际场景中可能会用到如下两种插入的操作:
// 第一种
String sql = "insert into comment (fid,uid,cont) values (?,?,?)";
DBUtils.insert(sql,100,50,"才是真的好");
// 第二种
DBUtils.insert("insert into comment (fid,uid,cont) values (1,2,'广州好迪')");
/**
* 预处理参数设置
* @param statement
* @param params
* @return
* @throws SQLException
*/
private static PreparedStatement paramsSet(PreparedStatement statement, Object... params) throws SQLException {
int paramSize = params.length;
for(int i=1; i<=paramSize; i++){
Object param = params[i-1];
if(param instanceof java.lang.Integer){
statement.setInt(i,(Integer)param);
}else if(param instanceof java.lang.String){
statement.setString(i, (String) param);
}else if(param instanceof java.lang.Float){
statement.setFloat(i, (Float) param);
}else if(param instanceof java.lang.Long){
statement.setLong(i,(Long)param);
}else if(param instanceof java.lang.Double){
statement.setDouble(i,(Double)param);
}else if(param instanceof java.sql.Date){
statement.setDate(i, (Date)param);
}else{
statement.setObject(i,param);
}
}
return statement;
}
/**
* 插入操作
* @param sql
* @param params
* @return
*/
public static int insert(String sql, Object... params){
int paramSize = sql.length() - sql.replaceAll("\\?","").length();
int paramsNum = params.length;
if (paramSize != paramsNum) {
return -1;
}
if (!sql.toLowerCase().startsWith("insert")) {
return -1;
}
Connection conn = null;
PreparedStatement statement = null;
try {
conn = getConnection();
statement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
if(paramSize > 0){
statement = paramsSet(statement,params);
}
statement.executeUpdate();
ResultSet rs = statement.getGeneratedKeys();
if (rs.next()){
return rs.getInt(1);
}
}catch (SQLException e){
e.printStackTrace();
}finally {
closeConnection(statement, conn);
}
return 0;
}
public static int insert(String sql,ArrayList data){
return insert(sql, data.toArray());
}
测试了下,插入 OK. 棒棒的,没错又是好友@明明帮忙,参数Object...表示任意多个参数,这点非常不错.我们在 php 里面与之类似的就是func_get_args和func_num_args.
-
java使用JDBC动态创建数据表及SQL预处理的方法
2020-08-29 16:30:01主要介绍了java使用JDBC动态创建数据表及SQL预处理的方法,涉及JDBC操作数据库的连接、创建表、添加数据、查询等相关实现技巧,需要的朋友可以参考下 -
mysql的SQL预处理
2019-10-11 16:49:18我们一般处理SQL语句时,都是即时的,mysql客户端发送一条SQL语句到mysql服务器,mysql... 对于这一问题我们通过SQL预处理,可以很好地解决这一问题。SQL预处理步骤一般为prepare,execute,drop。 我们现在来...我们一般处理SQL语句时,都是即时的,mysql客户端发送一条SQL语句到mysql服务器,mysql服务器进行编译处理运行。但是有的时候我们会重复的执行一条SQL语句,这是使用即时处理,就会造成很大的资源浪费,而且耗费时间,特别是操作大量数据时。
对于这一问题我们通过SQL预处理,可以很好地解决这一问题。SQL预处理步骤一般为prepare,execute,drop。
我们现在来测试一下SQL预处理。
先连接数据库。
先预处理SQL。
在执行预处理SQL,注意占位符要按照顺序来。
我们可以查找到数据,然后在删除预处理SQL。
这就是SQL预处理的一般过程。
预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止 SQL 注入。
-
PrepareStatement(sql预处理类)
2021-02-28 09:17:58PrepareStatement(sql预处理类)import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import javax.swing.... -
go语言MySQL的SQL预处理,sql注入,go语言操作mysql数据库事务
2021-03-20 15:08:58先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。 然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。 MySQL服务端执行完整的SQL语句并将结果返回给客户端。 为什么要预处理? 优化... -
mysql中的sql预处理
2017-09-28 10:08:27所谓的预处理技术,最初也是由MySQL提出的一种减轻服务器压力的一种技术! 传统mysql处理流程 1, 在客户端准备sql语句 2, 发送sql语句到MySQL服务器 3, 在MySQL服务器执行该sql语句 4, 服务器... -
SQL语句预处理
2021-02-28 09:17:13执行预处理的SQL语句 a、描述:执行一条预处理语句 b、语法:bool PDOStatement::execute( ) c、返回:成功时返回 TRUE, 或者在失败时返回 FALSE。 ":value"占位符综合示例: //$dsn,数据源名称或叫做 DSN,包含... -
PHP SQL预处理
2019-10-04 22:30:17php预处理查询 $query='insert into p1(info) values(?)'; $query2='select info from p1 where id=?...// 创建预处理语句 $stmt=mysqli_stmt_init($link); if (mysqli_stmt_prepare($stmt,$query2... -
MySQL的SQL预处理(Prepared)
2021-01-26 01:28:59Prepared SQL Statement:SQL的执行、预编译处理语法、注意点一、SQL 语句的执行处理1、即时 SQL一条 SQL 在 DB 接收到最终执行完毕返回,大致的过程如下:1. 词法和语义解析;2. 优化 SQL 语句,制定执行计划;3. ... -
预处理SQL语句
2020-08-19 13:36:37所谓预处理指的就是将SQL语句中的关键字(如 SELECT…FROM…)与数据(如字段名,数据表名)分离,使得针对不同数据的相同SQL语句的执行开销更小,同时又可防止SQL注入的攻击 **传统方式的SQL语句,**在执行时每条SQL都... -
MySQL sp_executesql 预处理动态拼接的sql语句
2021-04-16 09:56:02MySQL预处理动态拼接的sql语句 背景:将sqlserver存储过程翻译到mysql中 需求:执行动态拼接的sql语句,并获取其语句的输出结果 问题: sqlserver中使用了如下代码执行动态拼接语句,并获取输出结果 EXEC sp_execute... -
SQL注入防御之三——SQL语句预处理(PHP)
2021-04-02 08:33:34许多成熟的数据库都支持预处理语句(Prepared Statements)的概念。它们是什么东西?你可以把它们想成是一种编译过的...概述预处理语句用于执行多个相同的 SQL 语句,并且执行效率更高。预处理语句的工作原理如下:预... -
SQL预处理语句的使用及防注入
2018-04-10 17:59:002019独角兽企业重金招聘Python工程师标准>>> 许多成熟的数据库都支持预处理语句(Prepared Statements)的...SQL预处理语句 转载于:https://my.oschina.net/u/616147/blog/1793442 -
使用Go语言ORM库worm的SQL预处理功能
2022-03-27 21:53:57worm支持SQL语句的预编译,使用SQL语句预编译可以提升数据库访问的效率。在worm中可以通过三种方式开启SQL语句预编译:全局开启、会话中开启、语句中开启。 -
MySQL快查-预处理SQL语句
2021-10-21 15:48:07MySQL快查 因为在日常工作学习中经常忘记mysql的...预处理SQL语句MySQL快查创建预处理语句执行预处理语句释放预处理语句 MySQL数据库中的prepare、execute、deallocate统称为预处理语句。 创建预处理语句 prepare stmt -
java sql 预处理问题, 如何往 id in (?) 传"1,2,3,4
2021-03-06 17:43:44想执行的语句是select id,uid,fnum,cont,icons,ts from feed where id in... } 从查询的结果来看,实际执行的 sql 是select id,uid,fnum,cont,icons,ts from feed where id in (1) 最后我的解决办法就是直接组装这条 SQL -
PHP防止sql注入小技巧之sql预处理
2018-08-29 09:58:01我们可以把sql预处理看作是想要运行的 SQL 的一种编译过的模板,它可以使用变量参数进行定制。 我们来看下它有什么好处: 预处理语句大大减少了分析时间,只做了一次查询(虽然语句多次执行)。 绑定参数... -
SQL预处理语句(Prepared Statements)
2012-05-20 11:03:07SQL预处理语句(Prepared Statements) 许多成熟的数据库都支持预处理语句(Prepared Statements)的概念。它们是什么东西?你可以把它们想成是一种编译过的要执行的SQL语句模板,可以使用不同的变量参数定制它。...