最近在努力研究关于数据库相关的知识,正好研究到了Mysql InnoDB锁相关的内容,所以特意写下此文,与各位分享我的知识,如有错误,麻烦与我一起探讨,谢谢。
锁是什么?
在业务运行过程中,可能会有很多用户同时操作数据库同一个数据,这个时候就需要有一种机制来保证数据的写入和存储是安全的,这个时候锁就诞生了,锁顾名思义,可以帮助我们锁住某些内容,防止一个用户在修改的过程中被另外的用户修改,导致前后数据不一致。
如何验证锁?
在写文过程中,只是单纯的讲概念,贴文字简直就是太枯燥了,所以在全文我会使用Navicat来展示我的发现。
- navicat如何查看Mysql数据库线程:使用【show processlist】命令查看,该命令可以查看用户正在运行的线程,如果是root用户,可以看所有正在运行的线程,如果是其他用户,则只能看到自己正在运行的线程
- navicat中每新增加一个查询窗口就相当于开启了一个线程,这对于我们伪造两个事务一个先执行更新操作但是不提交,然后另外一个线程接着来更新同一行,看锁怎么处理提供了方便。
锁的分类:
按锁的颗粒度分:
- 全局锁
- 使用场景:全库逻辑备份
- 定义:对整个数据库进行加锁,导致数据库没办法增删改,只能读
- 使用方法:使用【flush tables with read lock】命令即可锁住整个库
- 表锁
- 表锁
- 使用场景:需要限制本线程和其他线程操作的时候。比如说1号线程上了读锁,其他线程都只能阻塞
- 定义:该锁需要执行命令才会给表加上读锁或者写锁
- 使用方法:使用【lock tables … read/write】即可锁住对应的表,可以上读锁,也可以上写锁
- 元数据锁
- 使用场景:当执行增删改查操作时,会自动加上元数据读锁;当执行修改表结构的时候,会自动加上元数据写锁;是为了保证表结构的一致性
- 定义:在执行操作的时候,会自动给表加锁,该锁为元数据锁,是为了保证表结构一致性而出现的
- 使用方法:不需要显式调用,该机制由mysql自身保证
- 行锁
- 使用场景:在并发操作时,多个线程需要对同一行进行增删改查,需要有一把锁控制有序修改
- 定义:对某一行进行加锁,锁住后其他线程不能操作当前线程
- 方法:有悲观锁【select * from tables for update】和乐观锁【由用户自行控制,操作前查询版本号,修改后对比前后版本号,符合版本更新原则即更新完成】两种,都是需要用户自行控制
- 行锁的具体分析:因为行锁是InnoDB引擎默认使用的锁方式,但是会在使用过程中因为使用不当,会升级成表锁,所以这一块需要单独拿出来分析一下
- 行锁和表锁区别:
- 行锁开销大,加锁慢(因为操作级别很细,需要具体到某一行),锁定颗粒度小,发生锁冲突的概率小,并发度高
- 表锁开销小,枷锁快,锁定颗粒度大,发生锁冲突概率大,并发度低
- 应用场景:InnoDB是通过给索引上的索引加锁来实现的,也就意味着使用了索引条件来检索数据使用的是行锁,反之则会升级成为表锁
- 使用主键作为查询条件去修改某一数据使用的是行锁
- 使用非索引字段作为条件去修改某一数据使用的是表锁,此处后续再进行更新
按加锁机制分:
- 乐观锁:针对每一次操作,都乐观的认为不会发生锁碰撞,一般做法是首先查询出目前数据库记录数据的版本后,然后再去更新,更新后再查询版本号是否按照预期增长,如果没有按照预期增长,则直接回滚事务
- 悲观锁:针对每一次操作,都悲观的认为会发生碰撞,所以在操作之前就去获取锁,获取到了再进行操作,反之则进行等待
按兼容性分:
- 共享锁:又称之为读锁,也称S锁。即针对多个事务可以同时来读取相同数据,但是不能修改
- 排他锁:又称之为写锁,也称X锁。获取写锁后,将会阻碍其他线程读取或者更新