MySQL锁机制
服务器并发时,会用一套锁机制来控制请求,以防数据出现混乱。
锁有两种:读锁和写锁。
读锁也称共享锁,加上该锁时其他人不受影响,都可以读取数据。
写锁加上后其他锁就再不能加上去了,也就是其他人不能再进行读取和变更,所以它又称:排他锁。
写锁比读锁拥有更高的优先级,即使有读操作用户在排队的队列中,一个被申请的写操作仍可排列在队列的前面。写锁会被安置在读锁之前,读锁不能排在写锁前。
但什么时候加锁,锁哪些数据。要有相应的策略,因为加锁解锁也要系统开锁,策略不当会直接影响服务的性能。通常有两种策略:表锁和行锁。行锁较常用。
表锁
开销最小,因为它不用去找出某一行记录然后去给它加锁,而是直接把整个表加上锁标记。当用户对表进行写(插入,删除,更新)时,用户获得一个写锁,它会禁止其它任何用户的读和写操作。只有无人操作时,用户才能获得读锁。读锁和读锁之前不冲突。
行锁
行锁可以最大程度的支持并发,当然开销也更大。行锁只能在 InnoDB 和 Falcon 引擎中被实现。行锁是由引擎自己实现的,不是 MySQL 自身支持的。
多版本并发控制
大多事务型引擎除了用行级锁外,还用一种叫 “多版本并发控制MVCC”Multiversion Concurrency Control 的技术,和行锁关联使用。它不是MySQL 专用的,Oracle, PostSQL 及其它数据库也使用。
它是一种锁的变形,避免很多情况的加锁操作,大大降低系统开销。每种存储引擎实现MVCC的方式不一样。
InnoDB 实现方式
为每个数据行增加两行隐含值,用来记录行的创建时间及过期时间(删除时间,实际上是版本号)。每一行都存储了事件发生时的版本号,用来代替事件发生时的实际时间。开始一个新事务时,版本号自增。每个事务都会保存它在开始时的 “当前系统版本”的记录,而每个查询都会根据事务的版本号,检查每行数据的版本。
实例:
Select 时:
InnoDB 只找版本早于当前事务版本的行。这确保了事务读取的行都是在事务开始前已经存在的,或者由当前事务创建或修改的行。
且数据行的删除版本必须是未定义或者大于事务版本,这保证了事务读取的行在事务开始时是未被删除。
Insert 时: 为每个新增行记录当前系统版本号。
Delete 时: 将当前系统版本号作为行的删除标识。
Update 时: 为要更新的行建立一个拷贝,并在新的拷贝记录当前系统版本号。同时为更新前的旧行记录系统版本号作为删除标识。
保存额外的记录使其它事务的读取不必申请加锁,这使读操作变得更快,因为只需要按当前版本号去取相应的数据即可。但这种方式的缺点是存储引擎必须为每行都存储额外的数据,做更多检查,以及整理。
索引可以让查询锁定更少的行.InnoDB 只有在访问行的时候才会对其加锁,而索引能够减少访问的行数,从而减少锁的数量.
InnoDB 存储引擎在检索到数据并返回给服务器层后,MySQL 服务器才能应用 where 语句,而InnoDB 的行锁机制是引擎实现的,而不是 MySQL 服务器.
所以,在返回所有数据时,这些行就已经锁定了,当 MySQL 服务器过滤掉不需要的数据后,这些行才会被引擎释放锁.