锁的分类

    • 从对数据操作的类型(读/写)分
      • 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
      • 写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
    • 对数据操作的粒度分

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

    • 表锁
    • 行锁

    3.2 表锁
    特点:偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
    案例1【加读锁】:
    [session1]
    ———-
    lock table user read;
    ———-
    这里只能执行查询当前表,不能查询其他表,插入或更新当前表都会提示错误
    ———-
    unlock tables;

    [session2]
    ———-
    在session1锁定表后,session2能查询或更新未锁定的表,能查询锁定表,插入或者更新锁定表会一直等待锁被释放。
    案例1【加写锁】:
    [session1]
    ———-
    lock tables user write;
    ———-
    这里可以对锁定表做查询、更新、插入操作
    ———-
    unlock tables;

    [session2]
    ———-
    在session1锁定表后,查询、更新、插入操作均需要等到锁被释放。
    结论:

    1. MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会堵塞同一表的写请求。只要当读锁释放后,才会执行其他进程的写操作。
    2. MyISAM表的写操作(加写锁),会阻塞其他进程的对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。

    查看哪些表被加锁:show open tables;
    分析表锁定:show status like ‘table%’;
    .
    Table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1 ; Table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在着较严重的表级锁争用情况;
    Myisam的读写锁调度是读优先,这也是myisam不适合做写为主表的引擎。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞
    3.3 行锁
    特点:

    1. 偏向InnoDB存储引擎,开销大,加锁慢;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
    2. InnoDB和MyISAM的最大不同有两点:一是支持事务;二是采用了行级锁。

    案例【加行锁】
    [session1]
    ———-
    set autocommit=0;
    ———-
    这里可以对锁定表做更新操作
    ———-
    commit;

    [session2]
    ———-
    在session2锁定表后不commit时,这里对锁定表进行update操作,会等待锁释放。
    无索引行锁升级为表锁
    当某个索引列没有正常使用,如赋错误的类型的值,会导致行锁变表锁。
    间隙锁危害
    间隙锁:当我们用范围条件而不是相等条件检索数据,并请求共享或拍他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”进行加锁,这种锁机制就是所谓的间隙锁(Next-Key)。
    危害:当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据,在某些场景下这可能会对性能造成很大的危害。
    【面试题】如何锁定一行
    select * from user for update;
    结论:
    Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的的性能损耗可能比表级锁定会要更高一些,但是整体并发处理能力方面要远远优于MyISAM的表级锁定。当系统并发量较高的时候,InnoDB的整体性能和MyISAM相比就有比较明显的优势了。
    但是Innodb的行级锁定同样也有脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅不能比MyISAM高,甚至可能会更差。
    分析行锁定:
    通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况,命令:mysql> 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:系统启动后到现在总共等待的次数;
    优化建议

    • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁。
    • 合理设计索引,尽量缩小锁的范围
    • 尽可能较少检索条件,避免间隙锁
    • 尽量控制事务大小,减少锁定资源量和时间长度
    • 尽可能低级别事务隔离

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