• mysql19-锁


    1.什么是锁

      锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。

    2.锁的分类

    2.1从操作来分

    2.1.1读锁

    共享锁,针对同一份数据,多个读操作可以同时进行而不会互相影响。

    2.1.2写锁

    排它锁,当前写操作没有完成前,它会阻断其他写锁和读锁。

    2.2从锁粒度来分

    2.2.1表锁

    锁定整张表。开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低

    2.2.2行锁

    只锁定需要的行数据。开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高

    2.2.3说明
      为了尽可能提高数据库的并发度,每次锁定的数据范围越小越好,理论上每次只锁定当前操作的数据的方案会得到最大的并发度,但是管理锁是很耗资源的事情(涉及获取,检查,释放锁等动作),因此数据库系统需要在高并发响应和系统性能两方面进行平衡,这样就产生了“锁粒度(Lock granularity)”的概念。
      一种提高共享资源并发发性的方式是让锁定对象更有选择性。尽量只锁定需要修改的部分数据,而不是所有的资源。更理想的方式是,只对会修改的数据片进行精确的锁定。任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可。

    3.mysql隔离级别

    https://www.cnblogs.com/jthr/p/15218682.html

    4.MyISAM和InnoDB

    mysql中使用比较多的两种引擎是MyISAM和InnoDB。MyISAM只使用表级锁。InnoDB默认使用行级锁,也支持表锁。

    4.1MyISAM

      MyISAM不支持事务。

      MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。
      用户也可以用LOCK TABLES给表显式加表锁时,必须同时取得所有涉及到表的锁,并且MySQL不支持锁升级。也就是说,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表;同时,如果加的是读锁,那么只能执行查询操作,而不能执行更新操作。其实,在自动加锁的情况下也基本如此,MyISAM总是一次获得SQL语句所需要的全部锁。这也正是MyISAM表不会出现死锁(Deadlock Free)的原因。

    4.2InnoDB

      InnoDB支持事务。InnoDB默认使用行锁。InnoDB无索引行锁会升级为表锁

    5.MyIsam表锁

    5.1表锁语法

    1)手动增加表锁

     lock table 表名字1 read(write),表名字2 read(write),其它;

     
    2)查看表上加的锁

     show open tables;

    3)释放表锁

    unlock tables;

    5.2示例准备

    5.2.1建表

    CREATE TABLE `mylock` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) DEFAULT NULL,
      `phone` varchar(20) DEFAULT NULL,
      `address` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

    这里引擎用的是myisam

    5.2.2插入数据

    insert into mylock(name,phone,address) values('a','pa','da');
    insert into mylock(name,phone,address) values('b','pb','db');
    insert into mylock(name,phone,address) values('c','pc','dc');
    insert into mylock(name,phone,address) values('d','pd','dd');
    insert into mylock(name,phone,address) values('e','pe','de');

    5.3读锁示例

    5.3.1打开两个session

    5.3.2session1

    手动给表mylock加读锁

    LOCK TABLE mylock READ;

    5.3.32session1

    查询mylock,可以查询,说明加读锁的当前session可以读取本表

    SELECT * FROM mylock;

    5.3.4session2

    查询mylock,可以查询,说明其它session也可以读取本表

    SELECT * FROM mylock

    5.3.5session1

    查询其他表,不能查询,说明当前session,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表

    SELECT * FROM jcustomer;

    5.3.6session2

    查询其他表,可以查询

    5.3.7session1

    修改mylock某条数据,不能修改,读锁不允许写

    UPDATE mylock SET `name` = 'aa' WHERE id = 1;

    5.3.7session2

    1)修改mylock某条数据,发现阻塞了,它会等待读锁被释放后再执行

    UPDATE mylock SET `name` = 'aa' WHERE id = 1;

    2)在session释放锁

    UNLOCK TABLES;

    3)再看session,发现修改语句已执行成功

    4)session2查看修改的数据,发现数据确实已修改

    5.3.8session1

    1)上面锁释放了,先把读锁再次加上

    LOCK TABLE mylock READ;

    2)修改其他表,不能修改,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表

    UPDATE jcustomer SET custremark1 = 'aa' WHERE CustomerNo = 1;

    5.3.9session2

    修改其他表,可以修改

    UPDATE jcustomer SET custremark1 = 'aa' WHERE CustomerNo = 1;

    5.3.10session1

    释放锁

    UNLOCK TABLES

    5.4读锁小结

    当前session给某些表(锁表)加了读锁,访问权限小结

      读取锁表 读取其他表 修改锁表 修改其它表
    当前session 可以 不可以 不可以 不可以
    其它session 可以 可以 阻塞 可以

    在执行LOCK TABLES后,当前session只能访问显式加锁的这些表-锁表,不能访问未加锁的表

    5.5写锁示例

    5.5.1打开两个session

    5.5.2session1

    给mylock表加写锁

    LOCK TABLE mylock WRITE;

    5.5.3session1

    查询表mylock,可以查询

    5.5.4

    1)session2:查询表mylock,阻塞了,需要释放锁后才能查询结果

    SELECT * FROM mylock;

    2)session1:释放锁

    SELECT * FROM mylock;

    3)再看session2,发现已查询出结果

    5.5.5

     1)session1添加写锁

    LOCK TABLE mylock WRITE;

    2)session1查询其它表

    不能查询,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表

    SELECT * FROM jcustomer;

    5.5.6session2查询其它表

    可以查询

    SELECT * FROM jcustomer;

    5.5.7session1

    修改mylock表的数据,修改成功

    UPDATE mylock SET `name` = 'update_1' WHERE id = 1; 

    5.5.8

    1)session2修改mylock表数据,阻塞了,锁释放后才能执行

    2)session1释放锁

    UNLOCK TABLES;

    3)再看session2,发现已执行成功

    5.5.9

    1)session1给mylock加写锁

    LOCK TABLE mylock WRITE;

    2)session1对其它表数据进行修改

    不能修改,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表

    UPDATE jcustomer SET custremark1 = 'update_1' WHERE CustomerNo = 1;

    5.5.10session2

    修改其它表,可以修改

    UPDATE jcustomer SET custremark1 = 'update_2' WHERE CustomerNo = 1;

    5.6写锁小结

    当前session给某些表(锁表)加了写锁,访问权限小结

      读取锁表 读取其他表 修改锁表 修改其它表
    当前session 可以 不可以 可以 不可以
    其它session 阻塞 可以 阻塞 可以

    在执行LOCK TABLES后,当前session只能访问显式加锁的这些表-锁表,不能访问未加锁的表

    5.7表锁分析

    show status like 'table%';

     table_locks-waited:需要等待的表锁数

    Table_locks_immediate:产生表级锁的次数,可立即释放表锁数
    如果Table_locks_immediate / Table_locks_waited > 5000,最好采用InnoDB引擎
    因为InnoDB是行锁而MyISAM是表锁,对于高并发写入的应用InnoDB效果会好些

    6.InnoDB表锁示例

    6.1建表

    CREATE TABLE `test_innodb_lock` (
      `a` int(11) DEFAULT NULL,
      `b` varchar(16) DEFAULT NULL,
      `c` varchar(20) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;


    6.2插入数据

    insert into test_innodb_lock  values(1,'1000','10');
    insert into test_innodb_lock values(3,'2000','20');
    insert into test_innodb_lock values(4,'4000','40');
    insert into test_innodb_lock values(5,'5000','50');
    insert into test_innodb_lock values(6,'6000','60');
    insert into test_innodb_lock values(7,'7000','70');
    insert into test_innodb_lock values(8,'8000','80');
    insert into test_innodb_lock values(9,'9000','90');
    insert into test_innodb_lock values(1,'10000','100');


    6.3创建索引

    create index test_innodb_a_ind on test_innodb_lock(a);
    create index test_innodb_lock_b_ind on test_innodb_lock(b);

    6.4简要说明

    innoDB支持事务,且每一个sql语句默认是自动提交的。一个事务内,对表数据进行编辑,会对编辑涉及到的表的相关行数据加上行锁,特殊情况下特会加表锁,在事务提交后会释放。它的默认的隔离级别是可重复度。


    6.4示例1

    1)打开两个session

     

    2)session1取消自动提交

    SET autocommit = 0;

    3)session1查询test_innodb_lock

    查询出结果

    SELECT * FROM test_innodb_lock;

    4)session2查询test_innodb_lock

    可以查询

    5)session1修改test_innodb_lock数据

    执行成功,但是没有commit

    UPDATE test_innodb_lock SET b = 'update_1000' WHERE a = 1;

    6)session1查询test_innodb_lock

    查询成功,且可以看到修改后的数据。说明即便自己还没有commit,也可以查询到修改的数据

    SELECT * FROM test_innodb_lock;

    7)session2查询test_innodb_lock

    查询成功,但是查询到的数据还没有被修改,说明

    8)session1提交

    COMMIT;

    9)session2查询test_innodb_lock

    查询成功,查询到的是修改后的数据

    SELECT * FROM test_innodb_lock;

    10)简要说明

    innodb的默认的隔离级别是可重复度,不会出现脏读,一个事务不能读取到另一个事务未提交的数据

     

    6.5示例2

    1)打开两个session

     

    2)session1和session2取消自动提交

    SET autocommit = 0;

     

    3)session1修改test_innodb_lock数据

    执行成功,但是没有commit

    UPDATE test_innodb_lock SET b = 'update_3000' WHERE a = 3;

     4)session1查询

    查询成功,可以查到自己已修改未提交的数据

    SELECT * FROM test_innodb_lock WHERE a = 3;

    5)session2修改test_innodb_lock

    阻塞了,需要session1释放行锁才会执行

    UPDATE test_innodb_lock SET c = 'update_300' WHERE a = 3;

    6)session1提交

    COMMIT;

    7)此时查看session2,由于session1已释放行锁,session2不在阻塞,修改的sql已成功执行

     

    8)session2查询

    查询成功,可以看到session1已提交的修改数据和自己已修改未提交的数据

    SELECT * FROM test_innodb_lock WHERE a = 3;

    9)session1查询

    查询成功,可以看到session2提交的修改的数据(这里如果查询不到session2修改的数据,就再commit一下再查询)

    SELECT * FROM test_innodb_lock WHERE a = 3; 

    10)说明

    一个事务在对数据进行修改时,会对涉及的行自动上行锁,其它的事务对上了行锁的数据的修改会阻塞,等待行锁释放后再执行。

    7.5示例3

    1)打开两个session

     

    2)session1和session2取消自动提交

    SET autocommit = 0;

     

    3)session1修改test_innodb_lock数据

    执行成功,但是没有commit

    UPDATE test_innodb_lock SET b = 'update_4000' WHERE a = 4;

     

    4)session1查询test_innodb_lock

    可以看到自己已修改未提交的数据

    5)session2修改test_innodb_lock其它行的数据

    成功执行,没有阻塞,证明只有a=4的加了行锁,其它行美影响

    UPDATE test_innodb_lock SET c = 'update_500' WHERE a = 5;

    6)session2提交再查询

    查询成功,可以看到a=5数据已修改,看不到a=4数据的修改,因为session1还没有提交

    COMMIT;
    SELECT * FROM test_innodb_lock;

    7)session提交再查询

    查询成功,可以看到a=4,a=5的数据的修改

    COMMIT;
    SELECT * FROM test_innodb_lock ;

     

    8)说明

    行锁锁的是行,对其它行的数据没有有效

    7.6示例4

    1)打开两个session

     

    2)session1和session2取消自动提交

    SET autocommit = 0;

     

    3)session1编辑数据

    执行成功,未提交

     UPDATE test_innodb_lock SET b = 'update_6000' WHERE c = '60';

    4)session2编辑数据

      阻塞了。为什么会阻塞呢?session1是对c='60这条数据进行修改',而这里session2是对a='7'这条数据进行修改,不是同一行数据,为什么a='7'这一行也被锁了呢?这是因为行锁升级为表锁了。

      InnoDB 行级锁是通过给索引上的索引项加锁来实现的,InnoDB行级锁只有通过索引条件检索数据,才使用行级锁;否则,InnoDB使用表锁 在不通过索引(主 键)条件查询的时候,InnoDB是表锁而不是行锁。
      而session1通过c=‘60’来编辑,c是没有索引的,所以加的是表锁。
    UPDATE test_innodb_lock SET b = 'update_7000' WHERE a = 7;

    5)说明

     在没有使用索引的情况下InnoDB就会使用表级锁(共享锁不会有这个情况)

    7.7示例5

    1)打开两个session

     

    2)session1和session2取消自动提交

    SET autocommit = 0;

     

    3)session1编辑数据

    执行成功,未提交

     UPDATE test_innodb_lock SET b = 'update_6000' WHERE a > 1 and a < 6;

    4)session2编辑数据

    执行成功

    UPDATE test_innodb_lock SET b = 'update_8000' WHERE a = 8;

    5)session2新增数据

    插入成功

    INSERT INTO test_innodb_lock VALUES (12,'1200','120');

    6)session2再次插入数据

      阻塞了。命名表中都没有这条数据,为什么还会被锁了。这是session1因为使用范围产生了间隙锁。

      当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,
    InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(GAP Lock)。

      session1使用a>1 and a <6,即便没有a=2,这条数据。也会对a=2这个间隙上锁。

    INSERT INTO test_innodb_lock VALUES (2,'200','20');

    7)说明

      因为Query执行过程中通过过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。也就是间隙锁。
      间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害

    7.8行锁小结

    1) innobd默认使用行锁

    2)当我们对表的某些数据进行修改插入时,会默认对这些数据行加上行锁排它锁

    3)在没有使用索引的情况下InnoDB就会使用表级锁(排它锁才会这样)

    4)当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁,也就是间隙锁

    7.9行锁分析
    1)通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况

    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:从系统启动到现在等待最常的一次所花的时间;
    Innodb_row_lock_waits:系统启动后到现在总共等待的次数;
    对于这5个状态变量,比较重要的主要是
    Innodb_row_lock_time_avg(等待平均时长),
    Innodb_row_lock_waits(等待总次数)
    Innodb_row_lock_time(等待总时长)这三项。
    尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手指定优化计划。


    2)可以通过下面语句查询正在被锁阻塞的sql语句。

    SELECT * FROM information_schema.INNODB_TRXG

    3)查看正在锁的事务

    SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 

    4)查看等待锁的事务

    SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; 

    5)查看表使用情况

    SHOW OPEN TABLES;  列举在表缓存中当前被打开的非TEMPORARY表
    SHOW OPEN TABLES In_use > 0;  

    • Table:表名称。
    • In_use:表当前被查询使用的次数。如果该数为零,则表是打开的,但是当前没有被使用。
    • Name_locked:表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作

     

    7.10手动加上锁

    1)读锁
      共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
     
    用法

    SELECT ... LOCK IN SHARE MODE;

    2)写锁
    排他锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。

    用法

    SELECT ... FOR UPDATE;

    在查询语句后面增加 FOR UPDATE ,Mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。



     

  • 相关阅读:
    上涨、下跌、震荡,我只做下跌 (有钱人赚钱太有心机!
    股票操作指南
    股票要素与心理学研究
    时序图组成
    软件描述的静态与动态
    用dedecms做网站时,空间服务器选择IIS还是apache???
    dedecms 图集标签{dede:productimagelist} {dede:field name='imgurls'}&nbs
    dede内容页调用图片集下所有图片方法!
    dede文章插入分页符不起作用,编辑器中出现分页符,导致文章显示不全
    dede织梦怎么修改description的字数
  • 原文地址:https://www.cnblogs.com/jthr/p/15398518.html
Copyright © 2020-2023  润新知