MySQL
相比起简单的锁表,事务提供了更好的并发性能,但同时也带来更大的复杂性,如隔离级别,mvcc,死锁等。网上关于事务隔离级别的介绍遍地都是,就不再赘述了。
MySQL提供了3个等级的隔离级别配置,下面分别列出配置方法:
全局
直接改配置文件
1
|
set global transaction isolation level repeatable read;
|
当前session
1
2
|
set tx_isolation = 'repeatable-read';
set session transaction isolation level repeatable read;
|
下一个事务
1
|
set transaction isolation level repeatable read;
|
Go
但是在Go的MySQL驱动是自带连接池的,这使得这3个等级都无法直接使用,毕竟谁知道下一条sql会跑在哪个连接上呢?那么有什么解决方案呢?
Go1.8以后的版本,sql库新增了API BeginTx,直接调用即可
1
2
3
|
tx0, err := db.BeginTx(context.Background(), &sql.TxOptions{
Isolation: sql.LevelReadUncommitted,
})
|
如果是1.8以前的版本,可以利用事务在一个session上处理的特性hack一下
1
2
3
4
5
6
7
|
tx1, err := db.Begin()
_, err = tx1.Exec("ROLLBACK")
_, err = tx1.Exec("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED")
_, err = tx1.Exec("BEGIN")
rows, err := tx1.Query("SELECT COUNT(*) FROM USER")
rows.Close()
tx1.Commit()
|
或者也可以使用不同的配置创建两个db对象,每个对象有自己独立的连接池
1
2
|
db0, err := sql.Open("mysql", "root@/test")
db1, err := sql.Open("mysql", "root@/test?tx_isolation='read-uncommitted'")
|
最后提供一段测试代码,可以很清楚的看到read-uncommitted带来的脏读问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
func main() {
db, err := sql.Open("mysql", "root@/test")
if err != nil {
panic(err)
}
db1, err := sql.Open("mysql", "root@/test?tx_isolation='read-uncommitted'")
if err != nil {
panic(err)
}
tx0, err := db.Begin()
if err != nil {
panic(err)
}
_, err = tx0.Exec("insert into user value (null,?)", "cc")
if err != nil {
panic(err)
}
rows, err := db1.Query("select count(*) from user")
if err != nil {
panic(err)
}
for rows.Next() {
s := 0
err = rows.Scan(&s)
if err != nil {
panic(err)
}
fmt.Println(s)
}
tx0.Rollback()
}
|
转载:https://www.jianshu.com/p/b324b368a210