zoukankan      html  css  js  c++  java
  • mysql 锁

    • table-level locking(表级锁)
    • row-level locking(行级锁)
    • page-level locking(页级锁)
    1、auto-inc锁
    2、全表更新、全索引更新
    3、使用SR事务隔离级别
    1、record lock(行/记录锁)
    2、gap lock(间隙锁)
    3、next-key lock (record lock + gap lock)
    MyISAM表的锁
    • 读锁,LOCK TABLE USER READ,自身只读,不能写;其他线程仍可读,不能写。多个线程都可提交read lock
    • 写锁,LOCK TABLE USER [LOW_PRIORITY] WRITE ,自身可读写;其他线程完全不可读写。
    • 释放锁,UNLOCK TABLES
    • SELECT自动加读锁
    • 其他DML、DDL自动加写锁

    InnoDB是通过给索引上的索引项加锁来实现行锁
    InnoDB有几种锁:
    • 共享锁(S - LOCKING),允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁
    • 排它锁(X - LOCKING),允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他锁
    InnoDB还独有的实现了2种锁:
    • 意向共享锁(IS),事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁
    • 意向独占锁(IX),事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁
    注意:
    1)在不通过索引条件查询的时候,InnoDB使用的是表锁(默认地,全表所有行加锁,和表级锁相当,例外条件是 RC + innodb_locks_unsafe_for_binlog 组合选项),而不是细粒度行锁。
    2)由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。
    加共享锁:SELECT * FROM xx WHERE … LOCK IN SHARE MODE
    加排他锁:SELECT * FROM xx WHERE … FOR UPDATE
    INNODB_TRX
    INNODB_LOCKS
    InnoDB_LOCK_WAITS

    由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。

    innodb锁

    主键索引 = record lock(但外键约束、唯一约束检测仍然使用gap lock)
    唯一辅助索引 = record lock(但外键约束、唯一约束检测仍然使用gap lock)
    非唯一辅助索引 = next-key lock
    上述结论的前提:
    1、RR级别
    2、innodb_locks_unsafe_for_binlog = 0

    对无索引的字段检索更新时,升级成表级锁(表中全部记录被锁,除非在RC或innodb_locks_unsafe_for_binlog=1模式下,采用semi-consitent read机制)
    SELECT … FROM,一致性非锁定读,除非是SERIALIZABLE隔离级别,在其影响的索引记录上设置一个共享锁
    LOCK IN SHARED MODE,使用共享next-key lock
    FOR UPDATE使用排他next-key lock锁,会阻止LOCK IN SHARED MODE请求
    UPDATE/DELETE,排他next-key lock
    INSERT,排他record lock,而非next-key lock,但在写入新记录之前需要加意向插入gap lock(insertion intention gap lock)--插入意向锁
    INSERT ... ON DUPLICATE KEY UPDATE,排他next-key lock(即将被UPDATE的记录上)
    REPLACE,没冲突/重复时,和INSERT一样,否则(有冲突时是先DELETE后INSERT)加next-key lock
    INSERT INTO T SELECT ... FROM S WHERE,T表上排他record lock;事务隔离级别为RC或者启用innodb_locks_unsafe_for_binlog并且隔离
    级别不是SERIALIZABLE时,S表上采用无锁一致性读。否则,加排他next-key lock(RC不加锁,RR加next-key lock)
    CREATE TABLE..SELECT,和INSERT…SELECT一样
    REPLACE INTO t SELECT ... FROM s WHERE或 UPDATE t ... WHERE col IN (SELECT ... FROM s ...),都会在s表上加next-key lock
    AUTO_INCREMENT列上写入新数据时,索引末尾设置排他锁。请求自增列计数器时,InnoDB使用一个AUTO-INC表锁,只对请求的那个SQL有影响,不会影响整个事务。该锁被持有时,其他会话不能往InnoDB表中写入新行
    LOCK TABLES,设置表锁

    几个重点的锁类型,需要关注下
    • 如果辅助索引上的搜索及锁定是排他的,则会取回其相应的聚集索引,并且在它上面加锁
    • 对无索引的字段检索更新时,升级成表级锁
    • SELECT … FROM,一致性非锁定读,除非是SERIALIZABLE隔离级别
    • INSERT INTO T SELECT ... FROM S WHERE,T表上排他record lock;事务隔离级别为RC或者启用innodb_locks_unsafe_for_binlog
    并且隔离级别不是SERIALIZABLE时,S表上采用无锁一致性读。否则,加排他next-key lock
    • INSERT,排他record lock,非next-key lock,但加意向插入gap lock(还有一种叫做 意向插入(insertion intention) 的gap lock,
    如果两个不同事务想往同一个gap lock中写入数据,但写入位置不一样时,是无需等待,可以直接写入的,因为没有冲突)
    • AUTO_INCREMENT列上写入新数据时,索引末尾设置排他锁。请求自增列计数器时,InnoDB使用一个AUTO-INC表锁,只对请求的那个SQL有影响,
    不会影响整个事务。该锁被持有时,其他会话不能往InnoDB表中写入新行

    SHOW ENGINE INNODB STATUS,只显示最后的死锁信息
    设置 innodb_print_all_deadlocks = 1,在日志中记录全部死锁信息
    自动检测死锁,并优先回滚小事务(影响较小的事务)
    加表锁时,不会发生死锁
    事务中,如果SELECT调用存储函数/存储过程失败了,对应的SQL会回滚事务。如果再显式执行ROLLBACK,那么整个事务都回滚。
    事务回滚时,会释放全部锁。个别情况下,如果个别SQL因为某些错误回滚事务的话,它所持有的行锁可能无法释放,因为InnoDB的行锁信息并没有记录是哪个SQL持有的,这时候,建议执行一次显式的ROLLBACK
    事务尽快提交,小事务越不容易发生死锁
    加FOR UPDATE、LOCK IN SHARE MODE读锁时,最好降低事务隔离级别,例如用RC级别,降低死锁发生概率
    事务中涉及多个表,或者涉及多行记录时,每个事务的操作顺序都要保持一致,降低死锁概率,最好用存储过程/存储函数固化
    通过索引等方式优化SQL效率,降低死锁发生概率(减小扫描/锁范围,降低概率)

    InnoDB多版本的实现机制
    • 为了实现多版本,InnoDB必须在表空间中保存行的旧版本信息。这些信息被保存在回滚段中。
    • 在内部,InnoDB为每个行增加了两个域,一个6-byte的域来指示最后插入或更新这个行的事务标识符(DB_TRX_ID/Trx id),删除标志也被认为是一个更新,
    因为它在提交前只是在行上做了一个标记。另外一个7-byte的域被称为回滚指针(DB_ROLL_PTR/roll pointer),回滚指针指向一个由回滚段写入的undo日志记
    录。如果一个行被更新了,undo日志记录包含了重建这行更新前信息的一些必要数据。
    • InnoDB使用回滚段的信息来执行事务回滚所必须的一些undo操作,而且也使用这些信息来重建更新前的行信息。
    • 回滚段中的undo日志被分为插入日志和更新日志。插入日志仅在事务回滚的时候有用,事务提交之后就可以马上删除掉。更新日志在一致性读的时候需要使
    用,但是,如果当前没有事务再可能使用回滚段中的记录的时候,这些记录就可以删除掉了。因此,你必须经常提交你的事务,就算这些事务只是进行一致性
    读操作而已。否则,InnoDB不能删除掉某些更新日志,这样回滚段将变得越来越大。
    read only transaction,从5.7开始支持
    • 回滚段中undo日志记录的物理大小要比其对应的插入或更新的行要小很多。
    • 在多版本方式下,当你使用SQL语句删除某一行的时候,该行并不会马上从数据库的物理文件上移除。只有当InnoDB能够删除掉更新日志记录的时候,
    那些行及其对应的索引记录才会真正从物理上删除掉。这个移除操作称为purge
    • rollback segment(回滚段)保存历史修改版本信息,历史版本数据放在undo log里
    • 7字节的DB_ROLL_PTR,指向undo tablespace的回滚指针,undo tablespace了存储了行记录修改前后有变化的数据,而不是整行记录(所以如果undo log损坏
    的话,会导致事务回滚失败,无法恢复数据,InnoDB报错)
    • 6字节的DB_ROW_ID,指向对应的行记录,每次新写入一行,该ID自增。InnoDB没指定主键而自动创建隐含的聚集索引时,则该聚集索引会包含该ROWID的值
    (自主创建聚集索引时,则不会包含ROWID的值)
    • undo log里是区分INSERT、UPDATE(DELETE认为是特殊的UPDATE)的,INSERT事务提交后,立刻可以PURGE了;而UPDATE事务必须等所有一致性读事务都提交后,
    确认无需再次读取了,才能PURGE。此外,UPDATE产生的undo比INSERT产生的undo更大
    • 所有事务尽快提交,包括一致性只读事务,否则一些UPDATE产生的undo log没办法及时释放,导致rollback seg、undo log越来越大
    • DELETE事务删除的行记录并不真正立刻被删除,而是先打标记,直到所有相关事务都结束,然后才执行PURGE操作
    • 适当调整innodb_max_purge_lag,避免事务PURGE堆积,影响性能

    • innodb的逻辑存储单元由大到小分别是 tablespace =&get; segment =&get; extent =&get; page(block)组成
    段(segment)
    • 常见的segment有数据段、索引段、回滚段
    • innodb是索引聚集表,所以数据就是索引,索引就是数据
    • 数据段即是B+树的页节点(leaf node segment)
    • 索引段即为B+树的非索引节点(non-leaf node segment)
    • 段的管理是由引擎本身完成
    区(extend)
    • 区是由64个连续的页主成
    • 每个页大小为16K
    • 即每个区的大小为(64*16K)=1MB
    • 对于大的数据段,mysql每次最多可以申请4个区,以此保证数据的顺序性能
    页(page)
    • 页是innodb磁盘管理最小的单位
    • innodb每个页的大小是16K
    • 常见的类型有
    • 数据页 B-tree Node
    • undo页 Undo Log Page
    • 系统页 System Page
    • 事务数据页 Transaction system Page
    • 插入缓冲位图页 Insert Buffer Bitmap
    • 插入缓冲空闲列表页 Insert Buffer freeBitmap
    • 未压缩的二进制大对象页Uncompressed BLOB Page
    • 压缩的二进制大对象页 Compressed BLOB Page
    行(row)
    • innodb存储引擎是面向行的(row-oriented),也就是说数据的存放按行进行存放
    • 每个页最多可以存放16K/2~200行,也就是7992个行--每页至少存2行记录,由于b+tree的双向链表决定
    • 每个page大小16KB
    • 每64个连续的page组成一个extend(1MB)
    • 多个extend和page组成一个segment
    • segment初始化时,会先初始化32个page,之后根据需要会将extent分配给 segment,单次最多会分配4个extends给segment
    • InnoDB中一个索引(B-tree)由两个segment组成。其中,所有的叶子节点(leaf nodes)存放在一个segment中(更连续,更高效),所有的非叶子节点(nonleaf nodes)存放在一个segment中
    • 行记录(row)存放在数据页(page)里,page由:page header、page trailer、page body组成

    在系统表空间中,innodb会维护一些系统信息
    • Internal data dictionary
    • Rollback segments
    • Undo Space
    • Insert buffer
    • Double write buffer
    • MySQL replication info
    • data page默认16KB,当有新索引记录写入时,会预留1/16(1KB)空闲空间用于以后的索引记录写入
    • 当数据记录按照顺序(正序、倒序)写入时,最理想的结果是数据页能填充15/16;如果是随机无序写入,则数据页填充率可能会从1/2 ~ 15/16,
    当fill factor(填充因子)小于1/2时,会开始收缩数据页,释放空闲空间
    • 5.6开始支持修改page size,支持4K、8K、16K,初始化时指定,后续无法修改,不同page size的实例间也不能直接迁移使用。5.7开始支持扩展到32KB、64KB。
    • Insert Buffer的作用,是将辅助索引上的IUD操作从随机变成顺序,提高IO写入效率(约15倍)。Insert Buffer合并到辅助索引树中,可能会有多个Buffer合并在一起
    4种row format:REDUNDANT、 COMPACT、COMPRESS、DYNAMIC
    当file format为 Antelope时,支持:REDUNDANT、 COMPACT 这两种行格式,发生行溢出时,在当前page会存储前768字节,多余的放在off-page。
    当file format为 Barracuda时,支持:COMPRESS、DYNAMIC 这两种行格式,并且兼容前两种,并且兼容前两种,在当前page会存储前20字节,多余的放在off-page
    选项
    innodb_file_format_max = Barracuda
    innodb_file_format = Barracuda
    要设置成一样,并且同时最好把 innodb_file_format_check设置为1(默认值也是1),避免InnoDB在启动过程中需要恢复数据,因为没有检查而写入不支持格式的表中,导致数据丢失。

  • 相关阅读:
    算法竞赛入门经典习题2-3 韩信点兵
    ios入门之c语言篇——基本函数——5——素数判断
    ios入门之c语言篇——基本函数——4——数值交换函数
    144. Binary Tree Preorder Traversal
    143. Reorder List
    142. Linked List Cycle II
    139. Word Break
    138. Copy List with Random Pointer
    137. Single Number II
    135. Candy
  • 原文地址:https://www.cnblogs.com/yhq1314/p/10649789.html
Copyright © 2011-2022 走看看