美文网首页MySQLGolang 入门资料+笔记系统运维专家
使用golang实现类InnoDB数据行锁效果

使用golang实现类InnoDB数据行锁效果

作者: 道闻 | 来源:发表于2019-08-09 18:08 被阅读1次

在关系型数据库领域,为人津津乐道的一个特性,便是数据库的锁设计及事务隔离级别。
本文通过golang系统库sync,来实现简单的数据库数据读写操作。

场景说明

小明经营一家水果店,创业初始资金为100000元,所有的收入以及支出通过2个银行账户进行往来。
因交易频繁,可能存在并发更新账户数据及查账的需求,需要保障账户数据针对所有操作的一致性。
此处需要引入读写锁,保障读写的安全性及高效性。

需求分析

在MySQL中,使用InnoDB存储引擎,配合合适的事务隔离级别,可以做到数据行级锁定,也就是:

操作类型 查账户A 查账户B 写账户A 写账户B
查账户A 可并发 可并发 互斥 可并发
查账户B 可并发 可并发 可并发 互斥
写账户A 互斥 可并发 互斥 可并发
写账户B 可并发 互斥 可并发 互斥

账户A和账户B的读写操作相互独立,最大化账户的并发操作。
那么,如何使用golang实现简单表格中的场景呢?另外, 是否可以设置读写的优先级呢?
我们下面先来介绍下golang的两个类:

  • sync.RWMutex
    读写锁,支持单写多读特性。区别于sync.Mutex的全局互斥锁特性(不支持同时读)
  • sync.WaitGroup
    可通过Add方法,将请求分组,同一组的gorountine可通过Wait方法,控制组内所有gorountine全部结束,才能继续主线程,否则一直阻塞主线程。

如下代码为实现样例,假设当前有如下数量请求并发:
5个A账户读,B账号读,3个A账号写,B账户写
其中A账户设置了低优先级读。

功能实现

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)
// 账户的初始数据
var accountTypeMap = map[string]int{
    "A": 50000,
    "B": 50000,
}
// init方法,设置seed,用于控制随机数生成的初始值,确保其随机性
func init() {
    rand.Seed(time.Now().Unix())
}

func sleep() {
    time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
}

// 读取账号余额,低优先级的读,采取随机sleep的方式,等待请求
func readAccount(accountType string, m *sync.RWMutex, wg *sync.WaitGroup, lowPriority ...string) {
    if (len(lowPriority)) > 0 {
        sleep()
    }
   // 使用读锁
    m.RLock()
    fmt.Println("time:", time.Now().UnixNano()/1e6, " read account ", accountType, " left money:", accountTypeMap[accountType])
    // sleep 10毫秒,方便确认并发是否生效
    time.Sleep(time.Duration(10) * time.Millisecond)
    // 释放读锁
    m.RUnlock()
    wg.Done()
}

// 写入账号,低优先级的写,采取随机sleep的方式,等待请求
func writeAccount(accountType string, addMoney int, m *sync.RWMutex, wg *sync.WaitGroup, lowPriority ...string) {
    if (len(lowPriority)) > 0 {
        sleep()
    }
    // 使用写锁(排他锁)
    m.Lock()
    accountTypeMap[accountType] = accountTypeMap[accountType] + addMoney
    fmt.Println("time:", time.Now().UnixNano()/1e6, "modify account ", accountType, " add money:", addMoney)
    // sleep 10毫秒,方便确认并发是否生效
    time.Sleep(time.Duration(10) * time.Millisecond)
    m.Unlock()
    wg.Done()
}

func main() {
    var mutexA sync.RWMutex
    var mutexB sync.RWMutex

    wg := sync.WaitGroup{}

    // 设置5个A账户读,B账号读,3个A账号写,B账户写
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go readAccount("A", &mutexA, &wg, "lowpriority")
    }

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go readAccount("B", &mutexB, &wg)
    }

    for i := 0; i < 3; i++ {
        wg.Add(1)
        go writeAccount("A", 1000, &mutexA, &wg)
    }

    for i := 0; i < 3; i++ {
        wg.Add(1)
        go writeAccount("B", 3000, &mutexB, &wg)
    }

    wg.Wait()

    fmt.Println("account A left: ", accountTypeMap["A"])
    fmt.Println("account B left: ", accountTypeMap["B"])
}

结果分析

返回的结果具体随机性,其中A读具有低优先级,返回在最后。以下为一种结果:

time: 1565345040128 modify account  B  add money: 3000
time: 1565345040128 modify account  A  add money: 1000
time: 1565345040139  read account  B  left money: 53000
time: 1565345040139  read account  B  left money: 53000
time: 1565345040139 modify account  A  add money: 1000
time: 1565345040139  read account  B  left money: 53000
time: 1565345040139  read account  B  left money: 53000
time: 1565345040139  read account  B  left money: 53000
time: 1565345040149 modify account  A  add money: 1000
time: 1565345040149 modify account  B  add money: 3000
time: 1565345040160 modify account  B  add money: 3000
time: 1565345040493  read account  A  left money: 53000
time: 1565345040693  read account  A  left money: 53000
time: 1565345040693  read account  A  left money: 53000
time: 1565345040828  read account  A  left money: 53000
time: 1565345041091  read account  A  left money: 53000
account A left:  53000
account B left:  59000

从前面2行的返回结果看,写账户A和写账户B 可并发操作,
从第3,4,5 行看,读账户B和写账户可并发操作,满足前面表格的场景。

相关文章

  • 使用golang实现类InnoDB数据行锁效果

    在关系型数据库领域,为人津津乐道的一个特性,便是数据库的锁设计及事务隔离级别。本文通过golang系统库sync,...

  • Mysql InnoDB 排它锁

    1、InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,...

  • 数据库基础

    1 锁 1.1 InnoDB的锁 行级锁(InnoDB存储引擎实现了两种标准的)共享锁 允许事务读一行数据排他锁 ...

  • 腾讯预面试

    锁表 只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁! 在实际应用中,要特别注意...

  • 阿里P8大佬带你全面了解—MySQL锁:03.InnoDB行锁

    目录 InnoDB 行锁锁排查可以用的视图和数据字典InnoDB 行锁兼容性 InnoDB行锁之共享锁共享锁: 查...

  • InnoDB Locking And Transaction

    1.Locking 1.1加锁模式:共享锁与独占锁 InnoDB实现了两类行级锁, shared(S)locks ...

  • InnoDB行锁

    十、 InnoDB行锁0、 查看方式1、 共享锁2、 排他锁3、 意向锁4、行锁案列5、InnoDB锁实现 0、 ...

  • 文章总结(6)—数据库

    InnoDB和MyISAM存储引擎的区别 InnoDB使用的是行锁,MyISAM使用的是表锁; InnoDB支持事...

  • MySQL 锁详解

    InnoDB 锁 数据库使用锁是为了支持更好的并发,提供数据的完整性和一致性。InnoDB是一个支持行锁的存储引擎...

  • mysql学习笔记(三) 锁

    1. 行锁 InnoDB存储引擎实现了如下两种标准的行级锁: ❑共享锁(S Lock),允许事务读一行数据。 ❑排...

网友评论

    本文标题:使用golang实现类InnoDB数据行锁效果

    本文链接:https://www.haomeiwen.com/subject/swshjctx.html