を書く私はそれが2つの同時トランザクションを実行し、これはショーGolang における場合や、ここhttps://play.golang.org/p/2fGKEyh0Wlmysqlの安全な読み込み - 更新 -
で、MySQLのロック・レコードのメカニックを理解しよう、と彼らは同じ行の更新を読んで
- 最初のトランザクションが、行をロック(3秒間スリープ)何か
をしようとします - 2番目は、同じキー
テストソースコードの更新を読み取ろう
package main
import (
"github.com/jmoiron/sqlx"
"github.com/satori/go.uuid"
"log"
"sync"
"time"
_ "github.com/go-sql-driver/mysql"
)
type Wallet struct {
ID string
Balance int64
}
func main() {
db, err := sqlx.Connect("mysql", "root:[email protected](mysql:3306)/test?parseTime=true")
if err != nil {
log.Println(err)
return
}
db.Exec(`CREATE TABLE test_wallet (
id varchar(64) NOT NULL,
balance bigint(20) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`)
var wg sync.WaitGroup
wg.Add(2)
wID := uuid.NewV4().String()
db.Exec("INSERT INTO test_wallet (id,balance) VALUES (?,?)", wID, 10)
go func() {
defer wg.Done()
tx, err := db.Beginx()
w1 := &Wallet{}
err = db.Get(w1, "SELECT * FROM test_wallet WHERE id=? FOR UPDATE", wID) // read and lock the record
if err != nil {
log.Println(err)
}
log.Printf("got %+v on r1\n", w1)
time.Sleep(time.Second * 3)
res, err := tx.Exec("UPDATE test_wallet SET balance=? WHERE id=?", w1.Balance+5, wID)
if err != nil {
log.Println(err)
}
n, err := res.RowsAffected()
if n != 1 {
log.Println("update not affected r1")
}
tx.Commit()
log.Println("done on r1")
}()
time.Sleep(time.Second) // make sure go-routine lock `id` row
go func() {
defer wg.Done()
tx, err := db.Beginx()
w2 := &Wallet{}
err = db.Get(w2, "SELECT * FROM test_wallet WHERE id=? FOR UPDATE", wID)
if err != nil {
log.Println(err)
}
log.Printf("got %+v on r2\n", w2)
res, err := tx.Exec("UPDATE test_wallet SET balance=? WHERE id=?", w2.Balance+7, wID)
if err != nil {
log.Println(err)
}
n, err := res.RowsAffected()
if n != 1 {
log.Println("update not affected r2")
}
tx.Commit()
log.Println("done on r2")
}()
wg.Wait()
w := &Wallet{}
err = db.Get(w, "SELECT * FROM test_wallet WHERE id=?", wID)
if err != nil {
log.Println(err)
}
log.Printf("%+v\n", w)
}
私の端末
2016/10/01 09:57:00 got &{ID:aab7165c-4b3b-406d-b1d0-caf3f45f72be Balance:10} on r1
2016/10/01 09:57:01 got &{ID:aab7165c-4b3b-406d-b1d0-caf3f45f72be Balance:10} on r2
2016/10/01 09:57:01 done on r2
2016/10/01 09:57:03 done on r1
2016/10/01 09:57:03 &{ID:aab7165c-4b3b-406d-b1d0-caf3f45f72be Balance:15}
から
結果、第2のルーチンがロックされていないように見えます?
:あなたはすなわち
修正した後、私は次の結果を得た、クエリを実行するために
tx
を使用する必要があります。競争状態に陥り、最終的にあなたの顔に爆破するようになっているメカニズムであるロックを緩和しようとしています。お金やお金のような通貨を含むものについては、**常に**個々の借方とクレジットを記録します。**決して過去の情報に基づいて残高を直接決めることはありません。代わりに 'SET balance = balance +?'のようなことを行います。これは原子的で*は*働きます。原子性と完全性を確保するために、ロックではなく**トランザクション**を使用してください。 – tadmanええ、私は、トランザクション*(これは 'tx.Beginx'でこれを使っていますが、mysqlの分離レベルを' SERIALIZABLE'に設定していますが、まだ期待通りに動作しませんでした。外部の行を読み取って何かを検証し、新しい値で更新する必要があります。最後のクエリからバランスが変更されていないことを確認する必要があります。これをアーカイブする方法はありますか? – secmask
'balance'を更新する前に、 SETバランス=バランス+?... – secmask