mysql并发问题记录
来源:互联网 发布:数据库的分类 编辑:程序博客网 时间:2024/05/29 09:23
并发更新问题
悲观锁
假如有这样一张表:
| id |name |price |
每一行代表一个拍卖的商品,用户每次投标价格加1,初始价格为0, 有100个用户进行抢拍,每个用户进行了一次投标,那么最终的价格应该是100
对于上面这种情况,对于用户的每次投标,我们一般会先SELECT查出这条记录,然后根据查出记录的Price,加1 再UPDATE 。
如果所有的用户在几乎同时去投标,那么很可能大家在同一时间取出初始值,然后加1更新,最终的实际价格可能会远远小于100.
测试时模拟100个goroutine进行并发更新,共测试10次,看看测试结果。
func main() { test()}
//测试10次看看结果
func test() { db, err := sql.Open("mysql", "mysql:123@/test") if err != nil { log.Fatal(err) } defer db.Close() var result int var n int = 10 for i := 0; i < n; i++ { if checkRight(db) { result++ } } fmt.Printf("test %d times, %d times correct", n, result)}
//开启100个协程进行并发更新,看看结果是否正确
func checkRight(db *sql.DB) bool { db.Exec("create table mytable(id integer primary key auto_increment not null, name text, price integer );") defer db.Exec("drop table mytable;") stmt_insert, err := db.Prepare("insert into mytable(name,price) values(?,?)") if err != nil { log.Fatal(err) } _, err = stmt_insert.Exec("iphone6", 0)//初始价格为0,理论上100个人各投标一次后价格为100 if err != nil { log.Fatal(err) } var wg sync.WaitGroup times := 100 for i := 0; i < times; i++ { wg.Add(1) go func() { tx, _ := db.Begin() row := tx.QueryRow("select price from mytable where name=?", "iphone6") var price int64 row.Scan(&price) price++ _, err := tx.Exec("update mytable set price=? where name=?", price, "iphone6") if err != nil { log.Fatal(err) } tx.Commit() wg.Done() }() } wg.Wait() row := db.QueryRow("select price from mytable where name=?", "iphone6") var price int64 row.Scan(&price) fmt.Println("bid times: ", times, " price: ", price) if times == int(price) { return true } return false}
10次执行结果如下:
可见测试10次,结果因为没有处理并发的原因,实际值与期望相差太远。
这种情况下悲观锁就起作用了,先看度娘的解释:
悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
也就是说,大家在投标时,当某个用户在投标过程中(从执行select * for table_name for update;到commit数据),该条数据被锁定了,直到更新后(执行commit)才解锁,其他用户才能查询到数据。这样其他用户查询到的总是上一个用户更新后的价格,从而实现了更新操作的序列化。
使用悲观锁很简单,在选择要更新的数据时,加上for update。
修改checkRight函数:
... for i := 0; i < times; i++ { wg.Add(1) go func() { tx, _ := db.Begin() row := tx.QueryRow("select price from mytable where name=? for update", "iphone6")//注意此查询语句中的for update var price int64 row.Scan(&price) price++ _, err := tx.Exec("update mytable set price=? where name=?", price, "iphone6") if err != nil { log.Fatal(err) } tx.Commit() wg.Done() }() } wg.Wait() ...
再执行看看结果:
可以看到执行结果与期望完全符合。
*注:要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。本文代码中显式的使用事务操作。
- mysql并发问题记录
- php mysql 并发问题
- 并发导数据问题记录
- MySQL问题记录(一)
- mysql一些问题记录
- Mysql问题记录笔记
- MySql-碰到问题记录
- 记录mysql问题
- mysql问题记录
- MySQL-MVCC并发问题详述
- Hive客户端多并发问题解决方法记录
- SHELL脚本并发问题一则记录
- pure-ftpd+mysql 问题记录
- MySql使用问题总结记录
- mysql 常见操作&问题记录
- mysql主从问题处理记录
- 记录一下mysql中的问题
- mysql使用中问题记录
- RecyclerView 解析
- Writing Fast Matlab code 1
- java记——随机数三个数的和
- 大数据下的日志--ElasticSearch部分(三)--Bulk,Search操作
- ”Hello World!“的前世今生
- mysql并发问题记录
- 大数据下的日志--ElasticSearch部分(四)--Mapping
- UVa 1586 - Molar mass【字符串】
- Redis Persistence
- Writing Fast Matlab code 2
- SQL with as 的用法 以及递归函数的写法
- ViewPager——之——PagerAdapter
- SQL Server的 排序规则(collation)冲突和解决方案
- [BZOJ1628]=[BZOJ1683][Usaco2007 Demo]City skyline