什么是mvcc?
目的:提高事务的并发性能。
原则:写加锁,读不加锁,读写不冲突,只有写写才冲突。
具体措施:保存数据库每个修改的版本,增加两个字段创建时间和删除时间,分别存储创建和删除的事务ID,作为版本号。
事务处理的常见问题?
脏读:读取未提交的数据,该数据可能会回滚。
不可重复读:两次读取的数据不一致,针对的update。
幻读:读取同一范围的数据,读取的行记录不一样,有新增行或者删除行。
更新丢失:一个事务的更新,被另一个事务的更新给覆盖了。
事务的acid是怎么保证的?
基于CopyOnWrite机制,写时复制技术,修改时复制一个新的版本,从而保证了读写不冲突,也就是mvcc。
对DML(insert、delete、update、或者显示锁),每次都加锁,同时保证写读取的数据是最新提交的。
根据事务的隔离级别选择读取mvcc的哪个版本。
RC:mvcc当前读,每次读最新的版本。
RR:mvcc快照读,晚于当前版本号的不可见。
MySQL的RR隔离级别存在什么问题?
尽管解决了幻读问题,但是仍然存在更新丢失的问题。
第一类更新丢失问题,回滚覆盖,撤销一个事务时,在该事务内的写操作要回滚,把其他已提交事务的数据也被覆盖了。
经验证,MySQL已经杜绝了该类问题的发生。
第二类更新丢失问题,更新覆盖。提交一个事务时,写操作依赖于事务内读到的数据,读发生在其他事务提交之前,写发生在其他事务提交之后,把其他已提交事务的写入覆盖了。
参考:https://blog.csdn.net/seanxwq/article/details/90614370
更新丢失的解决办法?
1、原子操作,update ... set a = a + 1。
2、加锁:
1)最好加排他锁select ... for update,共享锁有可能会带来死锁问题。
共享锁的锁定行后面有更新操作,容易造成死锁。
参考:https://www.jianshu.com/p/beddb45070bb
2)乐观锁,增加一个版本号列。
update ... set a = 10 where id = 1 and version = 10
判断是否更新成功,如果更新失败,需要重新更新。极端情况下,会需要多次重试。
PS:自旋的递归调用不能放在同一个事务里,因为RR级别下是允许可重复读的。
乐观锁和悲观锁的选择(阿里建议):
1、写多使用悲观锁,冲突概率超过20%,重试次数超过3次,都最好使用悲观锁。
2、金融等敏感信息使用悲观锁。乐观锁冲突的解决策略较复杂,容易处理不当。
参考:https://www.jianshu.com/p/bfd7c684412d
RR隔离级别一定能解决幻读问题吗?
读->写->读,第二次读取的快照版本和第一次读的快照版本不一样。事务开始时,读取一个快照版本,写操作会触发刷新快照版本。
参考:https://blog.csdn.net/u011277123/article/details/107868336
采用RC隔离级别,如何规避不可重复读和幻读的问题?
不可重复度:1)只读取一次,本地缓存,不要重复的多次去读取。2)使用select … lock in share mode 或者 select … for update。
幻读:不要使用范围查询,尽量使用唯一查询命中,杜绝间隙锁的使用。
RU隔离级别下,是如何加锁的呢?
这种情况下,未提交的修改也可以读,是不是没有加锁呢?
mvcc和锁操作的数据是同一份吗?
锁住的是Copy出来的版本?或者说已经复制出来一个版本了,就拒绝再复制版本?因为要保证读写不冲突,写写冲突。
undo log、redo log、binlog有什么区别?
redo log为了保证事务的持久化,磁盘io和内存的速率不匹配,通过逻辑物理存储的方式先写到redo log里面,然后再慢慢同步到数据磁盘。
undo log记录的是回滚日志,用于mvcc、事务回滚。
binlog主从复制的日志。
redo log和undo log都是innodb的日志,binlog是mysql本身的。