一、乐观锁:直白的说就是乐观的认为程序不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现
解决办法:
1.加版本号
update tbl1 set name=XXX, version=version+1 where game_id=XXX and version=XXXX; //如果版本号和之前查到的不一样,说明有其他进程对该数据进行了修改
2.CAS操作
内存值、预期值、更新值 (直白就是sql语句where后面的条件就是预期值(game_id,log_date),数据库中的值就是内存值(game_id,log_date),要更新的值就是更新值)update tbl1 set name=XXX where game_id=XXX and log_date=XXXX;
二、悲观锁:总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁。
注意:要使用数据库的悲观锁,我们必须关闭mysql数据库的自动提交事物属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。
悲观锁分为两种:共享锁(读锁)和排它锁(写锁)
共享锁是其它事务可以读但是不能写:实现方式是在sql后加LOCK IN SHARE MODE,比如SELECT ... LOCK IN SHARE MODE
排他锁是只有自己的事务有权限对此数据进行读写:实现方式是在sql后加FOR UPDATE,比如SELECT ... FOR UPDATE ,即在符合条件的rows上都加了排它锁,其他session也就无法在这些记录上添加任何的S锁或X锁。
通过对比,发现for update的加锁方式无非是比lock in share mode的方式多阻塞了select...lock in share mode的查询方式,并不会阻塞快照读
从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适
使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。
悲观锁:总是认为会产生并发问题,其他线程会改变数据,共享锁和排他锁 (必须关闭数据库字自动提交事物,必须明确的指定主键,否则锁会锁住整张表)
网友评论