go 原子操作、加锁、MVCC多版本并发控制

原子操作和加锁对比

原子操作使用sync/atomic包,加锁使用sync包中的mutex、RWMutex;

实现方式上,原子由硬件上的原子指令直接实现,锁由软件实现;

性能上,原子操作由于由硬件实现,无上下文信息,所以更快;锁面临上下文切换、死锁等问题,性能开销大;

适用场景上,原子操作适用于简单的数据操作,如计数器加减;锁适合复杂的并发场景;

互斥锁(sync.Mutex)和读写锁(sync.RWMutex)对比

sync.Mutex 同一时间只允许一个goroutine访问共享资源,不论读写;

sync.RWMutex 支持多个读操作并发执行;写操作会阻塞所有其他的读写操作;读操作会阻塞写操作,不阻塞读操作;适用读多写少的情况

sync.Mutex 实现原理

竞争锁的两种模式:

  • 正常模式:和新来的(未释放资源,占着processor)一起抢锁,大概率失败(保证效率)
  • 饥饿模式:肯定能拿到锁(退出饥饿模式:队列中只剩下一个goroutine或者等待时间小于1ms

MVCC 多版本并发控制

questions:

  • 什么是MVCC?
  • 为什么有读写锁还需要MVCC?
  • Mysql的InnoDB引擎是怎么控制数据并发访问的?
  • 当一个线程修改数据时,另一个线程是否还能读数据?

为什么有读写锁还需要MVCC?

避免读写阻塞

即便在读写锁的机制下,写和读依然是互斥的。若某个线程修改某条数据,就不允许其他线程读这条数据,这种性能损耗在数据库(高并发)上是很难忍受的。所以InnoDB引擎引入MVCC,减少读写阻塞。

什么是MVCC?Mysql的InnoDB引擎是怎么控制数据并发访问的?当一个线程修改数据时,另一个线程是否还能读数据?

MVCC是Mysql InnoDB用于进行数据并发访问控制的协议。MVCC主要是通过版本链实现的。在InnoDB引擎里面,每行都有额外两列:trx_id和roll_ptr。trx_id表示修改该行数据的事务ID。roll_ptr表示回滚指针。InnoDB引擎通过回滚指针将不同版本串联起来,构成版本链。这些串联起来的历史版本,存在undolog里面。当某一事务发起查询时,MVCC会根据事务的隔离级别生成相应的read view, 返回合适的数据。

事务的隔离级别

事务的隔离级别越高,性能越差

事务的隔离级别脏读读异常幻读
未提交读(Read uncommitted)yesyesyes
已提交读(Read committed)noyesyes
重复读(Repeatable read)nonoyes
序列化(Serializable)nonono
  • 未提交读面临脏读问题,即事务A读到事务B未提交的变动

  • 已提交读、未提交读面临读异常的问题,即一个事务内使用相同查询语句读到数据不一致,存在不可重复读的可能——事务A 读到 事务B的UPDATE数据

  • 重复读 禁止UPDATE操作,但允许INSERT操作,所以存在幻读问题

相关示例见:理解事务的4种隔离级别

Read View

read view只用于已提交读和重复读,它用于这两个隔离级别的差异点在于什么时候创建read view

  • 已提交读:在每次发起查询时创建
  • 重复读:在事务开始时创建

相关示例见:MVCC协议:MySQL 在修改数据的时候,还能不能读到这条数据

乐观锁、悲观锁

乐观锁指假设在大多数情况下数据访问不会发生冲突,而减少锁的使用,提高并发性能;悲观锁相反,悲观锁对数据提前加锁防止其他事务或线程的并发修改。

常见乐观锁

  • 版本号或时间戳控制
  • sync/atomic原子操作

常见悲观锁

  • 数据库系统中的悲观锁:SELECT … FOR UPDATE 行加锁
  • sync.Mutex

相关资料

理解事务的4种隔离级别

MVCC协议:MySQL 在修改数据的时候,还能不能读到这条数据

浅谈数据库并发控制 - 锁和 MVCC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值