zoukankan      html  css  js  c++  java
  • MySQL事务(二)

    一、事务的隔离级别/锁问题

    基本的介绍: 当我们的mysql表,被多个线程或者客户端同时操作时,mysql提供一种机制,可以让不同的事务在操作数据时,具有隔离性。

    锁是计算机协调多个进程或线程并发访问某一资源的机制。锁冲突也是影响数据库并发访问性能的一个重要因素。MySQL不同的存储引擎支持不同的锁机制,如 MyISAM MEMORY 存储引擎采用表级锁BDB 采用页面锁,也支持表级锁;InnoDB 既支持行级锁也支持表级锁,默认采用行级锁

     

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

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

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

             

    锁冲突:例如说事务A将某几行上锁后,事务B又对其上锁,锁不能共存否则会出现锁冲突。(但是共享锁可以共存,共享锁和排它锁不能共存,排它锁和排他锁也不可以)

    死锁:例如说两个事务,事务A锁住了1~5行,同时事务B锁住了6~10行,此时事务A请求锁住6~10行,就会阻塞直到事务B施放6~10行的锁,而随后事务B又请求锁住1~5行,事务B也阻塞直到事务A释放1~5行的锁。死锁发生时,会产生Deadlock错误。两个事务都需要获得对方持有的排他锁才能继续完成事务,形成循环锁成死锁。如果锁是对表操作的,所以自然锁住全表的表锁就不会出现死锁。

    Show Innodb status 命令查看最后一个死锁的产生原因。

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

     

    二、MyISAM 表锁

    查询表级锁征用情况:

    Show status like ‘table%’;

    如果 table_locks_waited 值较高,则说明存在较严重的表级锁征用情况;

     

    对于MyISAM表

    读锁表共享读锁,不会阻塞其他用户对同一表的请求,但会阻塞请求(即我读的时候我不能改,你能读也不能写【写等待】);

    直到解锁表才执行:

    写锁表独占写锁,则即阻塞也阻塞(我能读写,你不能读且不能写【读写等待】);

    读写互斥(别人在读的时候你不能写,在写的时候不能读),但在一定条件下,也支持查询和插入操作的并发进行,设置系统变量concurrent_insert 值为0,1,2

    MyISAM 在执行 SELECT 前,自动 给涉及的所有表加 读锁,执行 UPDATE、DELETE|、INSERT 等前,自动写锁;一般不需要手动显示加锁。

    MyISAM 总是一次获得SQL语句所需的全部锁,所以不会出现死锁;

    在执行 LOCK TABLES 后,只能访问显示加锁的这些表,不能访问未加锁的表,自动加锁的情况也是如此;

    如果SQL中出现锁定表取表名的,也需要显示申明锁定别名表

    Lock  tables  a  read,  b  read ; # 同时锁定 a, b表

    给MyISAM表显式加锁,一般是为了在一定程度模拟事务操作,实现对某一时间点多个表的一致性读取。例如有一个订单表,其中记录有各订单的总金额total,同时还有一个订单明细表 order_detail, 其中记录有各个订单每一产品的金额小计 subtotal, 假设我么要检查这两个表的金额合计是否相符,就需要给两个表加 读锁 ,防止在第一个表统计的过程中, order_detail 表已经发生了改变。 

     

    MyISAM的锁调度:

    MyISAM的读锁和写锁互斥,读写操作是串行的。那么,一个进程请求读锁,同时另一个进程请求同一表的写锁,写进程优先获得锁,即使读请求先到锁等待队列,写请求后到,写锁也会插队到读锁请求之前。这也是MyISAM不适合有大量更新操作和查询操作应用的原因。因为有大量的更新操作会造成查询操作很难获得读锁,从而可能永久阻塞。尽量避免长时间运行的查询操作,如复杂查询不可避免,尽量安排在数据库空闲时段执行,如夜间

     

    解决查询相对重要的应用中读锁等待严重的问题

    1.指定启动参数low_priority_updates,使MyISAM引擎给予读请求以优先的权利

    2.命令set low_priority_updates = 1,使该连接发出的更新请求优先级降低

    3.指定insert、update、delete语句的low_priority属性,降低该语句的优先级

    4.折中,系统参数max_write_lock_count设置一个合适的值,当一个表的读锁达到这个值后,MySQL就暂时将写请求的优先级降低,给读进程一定获得锁的机会;

     

    并发插入(系统变量concurrent_insert

            concurrent_insert = 0 ,不允许并发插入

            concurrent_insert = 1,如果MyISAM表中没有空洞(没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。Mysql的默认设。比如,线程session1执行 lock table test read local 语句后就不能对该表写操作(innodb的不行,还是写等待)。但另一个线程session2虽然不能更新和删除,但却可以在尾部并发插入操作,且插入的数据在session1没有释放锁时是无法查询到的。

            concurrent_insert =2,无论有没有空洞,都允许在表尾并发插入记录

      利用MyISAM的并发插入来解决应用中对同一表查询和插入的锁争用。通过定期在系统空闲时执行 optimize table 语句来整理空间碎片,回收因删除而产生的中间空洞。

     

    三、查看Innodb行锁争用情况

    Show status like ‘innodb_row_lock%’;

    Innodb_row_waits 和 Innodb_row_lock_time_avg 的值比较高,可

    查看 information_schema 数据库中相关的表,如 innodb_locks 和 innodb_lock_waits;

    通过设置 Innodb monitors 来观察锁冲突情况:

    Create table innodb_monitor(a int) engine=innodb;

    然后 Show engine innodb statusG 查看

    Drop table innodb_monitor; //停止监视器,默认情况下每15秒写入监控日志

     

    四、Innodb实现两种类型的行锁(针对开启事务,否则无效)

      innodb 表锁操作跟 myisam 一样。与MyISAM最大的不同点有两点,一是支持事务,二是采用了行级锁。

      共享锁(S:读锁。当一个事务(必要条件否则无效)对某几行上读锁时,我可以读写允许其他事务对这几行进行读操作,但不允许其进行写操作,也不允许其他事务给这几行上排它锁,但允许上读锁

            上共享锁的写法select 语句 … lock in share mode

      

      

      

      

      得出结论:允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。注意:如果两个事务都对同一条数据上了共享锁,然后session1 要更新这条数据(会写等待),若此时session2 也对这条数据进行更新(则会造成死锁导致退出),session1的写执行。

            排他锁(X:写锁。当一个事务对某几个上写锁时,不允许其他事务写,但允许读。更不允许其他事务给这几行上任何锁。包括写锁。

            上排它锁的写法select 语句 … for update

      

      

      如果一个事务请求的锁模式与当前线程的锁兼容,Innodb 就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。

      即同时获得共享锁,但不能同时获得排他锁,会锁等待

      

      对于 UPDATEDELETEINSERT 语句,InnoDB会自动给涉及数据集加排他锁X;对于普通的 SELECT 语句,InnoDB不会加任何锁。意向锁是系统自动加的。

       

      

       

      

      得出结论排他锁,允许获的排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁排他写锁

            意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁

            意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁

     

    注意几点:

    1. Innodb行锁是通过给索引上的索引项加锁来实现。如果没有索引将通过隐藏的聚簇索引来对记录加锁,即如果不通过索引条件检索数据,将对表中所有记录加锁,实际效果跟表锁一样
    2. 两个事务不能锁同一个索引
    3. insert ,delete , update在事务中都会自动默认加上排它锁
    4. 在没有索引的情况下,Innodb会对所有记录都加锁,当给其增加一个索引后,Innodb只锁定了符合条件的行。
    5. 访问不同行的记录,但如果使用相同索引键,会出现锁冲突

       

      

      

      

       好文: https://juejin.im/post/5caaaa67e51d452b63241954

  • 相关阅读:
    Educational Codeforces Round 61
    Codeforces Round #548 (Div. 2)
    Codeforces Round #553 (Div. 2)
    spring mvc接口跨域访问
    json解析;百度统计API返回data解析
    HTML img标签使用base64展示图片
    My Sql 查询连续天数数据
    js 获取url地址栏拼接参数
    在jsp页面获取绝对路径
    java web项目配置Tomcat访问项目外文件
  • 原文地址:https://www.cnblogs.com/cshaptx4869/p/10477952.html
Copyright © 2011-2022 走看看