数据库两大必备神器:索引和锁底层原理是什么( 三 )


锁的相关知识又跟存储引擎,索引,事务的隔离级别都是关联的....
这就给初学数据库锁的人带来不少的麻烦~~~于是我下面就简单整理一下数据库锁的知识点,希望大家看完会有所帮助 。
1、为什么需要学习数据库锁知识
不少人在开发的时候,应该很少会注意到这些锁的问题,也很少会给程序加锁(除了库存这些对数量准确性要求极高的情况下)
一般也就听过常说的乐观锁和悲观锁,了解过基本的含义之后就没了~~~
定心丸:即使我们不会这些锁知识,我们的程序在一般情况下还是可以跑得好好的 。因为这些锁数据库隐式帮我们加了:

  • 对于UPDATE、DELETE、INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);
  • MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预;
只会在某些特定的场景下才需要手动加锁,学习数据库锁知识就是为了:
  • 能让我们在特定的场景下派得上用场
  • 更好把控自己写的程序
  • 在跟别人聊数据库技术的时候可以搭上几句话
  • 构建自己的知识库体系!在面试的时候不虚
2、表锁简单介绍
首先,从锁的粒度,我们可以分成两大类:
  • 表锁开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低;
  • 行锁开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高;
不同的存储引擎支持的锁粒度是不一样的:
  • InnoDB行锁和表锁都支持!
  • MyISAM只支持表锁!
InnoDB只有通过索引条件检索数据才使用行级锁,否则,InnoDB将使用表锁
  • 也就是说,InnoDB的行锁是基于索引的!
表锁下又分为两种模式:
  • 表读锁(Table Read Lock)
  • 表写锁(Table Write Lock)
  • 从下图可以清晰看到,在表读锁和表写锁的环境下:读读不阻塞,读写阻塞,写写阻塞!
  • 读读不阻塞:当前用户在读数据,其他的用户也在读数据,不会加锁
  • 读写阻塞:当前用户在读数据,其他的用户不能修改当前用户读的数据,会加锁!
  • 写写阻塞:当前用户在修改数据,其他的用户不能修改当前用户正在修改的数据,会加锁!
 
数据库两大必备神器:索引和锁底层原理是什么

文章插图
 
 
从上面已经看到了:读锁和写锁是互斥的,读写操作是串行 。
  • 如果某个进程想要获取读锁,同时另外一个进程想要获取写锁 。在mysql里边,写锁是优先于读锁的!
  • 写锁和读锁优先级的问题是可以通过参数调节的:max_write_lock_count和low-priority-updates
值得注意的是:
数据库两大必备神器:索引和锁底层原理是什么

文章插图
 
 
  • MyISAM可以支持查询和插入操作的并发进行 。可以通过系统变量concurrent_insert来指定哪种模式,在MyISAM中它默认是:如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录 。
  • 但是InnoDB存储引擎是不支持的!
3、乐观锁和悲观锁
无论是Read committed还是Repeatable read隔离级别,都是为了解决读写冲突的问题 。
单纯在Repeatable read隔离级别下我们来考虑一个问题:
数据库两大必备神器:索引和锁底层原理是什么

文章插图
 
 
此时,用户李四的操作就丢失掉了:
  • 丢失更新:一个事务的更新覆盖了其它事务的更新结果 。
(ps:暂时没有想到比较好的例子来说明更新丢失的问题,虽然上面的例子也是更新丢失,但一定程度上是可接受的..不知道有没有人能想到不可接受的更新丢失例子呢...)
解决的方法:
  • 使用Serializable隔离级别,事务是串行执行的!
  • 乐观锁
  • 悲观锁
  • 乐观锁是一种思想,具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段 。处理完业务逻辑开始更新的时候,需要再次查看该字段的值是否和第一次的一样 。如果一样更新,反之拒绝 。之所以叫乐观,因为这个模式没有从数据库加锁,等到更新的时候再判断是否可以更新 。
  • 悲观锁是数据库层面加锁,都会阻塞去等待锁 。
3.1、悲观锁
所以,按照上面的例子 。我们使用悲观锁的话其实很简单(手动加行锁就行了):


推荐阅读