同学参加CSCC2024数据库系统赛道比赛,我和他一起研究了一些优化的case,最后成功拿到全国2/325。在这里记录一下我们讨论优化过的问题(建议把源码下下来边读边搜代码,否则会晕)
行锁占用内存过大
Q:TPCC测试中行锁占了大量内存,疑似是glibc的malloc的内存管理有问题,生产中换个其他库比如jemalloc就能解决,但是OJ只有glibc
相关代码:
/**
* @description: 申请行级共享锁
* @return {bool} 加锁是否成功
* @param {Transaction*} txn 要申请锁的事务对象指针
* @param {Rid&} rid 加锁的目标记录ID 记录所在的表的fd
* @param {int} tab_fd
*/
bool LockManager::lock_shared_on_record(Transaction *txn, const Rid &rid, int tab_fd) {
std::lock_guard lock(latch_);
if (!check_lock(txn)) {
return false;
}
LockDataId lock_data_id(tab_fd, rid, LockDataType::RECORD);
auto &&it = lock_table_.find(lock_data_id);
if (it == lock_table_.end()) {
it = lock_table_.emplace(std::piecewise_construct, std::forward_as_tuple(lock_data_id),
std::forward_as_tuple()).first;
it->second.oldest_txn_id_ = txn->get_transaction_id();
}
auto &lock_request_queue = it->second;
for (auto &lock_request: lock_request_queue.request_queue_) {
// 如果锁请求队列上该事务已经有共享锁或更高级别的锁(X)了,加锁成功
if (lock_request.txn_id_ == txn->get_transaction_id()) {
// 事务能执行到这里,要么第一次申请,要么等待结束了,拿到锁了
assert(lock_request.granted_);
return true;
}
}
// 如果其他事务有 X 锁,加锁失败(no-wait)
// if (lock_request_queue.group_lock_mode_ == GroupLockMode::X || lock_request_queue.group_lock_mode_ == GroupLockMode::IX || lock_request_queue.group_lock_mode_ == GroupLockMode::SIX) {
// lock_request_queue.cv_.notify_all();
// throw TransactionAbortException(txn->get_transaction_id(), AbortReason::DEADLOCK_PREVENTION);
// }
// 第一次申请,检查锁队列中有没有冲突的事务
// Check for conflicting locks and apply wait-die logic
if (lock_request_queue.group_lock_mode_ == GroupLockMode::X || lock_request_queue.group_lock_mode_ ==
GroupLockMode::IX || lock_request_queue.group_lock_mode_ == GroupLockMode::SIX) {
if (txn->get_transaction_id() > lock_request_queue.oldest_txn_id_) {
throw TransactionAbortException(txn->get_transaction_id(), AbortReason::DEADLOCK_PREVENTION);
}
lock_request_queue.oldest_txn_id_ = txn->get_transaction_id();
lock_request_queue.request_queue_.emplace_back(txn->get_transaction_id(), LockMode::SHARED);
std::unique_lock<std::mutex> ul(latch_, std::adopt_lock);
auto &&cur = lock_request_queue.request_queue_.begin();
lock_request_queue.cv_.wait(ul, [&lock_request_queue, txn, &cur]() {
for (auto &&it = lock_request_queue.request_queue_.begin(); it != lock_request_queue.request_queue_.end();
++it) {
if (it->txn_id_ != txn->get_transaction_id()) {
if (it->lock_mode_ != LockMode::SHARED || it->granted_) {
return false;
}
} else {
cur = it;
break;
}
}
return true;
});
cur->granted_ = true;
lock_request_queue.group_lock_mode_ = static_cast<GroupLockMode>(std::max(
static_cast<int>(GroupLockMode::S), static_cast<int>(lock_request_queue.group_lock_mode_)));
++lock_request_queue.shared_lock_num_;
txn->get_lock_set()->emplace(lock_data_id);
ul.release();
return true;
}
// 每次事务申请锁都要更新最老事务id
if (txn->get_transaction_id() < lock_request_queue.oldest_txn_id_) {
lock_request_queue.oldest_txn_id_ = txn->get_transaction_id();
}
// 将当前事务锁请求加到锁请求队列中
lock_request_queue.request_queue_.emplace_back(txn->get_transaction_id(), LockMode::SHARED, true);
// 更新锁请求队列锁模式为共享锁
lock_request_queue.group_lock_mode_ = GroupLockMode::S;
++lock_request_queue.shared_lock_num_;
txn->get_lock_set()->emplace(lock_data_id);
return true;
}
/**
* @description: 申请行级排他锁
* @return {bool} 加锁是否成功
* @param {Transaction*} txn 要申请锁的事务对象指针
* @param {Rid&} rid 加锁的目标记录ID
* @param {int} tab_fd 记录所在的表的fd
*/
bool LockManager::lock_exclusive_on_record(Transaction *txn, const Rid &rid, int tab_fd) {
std::lock_guard lock(latch_);
if (!check_lock(txn)) {
return false;
}
Loc