innodb引擎中的锁分两种

1)针对数据结构, 如链表

互斥锁

读写锁

http://mysqllover.com/?p=425

http://www.cnblogs.com/justfortaste/p/3668935.html

2)针对数据表中记录

行锁

表锁

innodb中的互斥锁是依据操作系统中的spin lock自旋锁,进行一些修改而成的

修改方面:

  1.当获得不到锁时, 一直在CPU高速缓冲区中读取值,避免与内存打交道,以免造成总线风暴

2.当自旋超过20us后,将线程放入wait array,待时机成熟后再唤醒,而不是放入系统的等待队列,避免上下文切换

详情请见 http://www.cnblogs.com/taek/p/4809685.html

这里主要分析下innodb的行锁

当执行sql 如 update user set name='xx' where id=1;时

innodb引擎会针对id=1这条记录加一个LOCK_REC行锁

这个锁并没有附在物理记录本身,而是存储于单独一个结构体中

对于行锁,先尝试加快锁,如果不行,再加慢锁

/*********************************************************************//**
Tries to lock the specified record in the mode requested. If not immediately
possible, enqueues a waiting lock request. This is a low-level function
which does NOT look at implicit locks! Checks lock compatibility within
explicit locks. This function sets a normal next-key lock, or in the case
of a page supremum record, a gap type lock.
@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, DB_DEADLOCK,
or DB_QUE_THR_SUSPENDED */ /**尝试用某种请求的锁,去锁住指定的行记录,如果不能立即上锁,将等待的锁入队列。
该函数级别较低,不能看做隐式锁,检查该锁与显示锁的相容性。该功能设置常规的next-key lock,
或为页面的supremum record设置gap lock
*/
static
enum db_err
lock_rec_lock(
/*==========*/
ibool impl, /*!< in: if TRUE, no lock is set
if no wait is necessary: we
assume that the caller will
set an implicit lock */
ulint mode, /*!< in: lock mode: LOCK_X or LOCK_S possibly ORed to either
          LOCK_GAP or LOCK_REC_NOT_GAP */
const buf_block_t* block, /*!< in: buffer block containing the record */ //记录所在的block块
ulint heap_no, /*!< in: heap number of record */
dict_index_t* index, /*!< in: index of record */
que_thr_t* thr) /*!< in: query thread */
{
/* We try a simplified and faster subroutine for the most
common cases */
switch (lock_rec_lock_fast(impl, mode, block, heap_no, index, thr)) {
case LOCK_REC_SUCCESS:
return(DB_SUCCESS);
case LOCK_REC_SUCCESS_CREATED:
return(DB_SUCCESS_LOCKED_REC);
case LOCK_REC_FAIL:
return(lock_rec_lock_slow(impl, mode, block,
heap_no, index, thr));
}
return(DB_ERROR);
}

加快锁

UNIV_INLINE
enum lock_rec_req_status
lock_rec_lock_fast(
/*===============*/
ibool impl, /*!< in: if TRUE, no lock is set
if no wait is necessary: we
assume that the caller will
set an implicit lock */
ulint mode, /*!< in: lock mode: LOCK_X or
LOCK_S possibly ORed to either
LOCK_GAP or LOCK_REC_NOT_GAP */
const buf_block_t* block, /*!< in: buffer block containing
the record */
ulint heap_no,/*!< in: heap number of record */
dict_index_t* index, /*!< in: index of record */
que_thr_t* thr) /*!< in: query thread */
{
lock_t* lock; //详见 结构体
trx_t* trx; //详见 结构体
   lock = lock_rec_get_first_on_page(block); //根据block,取出space,page_no构成hash,取出相应值,详见 trx = thr_get_trx(thr); if (lock == NULL) {
if (!impl) {//显示锁
lock_rec_create(mode, block, heap_no, index, trx); //如果为空,则创建锁, 并返回加锁成功,详见
} return(LOCK_REC_SUCCESS_CREATED);
}

//不清楚为什么要取下一个lock记录
if (lock_rec_get_next_on_page(lock)) { return(LOCK_REC_FAIL);
}
  
/**
*锁的事务不是当前事务
*或者锁的类型不是 mode|LOCK_REC (不包含行锁)
*lock_rec_get_n_bits返回lock->un_member.rec_lock.n_bits,这个n_bits可以理解为page中记录个数,对应的byte是n_bits/8+1
*例如 page中只有5条记录 5/8+1=1 只需要1+64=65字节即可表示这此记录
     *如果heap_no大于上面的n_bits,意味着 heap_no不能存放到由n_bits构造的二维数组中(该数组其实是n_bits/8+1)
*返回failed
*/
if (lock->trx != trx
|| lock->type_mode != (mode | LOCK_REC)
|| lock_rec_get_n_bits(lock) <= heap_no) { //详见 return(LOCK_REC_FAIL);
} if (!impl) {
/* If the nth bit of the record lock is already set then we
do not set a new lock bit, otherwise we do set */ if (!lock_rec_get_nth_bit(lock, heap_no)) {
lock_rec_set_nth_bit(lock, heap_no);
return(LOCK_REC_SUCCESS_CREATED);
}
} return(LOCK_REC_SUCCESS);
}

慢加锁

static
enum db_err
lock_rec_lock_slow(
/*===============*/
ibool impl, /*!< in: if TRUE, no lock is set
if no wait is necessary: we
assume that the caller will
set an implicit lock */
ulint mode, /*!< in: lock mode: LOCK_X or
LOCK_S possibly ORed to either
LOCK_GAP or LOCK_REC_NOT_GAP */
const buf_block_t* block, /*!< in: buffer block containing
the record */
ulint heap_no,/*!< in: heap number of record */
dict_index_t* index, /*!< in: index of record */
que_thr_t* thr) /*!< in: query thread */
{
trx_t* trx;
lock_t* lock; trx = thr_get_trx(thr);
/**
*根据block计算出space_id和page_no,再根据这两个值计算出hash值
*通过hash值在lock_sys->rec_hash这个HashTable中查询,当找到的头结点不符号条件,则遍历
*如果HashTable中某一个锁权限大于预加锁的权限时,直接返回

*
*如果HashTable中的某一个锁权限小于预加锁权限时,再判断兼容性
*例如在HashTable中找到一个lock,其mode为S
*预加锁的mode为X,S强度要小于X
*
*锁之间兼容性判断
*预加锁的权限 是否 兼容 HashTable中根据条件找到的lock的权限
*例如:
*预加锁权限 LOCK_X
*找到的lock 权限 LOCK_S
*经查找兼容矩阵:
* 不兼容:新建lock,其mode为 LOCK_X|LOCK_WAIT
* 兼容: 新建lock,其mode为LOCK_X|LOCK_REC */
lock = lock_rec_has_expl(mode, block, heap_no, trx); //函数实现    if (lock) {
if (lock->type_mode & LOCK_CONV_BY_OTHER) { //#define LOCK_CONV_BY_OTHER 4096
/* This lock or lock waiting was created by the other
transaction, not by the transaction (trx) itself.
So, the transaction (trx) should treat it collectly
according as whether granted or not. */ if (lock->type_mode & LOCK_WAIT) { //#define LOCK_WAIT 256
/* This lock request was not granted yet.
Should wait for granted. */ goto enqueue_waiting;
} else {
/* This lock request was already granted.
Just clearing the flag. */ lock->type_mode &= ~LOCK_CONV_BY_OTHER;
}
} /* The trx already has a strong enough lock on rec: do
nothing */
} else if (lock_rec_other_has_conflicting(mode, block, heap_no, trx)) {
//如果出现冲突,即不兼容,则创建一个新lock,且锁模式为 预加锁模式+ lock_wait
/* If another transaction has a non-gap conflicting request in
the queue, as this transaction does not have a lock strong
enough already granted on the record, we have to wait. */ ut_ad(lock == NULL);
enqueue_waiting:
return(lock_rec_enqueue_waiting(mode, block, heap_no,ock, index, thr)); //函数实现
} else if (!impl) {
/* Set the requested lock on the record */ lock_rec_add_to_queue(LOCK_REC | mode, block,heap_no, index, trx); //锁模式加上行锁 函数实现    return(DB_SUCCESS_LOCKED_REC);
} return(DB_SUCCESS);
}

检查已存在的lock的模式是否与欲加锁的模式有冲突,返回true 说明有冲突

static
lock_t*
lock_rec_other_has_conflicting(
/*===========================*/
enum lock_mode mode, /*!< in: LOCK_S or LOCK_X,
possibly ORed to LOCK_GAP or
LOC_REC_NOT_GAP,
LOCK_INSERT_INTENTION */
const buf_block_t* block, /*!< in: buffer block containing
the record */
ulint heap_no,/*!< in: heap number of the record */
trx_t* trx) /*!< in: our transaction */
{
lock_t* lock;lock = lock_rec_get_first(block, heap_no);
if (UNIV_LIKELY_NULL(lock)) {
if (UNIV_UNLIKELY(heap_no == PAGE_HEAP_NO_SUPREMUM)) {
do {
if (lock_rec_has_to_wait(trx, mode, lock,
TRUE)) {
return(lock);
}
lock = lock_rec_get_next(heap_no, lock);
} while (lock);
} else {
do {
if (lock_rec_has_to_wait(trx, mode, lock,
FALSE)) {
return(lock);
}
lock = lock_rec_get_next(heap_no, lock);
} while (lock);
}
} return(NULL);
}
ibool
lock_rec_has_to_wait(
/*=================*/
const trx_t* trx, /*!< in: trx of new lock */
ulint type_mode,/*!< in: precise mode of the new lock
to set: LOCK_S or LOCK_X, possibly
ORed to LOCK_GAP or LOCK_REC_NOT_GAP,
LOCK_INSERT_INTENTION */
const lock_t* lock2, /*!< in: another record lock; NOTE that
it is assumed that this has a lock bit
set on the same record as in the new
lock we are setting */
ibool lock_is_on_supremum) /*!< in: TRUE if we are setting the
lock on the 'supremum' record of an
index page: we know then that the lock
request is really for a 'gap' type lock */
{
ut_ad(trx && lock2);
ut_ad(lock_get_type_low(lock2) == LOCK_REC); if (trx != lock2->trx
&& !lock_mode_compatible(LOCK_MODE_MASK & type_mode,lock_get_mode(lock2))) { //检查锁与锁之间的兼容性, 详情 /* We have somewhat complex rules when gap type record locks
cause waits */ if ((lock_is_on_supremum || (type_mode & LOCK_GAP))&& !(type_mode & LOCK_INSERT_INTENTION)) { /* Gap type locks without LOCK_INSERT_INTENTION flag
do not need to wait for anything. This is because
different users can have conflicting lock types
on gaps. */ return(FALSE);
} if (!(type_mode & LOCK_INSERT_INTENTION)
&& lock_rec_get_gap(lock2)) { /* Record lock (LOCK_ORDINARY or LOCK_REC_NOT_GAP
does not need to wait for a gap type lock */ return(FALSE);
} if ((type_mode & LOCK_GAP)
&& lock_rec_get_rec_not_gap(lock2)) { /* Lock on gap does not need to wait for
a LOCK_REC_NOT_GAP type lock */ return(FALSE);
} if (lock_rec_get_insert_intention(lock2)) { /* No lock request needs to wait for an insert
intention lock to be removed. This is ok since our
rules allow conflicting locks on gaps. This eliminates
a spurious deadlock caused by a next-key lock waiting
for an insert intention lock; when the insert
intention lock was granted, the insert deadlocked on
the waiting next-key lock. Also, insert intention locks do not disturb each
other. */ return(FALSE);
} return(TRUE);
} return(FALSE);
}

innodb 源码分析 --锁的更多相关文章

  1. InnoDB源码分析--缓冲池(三)

    转载请附原文链接:http://www.cnblogs.com/wingsless/p/5582063.html 昨天写到了InnoDB缓冲池的预读:<InnoDB源码分析--缓冲池(二)> ...

  2. InnoDB源码分析--缓冲池(二)

    转载请附原文链接:http://www.cnblogs.com/wingsless/p/5578727.html 上一篇中我简单的分析了一下InnoDB缓冲池LRU算法的相关源码,其实说不上是分析,应 ...

  3. InnoDB源码分析--事务日志(一)

    原创文章,转载请注明原文链接(http://www.cnblogs.com/wingsless/p/5705314.html) 在之前的文章<InnoDB的WAL方式学习>(http:// ...

  4. innoDB源码分析--缓冲池

    最开始学Oracle的时候,有个概念叫SGA和PGA,是非常重要的概念,其实就是内存中的缓冲池.InnoDB的设计类似于Oracle,也会在内存中开辟一片缓冲池.众所周知,CPU的速度和磁盘的IO速度 ...

  5. JAVA源码分析------锁(1)

    http://870604904.iteye.com/blog/2258604 第一次写博客,也就是记录一些自己对于JAVA的一些理解,不足之处,请大家指出,一起探讨. 这篇博文我打算说一下JAVA中 ...

  6. InnoDB源码分析--事务日志(二)

    原创文章,转载请标明原文链接:http://www.cnblogs.com/wingsless/p/5708992.html 昨天写了有关事务日志的一些基本点(http://www.cnblogs.c ...

  7. MySQL系列:innodb源码分析之内存管理

    http://blog.csdn.net/yuanrxdu/article/details/40985363 http://book.2cto.com/201402/40307.html 从MySQL ...

  8. MySQL系列:innodb源码分析 图 ---zerok的专栏

    http://blog.csdn.net/yuanrxdu/article/details/40985363

  9. AtomicInteger源码分析——基于CAS的乐观锁实现

    AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...

随机推荐

  1. [工作中的设计模式]备忘录模式memento

    一.模式解析 备忘录对象是一个用来存储另外一个对象内部状态的快照的对象.备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把 ...

  2. Javascript创建对象的学习和使用

    <html> <head> <meta charset="utf-8"> <title>javascript对象的学习</ti ...

  3. Hive常用命令

    本位为转载,原地址为:http://www.cnblogs.com/BlueBreeze/p/4232421.html #创建新表 hive> CREATE TABLE t_hive (a in ...

  4. C# 获取某月的第一天和最后一天

    /// <summary>        /// 取得某月的第一天        /// </summary>        /// <param name=" ...

  5. 链表的倒数第K个节点

    题目:输入一个链表,输出该链表中倒数第K个节点.为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个结点. package com.edu; class LinkNode{ //定义一 ...

  6. 1920-Jangbi的Rush

    描述 最后一届的OSL决赛由神族的Jangbi对阵人族Fantasy.Jangbi5BG爆叉叉准备一波rush,但是范特西早有防备,在地图上埋下了许多地雷.但是Jangbi显然不是毕姥爷那样的无脑平A ...

  7. (转)ObjC利用正则表达式抓取网页内容(网络爬虫)

    转自:http://www.cocoachina.com/bbs/read.php?tid=103813 *****boy]原创 2012年5月20日 在开发项目的过程,很多情况下我们需要利用互联网上 ...

  8. Zencart 500错误查找和解决方法

    有时在一些虚拟主机下(如HostMonster,BlueHost),Zencart网站会莫名奇妙输出空白页面,查看HTTP头,其实可以看到是500错误.至于500错误的出现原因,一般是由于服务器脚本或 ...

  9. 关于xml文档使用出现的错误及其解决方法

    在学习xml的时候在运行下面程序时候出现了错误,很是烦恼,翻遍了许多博客都没有找到方法,终于在一个不起眼的地方找到了解决方法,很高兴记录下来 运行的程序如下: import xml.etree.Ele ...

  10. Android基础_web通信2

    一.移动客服端实现对PC端数据的操作 在PC端模拟一个数据库,实现用户的增删改查,然后在移动客服端实现对PC端数据库的操作 在PC端建立三个表 用户表(Users),员工表(Emp), 部门表(Dep ...