zoukankan      html  css  js  c++  java
  • MySQL锁篇

    1、MySQL锁介绍

    MyISAM和MEMORY存储引擎采取的是表级锁
    InnoDB既支持表级锁,又支持行级锁,但默认是行级锁。

    • 表级锁:开销小,加锁快,不会死锁。锁定粒度大,发生锁冲突的概率最高,并发度最低。
    • 行级锁:开销大,加锁慢,也会死锁。锁定粒度大,发生锁冲突的概率最高,并发度最低。
    • 页面锁:开销和加锁时间,锁定粒度,介于表级行级锁中间,也会死锁。并发度一般。

    表级锁适合以查询为主,只有少量按索引条件更新数据的应用。
    行级锁适合有大量按索引条件并发,更新少量数据,同时又有并发查询的应用。

    2、MySQL表级锁

    2.1、表级锁介绍

    由MySQL SQL layer层实现。

    MySQL的表级锁有两种:
    一种是表锁。一种是元数据锁(meta data lock,MDL)。

    show status like 'table%;
    

    -- able_locks_immediate:产生表级锁定的次数;
    -- able_locks_waited:出现表级锁定争用而发生等待的次数
    

    2.2、表锁介绍

    2.2.1 表现模式形式:

    表共享读锁(Table Read Lock)
    表独占写锁(Table Write Lock)
    

    2.2.2 手动增加表锁

    lock table 表名称 read(write),表名称2 read(write);
    

    2.2.3 查看表锁情况

    show open tables;
    

    2.2.4 删除表锁

    unlock table;
    

    2.3 表锁演示

    2.3.1环境准备

    -- 新建表
    CREATE TABLE mylock (
    	id int(11) NOT NULL AUTO_INCREMENT,
    	NAME varchar(20) DEFAULT NULL,
    	PRIMARY KEY (id));
    	INSERT INTO mylock (id,NAME) VALUES (1, 'a');
    	INSERT INTO mylock (id,NAME) VALUES (2, 'b');
    	INSERT INTO mylock (id,NAME) VALUES (3, 'c');
    	INSERT INTO mylock (id,NAME) VALUES (4, 'd');
    

    2.4 读锁演示

    2.4.1、表读锁

    session1(Navicat)、session2(mysql)

    1、session1: lock table mylock read; -- 给mylock表加读锁
    2、session1: select * from mylock; -- 可以查询
    3、session1:select * from tdep; --不能访问非锁定表
    4、session2:select * from mylock; -- 可以查询 没有锁
    5、session2:update mylock set name='x' where id=2; -- 修改阻塞,自动加行写锁
    6、session1:unlock tables; -- 释放表锁
    7、session2:update mylock set name='x' where id=2; -- 这次就修改执行完成
    8、session1:select * from tdep; --也可以访问
    

    2.4.2、表写锁

    1、session1: lock table mylock write; -- 给mylock表加写锁
    2、session1: select * from mylock; -- 实测不可以查询
    3、session1:select * from tdep; --不能访问非锁定表
    4、session1:update mylock set name='y' where id=2; --可以执行
    5、session2:select * from mylock; --别进程写锁, 查询阻塞
    6、session1:unlock tables; -- 释放表锁
    7、session2:select * from mylock; -- 别进程解锁后,才能查询
    8、session1:select * from tdep; --可以访问
    

    3、元数据锁 MDL (metaDataLock)

    作用是保护表的元数据信息一致性,解决或者保证DDL操作与DML操作之间的一致性。
    当对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁。

    1、session1: begin; --开启事务
       select * from mylock;--加MDL读锁
    2、session2: alter table mylock add f int; -- 修改表结构阻塞
    3、session1:commit; --提交事务 或者 rollback 释放读锁
    4、session2:alter table mylock add f int;  --修改完成
    

    4、行级锁

    4.1、行级锁介绍

    InnoDB存储引擎实现,行锁必须有索引才能实现,不然会自动锁全表,那么就不是行锁了。

    4.1.1 按锁定范围来说,分为三种:

    • 记录锁(Record Locks):锁定索引中一条记录。 主键指定 where id=3
    • 间隙锁(Gap Locks): 锁定记录前、记录中、记录后的行 RR隔离级 (可重复读)-- MySQL默认隔离级
    • Next-Key锁: 记录锁 + 间隙锁

    4.1.2 按功能来说,分为两种:

    共享读锁(S):当一个事务对某几行上读锁时,允许其他事务对这几行进行读操作。
    但不允许进行写操作,也不允许其他事务给这几行上排它锁,但允许上读锁。

    SELECT * FROM 表 WHERE 条件 LOCK IN SHARE MODE ;-- 共享读锁 手动添加
    

    排他写锁(X):一个事务对某几行上写锁时,其他事务允许读,但不能写,更不允许上锁。

    4.1.2.1、自动加 DML

    对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁

    4.1.2.2、手动加
    SELECT * FROM 表名 WHERE 条件 FOR UPDATE;
    

    示例:

    1.
    BEGIN
    select * from testdemo where id =1 for update
    在另外一个session中
    update testdemo set c1 = '1' where id = 2 成功
    update testdemo set c1 = '1' where id = 1 等待
    2.BEGIN
    update testdemo set c1 = '1' where id = 1
    在另外一个session中
    update testdemo set c1 = '1' where id = 1 等待
    3.
    BEGIN
    update testdemo set c1 = '1' where c1 = '1' 在另外一个session中
    update testdemo set c1 = '2' where c1 = '2' 等待
    

    4.2 行锁演示

    InnoDB行锁是通过给索引上的索引项加锁来实现的
    只有通过索引条件检索的数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁

    4.2.1 行读锁

    -- 查看行锁状态
    show STATUS like 'innodb_row_lock%';

    • Innodb_row_lock_current_waits:当前正在等待锁定的数量;
    • Innodb_row_lock_time:从系统启动到现在锁定总时间长度;
    • Innodb_row_lock_time_avg:每次等待所花平均时间;
    • Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花的时间;
    • Innodbrowlockwaits:系统启动后到现在总共等待的次;

    注:使用索引加行锁 ,未锁定的行可以访问

    总结:索引的行读锁,所有session的这个索引条件都锁死,修改不了。

    1、session1: begin;-- 开启事务未提交
                 select * from mylock where id=1 lock in share mode; -- 手动加id=1的行读锁,使用索引
    2、session2:update mylock set name='y' where id=2; -- 未锁定该行可以修改
    3、session2:update mylock set name='y' where id=1; -- 锁定该行修改阻塞 锁定超时
    4、session1:update mylock set name='y' where id=1; -- 锁定该行修改阻塞 锁定超时
    5、session1: commit; --提交事务 或者 rollback 释放读锁
    6、session2:update mylock set name='y' where id=1; --修改成功
    

    4.2.2 行读锁升级为表锁

    注:未使用索引行锁升级为表锁

    总结:非索引的行读锁,行锁失效,锁表,自己session所有条件啥影响都没,别session所有条件修改锁的死死。

    1、session1: begin;-- 开启事务未提交
                 select * from mylock where name='c' lock in share mode;-- 手动加name='c'的行读锁,未使用索引
    2、session2:update mylock set name='y' where id=2; -- 修改阻塞 未用索引行锁升级为表锁
    3、session1: commit; --提交事务 或者 rollback 释放读锁
    4、session2:update mylock set name='y' where id=2; --修改成功
    

    4.2.3 行写锁

    主键索引产生记录锁

    总结:索引键的行写锁,条件不失效,所有session查询都没影响,但别session加锁影响,当前session没

    1、session1: begin;--开启事务未提交
                 select * from mylock where id=1 for update;--手动加id=1的行写锁
    2、session2:select * from mylock where id=2 ; -- 可以读 不加锁
    3、session2: select * from mylock where id=1 ; -- 可以读 不加锁
    4、session2: select * from mylock where id=1 lock in share mode; --加读锁被阻塞
    5、session1: select * from mylock where id=1 lock in share mode; --不阻塞
    6、session1:commit; -- 提交事务 或者 rollback 释放写锁
    7、session2:执行成功
    

    4.2.3 间隙锁

    4.2.3.1 什么是间隙锁?

    间隙锁是一个在索引记录之间的间隙上的锁。当我们采用范围查询时,InnoDB会对这个范围内的数据进行加锁。

    示例1:
    比如有id为:1、3、5、7 的4条数据,我们查找1-7范围的数据。
    那么1-7都会被加上锁。2、4、6也在 1-7的范围中,但是不存在这些数据记录,这些 2、4、6就被称为间隙。

    示例2:
    我们向表中新增一条数据age=20,这条数据在本来是没有的,在insert还没有提交的时候查询age>15 and a.age<25,这个时候就会触发间隙锁,我们必须等待insert提交后才能执行select语句。

    4.2.3.2 为什么会出现间隙锁?

    在MySQL的innoDB引擎中,如果操作的是一个区间的数据,会锁住这个区间所有的记录。
    即使这个记录不存在,这个时候另一个会话去插入这个区间的数据,就必须等待上一个结束。

    注:对于主键索引或唯一索引,如果where条件不能全命中,也会加间隙锁,全命中则加记录锁。

    间隙锁的作用:
    保证某个间隙内的数据在锁定情况下不会发生任何变化。
    当使用唯一索引来搜索唯一行的语句时,不需要间隙锁定。

    4.2.4死锁

    两个 session 互相等等待对方的资源释放之后,才能释放自己的资源,造成了死锁.

    1、session1: begin;-- 开启事务未提交
    update mylock set name='m' where id=1;-- 手动加行写锁 id=1 ,使用索引
    2、session2:begin;-- 开启事务未提交
    update mylock set name='m' where id=2;-- 手动加行写锁 id=2 ,使用索引
    3、session1: update mylock set name='nn' where id=2; -- 加写锁被阻塞
    4、session2:update mylock set name='nn' where id=1; -- 加写锁会死锁,不允许操作
    

    4.3 面试题

    系统运行一段时间后,数据量已经很大,这时A表需要增加个字段,并发量白天晚上都很大,请问怎么修改表结构?

    考点:修改表结构会导致表锁,数据量大,修改数据时间久,可能会导致大量用户阻塞,无法访问。

    • 1.首选创建一个和要修改表一样结构的空表。
    • 2.执行我们需要对表结构进行修改的操作,然后复制原表数据到新表里面。
    • 3.在原表创建一个触发器,在数据复制的过程中,将原表的更新数据的操作,全部更新到新的表中。
    • 4.复制完成后,用rename table新表代替原表,会默认删除原表。
  • 相关阅读:
    Android开发学习——应用安装过程
    飞信接口
    sql联合查询
    宽度自适应
    数据绑定
    分页查询sql
    asp.net读取文件
    oracle数据库连接
    oracle服务的开始和关闭 CMD
    css导航条
  • 原文地址:https://www.cnblogs.com/fuyublog/p/14024744.html
Copyright © 2011-2022 走看看