在事务相关话题中,已经提到事务隔离性依靠锁机制实现的。在本篇中围绕着InnoDB与MyISAM锁机制的不同展开,进而描述锁的实现方式,多种锁的概念,以及死锁产生的原因。

 
Mysql常用存储引擎的锁机制

MyISAM和MEMORY采用表级锁(table-level locking);

BDB采用页面锁(page-leve locking)或表级锁,默认为页面锁;

InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁;

各种锁特点


表级锁(table-level locking):开销小,加锁快;不会出现死锁;锁定粒度大,发生冲突的概率最高,并发度最低

行级锁(row-level locking):开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高

页面锁(page-level locking):开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般

 

锁的算法


InnoDB存储引擎有3种行所算法设计,分别是:

  • Record Lock:单个行记录上的锁
  • Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
  • Next-key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身

Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表建立的时候没有设置任何一个索引,这时InnodB存储引擎会使用隐式的主键来进行锁定,在Repeatable Read隔离级别下,Next-key Lock 算法是默认的行记录锁定算法。

阻塞、系统互斥量(mutex)和信号量(event)


因为不同锁之间兼容性关系,一个事务中的锁需要等待另一个事务中的锁释放它所占用的资源。在InnoDB存储引擎的源代码中,用Mutex数据结构来实现锁。在访问资源前需要用mutex_enter函数进行申请,在资源访问或修改完毕后立即执行mutex_exit函数。当一个资源已被一个事务占有时,另一个事务执行mutex_enter函数会发生等待,这就是阻塞。阻塞并不是一件坏事,阻塞是为了保证事务可以并发并且正常运行。

在InnoDB引擎当中,封装了操作系统提供的基本mutex(互斥量)和event(信号量),在WINDOWS下的实现暂时不做记录,主要还是对支持POSIX系统来做介绍。在POSIX系统的实现是os_fast_mutex_t和os_event_t。os_fast_mutex_t相对简单,其实就是pthread_mutex。定义如下:

  1. typedef pthread_mutex os_fast_mutex_t;
而os_event_t相对复杂,它是通过os_fast_mutex_t和一个pthread_cond_t来实现的,定义如下:
  1. typedef struct os_event_struct
  2. {
  3. os_fast_mutex_t os_mutex;
  4. ibool is_set;
  5. pthread_cond_t cond_var;
  6. }os_event_t;
 

以下是os_event_t的两线程信号控制的例子流程:

 

对于系统的封装,最主要的就是os_event_t接口的封装,而在os_event_t的封装中,os_event_set、os_event_reset、os_event_wait这三 个方法是最关键的。

 

CAS原子操作


在InnoDB的mutex(互斥量)的实现中,除了引用系统的os_mutex_t以外,还使用了原子操作来进行封装一个高效的mutex实现。在系统支持原子操作的情况下,会采用自己封装的mutex来做互斥,如果不支持,就使用os_mutex_t。
因为我对于操作系统编程不熟,在此用Java的util.concurrent.atomic包举例,其封装了一些具有原子性操作的类,大量的利用了类似于mutex(互斥量)的操作。
CAS原子操作——Compare & Set,自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时才能进入临界区。
  1. /**
  2. * Atomically updates the current value with the results of
  3. * applying the given function, returning the previous value. The
  4. * function should be side-effect-free, since it may be re-applied
  5. * when attempted updates fail due to contention among threads.
  6. *
  7. * @param updateFunction a side-effect-free function
  8. * @return the previous value
  9. * @since 1.8
  10. */
  11. public final int getAndUpdate(IntUnaryOperator updateFunction) {
  12. int prev, next;
  13. do {
  14. prev = get();
  15. next = updateFunction.applyAsInt(prev);
  16. } while (!compareAndSet(prev, next));
  17. return prev;
  18. }
  19. /**
  20. * Atomically sets the value to the given updated value
  21. * if the current value {@code ==} the expected value.
  22. *
  23. * @param expect the expected value
  24. * @param update the new value
  25. * @return {@code true} if successful. False return indicates that
  26. * the actual value was not equal to the expected value.
  27. */
  28. public final boolean compareAndSet(int expect, int update) {
  29. return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
  30. }
 

InnoDB存储引擎中的锁


InnoDB是一个多线程并发的引擎,内部的读写都是用多线程来实现的,所以InnoDB内部实现了一个比较高级的并发同步机制。InnoDB并没有直接使用系统提供的锁(latch)同步结构,而是对其进行自己的封装和实现优化,但是也兼容系统的锁。我们先看一段InnoDB内部的注释(MySQL-3.23):

Semaphore operations in operating systems are slow: Solaris on a 1993 Sparc takes 3 microseconds (us) for a lock-unlock pair and Windows NT on a 1995 Pentium takes 20 microseconds for a lock-unlock pair. Therefore, we have toimplement our own efficient spin lock mutex. Future operating systems mayprovide efficient spin locks, but we cannot count on that.

大概意思是说1995年的时候,一个Windows NT的 lock-unlock所需要耗费20us,即使是在Solaris 下也需要3us,这也就是他为什么要实现自定义latch的目的,在innodb中作者实现了系统latch的封装、自定义mutex和自定义 rw_lock。
InnoDB存储引擎实现了以下两种标准的行机锁:

共享锁(S Lock):允许事务读一行数据。
排他锁(X Lock):允许事务删除或者更新一行数据。
 
未完待续...
 
 
参考文献:

《MySQL技术内幕InnoDB存储引擎》

MySQL学习笔记-锁相关话题的更多相关文章

  1. MySQL学习笔记-事务相关话题

    事务机制 事务(Transaction)是数据库区别于文件系统的重要特性之一.事务会把数据库从一种一致状态转换为另一个种一致状态.在数据库提交工作时,可以确保其要么所有修改都已经保存了,要么所有修改都 ...

  2. MySQL学习笔记-数据库文件

    数据库文件 MySQL主要文件类型有如下几种 参数文件:my.cnf--MySQL实例启动的时候在哪里可以找到数据库文件,并且指定某些初始化参数,这些参数定义了某种内存结构的大小等设置,还介绍了参数类 ...

  3. MySQL学习笔记-数据库内存

    数据库内存 InnoDB存储引擎内存由以下几个部分组成:缓冲池(buffer pool).重做日志缓冲池(redo log buffer)以及额外的内存池(additional memory pool ...

  4. MySQL学习笔记-数据库后台线程

    数据库后台线程 默认情况下讲述的InnoDB存储引擎,以后不再重复声明.后台线程有7个--4个IO thread,1个master thread,1个锁监控线程,1个错误监控线程.IO thread的 ...

  5. MySQL学习笔记-cache 与 buffer

    Cache和Buffer是两个不同的概念,简单的说,Cache是加速"读",而 buffer是缓冲"写",前者解决读的问题,保存从磁盘上读出的数据,后者是解决写 ...

  6. MySQL学习笔记-大纲

    软件程序性能测试在之前<品味性能之道>系列中已经大量提到,讲解了很多测试方法.测试观念.测试思想等等.最近准备深入MySQL进行学习并总结.分别查阅<MySQL性能调优与架构设计&g ...

  7. MySQL学习笔记-MySQL体系结构总览

    MySQL体系结构总览 不管是用哪种数据库,了解数据库的体系结构都是极为重要的.MySQL体系结构主要由数据库和数据库实例构成. 数据库:物理操作系统文件或者其它文件的集合,在mysql中,数据库文件 ...

  8. 一千行MySQL学习笔记 (转)

    出处:  一千行MySQL学习笔记 /* 启动MySQL */ net start mysql /* 连接与断开服务器 */ mysql -h 地址 -P 端口 -u 用户名 -p 密码 /* 跳过权 ...

  9. mysql basic operation,mysql总结,对mysql经常使用语句的详细总结,MySQL学习笔记

    mysql> select * from wifi_data where dev_id like "0023-AABBCCCCBBAA" ; 1.显示数据库列表.show d ...

随机推荐

  1. Redis安装完后redis-cli无法使用(redis-cli: command not found)已使用

    wget http://download.redis.io/redis-stable.tar.gz(下载redis-cli的压缩包) tar xvzf redis-stable.tar.gz(解压) ...

  2. 2018面向对象程序设计(Java)第3周学习指导及要求

    2018面向对象程序设计(Java) 第3周学习指导及要求(2018.9.11-2018.9.16)   学习目标 适应老师教学方式,能按照自主学习要求完成本周理论知识学习: 掌握Java Appli ...

  3. ssh架构之hibernate(三)关系映射

    1.单向多对一 1.映射文件配置 2.model: 测试 1.查询测试 执行顺序,先查询多方,在查询一方,一方采用延迟加载 注意:如果不使用一方的数据,就关闭session,报错,同延迟加载中的报错类 ...

  4. Shiro在Spring session管理

    会话管理 在shiro里面可以发现所有的用户的会话信息都会由Shiro来进行控制,那么也就是说只要是与用户有关的一切的处理信息操作都可以通过Shiro取得,实际上可以取得的信息可以有用户名.主机名称等 ...

  5. Dubbo启动过程(Spring方式)详解

    一.使用Spring xml配置方式的启动过程 1. 解析XML,注册Bean 2. 监听Spring事件 3. 启动或关闭dubbo 二.详细过程 1. 解析XML,注册Bean 利用Spring提 ...

  6. SXSSExcelUtil

    package com.numa.util; import org.apache.poi.hssf.usermodel.HSSFCell;import org.apache.poi.hssf.util ...

  7. python找包的路径(找不到自定义包的问题解决)

    问题:工程下自定义的包,python在执行时经常找不到包   python找包的路径:python安装路径下的lib包和PYTHONPATH下的包     可以使用[sys.path]打印出pytho ...

  8. 【Django】关于ORM的使用

    添加模型并映射到数据库中: 1. 在 settings.py 中,配置好 DATABASES ,做好数据库相关的配置. 以mysql为例: DATABASES = { 'default': { 'EN ...

  9. 调整数组顺序使奇数位于偶数前面(python)

    题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. # -*- codi ...

  10. DBCP连接数据库了解一下

    ---恢复内容开始--- package com.kevin.Utils; import javax.sql.DataSource; import org.apache.commons.dbcp.Ba ...