- 概述
- 锁类型
- 隔离级别
- 隐式主键
- 加锁分析
- 解锁
- 总结
- 堆栈
- 0 rocksdb::TransactionLockMgr::TryLock
- 1 rocksdb::PessimisticTransactionDB::TryLock
- 2 rocksdb::PessimisticTransaction::TryLock
- 3 rocksdb::TransactionBaseImpl::GetForUpdate
- 4 myrocks::Rdb_transaction_impl::get_for_update
- 5 myrocks::ha_rocksdb::get_for_update
- 6 myrocks::ha_rocksdb::get_row_by_rowid
- 7 get_row_by_rowid
- 8 myrocks::ha_rocksdb::read_row_from_secondary_key
- 9 myrocks::ha_rocksdb::index_read_map_impl
- 10 myrocks::ha_rocksdb::read_range_first
- 11 handler::multi_range_read_next
- 12 QUICK_RANGE_SELECT::get_next
- 13 rr_quick
- 14 mysql_delete
- 15 mysql_execute_command
- 16 mysql_parse
- 17 dispatch_command
- 0 rocksdb::TransactionLockMgr::UnLockKey
- 1 rocksdb::TransactionLockMgr::UnLock
- 2 rocksdb::PessimisticTransactionDB::UnLock
- 3 rocksdb::PessimisticTransaction::Clear
- 4 rocksdb::PessimisticTransaction::Commit
- 5 myrocks::Rdb_transaction_impl::commit_no_binlog
- 6 commit
- 7 myrocks::rocksdb_commit
- 8 ha_commit_low
- 9 TC_LOG_DUMMY::commit
- 10 ha_commit_trans
- 11 trans_commit
- 12 mysql_execute_command
- 13 mysql_parse
- 14 dispatch_command
概述
MyRocks中RocksDB作为基于快照的事务引擎,其在事务支持上有别于InnoDB,有其自身的特点。在早期的月报[myrocks之事务处理] 中,我们对锁的实现有过简单的分析,本文会以一些例子来介绍MyRocks是如果来加锁解锁的。
锁类型
MyRocks早期只支持排他锁,支持SELEC… IN SHARE MODE后,MyRocks才开始引入共享锁。
1. /* Type of locking to apply to rows */
2. enum { RDB_LOCK_NONE, RDB_LOCK_READ, RDB_LOCK_WRITE } m_lock_rows;
#587是关于共享锁的一个有趣BUG,有兴趣的同学可以看看。
MyRocks的锁都是内存锁,因此MyRocks事务不宜持有过多的锁,以避免占用过多的内存。
MyRocks通过参数rocksdb_max_row_locks来控制单个事务所持有锁的总数。另外,rocksdb锁系统还支持以下参数
max_num_locks:系统锁个数总限制
expiration_time:锁过期时间
如果锁个数超出限制,客户端会返回下面的错误
1. failed: 12054: Status error 10 received from RocksDB: Operation aborted: Failed to acquire lock due to max_num_locks limit
隔离级别
MyRocks的事务隔离级只支持的READ-COMMITED和REPEATABLE-READ。隔离级别的支持和snapshot密切相关,隔离级别为READ-COMMITED时,事务中每的个stmt都会建立一个snapshot, 隔离级别为REPEATABLE-REA时,只在事务开启后第一个stmt建立一次snapshot。MyRocks中隔离级别不同不会影响加锁和解锁的行为,因此,后面在分析MyRocks的加锁解锁时不区分隔离级别。
隐式主键
MyRocks支持创建无主键的表,但RocksDB作为KV存储,是需要KEY的。因此,RocksDB内部会给表增加一个名为”HIDDEN_PK_ID”的隐式主键列,此值自增,类似与自增列。此列对于MySQL server层是透明的,读取表数据时会自动跳过”HIDDEN_PK_ID”列。
对于无主键的表,MyRocks的锁都是加在隐式主键上的。
对于binlog复制来说,MyRocks隐式主键并不会提升复制速度,因为隐式主键对server层是透明的,主键列不会记入binlog。 因此,建议MyRocks表都指定主键。
加锁分析
以此表结构来分析各类语句的加锁情况。
1. create table t1(id int primary key, c1 int unique, c2 int, c3 int, key idx_c2(c2)) engine=rocksdb;
2. insert into t1 values(1,1,1,1);
3. insert into t1 values(2,2,2,2);
4. insert into t1 values(3,3,3,3);
5. insert into t1 values(4,4,4,4);
- 示例 select
1. select * from t1;
MVCC, 普通读不加锁
- 示例 select .. in share mode
1. select * from t1 where id=1 in share mode;
对主键id=1记录加S锁
- 示例 select .. for update
1. select * from t1 where id=1 for update;
对主键id=1记录加X锁
- 示例 insert
1. begin;
2. insert into t1 values(1,1,1,1);
3. rollback;
主键id=1加X锁 唯一索引c1=1加X锁
- 示例 delete by主键
1. begin;
2. delete from t1 where id=1;
3. rollback;
主键id=1加X锁
- 示例 delete by唯一索引
1. begin;
2. delete from t1 where c1=2;
3. rollback;
主键id=2加X锁,其他索引不加锁
- 示例 delete by普通索引
1. begin;
2. delete from t1 where c2=3;
3. rollback;
主键id=3加X锁,其他索引不加锁
- 示例 delete by无索引
1. begin;
2. delete from t1 where c3=4;
3. rollback;
对主键每条加X锁,其他索引不加锁
实际上server层过滤不符合条件的行会释放锁,最终只对主键id=4加X锁
- 示例 delete by 主键不存在的行
1. begin;
2. delete from t1 where id=100;
3. rollback;
主键id=100加X锁
- 示例 delete by 其他索引不存在的行
1. begin;
2. delete from t1 where c1=100;
3. rollback;
没有锁可以加
以上例子基本可以覆盖所有加锁的情况,再举例几个例子练习下
- 示例 select for update
1. begin;
2. select * from t1 where c2=3 for update;
3. rollback;
主键id=3加X锁, 其他索引不加锁
- 示例 update更新无索引列
1. begin;
2. update t1 set c3=5 where c3=4;
3. rollback;
对主键每条加X锁,其他索引不加锁
实际上server层过滤不符合条件的行会释放锁,最终只对主键id=4加X锁
- 示例 update更新索引列
1. begin;
2. update t1 set c2=5 where c3=4;
3. rollback;
对主键每条加X锁,其他索引不加锁
实际上server层过滤不符合条件的行会释放锁,最终只对主键id=4加X锁
同时会对唯一索引c2=5加X锁
对于无主键表的表说,RocksDB内部会有隐式主键,所加锁都在隐式主键上
解锁
事务提交或回滚时都会将事务所持有的锁都释放掉。
另外一种情况是,对于不满足查询条件的记录,MySQL会提前释放锁。
总结
- MyRocks只会对主键和唯一索引加锁,普通索引不会加锁。
- 只有插入或更新了唯一索引时,才会对唯一索引加锁,对唯一索引加锁的目的是为了保证唯一性。
- 按主键锁定查找不存在的行时,会对不存在的行主键加X锁。
- 按二级索引查找时,只会对主键加锁,不会对二级锁引加锁。
- S锁只应用于SELECT … IN SHARE MODE语句。
堆栈
最后提供一些堆栈信息,方便学习
- 走唯一索引对主键加锁
```
-
0 rocksdb::TransactionLockMgr::TryLock
-
1 rocksdb::PessimisticTransactionDB::TryLock
-
2 rocksdb::PessimisticTransaction::TryLock
-
3 rocksdb::TransactionBaseImpl::GetForUpdate
-
4 myrocks::Rdb_transaction_impl::get_for_update
-
5 myrocks::ha_rocksdb::get_for_update
-
6 myrocks::ha_rocksdb::get_row_by_rowid
-
7 get_row_by_rowid
-
8 myrocks::ha_rocksdb::read_row_from_secondary_key
-
9 myrocks::ha_rocksdb::index_read_map_impl
-
10 myrocks::ha_rocksdb::read_range_first
-
11 handler::multi_range_read_next
-
12 QUICK_RANGE_SELECT::get_next
-
13 rr_quick
-
14 mysql_delete
-
15 mysql_execute_command
-
16 mysql_parse
-
17 dispatch_command
``` - 提交时解锁
```
-
0 rocksdb::TransactionLockMgr::UnLockKey
-
1 rocksdb::TransactionLockMgr::UnLock
-
2 rocksdb::PessimisticTransactionDB::UnLock
-
3 rocksdb::PessimisticTransaction::Clear
-
4 rocksdb::PessimisticTransaction::Commit
-
5 myrocks::Rdb_transaction_impl::commit_no_binlog
-
6 commit
-
7 myrocks::rocksdb_commit
-
8 ha_commit_low
-
9 TC_LOG_DUMMY::commit
-
10 ha_commit_trans
-
11 trans_commit
-
12 mysql_execute_command
-
13 mysql_parse
-
14 dispatch_command
```
