go 关于mysql

检验连接正确性,需要使用sql.Ping()

来自阿里云

执行sql.Open()并未实际建立起到数据库的连接,也不会验证驱动参数。第一个实际的连接会惰性求值,延迟到第一次需要时建立。用户应该通过db.Ping()来检查数据库是否实际可用。

被坑过一次的坑……

用Query的话记得读取数据

同样的语句使用Exec和Query执行有巨大的差别。如上文所述,Query会返回结果集Rows,而存在未读取数据的Rows其实会占用底层连接直到rows.Close()为止。因此,使用Query但不读取返回结果,会导致底层连接永远无法释放。database/sql期望用户能够用完就把连接还回来,所以这样的用法很快就会导致资源耗尽(连接过多)。所以,应该用Exec的语句绝不可用Query来执行。

需要注意的地方

sql中,不需要每次调用Open,可以只有一个全局

来自知乎 - 作者:汪亚军

sql.Open 不会创建连接 ,只会创建一个DB实例,同时会创建一个go程来管理该DB实例的一个连接池(是长连接,但不是在Open的时候创建)。 在调用Begin()取一个连接,回滚或者提交得时候归还。如果你直接使用时Exec()执行,则每次会从连接池里面取出一个连接,到Exec执行完毕的时候归还。可以通过参数来调节连接池的大小。
golang sql包已经做了并发处理,自己通过channel的方式维护了一个连接池,所以没有必要在go程中加锁共享。
关于每次sql.Open,我觉得应该是一个不恰当的做法,这会导致连接得不到重用,并产生过多的短时间关闭的连接,给数据库造成没必要的压力。
来自阿里云
sql.DB对象是为了长连接而设计的,不要频繁Open()和Close()数据库。而应该为每个待访问的数据库创建一个sql.DB实例,并在用完前一直保留它。需要时可将其作为参数传递,或注册为全局对象。
这一抽象让用户不必考虑如何管理并发访问底层数据库的问题。当一个连接在执行任务时会被标记为正在使用。用完之后会放回连接池中。不过用户如果用完连接后忘记释放,就会产生大量的连接,极可能导致资源耗尽(建立太多连接,打开太多文件,缺少可用网络端口)。

找到的许多资料都这么说,那么大概可以放心用了?

重复调用Close()不会出现错误

来自阿里云

rows.Close方法是幂等的,重复调用不会产生副作用,因此建议使用 defer rows.Close()来关闭结果集。

安心用函数系列+1

事务中第二次查询之前先将第一次查询结果读取完毕

因为事务保证在它上面执行的查询都由同一个连接来执行,因此事务中的语句必需按顺序一条一条执行。对于返回结果集的查询,结果集必须Close()之后才能进行下一次查询。用户如果尝试在前一条语句的结果还没读完前就执行新的查询,连接就会失去同步。这意味着事务中返回结果集的语句都会占用一次单独的网络往返。

Scan实参的方法

不定参数的时候可以使用rows.Columns()来获取列名列表,不定类型的时候,可以使用sql.RawBytes()来获取类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cols, err := rows.Columns()
if err != nil {
// handle this....
}

// 目标列是一个动态生成的数组
dest := []interface{}{
new(string),
new(uint32),
new(sql.RawBytes),
}

// 将数组作为可变参数传入Scan中。
err = rows.Scan(dest...)
// ...

关于prepare的详情

(还没有看的资料)[http://go-database-sql.org/prepared.html]
疑问在于,prepare什么时候用?全局注册后重复使用还是用的时候注册,用完销毁的使用?

提高SQL执行效率的方法

来自StackOverflow

It’s an old question but still - better late than never; you’re in for a treat:
put all your data into a bytes.Buffer as tab-separated, newline terminated and unquoted lines (if the text causes problems, it has to be escaped first). NULL has to be encoded as \N.
Use http://godoc.org/github.com/go-sql-driver/mysql#RegisterReaderHandler and register a function returning that buffer under “instream”. Next, call LOAD DATA LOCAL INFILE “Reader::instream” INTO TABLE … - that’s a very fast way to pump data into MySQL (I measured about 19 MB/sec with Go from a file piped from stdin compared to 18 MB/sec for the MySQL command line client when uploading data from stdin).
As far as I know, that very driver is the only way to LOAD DATA LOCAL INFILE without the need of a file.

避免Null值带来的麻烦,可以使用sql.NullString,sql.NullInt64等含null的类型

来自github mysql官方插件说明

Ignoring NULL values
Note: This might cause problems in Go 1.0 since the conversion from integer types to []byte is missing. The Issue is fixed in Go 1.1+
Maybe you already encountered this error: sql: Scan error on column index 1: unsupported driver -> Scan pair: -> string
Normally you would use sql.NullString in such a case. But sometimes you don’t care if the value is NULL, you just want to treat it as an empty string.
You can do this with a small workaround, which takes advantage of the fact, that a nil-[]byte gets converted to an empty string. Instead of using
string as a rows.Scan(…) destination, you simple use []byte (or sql.RawBytes), which can take the nil value:

文章目录
  1. 1. 检验连接正确性,需要使用sql.Ping()
  2. 2. 用Query的话记得读取数据
  3. 3. sql中,不需要每次调用Open,可以只有一个全局
  4. 4. 重复调用Close()不会出现错误
  5. 5. 事务中第二次查询之前先将第一次查询结果读取完毕
  6. 6. Scan实参的方法
  7. 7. 关于prepare的详情
  8. 8. 提高SQL执行效率的方法
  9. 9. 避免Null值带来的麻烦,可以使用sql.NullString,sql.NullInt64等含null的类型
|