首先我们必须明白:
乐观锁和悲观锁是数据库操作的两种安全实现的方式,各有优劣,谁好谁坏要具体结合实际场景分析!
什么是乐观锁?
乐观锁总是乐观的认为:我们在数据库操作的过程中,操作的数据库数据不会被其他人修改,所以不上锁,但是在最后更新之前会检查这条数据是否为之前的数据。
什么是悲观锁?
悲观锁总是悲观的认为:在我们对数据库进行操作的过程中,会有人来修改我们的数据,所以每次拿完数据,都要给表或者列上锁。
场景比较:
1、经常查找,却很少更新操作的表(多读少写的场景)
乐观锁要优于悲观锁。
因为悲观锁每次查完数据库数据,都要锁住数据,直到自己操作结束才释放。这个过程中,其他要操作此数据的线程都会处于阻塞等待状态。在多读少写的场景中,大多数线程只是读取数据,而没有更新数据,所以不上锁的乐观锁支持多线程同时操作,具有更大的优势。
2、经常更新、删除操作的表(多写场景)
悲观锁要优于乐观锁。
因为乐观锁每次查完数据都不上锁,多写场景容易造成多人同时操作同一条数据的情况,当其中一人先完成操作,其他人只能回滚重新执行了,这样就造成了很多的无用功。而使用悲观锁,只能一个一个排着队去更新操作,虽然效率低了,但不会造成无用功操作,增加服务器压力。
实现方式:
乐观锁:
①、在数据库表中加上一个版本号字段(version)
②、每次读取自己要操作的数据时,一同读取保存version字段值
③、更新操作前,再从数据库表中的对应version字段值,与之前保存的version作比较,相等可以进行更新,不等就回滚重新来过。
④、更新时,一同让 version+1,表示数据已被修改过。
什么是乐观锁的ABA问题?
乐观锁机制存在一个问题:一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
关于ABA问题,只要我们严格遵循“每次更新完数据之后对version+1”操作,就可以规避了。
悲观锁:
在数据查询的时候,加上 for update。
具体步骤:
①、开启事务
begin; / begin work; / start transaction; (三者选一即可)
②、查询某表数据(id为主键)
select * from 表名 where id = 1 for update;
③、修改表数据
update 表名 set 表字段 = xxx;
④、提交事务
commit; / commit work; (二选一即可)
关于for update的注意事项:
1、如果不开启事务,for update是不会锁任何东西的。
2、开启事务后,where条件字段是索引字段的话,该锁为行锁,如果不是索引字段,则为表锁。
3、for update 是写锁,该操作不会影响读操作。
关于悲观锁的困惑过我的问题解释:
1、mysql使用事务前,需要关闭自动提交(set autocommit=0;)吗?
答:不需要!mysql 使用事务功能是不需要手动关闭自动提交的,只要你 begin 开启了事务,自动提交就会被自动关闭,然后在使用 commit 之后,又会被重新打开。
2、网上怎么有人说 for update 会锁住读操作?
答:正常的读操作是不会受到影响的!当我执行 select * from 表名 where id=1 for update;后,在另外的事务中如果再次执行select * from 表名 where id=1 for update;则第二个事务会一直等待第一个事务的提交,此时第二个查询处于阻塞的状态。如果我是在第二个事务中执行select * from 表名 where id=1;则能正常查询出数据,不会受第一个事务的影响。