December 23, 2012

[MySQL学习]Innodb锁相关描述翻译

以下翻译自lock0lock.c的文件头部注释,翻译的比较凌乱… //////////////////////////////////////////////////////////////////////////////////// 几个hardcode的宏: LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000 该宏用于控制在事务的waits-for-graph中的查找深度 LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK 200 用于控制死锁递归检测深度 LOCK_RELEASE_KERNEL_INTERVAL 1000 在释放锁的过程中(lock_release_off_kernel),每隔这么多次循环释放kernel mutex LOCK_PAGE_BITMAP_MARGIN 64 创建锁的bitmap至少有这么多个比特位 一个显式锁同时影响记录和记录前面的gap。一个隐式x锁不影响gap,只会锁住读活更新的索引记录。 如果一个事务修改或插入了一个索引记录,它拥有在该就上的隐式x锁。如果事务修改了聚集索引记录,会在二级索引记录上也有一个隐式x锁,二级索引记录所在的page上的事务id>=当前事务的trx id, 并且没有二级索引记录上的显式 非gap锁请求 这种对二级索引的相容性定义是根据其实现决定的:我们希望通过查看聚集索引记录来判断一个二级索引记录是否有一个隐式x锁,而不是根据二级索引记录的历史版本。 不同的事务可能同时在同一个GAP上产生冲突锁。在gap上的锁被单纯的禁止:无法插入,或者如果一个不同的事务在gap上有一个冲突的锁,一个select cutsor可能需要等待。GAP上的x锁并不意味着有权力向GAP中插入记录。 一个显式锁可以设置在一个用户记录或者supremum记录上。在supremum记录上的锁通常被认为是一个GAP锁,尽管GAP位没有设置。当我们修改一条记录并且修改了记录的大小时,可能会临时的将它的显式锁存储到infimum记录上,除此之外,不会在infimum记录上加锁。 一个正在等待的记录锁也可以是GAP类型;当在显式锁队列中,一个锁请求的前面没有另外一个冲突模式锁对象时,就可以获得锁。 RULE1:如果在记录上有一个隐式X锁,并且有一个non-gap锁请求在队列中等待,这时候持有隐式x锁的事务同样也会有一个显式的Non-gap记录X锁。这样当锁被释放时,我们只需要通过查看队列中的显式锁请求来为等待锁请求赋予锁 RULE3:不同的事务不可以在同一个记录上同时拥有冲突的NON-GAP锁。但是,他们可以拥有冲突的GAP锁。 RULE4:如果在队列中有一个等待的锁请求。没有锁请求,gap或者not-gap锁可以插入到队列中该等待锁请求前面。在记录删除和page分裂时,数据库管理者可以为一个事务创建新的gap类型的锁。如果没有RULE4,事务waits-for graph可能变成循环而数据库无法注意到这一点,因为死锁检测只有在一个事务本身请求一个锁时才发生。 当在下一个记录上没有其他事务持有的显式锁时,允许在GAP中插入记录。这些锁请求被赋予或等待,gap位是否被设置都无所谓,除了另外一个事务的gap类型请求,并且正在等待轮到他去插入时,会被忽略掉。换句话说,一个其他事务持有的隐式X锁不会阻止一次写入,这允许在是用类似ORACLE序列生成器为许多事务并发产生主键,这会允许更大的并发度。 如果事务在记录上有X锁,或者其他事务没有在该记录上任何non-gap锁时,则允许该事务修改记录 如果事务有一个Non-gap显式、或者隐式锁在记录上,或者其他事务有在该记录上的非X锁请求,则允许是用一个cursor读取一个用户记录。在一个page上supremum总是允许被读。 总的来说,一个隐式锁看起来仅仅像是在一个记录上被赋予的X锁,而不是在GAP上。一个没有设置GAP位的显示锁是一个同时在记录和GAP上的锁。如果设置了GAP位,则表明锁只在GAP上。不同的事务不可以在同一时间拥有相互冲突的锁。但可以在GAP上拥有冲突的锁。被赋予的记录锁允许操作记录的权限,但gap类型的锁仅仅禁止操作。 注意:如果一些事务在一个二级索引记录上拥有隐式x锁,这种情况比较难处理。我们可能需要查看对应聚集索引记录的前面的版本,以查找一个被标记删除的二级索引记录是否是被一个活跃的事务标记删除的,而不是一个已经提交的事务。 FACT A:如果一个事务已经插入了一条记录,它可以在任何时候删除它,无需等待任何锁。 PROOF:事务拥有插入记录在每个索引记录上的隐式X锁,并且可以修改每个索引记录而无需等待。 FACTB:如果一个事务通过一个cursor产生结果集,它可以再次读取,并获取一些结果集合,如果它没有同事修改他们的话。因此没有幻读问题。如果被cursor访问到的按照字母序的最大记录被移除,一次锁等待可能会发生,其他情况则不会。 PROOF:当一次读cursor执行时,它会对扫描过的用户记录设置S锁,以及每个page supremum上一个gap类型的S锁。cursor必须等待直到它拥有了这些锁。这样其他事务就不可以在这些用户记录上加任何X锁,也不能修改用户记录。甚至其他事务不可以插入到cursor扫奥的GAP中。Page分裂和合并,或者移除废弃版本的记录不会受此影响,因为当一个用户记录或一个Page上的supremum被移除时,下一个记录继承 了其锁作为GAP类型,因此会阻塞插入到相同的GAP中。同样的,如果一个page supremum被插入,它从成功的记录那继承它的锁。当cursor被重新放置到结果集的起始位置时,它将查看到的记录是上次查看到的或者新插入的page supremums.它能够立刻获取这些记录,当到达最大的记录时,它注意到结果集已经完成。如果最大的记录被移除,可能需要等待锁,因此下一个记录只继承了GAP类型的锁,这时候需要等待。 如果一个索引记录可以被修改或者新的插入,我们必须检查该记录和下一个记录上的锁。当一个read cusor开始读数据时,我们会在每个经过的记录上设置一个记录级别的s锁,除了在开始获取记录之前cursor所在的初始化记录。我们的索引树查找约定B-TREE上的cursor在查询时被防止在第一个可能匹配的记录前面,这里可能会有一些优化:如果记录是通过唯一索引上的等值条件查找的,我们实际上设置了一个特殊的锁在记录上,这个锁不会阻止任何在该记录之前的插入。而一个记录上的next-key x Lock同样会阻止该记录之前的插入。 每个Page上有特殊的infimum和supremum记录。一个supremum记录可以被一个read curosr锁住。这个记录无法被更新,但这个锁可以阻止将用户记录插入到该Page的用户记录尾部(也就是最大的那个记录之后) next-key锁也可以防止幻读,防止两次select的结果集不同,防止幻读保证了事务的串行化。 当需要插入一条新记录时,我们需要检查什么呢?只需要检查同一个Page上的下一条记录,因为supremum记录也会持有一个锁。一个S锁会阻止插入,但一个X锁呢?如果是由一个查询更新(searched update)加上的锁,这时候同样有一个隐式的s锁,插入会被阻止。但如果我们的事务拥有下一个记录的X锁,但在下一个记录上有一个等待的S锁请求呢?如果这个S锁是被一个在索引上升序移动的read cursor锁设置的,我们无法立刻做插入操作,因为当我们最终提交事务时,read cursor应该可以看到新插入的记录。所以我们应该跳到新插入记录的下一个记录。这种向后移动可能太难处理。如果我们正处于将在下一个记录上第二个x锁请求入队列的过程中,这时候死锁检测机制会发现我们的事务和其他持有s锁请求事务之间的死锁。这种解决方案看起来是Ok的。 […]