golang Mysql -- Tx
Transaction 事务
事务处理是数据的重要特性。尤其是对于一些支付系统,事务保证性对业务逻辑会有重要影响。golang的mysql驱动也封装好了事务相关的操作。我们已经学习了db的Query和Exec方法处理查询和修改数据库。
tx对象
一般查询使用的是db对象的方法,事务则是使用另外一个对象。sql.Tx对象。使用db的Begin方法可以创建tx对象。tx对象也有数据库交互的Query,Exec和Prepare方法。用法和db的相关用法类似。查询或修改的操作完毕之后,需要调用tx对象的Commit提交或者Rollback方法回滚。
一旦创建了tx对象,事务处理都依赖与tx对象,这个对象会从连接池中取出一个空闲的连接,接下来的sql执行都基于这个连接,直到commit或者rollback调用之后,才会把连接释放到连接池。
在事务处理的时候,不能使用db的查询方法,虽然后者可以获取数据,可是这不属于同一个事务处理,将不会接受commit和rollback的改变,一个简单的事务例子如下:
tx, err := db.Begin()
tx.Exec(query1)
tx.Exec(query2)
tx.commit()
在tx中使用db是错误的:
tx, err := db.Begin()
db.Exec(query1)
tx.Exec(query2)
tx.commit()
上述代码在调用db的Eexc方法的时候,tx会绑定连接到事务中,db则是额外的一个连接,两者不是同一个事务。需要注意,Begin和Commit方法,与sql语句中的BEGIN或COMMIT语句没有关系。
事务与连接
创建Tx对象的时候,会从连接池中取出连接,然后调用相关的Exec方法的时候,连接仍然会绑定在改事务处理中。在实际的事务处理中,go可能创建不同的连接,但是那些其他连接都不属于该事务。例如上面例子中db创建的连接和tx的连接就不是一回事。
事务的连接生命周期从Beigin函数调用起,直到Commit和Rollback函数的调用结束。事务也提供了prepare语句的使用方式,但是需要使用Tx.Stmt方法创建。prepare设计的初衷是多次执行,对于事务,有可能需要多次执行同一个sql。然而无论是正常的prepare和事务处理,prepare对于连接的管理都有点小复杂。因此私以为尽量避免在事务中使用prepare方式。例如下面例子就容易导致错误:
tx, _ := db.Begin()
defer tx.Rollback()
stmt, _ tx.Prepare("INSERT ...")
defer stmt.Close()
tx.Commit()
因为stmt.Close使用defer语句,即函数退出的时候再清理stmt,可是实际执行过程的时候,tx.Commit就已经释放了连接。当函数退出的时候,再执行stmt.Close的时候,连接可能有被使用了。
事务并发
对于sql.Tx对象,因为事务过程只有一个连接,事务内的操作都是顺序执行的,在开始下一个数据库交互之前,必须先完成上一个数据库交互。例如下面的例子:
rows, _ := db.Query("SELECT id FROM user")
for rows.Next() {
var mid, did int
rows.Scan(&mid)
db.QueryRow("SELECT id FROM detail_user WHERE master = ?", mid).Scan(&did)
}
调用了Query方法之后,在Next方法中取结果的时候,rows是维护了一个连接,再次调用QueryRow的时候,db会再从连接池取出一个新的连接。rows和db的连接两者可以并存,并且相互不影响。
可是,这样逻辑在事务处理中将会失效:
rows, _ := tx.Query("SELECT id FROM user")
for rows.Next() {
var mid, did int
rows.Scan(&mid)
tx.QueryRow("SELECT id FROM detail_user WHERE master = ?", mid).Scan(&did)
}
tx执行了Query方法后,连接转移到rows上,在Next方法中,tx.QueryRow将尝试获取该连接进行数据库操作。因为还没有调用rows.Close,因此底层的连接属于busy状态,tx是无法再进行查询的。上面的例子看起来有点傻,毕竟涉及这样的操作,使用query的join语句就能规避这个问题。例子只是为了说明tx的使用问题。
实践
前面对事务解释了一堆,说了那么多,其实还不如share的code。下面就事务的使用做简单的介绍。因为事务是单个连接,因此任何事务处理过程的出现了异常,都需要使用rollback,一方面是为了保证数据完整一致性,另一方面是释放事务绑定的连接。
func doSomething(){
panic("A Panic Running Error")
}
func clearTransaction(tx *sql.Tx){
err := tx.Rollback()
if err != sql.ErrTxDone && err != nil{
log.Fatalln(err)
}
}
func main() {
db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")
if err != nil {
log.Fatalln(err)
}
defer db.Close()
tx, err := db.Begin()
if err != nil {
log.Fatalln(err)
}
defer clearTransaction(tx)
rs, err := tx.Exec("UPDATE user SET gold=50 WHERE real_name='vanyarpy'")
if err != nil {
log.Fatalln(err)
}
rowAffected, err := rs.RowsAffected()
if err != nil {
log.Fatalln(err)
}
fmt.Println(rowAffected)
rs, err = tx.Exec("UPDATE user SET gold=150 WHERE real_name='noldorpy'")
if err != nil {
log.Fatalln(err)
}
rowAffected, err = rs.RowsAffected()
if err != nil {
log.Fatalln(err)
}
fmt.Println(rowAffected)
doSomething()
if err := tx.Commit(); err != nil {
// tx.Rollback() 此时处理错误,会忽略doSomthing的异常
log.Fatalln(err)
}
}
我们定义了一个clearTransaction(tx)函数,该函数会执行rollback操作。因为我们事务处理过程中,任何一个错误都会导致main函数退出,因此在main函数退出执行defer的rollback操作,回滚事务和释放连接。
如果不添加defer,只在最后Commit后check错误err后再rollback,那么当doSomething发生异常的时候,函数就退出了,此时还没有执行到tx.Commit。这样就导致事务的连接没有关闭,事务也没有回滚。
总结
database/sql提供了事务处理的功能。通过Tx对象实现。db.Begin会创建tx对象,后者的Exec和Query执行事务的数据库操作,最后在tx的Commit和Rollback中完成数据库事务的提交和回滚,同时释放连接。
tx事务环境中,只有一个数据库连接,事务内的Eexc都是依次执行的,事务中也可以使用db进行查询,但是db查询的过程会新建连接,这个连接的操作不属于该事务。
关于database/sql和mysql的驱动,我们已经分三部分内容介绍了。下一节,将会对之前的内容进行梳理总结,包括错误处理和注意事项的补充。
作者:人世间
链接:http://www.jianshu.com/p/bc8120bec94e
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
golang Mysql -- Tx的更多相关文章
- golang mysql 如何设置最大连接数和最大空闲连接数
本文介绍golang 中连接MySQL时,如何设置最大连接数和最大空闲连接数. 关于最大连接数和最大空闲连接数,是定义在golang标准库中database/sql的. 文中例子连接MySQL用的SQ ...
- Golang mysql 上线的一个坑 Db.close重要性
急冲冲完成的mysql的一个监控自动处理程序上线了,线下处理是正常的,没想到线上才半小时就奔溃了. 现在时间是晚上11点,心慌焦虑涌上心头,需要熬夜?肾上腺素激增. 程序主要是一个定时任务的处理程序, ...
- golang mysql 的 packet sequence error 这个错
在公司用golang 写了个插入外链数据的服务,这服务是2016年写的,大概作用就是,python 爬取的数据,要同步到 wordpress中,golang就负责,将数据整理,图片下载弄到 wordp ...
- Golang mysql
还是那句话,服务器嘛,每个数据库支持,那成啥子啦嘛! 好吧,今天,就让Go能连上数据库,当然是之前给你铺垫的MySql的啦,哈哈 一.安装第三方包支持访问mysql数据库 #go get github ...
- Golang mysql数据库
基本操作: Open() – create a DB Close() - close the DB Query() - 查询 QueryRow() -查询行 Exec() -执行操作,update,i ...
- golang mysql demo
Go操作Mysql数据库 使用Go操作MySQL等数据库,一般有两种方式:一是使用database/sql接口,直接在代码里硬编码sql语句:二是使用gorm,即对象关系映射的方式在代码里抽象的操作数 ...
- golang mysql 模糊查询
db.SqlDB.Query("SELECT id,name FROM test_table where title name like CONCAT('%',?,'%');", ...
- 我的Vue之旅 10 Gin重写后端、实现页面详情页 Mysql + Golang + Gin
第三期 · 使用 Vue 3.1 + Axios + Golang + Mysql + Gin 实现页面详情页 使用 Gin 框架重写后端 Gin Web Framework (gin-gonic.c ...
- Go Mysql驱动
Golang中MYSQL驱动 Mysql库https://github.com/go-sql-driver/mysql Go本身不提供具体数据库驱动,只提供驱动接口和管理. 各个数据库驱动需要第三方实 ...
随机推荐
- 赶集网三年 DBA 总结(转)
2012年初入职赶集,当时处在流量讯猛增长的阶段,3年DBA生涯收获坡多,其实坑更多(泪... 后来在做开发时,慢慢体会到 ”运维“ 和 “开发” 确实存在沟通问题:知识不对称.如何解决呢?先总结下这 ...
- 学号 20175201张驰 《Java程序设计》第7周学习总结
学号 20175201张驰 <Java程序设计>第7周学习总结 教材学习内容总结 第八章 String类能有效地处理字符序列信息,它的常用方法有: public int length()可 ...
- python迭代-如何实现反向迭代
如何实现反向迭代 问题举例 实现一个连续浮点数发生器FloatRange,根据给定范围(start, end)和步进值(step) 产生一系列连续的浮点数,如FloatRange(3.0, 4.0, ...
- 2018-2019-2 《网络对抗技术》Exp0 Kali安装 Week1 20165321
安装kali 在vm里面新建虚拟机,选择典型 选择安装程序光盘镜像文件,系统出现无法检测此光盘镜像中的操作系统 虚拟机命名选择安装位置 给虚拟机分配的磁盘大小 点击自定义硬件,更改虚拟机硬件 选择Gr ...
- hook
hook的定义 hook,钩子,勾住系统的程序逻辑. 在某段SDK源码逻辑执行的构成中,通过代码手段拦截执行该程序,加入自己的代码逻辑 使用价值 hook是安卓面向切面(aop)编程的基础,可以让我们 ...
- (转)jmeter接口测试--获取token
Jmeter进行接口测试-提取token 项目一般都需要进行登陆才能进行后续的操作,登陆有时发送的请求会带有token,因此, 需要使用后置处理器中的正则表达式提取token,然后用BeanShell ...
- java的智能提示无法打开
第一步:选中“window”->“preference” 第二步:选中“java”,并展开 第三步:选中“Editor”,并展开 第四步:选中“Content Assist”,在右侧 ...
- python拼接multipart/form-data类型post请求格式
# 最近要做form-data类型接口,大多数这种格式用来文件上传,但是我们公司就是用这种格式传输请求数据. # 百度了一些基本都是files方式的,可是我们需要data=方式的.下面自己来拼接,代码 ...
- 2017UGUI之slider
不让鼠标控制slider的滑动: 鼠标之所以可以控制滑动是因为slider具有interactable这个属性(下图红色的箭头的地方):如果取消了这个属性的运行的时候就不能滑动了.如果要代码去控制这个 ...
- windows 下安装weblogic
下载weblogic安装文件 https://www.oracle.com/technetwork/middleware/weblogic/downloads/index.html 在目录下, 下载后 ...