【数据库内核】RocksDB:事务锁设计与实现

2022-04-15 00:00:00 代码 事务 内存 复制 加锁
本文主要介绍 RocksDB 锁结构设计、加锁解锁过程,并与 InnoDB 锁实现做一个简单对比。

本文由作者授权发布,未经许可,请勿转载。

作者:王刚,网易杭研数据库内核开发工程师

MyRocks 引擎目前是支持行锁的,包括共享锁和排它锁,主要是在 RocksDB 层面实现的,与 InnoDB 引擎的锁系统相比,简单很多。

本文主要介绍 RocksDB 锁结构设计、加锁解锁过程,并与 InnoDB 锁实现做一个简单对比。

事务锁的实现类是:TransactionLockMgr ,它的主要数据成员包括:

private:
  PessimisticTransactionDB* txn_db_impl_;
  // 默认16个lock map 分片
  const size_t default_num_stripes_;
  // 每个column family 大行锁数
  const int64_t max_num_locks_;
  // lock map 互斥锁
  InstrumentedMutex lock_map_mutex_;

  // Map of ColumnFamilyId to locked key info
  using LockMaps = std::unordered_map<uint32_t, std::shared_ptr<LockMap>>;
  LockMaps lock_maps_;
  std::unique_ptr<ThreadLocalPtr> lock_maps_cache_;
  // Must be held when modifying wait_txn_map_ and rev_wait_txn_map_.
  std::mutex wait_txn_map_mutex_;
  // Maps from waitee -> number of waiters.
  HashMap<TransactionID, int> rev_wait_txn_map_;
  // Maps from waiter -> waitee.
  HashMap<TransactionID, TrackedTrxInfo> wait_txn_map_;
  DeadlockInfoBuffer dlock_buffer_;
  // Used to allocate mutexes/condvars to use when locking keys
  std::shared_ptr<TransactionDBMutexFactory> mutex_factory_;复制代码

加锁的入口函数是:TransactionLockMgr::TryLock

Status TransactionLockMgr::TryLock(PessimisticTransaction* txn, //加锁的事务
                                   uint32_t column_family_id, //所属的CF
                                   const std::string& key, //加锁的健 Env* env,
                                   bool exclusive //是否排它锁) {
  // Lookup lock map for this column family id
  std::shared_ptr<LockMap> lock_map_ptr = GetLockMap(column_family_id); //1. 根据 cf id 查找其 LockMap
  LockMap* lock_map = lock_map_ptr.get();
  if (lock_map == nullptr) {
    char msg[255];
    snprintf(msg, sizeof(msg), "Column family id not found: %" PRIu32,
             column_family_id);

    return Status::InvalidArgument(msg);
  }

  // Need to lock the mutex for the stripe that this key hashes to
  size_t stripe_num = lock_map->GetStripe(key);// 2. 根据key 的哈希获取 stripe_num,默认16个stripe
  assert(lock_map->lock_map_stripes_.size() > stripe_num);
  LockMapStripe* stripe = lock_map->lock_map_stripes_.at(stripe_num);

  LockInfo lock_info(txn->GetID(), txn->GetExpirationTime(), exclusive);
  int64_t timeout = txn->GetLockTimeout();

  return AcquireWithTimeout(txn, lock_map, stripe, column_family_id, key, env,
                            timeout, lock_info); // 实际加锁函数
} 复制代码

GetLockMap(column_family_id) 函数根据 cf id 查找其 LockMap , 其逻辑包括两步:

  1. 在 thread 本地缓存 lock_maps_cache 中查找;
  2. 第1步没有查找到,则去全局的 lock_map_ 中查找。

std::shared_ptr<LockMap> TransactionLockMgr::GetLockMap(
    uint32_t column_family_id) {
  // First check thread-local cache
  if (lock_maps_cache_->Get() == nullptr) {
    lock_maps_cache_->Reset(new LockMaps());
  }
  auto lock_maps_cache = static_cast<LockMaps*>(lock_maps_cache_->Get());
  auto lock_map_iter = lock_maps_cache->find(column_family_id);
  if (lock_map_iter != lock_maps_cache->end()) {
    // Found lock map for this column family.
    return lock_map_iter->second;
  }
  // Not found in local cache, grab mutex and check shared LockMaps
  InstrumentedMutexLock l(&lock_map_mutex_);
  lock_map_iter = lock_maps_.find(column_family_id);
  if (lock_map_iter == lock_maps_.end()) {
    return std::shared_ptr<LockMap>(nullptr);
  } else {
    // Found lock map.  Store in thread-local cache and return.
    std::shared_ptr<LockMap>& lock_map = lock_map_iter->second;
    lock_maps_cache->insert({column_family_id, lock_map});
    return lock_map;
  }
}复制代码

相关文章