• 数据库读现象 数据库锁


    数据库读现象

    """
    数据库管理软件的“读现象”指的是当多个事务并发执行时,在读取数据方面可能碰到的问题,包括有脏读、不可重复读和幻读。
    """

    脏读(dirty read)

    """
    事务T2更新了一行记录的内容,但是并没有提交所做的修改。
    
    事务T1读取更新后的行,然后T1执行回滚操作,取消了刚才所做的修改。此时T1所读取的行就无效了,称之为脏数据。
    """

     在这个例子中,事务2回滚后就没有id是1,age是21的数据了。所以,事务一读到了一条脏数据。

    不可重复读取 (nonrepeatable read)

    """
    事务T1读取一行记录,紧接着事务T2修改了T1刚才读取的那一行记录并且提交了。
    
    然后T1又再次读取这行记录,发现与刚才读取的结果不同。这就称为“不可重复”读,因为T1原来读取的那行记录已经发生了变化。
    """

    幻像读取 (phantom read)

    """
    幻读(phantom read)”是不可重复读(Non-repeatable reads)的一种特殊场景:
    当事务没有获取范围锁的情况下执行SELECT … WHERE操作有可能会发生“幻影读(phantom read)”。
    """
    """
    事务T1读取或修改了指定的WHERE子句所返回的结果集。
    
    然后事务T2新插入一行记录,这行记录恰好可以满足T1所使用的查询条件中的WHERE 子句的条件。
    
    然后T1又使用相同的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行或者发现了处于WHRE子句范围内,但却未曾修改过的记录。就好像“幻觉”一样,因为对T1来说这一行就像突然出现的一样。
    
    一般解决幻读的方法是增加范围锁RangeS,锁定检锁范围为只读,这样就避免了幻读。
    """

    解决方案

    """
    其实,脏写、脏读、不可重复读、幻读,都是因为业务系统会多线程并发执行,每个线程可能都会开启一个事务,每个事务都会执行增删改查操作。
    然后数据库会并发执行多个事务,多个事务可能会并发地对缓存页里的同一批数据进行增删改查操作,于是这个并发增删改查同一批数据的问题,
    可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题。 所以这些问题的本质,都是数据库的多事务并发问题,那么为了解决多事务并发带来的脏读、不可重复读、幻读等读等问题,数据库才设计了锁机制、
    事务隔离机制、MVCC 多版本隔离机制,用一整套机制来解决多事务并发问题,下面我们来分别介绍一下它们
    """

    数据库锁

    详情参见https://www.cnblogs.com/linhaifeng/articles/14386584.html

    什么是锁?

    """
    锁是计算机协调多个进程或线程并发访问某一资源的机制
    """

    为何要加入锁机制?

    """
    因为在数据库中,除了传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供需要用户共享的资源。
    
    当并发事务同时访问一个共享的资源时,有可能导致数据不一致、数据无效等问题
    
    例如我们在数据库的读现象中介绍过,在并发访问情况下,可能会出现脏读、不可重复读和幻读等读现象
    
    为了应对这些问题,主流数据库都提供了锁机制,以及事务隔离级别的概念,
    
    而锁机制可以将并发的数据访问顺序化,以保证数据库中数据的一致性与有效性
    
    此外,锁冲突也是影响数据库并发访问性能的一个重要因素,锁对数据库而言显得尤其重要,也更加复杂。
    """

    锁的分类

    按锁的粒度划分

    """
    行级锁  表级锁  页级锁
    """
    行级锁
    """
    行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。
    """
    特点
    """
    开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
    """
    支持引擎
    """
    InnoDB
    """
    行级锁的三种算法
    """
    record lock:单个记录上锁
    
    gap lock:间隙锁,锁定一个范围,但不包括记录本身
    
    next-key lock:前面两者相加,既锁定符合条件的行,也锁定符合条件的上下各一间隙 解决幻读问题
    """
    行级锁分为共享锁和排他锁
    """
    用法如下:
    
    共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
    排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE
    """
    表级锁
    """
    表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。
    
    表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
    """
    特点
    """
    开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低
    """
    支持引擎
    """
    MyISAM、MEMORY、InNoDB
    """
    分类
    表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁
    使用
    """
    lock table 表名 read(write),表名 read(write),.....;
    //给表加读锁或者写锁
    
    unlock tables; -- UNLOCK TABLES释放被当前会话持有的任何锁
    """
    页级锁
    """
    页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。BDB支持页级锁
    """
    特点
    """
    开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
    """

    按锁级别划分

    """
    共享锁  排它锁
    """
    """
    与行处理相关的sql有:insert、update、delete、select,这四类sql在操作记录行时,可以为行加上锁,但需要知道的是:
    
    1、对于insert、update、delete语句,InnoDB会自动给涉及的数据加锁,而且是排他锁(X);
    
    2、对于普通的select语句,InnoDB不会加任何锁,需要我们手动自己加,可以加两种类型的锁,如下所示
    # 共享锁(S):SELECT ... LOCK IN SHARE MODE;  -- 查出的记录行都会被锁住
    
    # 排他锁(X):SELECT ... FOR UPDATE;  -- 查出的记录行都会被锁住
    """
    共享锁(Share Lock)
    定义
    """
    共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,获准共享锁的事务只能读数据,不能修改数据直到已释放所有共享锁,所以共享锁可以支持并发读。
    
    如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁或不加锁(在其他事务里一定不能再加排他锁,但是在事务T自己里面是可以加的),反之亦然。
    """
    用法
    """
    SELECT ... LOCK IN SHARE MODE;
    
    在查询语句后面增加LOCK IN SHARE MODE,Mysql会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,
    可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。
    """
    排他锁(eXclusive Lock)
    定义
    """
    排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再对该行加任何类型的其他他锁(共享锁和排他锁),
    但是获取排他锁的事务是可以对数据就行读取和修改。
    """
    用法
    """
    SELECT ... FOR UPDATE;
    
    在查询语句后面增加FOR UPDATE,Mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
    
    特例:加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select ...from...查询数据,
    因为普通select查询没有任何锁机制。
    """

    按使用方式划分

    乐观锁  悲观锁
    悲观锁
    """
    悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度(悲观),因此,在整个数据处理过程中,将数据处于锁定状态。 
    
    悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据),
    现在互联网高并发的架构中,受到fail-fast思路的影响,悲观锁已经非常少见了。
    """
    悲观锁的流程
    """
    在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。
    
    如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。
    
    如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
    
    其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。
    
    ps:行锁、表锁、读锁、写锁都是在操作之前先上排他锁
    """
    悲观锁的实现
    """
    在MySQL中使用悲观锁,必须关闭MySQL的自动提交,set autocommit=0,因为MySQL默认使用自动提交autocommit模式,在执行完sql后会自动提交并释放锁
    set autocommit=0;
    """
    总结
    """
    优点:
        悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。
        
    缺点:
      (a)在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;
      (b) 在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数
    
    """
    乐观锁
    """
    乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,
    如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。
    """
    乐观锁的实现
    """
    1、使用版本号实现
    每一行数据多一个字段version,每次更新数据对应版本号+1,
    原理:读出数据,将版本号一同读出,之后更新,版本号+1,提交数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据,重新读取数据
    
    2、使用时间戳实现
    每一行数据多一个字段time
    原理:读出数据,将时间戳一同读出,之后更新,提交数据时间戳等于数据库当前时间戳,则予以更新,否则认为是过期数据,重新读取数据 
    """
    优点与不足
    """
    乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。
    """
    如何选择乐观锁与悲观锁
    """
    在乐观锁与悲观锁的选择上面,主要看下两者的区别以及适用场景就可以了。
    
    1、乐观锁并未真正加锁,效率高。一旦锁的粒度掌握不好,更新失败的概率就会比较高,容易发生业务失败。
    
    2、悲观锁依赖数据库锁,效率低。更新失败的概率比较低。
    
    随着互联网三高架构(高并发、高性能、高可用)的提出,悲观锁已经越来越少的被使用到生产环境中了,尤其是并发量比较大的业务场景。 
    """

    按加锁方式划分

    自动锁  显示锁

    按操作划分

    DML锁  DDL锁
    """
    DML锁(data locks,数据锁),用于保护数据的完整性,其中包括行级锁(Row Locks (TX锁))、表级锁(table lock(TM锁))。
    
    DDL锁(dictionary locks,数据字典锁),用于保护数据库对象的结构,如表、索引等的结构定义。
    其中包排他DDL锁(Exclusive DDL lock)、共享DDL锁(Share DDL lock)、可中断解析锁(Breakable parse locks)
    """ 粒度:行级< 表级 < 页级 级别越高并发越低,考虑到性能,innodb默认支持行级锁,但是只有在命中索引的情况下才锁行,否则锁住所有行,本质还是行锁, 但是此刻相当于锁表了
         行级锁有三种算法:
            Record lock
            Gap lock
            Next-key lock(默认)= Record lock+ Gap lock    ------》 解决幻读问题
    级别:排他、共享
        排他
        共享
    
    使用方式:悲观、乐观
  • 相关阅读:
    团队项目-选题报告
    图论3-分层图最短路
    图论2-次短路计数
    图论1-k短路
    noi online 提高组t2冒泡排序
    图和树
    搜索(bfs)
    搜索(dfs)
    笔记
    打工
  • 原文地址:https://www.cnblogs.com/ZhZhang12138/p/14872481.html
Copyright © 2020-2023  润新知