MYSQL乐观锁实现

目录

一、实现方法:

二、伪SQL代码

三、go代码实现:

四、优缺点:


一、实现方法:

        通过对数据表增加一个数字类型的version字段来实现。读取记录时,将version字段一同读出,数据每更新一次,对version字段+1。当更新记录时,检查记录当前version是否与之前读取时的相同,只有相同才给予记录更新操作。

        乐观锁不是数据库自带的,需要自己在代码中实现。乐观锁指更新数据库时,想法很乐观,认为这次操作不会导致冲突。在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新时,再去判断是否有冲突了。整体思想就是CAS思想。

        从字面意思看,用于读多写少的场景。

二、伪SQL代码

SELECT count,version FROM product WHERE id = 10000;
UPDATE product SET count = count -1,version = version+1 WHERE id = 10000 AND version= oldversion;

三、go代码实现:

package main

import (
	"fmt"
	"log"
	"sync"
	"sync/atomic"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

type Product struct {
	ID      int `gorm:"primaryKey"`
	Count   int
	Version int
}

var DSN = "root:123@tcp(192.168.4.41:3306)/test?charset=utf8mb4"
var DB *gorm.DB

func init() {
	var err error
	DB, err = gorm.Open(mysql.Open(DSN), &gorm.Config{
		AllowGlobalUpdate: true,
		PrepareStmt:       true,
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true,
		},
	})
	if err != nil {
		log.Fatalln(err)
	}
	DB.Exec("DROP TABLE IF EXISTS product")
	DB.AutoMigrate(Product{})
	DB.Create(Product{ID: 10000, Count: 10, Version: 0})
}

//减库存
func Decr(id int) bool {
	for {
		var product Product
		DB.Debug().First(&product, id)
		if product.ID == 0 || product.Count <= 0 {
			return false
		}

		rf := DB.Debug().Exec("UPDATE product SET count = count -1,version = version +1 WHERE id = ? AND version = ?", id, product.Version).RowsAffected
		if rf > 0 {
			return true
		}
	}

	return false
}

func main() {
	var count int32
	wg := sync.WaitGroup{}
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			if ret := Decr(10000); ret {
				atomic.AddInt32(&count, 1)
			}
		}()
	}
	wg.Wait()
	fmt.Println("成功更新记录数:", count)
}

运行结果:只能有10个协程更新成功!

数据库库存成功减到零。

四、优缺点:

1.优点:在读操作下,不需要频繁加锁解锁来消耗系统资源,比较轻量

2.缺点:在写操作较多时,很多线程会不断循环来判断锁的状态,这给cpu带来很大的开销。所以乐观锁只适合读多写少的场景。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL中的乐观锁实现通常依赖于版本号或时间戳。其主要思路是,在数据更新时,先获取当前数据的版本号或时间戳,然后在更新数据时比较当前版本号或时间戳与更新前获取的版本号或时间戳是否相同,如果相同则执行更新操作,否则认为数据已经被其他事务更新,拒绝本次更新操作。 具体实现可以通过以下步骤来完成: 1. 在数据表中添加版本号或时间戳字段,用于记录数据的版本信息。 2. 在更新数据时,先查询出待更新数据的版本号或时间戳,并保存在变量中。 3. 执行更新操作前,再次查询数据的版本号或时间戳,并与之前保存的版本号或时间戳进行比较。 4. 如果两者相同,则执行更新操作;否则认为数据已经被其他事务更新,拒绝本次更新操作。 5. 在更新操作完成后,更新数据的版本号或时间戳。 具体实现可以使用MySQL的UPDATE语句,通过添加条件判断来实现乐观锁的效果。例如: ``` UPDATE table_name SET column1 = value1, ..., version = new_version WHERE id = target_id AND version = old_version; ``` 其中,`target_id`表示待更新数据的ID,`old_version`表示之前查询出的数据版本号或时间戳,`new_version`表示更新后的数据版本号或时间戳。如果`old_version`与`version`字段的值相同,说明数据没有被其他事务修改,可以执行更新操作;否则更新操作不会执行,可以通过返回值判断是否更新成功。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值