Mysql 锁

64 阅读6分钟

算一算也工作几年了,这几年的时间认识了很多人,有漂亮的姑娘,有特有趣的同事,有初来深圳时一无所有但是却都一起快乐的朋友,也有经历了很多美好的爱人,许多许多,林林种种,有句歌词说,没有梦想,何必远方,我到是觉得,有没有梦想,都要远方,因为远方有你没见过的风景,远方有你没交过的有趣的朋友,远方也有许多你没做过,但是一定很有意思的事,总之,人生是需要远方的。

BB结束-----

在接触数据库之前,很难想象技术中会有这么多的锁,数据信息时代,最重要的就是数据,一切技术归根到底,都是为数据服务,所以数据的安全尤为重要,所以数据不能放肆,而克制他的东西就是锁。

好多锁,现在一个一个说

1 乐观锁

我们修改数据的时候,我们会建立一个事务,然后查询出数据,然后进行修改的操作,这时候存在一种可能,别的事务也拿到了这个数据,并且有可能也修改了数据,这时候,第一个事务在去修改数据,那就不准确了,但是乐观锁的思想是,不回存在另一个事务修改这个数据。所以不去建锁,呵呵,所以乐观锁他就没有锁命令,但是却需要我们在程序中处理,要在数据库表增加一个version字段,记录当前数据的版本号,在修改数据时,判断当前数据的版本号和数据库是否一致,如果一致就修改,不一致就报错。

2 悲观锁

这个锁,他就是认为在A事务修改数据时,B事务也会过来修改数据,是不是很极端,没错就是很极端,他是一种思想,靠这个思想来解决问题的还有两种锁,共享锁(读锁),排他锁(写锁)

1)共享锁

这个锁,前面加了一个共享,这名字不太好,都共享了,还锁毛呀!,但是他确实锁了,在一个会话访问他的时候,其他的回话只允许读加共享锁的数据,不能写也就是修改。他是需要命令来加的,举个栗子

begin;/begin work;/start transaction;  (三者选一就可以) 

#(lock in share mode 共享锁)

 SELECT * from TABLE where id = 1  lock in share mode;

执行完这段话后,就会对这行数据加上了共享锁,然后其他的回话就只能读取他不能修改他了,只有执行完commit才能修改成功,负责就会一直阻塞。另外,如果当前的回话如果增加了对这条数据的delete,update,那就会自动对这条数据进行排他锁

2)排他锁

这个锁,相当与上一个锁的升级,他是当前回话访问的数据,其他的回话既不能读也不能写,多嚣张。举个栗子

   set autocommit=0; 

   # 设置完autocommit后,我们就可以执行我们的正常业务了。具体如下:

   # 1. 开始事务 begin;/begin work;/start transaction; (三者选一就可以) 

   # 2. 查询表信息(for update    加锁) select status from TABLE where id=1 for update;   

   # 3. 插入一条数据 insert into TABLE (id,value) values (2,2); 

   # 4. 修改数据为 update TABLE set value=2 where id=1; 

   # 5. 提交事务 commit;/commit work

值得一提的是,mysql默认的是自动提交事务,也就是autocommit,我之前自己学习的时候,使用两个回话测试事务,结果测试不成功,原来mysql的事务默认是自动提交的...

他的加锁是通过for update来执行的

3 行锁

这个锁,是最常见的锁,因为他是通过事务控制的,并不需要执行什么命令

他有一些特点

1)锁的粒度小,精确到行

2)开销大,加锁满,会出现死锁,因为太过精确,所以要耗费更多的资源

3)发生锁冲突低,适合高并发,因为锁在行上,所以可以有效的减少阻塞

除此之外非常值得一提的是,他会升级成表锁,啥时候呢,如果修改时的条件不是索引列时就会升级成表锁,表锁就有点可怕了,因为会增加阻塞的概率。

那为什么会是这样的呢,为什么一定要是索引列呢,因为mysql不能判定,要修改的数据是多少行,万一修改的数据行太多呢,这样数据库的开销就太大了,而索引的原则就是加载行数据较少的字段。

4 表锁

上面已经介绍了,就不多过说了。

5 间隙锁

这个是对一个区间的查询

举个栗子

   Select * from  emp where empid > 100 for update;

这样就加上了间隙锁,这个间隙锁也是针对索引列的,那为甚要用间隙锁呢,因为他可以防止幻读,幻读就是在一个事务里面第一次查询一个阶段的数据总数是A,但是同样的查询条件,第二次查询出来的总数就变成B了,导致好像出现了幻觉,而加上了间隙锁的数据,第二次查询他还是A,这样就解决了幻读,但是他有一个问题,加上了这个锁之后,这个锁之间的数据,是不允许被插入和修改的,就造成了阻塞,所以还是要慎用。

6 死锁

这个是最头疼的,这个场景也挺值得思考的,如果申请锁是有序的就不会造成死锁,但是什么时候会出现死锁呢,

比如A事务,他修改数据时会锁数据,然后继续执行,执行到下一个修改,执行不下去了,因为这个修改的数据被B事务锁住了。

然后我们看B事务,他修改数据是锁住数据(这个锁住的数据就是刚刚A事务要修改的数据),然后继续向下执行修改下一条数据,这时候发现这条数据被A事务锁住了,这下就杠上了,也就是死锁了。

那怎么解决掉呢,我们可以通过查进程的方式,然后杀进程来解决

也可以通过查看数据库表中是哪个事务对应的sql锁住来解决

但是最好的还是防患于未然,就是避免产生这种现象,那有啥方案呢

1)减少大事务,很简单,大事务就会增加阻塞的时间

2)给表添加合理的索引,因为不走索引,那就是表锁了,就容易死锁了

3)按顺序修改表,一次性最好把需要的所有数据都上锁。

4)降低事务的隔离级别

以上所写的锁,都是针对InnoDB引擎,mysql有两种引擎,MyIsam和InnoDb,这两个引擎的差别还是很大的

1 不同点

1)MyIsam是不支持事务的

2)MyIsam是不支持高并发的

3)MyIsam使用的是表锁而InnoDb默认的是行锁

这个不同点估计大家看到前两条就会放弃Myisam了,不支持事务问题就太大了。