本地事务的隔离

摘抄自:极客时间《周志明的软件架构课》 1. 数据库中的三种锁 写锁(Write Lock,也叫做排他锁 eXclusive Lock,简写为 X-Lock):只有持有写锁的事务才能对数据进行写入操作,数据加持着写锁时,其他事务不能写入数据,也不能施加读锁。 读锁(Read Lock,也叫做共享锁 Shared Lock,简写为 S-Lock):多个事务可以对同一个数据添加多个读锁,数据被加上读锁后就不能再被加上写锁,所以其他事务不能对该数据进行写入,但仍然可以读取。对于持有读锁的事务,如果该数据只有一个事务加了读锁,那可以直接将其升级为写锁,然后写入数据。 范围锁(Range Lock):对于某个范围直接加排他锁,在这个范围内的数据不能被读取,也不能被写入。如下语句是典型的加范围锁的例子: SELECT * FROM books WHERE price < 100 FOR UPDATE; 2. 本地事务的四种隔离级别 以下隔离级别从高到低 可串行化 顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。 串行化访问提供了强度最高的隔离性。可串行化比较符合普通程序员对数据竞争加锁的理解,如果不考虑性能优化的话,对事务所有读、写的数据全都加上读锁、写锁和范围锁即可(这种可串行化的实现方案称为 Two-Phase Lock)。 但数据库显然不可能不考虑性能,并发控制理论(Concurrency Control)决定了隔离程度与并发能力是相互抵触的,隔离程度越高,并发访问时的吞吐量就越低。现代数据库一定会提供除可串行化以外的其他隔离级别供用户使用,让用户调节隔离级别的选项,这样做的根本目的是让用户可以调节数据库的加锁方式,取得隔离性与吞吐量之间的平衡。 可重复读 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。 可串行化的下一个隔离级别是可重复读(Repeatable Read)。可重复读的意思就是对事务所涉及到的数据加读锁和写锁,并且一直持续到事务结束,但不再加范围锁。 可重复读比可串行化弱化的地方在于幻读问题(Phantom Reads),它是指在事务执行的过程中,两个完全相同的范围查询得到了不同的结果集。比如现在准备统计一下书店中售价小于 100 元的书有多少本,就可以执行以下第一条 SQL 语句: SELECT count(1) FROM books WHERE price < 100 /* 时间顺序:1,事务: T1 */ INSERT INTO books(name,price) VALUES ('深入理解Java虚拟机',90) /* 时间顺序:2,事务: T2 */ SELECT count(1) FROM books WHERE price < 100 /* 时间顺序:3,事务: T1 */ 可重复读级别对事务涉及到的数据加读锁和写锁,但不再加范围锁。这里事务T1中涉及的数据是原来数据库中已经存在的数据,但新插入的条目显然不在这个范围内。因此在事务T1再次执行读的时候就会与第一次读的结果不同。原因就是,可重复读没有范围锁来禁止在该范围内插入新的数据。...

October 8, 2021 · 2 min · 李昌

Go中的锁

1. sync.Mutex互斥锁 不同goroutine之间对公共资源进行访问需要使用互斥锁。例如在对银行账户的操作中,如果我们有两种操作,一个是查询余额,一个是存款。其操作如下: package bank // 存款余额 var balance int // 存款 func Deposit(amount int) { balance = balance + amount } // 查询 func Balance() int { return balance } // Alice: go func() { bank.Deposit(200) // A1 fmt.Println("=", bank.Balance()) // A2 }() // Bob: go bank.Deposit(100) // B 这其中,若把A1分为两个操作,A1r:把余额从内存中读出来;A2w:把修改后的余额写入内存。 若执行顺序为A1r → B → A1w → A2, 正常情况下,Alice和Bob分别存入了$200,$100,因此最后的存款应该是300,但最后输出结果为200。因为A在计算时是按照A1r读出的数值进行计算,忽略了B的操作,A与B之间发生了数据竞争。 数据竞争:无论任何时候,只要有两个goroutine并发访问同一变量,且至少其中的一个是写操作的时候就会发生数据竞争。 解决此问题的办法之一是使用互斥锁。 import "sync" var ( mu sync.Mutex // guards balance balance int ) func Deposit(amount int) { mu....

April 14, 2021 · 1 min · 李昌