mysql的锁机制,以及乐观锁,悲观锁,以及热点账户余额问题
mysql的简单锁机制。
myisam
1、只支持表级锁,所以经常更新的表结构不适宜用。
2、select也会产生锁表
innodb
1、支持事务,行级锁,表级锁,执行行级锁的前提是sql语句的索引有效,否则,执行表级锁。
2、不存在字段锁问题,直接锁行的。
3、select不会请求锁,自然也不会产生锁表,update,insert,delete都会请求锁,所以会产生锁表
执行串行并行问题
1、同一个connection下,串行执行。
2、不同connection下,并行关系。验证方法:执行一个慢查询中间执行一个快查询便可验证
3、所以java这种非页面级的mysql连接,一般有连接线程池。以提高并行执行效率。
4、但是从微观角度来说,所有执行都是串行的。譬如自增id永远不会重复出现一样。
像php这种页面级的mysql连接,则不存在连接线程池问题,因为每个页面过来其实都是新开一个线程的问题。
事务的乐观锁和悲观锁
悲观锁:
begin;/begin work;/start transaction; (三者选一就可以) //1.查询出商品信息 select status from t_goods where id=1 for update; //2.根据商品信息生成订单 insert into t_orders (id,goods_id) values (null,1); //3.修改商品status为2 update t_goods set status=2; //4.提交事务 commit;/commit work;
我们使用了select…for update
的方式,这样就通过开启排他锁的方式实现了悲观锁,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行, InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。悲观并发控制实际上是“先取锁再访问”的保守策略,则执行for update的时候锁定了t_goods表,也就是说没有commit之前已经锁表了,根据微观串行可知,是线程安全的。
死锁:
假如两个并行事务s1和s2,s1先锁定t1,s2先锁定t2,然后s1需要等待t2解锁,s2需要等待t1解锁。就陷入无限等待的情况。
乐观锁:
begin;
1.查询出商品信息
select (status,status,version) from t_goods where id=#{id} 2.根据商品信息生成订单 3.修改商品status为2 update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
commit;
一般我们使用版本号的方式来实现乐观锁,乐观锁直到commit的时候才去锁定,乐观锁由于是commit才锁定,所以不存在死锁问题。但是也存在线程安全问题,假如两个connection实例,同时在commit之前修改了同一数据,commit先后的时候就会出现数据version不一致的问题。
热点账户解决方案