MySQL数据库的锁机制

转载请标明出处:
原文首发于: http://www.zhangruibin.com
本文出自 RebornChang的博客

厚颜打广告,博主个人博客地址传送门 ,欢迎来访

抛开数据库引擎说数据库锁机制的都是流氓…
引言:MySQL数据库的引擎分为三种,MyISAM(ISAM)、InnoDB以及MEMORY,具体的引擎类型性能比较可以baidu到,这里就不多说了,本文中所说的锁机制基于InnoDB引擎,那为啥说基于InnoDB引擎说锁呢?因为InnoDB支持事务,支持行锁和表锁用的比较多,Myisam不支持事务,只支持表锁。InnoDB 与 MyISAM 的最大不同有两点:一是 InnoDB 支持事务;二是 InnoDB 采用了行级锁。也就是你需要修改哪行,就可以只锁定哪行。
Mysql 在5.5之前默认使用 MyISAM 存储引擎,之后使用 InnoDB 。查看当前存储引擎:show variables like ‘%storage_engine%’;

在说锁之前,要对数据库事物有一定的了解,那么什么是事物?

事务是一条或多条数据库操作语句的组合,具备ACID,4个特点。

原子性:要不全部成功,要不全部撤销;

隔离性:事务之间相互独立,互不干扰;

一致性:数据库正确地改变状态后,数据库的一致性约束没有被破坏;

持久性:事务的提交结果,将持久保存在数据库中。

事务并发可能会产生什么问题呢?

笼统的说,可能会产生脏读,幻读,丢失更新,不可重复读这几种。对这几种并发会产生的问题,可以baidu,点进去就OK啦。

再接下来说下为什么需要锁

在进行数据库数据的读写(CUID)时,当多事务争取一个资源时,有可能导致数据不一致,这个时候需要一种机制限制,并且将数据访问顺序化,用来保证数据库数据的一致性。当多个事务同时读取一个对象的时候,不会有冲突。同时进行读和写,或者同时写会产生冲突(对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁,因为,数据库的增删改操作默认都会加排他锁,而查询不会加任何锁。事务可以通过以下语句显示给记录集加共享锁或排他锁)。所以,为了保证数据的一致性以及提高数据库的并发效率,MySQL中提供了以下几种锁的机制:

共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。

排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

共享锁和排他锁都是行锁,意向锁都是表锁,应用中我们只会使用到共享锁和排他锁,意向锁是mysql内部使用的,不需要用户干预。

意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。

意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

先说下这两种锁的sql结构:

共享锁(Shared Lock,也叫S锁)

共享锁定义:Shared Lock,也叫S锁,共享锁(S)表示对数据进行读操作。因此多个事务可以同时为一个对象加共享锁。

sql格式:SELECT * FROM table_name WHERE … LOCK IN SHARE MODE;

共享锁的使用场景:

SELECT … LOCK IN SHARE MODE走的是IS锁(意向共享锁),即在符合条件的rows上都加了共享锁,这样的话,其他人可以读取这些记录,也可以继续添加IS锁,但是无法修改这些记录直到你这个加锁的过程执行完成(完成的情况有:事务的提交,事务的回滚,否则直接锁等待超时)。
  SELECT … LOCK IN SHARE MODE的应用场景适合于两张表存在关系时的写操作,比如:现在有两张表,一张是child表,一张是parent表,假设child表的某一列child_id映射到parent表的c_child_id。从业务角度讲,如果直接insert一条child_id=666记录到child表是存在风险的,因为刚insert的时候可能在parent表里删除了这条c_child_id=666的记录,那么业务数据就会存在数据不一致的风险。正确的方法是:在插入时执行select * from parent where c_child_id=666 lock in share mode,锁定了parent表的这条记录,然后执行insert into child(child_id) values (666),这样就不会存在这种问题了。

排它锁(Exclusive Lock,也叫X锁)

排它锁定义:

排他锁表示对数据进行写操作。如果一个事务对对象加了排他锁,其他事务就不能再给它加任何锁了。

排它锁sql:

SELECT * FROM table_name WHERE … FOR UPDATE;

排他锁使用场景:

比如电商系统订单处理的时候:

系统计算一种商品的剩余量,在产生订单之前需要确认商品数量>=1,产生订单之后应该将商品数量减1。

1 :select p.amount from product p where product_name=‘XX’;

2 :update product set amount=amount-1 where product_name=‘XX’;
显然1的做法有问题,因为如果1查询出amount为1,但是这时正好其他session也买了该商品并产生了订单,那么amount就变成了0,那么这时第二步再执行就有问题。那么采用lock in share mode可行吗,也是不合理的,因为两个session同时锁定该行记录时,这时两个session再update时必然会产生死锁导致事务回滚。

又如一个账户两方共同取钱,要先对账户余额进行判断,当剩余余额大于所取钱数才能取钱成功。

具体的锁机制baidu上有很多的,可以自行前去搜索学习,这里讲的比较笼统,仅供参考,over!

你可能感兴趣的:(database)