为何要加锁?java
问题背景mysql
要解决的问题面试
锁是什么sql
锁的分类数据库
行bash
表微信
页多线程
MySQL经常使用存储引擎的锁机制架构
MyISAM和MEMORY采用表级锁(table-level locking)并发
BDB采用页面锁(page-level locking)或表级锁,默认为页面锁
InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
在实际应用中,要特别注意InnoDB行锁的这一特性,否则的话,可能致使大量的锁冲突,从而影响并发性能。
行级锁都是基于索引的,若是一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁。行级锁的缺点是:因为须要请求大量的锁资源,因此速度慢,内存消耗大。
实例说明
select语句默认不会加任何锁类型,若是加排他锁可使用select …for update语句,加共享锁可使用select … lock in share mode语句。
因此加过排他锁的数据行在其余事务种是不能修改数据的,也不能经过for update和lock in share mode锁的方式查询数据,但能够直接经过select …from…查询数据,由于普通查询没有任何锁机制。
行级锁与死锁
MyISAM中是不会产生死锁的,由于MyISAM老是一次性得到所需的所有锁,要么所有知足,要么所有等待。而在InnoDB中,锁是逐步得到的,就形成了死锁的可能。
在MySQL中,行级锁并非直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,若是一条sql语句操做了主键索引,MySQL就会锁定这条主键索引;若是一条语句操做了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。在UPDATE、DELETE操做时,MySQL不只锁定WHERE条件扫描过的全部索引记录,并且会锁定相邻的键值,即所谓的next-key locking。
当两个事务同时执行,一个锁住了主键索引,在等待其余相关索引。另外一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。
发生死锁后,InnoDB通常均可以检测到,并使一个事务释放锁回退,另外一个获取锁完成事务。
共享锁与排它锁
共享锁(Share Lock)
若是事务T对数据A加上共享锁后,则其余事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
用法 SELECT ... LOCK IN SHARE MODE;
在查询语句后面增长LOCK IN SHARE MODE,Mysql会对查询结果中的每行都加共享锁,当没有其余线程对查询结果集中的任何一行使用排他锁时,能够成功申请共享锁,不然会被阻塞。其余线程也能够读取使用了共享锁的表,并且这些线程读取的是同一个版本的数据。
排它锁(eXclusive Lock)
用法 SELECT ... FOR UPDATE;
在查询语句后面增长FOR UPDATE,Mysql会对查询结果中的每行都加排他锁,当没有其余线程对查询结果集中的任何一行使用排他锁时,能够成功申请排他锁,不然会被阻塞。
乐观锁(Optimistic Lock)
是什么
相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。通常的实现乐观锁的方式就是记录数据版本。
数据版本,为数据增长的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当咱们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,若是数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,不然认为是过时数据。
实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。
使用版本号实现乐观锁
1.查询出商品信息select (status,status,version) from t_goods where id=#{id}2.根据商品信息生成订单3.修改商品status为2update t_goodsset status=2,version=version+1where id=#{id} and version=#{version};复制代码
优势与不足
悲观锁(Pessimistic Lock)
是什么
悲观锁的流程
MySQL InnoDB中使用悲观锁
//0.开始事务begin;//1.查询出商品信息select status from t_goods where id=1 for update;//2.根据商品信息生成订单insert into t_orders (id,goods_id) values (null,1);//3.修改商品status为2update t_goods set status=2;//4.提交事务commit;复制代码
上面的查询语句中,咱们使用了select…for update的方式,这样就经过开启排他锁的方式实现了悲观锁。此时在t_goods表中,id为1的 那条数据就被咱们锁定了,其它的事务必须等本次事务提交以后才能执行。这样咱们能够保证当前的数据不会被其它事务修改。
Java的锁机制
线程的同步问题
线程的同步方法:
ThreadLocal
当使用ThreadLocal维护变量时,ThreadLocal为每一个使用该变量的线程提供独立的变量副本,因此每个线程均可以独立地改变本身的副本,而不会影响其它线程所对应的副本。在ThreadLocal类中有一个Map,用于存储每个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。使用ThreadLocal的典型场景如数据库链接管理,线程会话管理等场景,只适用于独立变量副本的状况,若是变量为全局共享的,则不适用在高并发下使用。
最后给你们分享一些学习资料,里面包括:(BATJ面试资料、高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)和Java进阶学习路线图。
领取方式:加微信号 weixin99ting 备注 :(资料) 便可获取。
最后,祝你们早日学有所成!