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

本文通过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 mysql加锁_使用golang实现类InnoDB数据行锁效果相关推荐

  1. golang mysql 崩溃_使用GoLang与mysql连接失败

    我试图使用Go语言连接MySql数据库并给我以下错误. sql: unknown driver "mysql" (forgotten import?) 我的守则 package m ...

  2. golang mysql封装_使用Golang 封装一个Api 框架 ----- 数据库操作篇(gorm引入)

    在models/baseModel 定义了 一个BaseModel对象,在这个对象上定义了 获取数据库连接和 释放数据库连接的方法 type BaseModel struct { dbConnect ...

  3. 【Java面试题】6年开发去A里面试P6竟被Mysql难住了,说一下你对行锁、临键锁、间隙锁的理解

    一个工作了6年的程序员,最近去阿里面试p6这个岗位. 面试之前信心满满的和我说,这次一定要拿下 35k月薪的offer. 然后,在第一面的时候,被Mysql里面的一个问题难住了. 大家好,我是Mic, ...

  4. Mysql INNODB引擎行锁的3种算法 Record Lock Next-Key Lock Grap Lock

    Mysql INNODB引擎行锁的3种算法 InnoDB存储引擎有3种行锁的算法,其分别是: □ Record Lock:单个行记录上的锁 Record Lock总是会去锁住索引记录,如果InnoDB ...

  5. mysql 开启引擎命令_MySql中启用InnoDB数据引擎的方法

    1.存储引擎是什么? Mysql中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力.通过选择不同的技术, ...

  6. mysql事务基础+基于innodb的行锁+间隙锁+如何锁定行

    [0]README outlines are as follows : 行锁: 事务: 隔离级别: 行锁变表锁: 间隙锁: 如何锁定一行: 行锁总结: [1]行锁+事务+存储引擎基础 1.行锁: 偏向 ...

  7. golang mysql封装_自己封装的golang 操作数据库方法

    这个是我用Go写的第一个东东,可能还存在些BUG没有测试到.这里主要是提供一个参考.各位可以改写成自己的风格. 在命令行下输入如下两条命令,进行安装 go get github.com/male110 ...

  8. golang 读一行_与golang合作的第一步

    golang 读一行 If you like to understand know why we choose golang in the first as an alternative to pyt ...

  9. golang反编译_【Golang】脱胎换骨的defer(一)

    Go语言的defer是一个很方便的机制,能够把某些函数调用推迟到当前函数返回前才实际执行.我们可以很方便的用defer关闭一个打开的文件.释放一个Redis连接,或者解锁一个Mutex.而且Go语言在 ...

最新文章

  1. 一个例子看懂kotlin的集合和序列
  2. Python学习基础必备知识点:字典dict详解
  3. 数据结构与算法 | 二叉树四种的遍历方法(递归与非递归)
  4. Postgres客户端编码问题
  5. 【英语学习】【Level 07】U01 Making friends L2 A new friend from the past
  6. java正则表达式tab_Linux下如何使用grep命令查找带有tab(退格)的字符
  7. iOS内存分配五大区:
  8. 庆贺:在国产平台上成功运行gluegen/jogl 2.3.2
  9. 地图转换|用arcgis 将cad转kmz
  10. python tokenize_python – 滥用nltk的word_tokenize(已发送)的后果
  11. 洛谷P1510-精卫填海(01背包)
  12. Maven中Scop为test时Eclispe中需要注意的地方
  13. UG/NX二次开发 判断圆柱面是不是孔的方法
  14. Java“菜单“树递归-合并子树
  15. 解决xp共享的批处理文件
  16. ubuntu:下载ubuntu内核
  17. 安装docker 配置 oracle11g
  18. 使用Python PySNMP模块获取设备指标
  19. logAB = logA + logB; A,B0
  20. python爬网络图片脚步_小白福利!跟着我的脚步!老司机带你爬取整站妹子图!反爬不存在-站长资讯中心...

热门文章

  1. 利用pandas,matplotlib画饼图
  2. 匿名内部类 可以访问外部类_Java——内部类详解
  3. Java方法02 递归
  4. python控制结构实验结果分析_实验1_Python语法及控制结构
  5. HTML搜索框中加入提示文字,HTML 5 input placeholder 属性 实现搜索框提示文字点击输入后消失 - 尚码园...
  6. YApi--使用YApi的目的
  7. python 时间序列异常值_python中缺少时间序列值
  8. c++ array容器 传参_C++ 顺序容器基础知识总结
  9. c++怎么做app_怎么做一款app
  10. HTML渐变背景不重复,如何停止重复自身的背景颜色渐变? (css)