Innodb 锁系列2 事务锁

上一篇介绍了Innodb的同步机制锁:Innodb锁系列1

这一篇介绍一下Innodb的事务锁,只所以称为事务锁,是因为Innodb为实现事务的ACID特性,而添加的表锁或者行级锁。

这一部分分两篇来介绍,先来介绍下事务锁相关的数据结构

事务锁数据结构

1. 锁模式

/* Basic lock modes */
enum lock_mode {
    LOCK_IS = 0,    /* intention shared */
    LOCK_IX,    /* intention exclusive */
    LOCK_S,        /* shared */
    LOCK_X,        /* exclusive */
    LOCK_AUTO_INC,    /* locks the auto-inc counter of a table in an exclusive mode */
    LOCK_NONE,    /* this is used elsewhere to note consistent read */
    LOCK_NUM = LOCK_NONE/* number of lock modes */
};

锁的模式包括两大类,共享锁(S)和互斥锁(X),s/x锁可以加在表上或者行记录上,

比如:

  session1:alter table: table类型的X锁

  session2:select * from table where id =1 for update: record类型的X锁

因为session1和session2互斥,为了实现在table表上阻塞,所以session2增加了表锁(IS),这样session2无法获取IS锁而阻塞。

LOCK_AUTO_INC:自增锁,为了保证在多行插入的时候,自增键值是连续的。

2. 表锁和记录锁数据结构

--表锁
struct lock_table_struct {
    dict_table_t*    table;                /*!< database table in dictionary cache */
    UT_LIST_NODE_T(lock_t) locks;        /*!< list of locks on the same table */
};
--行锁
struct lock_rec_struct {
    ulint    space;            /*!< space id */
    ulint    page_no;        /*!< page number */
    ulint    n_bits;            /*!< number of bits in the lock
                    bitmap; NOTE: the lock bitmap is
                    placed immediately after the
                    lock struct */
};
--锁记录struct lock_struct {
    trx_t*        trx;        /*!< transaction owning the lock */
    UT_LIST_NODE_T(lock_t)
            trx_locks;    /*!< list of the locks of the transaction */
    ulint        type_mode;    /*!< lock type, mode, LOCK_GAP or
                    LOCK_REC_NOT_GAP,
                    LOCK_INSERT_INTENTION,
                    wait flag, ORed */
    hash_node_t    hash;        /*!< hash chain node for a record            lock */
    dict_index_t*    index;        /*!< index for a record lock */
    union {
        lock_table_t    tab_lock;/*!< table lock */
        lock_rec_t    rec_lock;/*!< record lock */
    } un_member;            /*!< lock details */
};

表锁:使用表的数据字典进行标示,并保持一个链表,记录table上的所有锁记录。

记录锁:记录锁使用space+page_no唯一进行标示一个page页,n_bits标示这个页的记录,1表示加锁。

锁记录:lock_t表示一个锁记录,事务锁永远都和trx关联,trx_locks表示这个事务的所有锁记录。

3. 全局结构

  

/** The lock system struct */
struct lock_sys_struct{
    hash_table_t*    rec_hash;    /*!< hash table of the record locks */
    ulint        rec_num;
};

/** The lock system */
extern lock_sys_t*    lock_sys;

Innodb建立了一个全局的hash表,所有事务添加的锁,都记录在全局的事务表中,并使用record的space+page_no进行hash bucket的计算。入下图所示:

对于不同的space+page_no,通过hash key计算,映射到不同的bucket上,对于比如两条不同的update语句更新同一条记录的时候,不同的session,分别创建lock_t并加入到node1和node2中。这样就实现了所有的事务锁维护在hash+link的结构中。

不同的bucket互不影响,所有的wait,signal都通过相同的bucket下的链表来实现。

我们可以看一个函数就比较清楚了:

通过扫描hash结构+link结构来计算当前系统上有多少lock_t.lock_get_n_rec_locks(void)
{
    lock_t*    lock;
    ulint    n_locks    = 0;
    ulint    i;
    ut_ad(mutex_own(&kernel_mutex));
    for (i = 0; i < hash_get_n_cells(lock_sys->rec_hash); i++) {
        lock = HASH_GET_FIRST(lock_sys->rec_hash, i);
        while (lock) {
            n_locks++;
            lock = HASH_GET_NEXT(hash, lock);
        }
    }
    return(n_locks);
}

注:所有的lock_t统计信息,以及锁的相容性测试对象,wait和signal的操作,都是通过这个全局结构进行实现的。

4. 锁的gap模式

  #define LOCK_ORDINARY 0

  #define LOCK_GAP 512

  #define LOCK_REC_NOT_GAP 1024

行级锁实现的gap模式:

  1. record lock:记录锁,根据索引记录锁主单行记录

  2. gap lock:gap锁,锁住一个记录的区间

  3. next-gap lock:包含了记录锁和gap锁

锁的gap模式主要为了实现以下:

  1. 丢失更新:对于多事务更新相同的记录,需要记录锁保证事务间的互斥,防止丢失更新。

  2. 可重复读:在read repeatable的事务隔离级别下,需要锁住记录的gap,来保证可重复读。

5. 隐式锁和显式锁

  对于cluster index的记录锁而言,如select * from table where id=1 for update而言,会显示的在id=1的聚簇索引上添加行级锁。

如果是update语句,除了在cluster_index上添加锁以为,还需要在二级索引上添加锁。

但cluster index添加的是显示的锁,而二级索引使用隐式锁。

隐式锁: 是一种延迟的加锁方式,毕竟加锁是有开销的,所以使用一种乐观的方式,因为主键索引记录上有trx_id,如果判断主键记录上的trx_id是活动的事务,那么才加显示的锁。

    这样,最大化的减少记录加锁的机会。

下一篇介绍具体的加锁过程:

Innodb 锁系列2 事务锁,布布扣,bubuko.com

时间: 2024-10-27 10:31:10

Innodb 锁系列2 事务锁的相关文章

MySQL锁系列2 表锁

上一篇介绍了MySQL源码中保护内存结构或变量的锁,这里开始介绍下MySQL事务中的表锁. 注1: 在表锁的实现上面,使用[mutex+condition+queue]的结构实现并发,阻塞,唤醒的表锁功能. 注2: 本文进行的一些实验,重要的配置项: 1. autocommit=0 2. tx_isolation=read-commited 3. engine=innodb 1. MySQL加锁的顺序: 这里包括了一个sql在执行的过程中,包括commit,所添加和释放跟事务相关的锁以及加不同锁

MySQL锁系列3 MDL锁

MySQL为了保护数据字典元数据,使用了metadata lock,即MDL锁,保证在并发的情况下,结构变更的一致性. MDL锁的加锁模式和源码上的组织上和上一篇blog中MySQL表锁的实现方式一致,都采用了[mutex+condition+queue]来实现并发,阻塞,唤醒的控制. 下面就来看看MDL锁: 1. 重要的数据结构:    1. MDL_map mdl_map使用hash表,保存了MySQL所有的mdl_lock,全局共享,使用MDL_KEY作为key来表,key=[db_nam

mysql 中的 latch锁和Tlock(事务锁), DML加锁规则,以及死锁分析。

一.Latch和Tlock的关系 Latch:为保护临界资源的正确性而设计,例如保护正在使用的内存页面不被破坏等. 没有死锁检测机制,轻量锁,并且作用对象时内存页面或是内存共享变量. Tlock:事务锁,作用对象是事务,有死锁检测机制. 在innodb内部,为了减少死锁的发生概率,Latch不会等待Tlock. 线程获取行锁的流程: 在对行加锁的时候会先对行所在的页面添加lath,然后再对行添加Tlock,待对行添加完Tlock后再释放页面的Lath. 这种机制主要是为了保证线程获取的行数据的一

innodb事务锁

计算机程序锁 控制对共享资源进行并发访问 保护数据的完整性和一致性 lock  主要是事务,数据库逻辑内容,事务过程 latch/mutex 内存底层锁: 更新丢失 原因: B的更改还没有提交时,A已经再次修改了数据. 此时A使用原来的元数据作为基础更新后,B的更新便会丢失: 解决办法: 在修改数据上加写锁,当有锁时,A会等B更新提交完,才可以继续在B的基础上继续更新: 事务锁粒度 行锁: innodb ,oracle 页锁:sql server 表锁:Myisam ,memory 获取inno

Innodb 锁系列1

同步机制 Innodb实现了不依赖于平台的mutex,rwlock. 1. 全局变量 对于mutex, rwlock,都有一个全局链表. 1. mutex全局链表:mutex_list 2. rwlock全局链表:rw_lock_list 在create创建mutex 或者rwlock的时候,加入链表. 在delete销毁mutex或者rwlock的时候,从链表中删除. 3. 全局等待数组: sync_primary_wait_array = sync_array_create(OS_THREA

【锁】Oracle锁系列

[锁]Oracle锁系列 1  BLOG文档结构图 2  前言部分 2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① 锁的概念.分类.及其模拟 ② 查询锁的视图及视图之间的关联 ③ 锁的参数(DML_LOCKS.DDL_LOCK_TIMEOUT) ④ FOR UPDATE及FOR UPDATE OF系列 ⑤ 带ONLINE和不带ONLINE创建索引的锁情况(是否阻塞DML操作) ⑥ 包或存过不能编译的解决方法

mysql中InnoDB存储引擎的行锁和表锁

Mysql的InnoDB存储引擎支持事务,默认是行锁.因为这个特性,所以数据库支持高并发,但是如果InnoDB更新数据的时候不是行锁,而是表锁的话,那么其并发性会大打折扣,而且也可能导致你的程序出错. 而导致行锁变为表锁的情况之一就是: SQL的更新(update)或者删除(delete)语句中未使用到索引,导致在InnoDB在对数据进行相应操作的时候必须把整个表锁起来进行检索(表锁).而如果使用了索引的话,InnoDB只会通过索引条件检索数据,而只锁住索引对应的行(行锁). 下面记录一下我遇到

MySQL 温故而知新--Innodb存储引擎中的锁

近期碰到非常多锁问题.所以攻克了后,细致再去阅读了关于锁的书籍,整理例如以下:1,锁的种类 Innodb存储引擎实现了例如以下2种标准的行级锁: ? 共享锁(S lock),同意事务读取一行数据. ?  排它锁(X lock).同意事务删除或者更新一行数据. 当一个事务获取了行r的共享锁.那么另外一个事务也能够马上获取行r的共享锁,由于读取并未改变行r的数据.这样的情况就是锁兼容. 可是假设有事务想获得行r的排它锁,则它必须等待事务释放行r上的共享锁-这样的情况就是锁不兼容.二者兼容性例如以下表

MySQL锁系列1

MySQL的锁:MySQL内部有很多种类的锁,按照用途不同,可以分为两类:1. 保护内存结构的锁 server层对于线程共享的变量,基本上使用mutex,rwlock来做保护. innodb层会增加使用spinlock自旋锁 2. 提供或者保证事务性功能的锁 server提供了MDL锁,表锁两种锁 innodb实现行级锁 原子操作: 在单处理器系统(UniProcessor)结构中,在一个指令周期内完成的都是原子指令,因为硬中断的响应是在每一个cpu指令完成后check的. 而在多处理器结构(S