zoukankan      html  css  js  c++  java
  • 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会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。



     

  • 相关阅读:
    Windows XP下 Android开发环境 搭建
    Android程序的入口点
    在eclipse里 新建android项目时 提示找不到proguard.cfg
    64位WIN7系统 下 搭建Android开发环境
    在eclipse里 新建android项目时 提示找不到proguard.cfg
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    Android requires compiler compliance level 5.0 or 6.0. Found '1.4' instead
    Windows XP下 Android开发环境 搭建
    Android程序的入口点
  • 原文地址:https://www.cnblogs.com/jthr/p/15398518.html
Copyright © 2011-2022 走看看