RocksDB 잠금 메커니즘의 예에 대한 자세한 설명
RocksDB는 오픈 소스 스토리지 엔진으로서 트랜잭션의 ACID 특성을 지원합니다. ACID에서 I(격리)를 지원하려면 동시성 제어가 필수적입니다. 이 기사에서는 주로 RocksDB의 잠금 메커니즘 구현에 대해 설명합니다. 이 글을 통해 독자들이 RocksDB 동시성 제어의 원리를 심도 있게 이해할 수 있기를 바랍니다. 이 기사는 주로 다음 네 가지 측면에서 시작됩니다. 먼저 RocksDB 잠금의 기본 구조를 소개하고 RocksDB 행 잠금 데이터 구조의 잠금 공간 오버헤드를 소개합니다. 그런 다음 몇 가지 일반적인 잠금 프로세스를 소개합니다. 마지막으로 메커니즘의 필수 교착 상태 감지 메커니즘을 소개하겠습니다.
1. 행 잠금 데이터 구조
RocksDB의 최소 잠금 단위는 행입니다. KV 저장소의 경우 잠금 개체가 키이고 각 키는 LockInfo 구조에 해당합니다. 모든 키는 해시 테이블을 통해 관리되며, 잠금을 찾을 때 해시 테이블을 통해 직접 찾아 키가 잠겨 있는지 확인할 수 있습니다. 그러나 전역적으로 해시 테이블이 하나만 있는 경우 이 해시 테이블에 액세스할 때 많은 충돌이 발생하여 동시성 성능에 영향을 미칩니다. RocksDB는 먼저 Columnfamily로 분할됩니다. 각 Columnfamily의 잠금은 LockMap으로 관리되며 각 LockMap은 LockMapStripe로 분할되고 해시 테이블(std::unordered_map
관련 데이터 구조는 다음과 같습니다:
struct LockInfo { bool exclusive; //排它锁或是共享锁 autovector<TransactionID> txn_ids; //事务列表,对于共享锁而言,同一个key可以对应多个事务 // Transaction locks are not valid after this time in us uint64_t expiration_time; } struct LockMapStripe { // Mutex must be held before modifying keys map std::shared_ptr<TransactionDBMutex> stripe_mutex; // Condition Variable per stripe for waiting on a lock std::shared_ptr<TransactionDBCondVar> stripe_cv; // Locked keys mapped to the info about the transactions that locked them. std::unordered_map<std::string, LockInfo> keys; } struct LockMap { const size_t num_stripes_; //分片个数 std::atomic<int64_t> lock_cnt{0}; //锁数目 std::vector<LockMapStripe*> lock_map_stripes_; //锁分片 } class TransactionLockMgr { using LockMaps = std::unordered_map<uint32_t, std::shared_ptr<LockMap>>; LockMaps lock_maps_; // Thread-local cache of entries in lock_maps_. This is an optimization // to avoid acquiring a mutex in order to look up a LockMap std::unique_ptr<ThreadLocalPtr> lock_maps_cache_; }
2.行锁空间代价
由于锁信息是常驻内存,我们简单分析下RocksDB锁占用的内存。每个锁实际上是unordered_map中的一个元素,则锁占用的内存为key_length+8+8+1,假设key为bigint,占8个字节,则100w行记录,需要消耗大约22M内存。但是由于内存与key_length正相关,导致RocksDB的内存消耗不可控。我们可以简单算算RocksDB作为MySQL存储引擎时,key_length的范围。对于单列索引,最大值为2048个字节,具体可以参考max_supported_key_part_length实现;对于复合索引,索引最大长度为3072个字节,具体可以参考max_supported_key_length实现。假设最坏的情况,key_length=3072,则100w行记录,需要消耗3G内存,如果是锁1亿行记录,则需要消耗300G内存,这种情况下内存会有撑爆的风险。因此RocksDB提供参数配置max_row_locks,确保内存可控,默认RDB_MAX_ROW_LOCKS设置为1G,对于大部分key为bigint场景,极端情况下,也需要消耗22G内存。而在这方面,InnoDB则比较友好,hash表的key是(space_id, page_no),所以无论key有多大,key部分的内存消耗都是恒定的。前面我也提到了InnoDB在一个事务需要锁大量记录场景下是有优化的,多个记录可以公用一把锁,这样也间接可以减少内存。
3.上锁流程分析
前面简单了解了RocksDB锁数据结构的设计以及锁对内存资源的消耗。这节主要介绍几种典型场景下,RocksDB是如何加锁的。与InnoDB一样,RocksDB也支持MVCC,读不上锁,为了方便,下面的讨论基于RocksDB作为MySQL的一个引擎来展开,主要包括三类,基于主键的更新,基于二级索引的更新,基于主键的范围更新等。在展开讨论之前,有一点需要说明的是,RocksDB与InnoDB不同,RocksDB的更新也是基于快照的,而InnoDB的更新基于当前读,这种差异也使得在实际应用中,相同隔离级别下,表现有所不一样。对于RocksDB而言,在RC隔离级别下,每个语句开始都会重新获取一次快照;在RR隔离级别下,整个事务中只在第一个语句开始时获取一次快照,所有语句共用这个快照,直到事务结束。
3.1.基于主键的更新
这里主要接口是TransactionBaseImpl::GetForUpdate
1).尝试对key加锁,如果锁被其它事务持有,则需要等待
2).创建snapshot
3).调用ValidateSnapshot,Get key,通过比较Sequence判断key是否被更新过
4).由于是加锁后,再获取snapshot,所以检查一定成功。
5).执行更新操作
这里有一个延迟获取快照的机制,实际上在语句开始时,需要调用acquire_snapshot获取快照,但为了避免冲突导致的重试,在对key加锁后,再获取snapshot,这就保证了在基于主键更新的场景下,不会存在ValidateSnapshot失败的场景。
堆栈如下:
1-myrocks::ha_rocksdb::get_row_by_rowid 2-myrocks::ha_rocksdb::get_for_update 3-myrocks::Rdb_transaction_impl::get_for_update 4-rocksdb::TransactionBaseImpl::GetForUpdate { //加锁 5-rocksdb::TransactionImpl::TryLock 6-rocksdb::TransactionDBImpl::TryLock 7-rocksdb::TransactionLockMgr::TryLock //延迟获取快照,与acquire_snapshot配合使用 6-SetSnapshotIfNeeded() //检查key对应快照是否过期 6-ValidateSnapshot 7-rocksdb::TransactionUtil::CheckKeyForConflict 8-rocksdb::TransactionUtil::CheckKey 9-rocksdb::DBImpl::GetLatestSequenceForKey //第一次读取 //读取key 5-rocksdb::TransactionBaseImpl::Get 6-rocksdb::WriteBatchWithIndex::GetFromBatchAndDB 7-rocksdb::DB::Get 8-rocksdb::DBImpl::Get 9-rocksdb::DBImpl::GetImpl //第二次读取 }
3.2. 기본 키를 기반으로 하는 범위 업데이트
1) 스냅샷을 생성하고 반복자를 기반으로 기본 키를 스캔합니다
2). get_row_by_rowid를 통해 키 잠금을 시도합니다
3). 키를 통과하고 시퀀스를 비교하여 키가 업데이트되었는지 확인합니다
4). 키가 다른 트랜잭션에 의해 업데이트된 경우(키에 해당하는 SequenceNumber가 스냅샷보다 최신임) 재시도가 트리거됩니다
5) 재시도할 경우 이전 스냅샷이 해제되고 잠금이 해제됩니다. tx->acquire_snapshot(false)을 사용하여 스냅샷 획득을 지연합니다(잠금 후 스냅샷 찍기)
5). get_for_update를 다시 시도하세요. 현재 키가 잠겨 있으므로 다시 시도하면 성공할 수 있습니다.
6) 업데이트 작업을 수행합니다
7). 1로 점프하여 기본 키가 조건을 충족하지 않을 때까지 계속 실행한 후 종료됩니다.
3.3. 보조 인덱스 기반 업데이트
이 시나리오는 보조 인덱스에서 기본 키를 찾는 프로세스에 한 단계가 더 있다는 점을 제외하면 3.2와 유사합니다.
1) 스냅샷을 생성하고 반복자를 기반으로 보조 인덱스를 스캔합니다.
2) 보조 인덱스에 따라 기본 키를 반대로 찾습니다. 실제로 이 프로세스도 호출됩니다. 키
3 ) 보조 인덱스에 따라 계속해서 다음 기본 키를 탐색하고 잠금을 시도합니다.
4) 반환된 보조 인덱스가 조건을 충족하지 않으면 종료됩니다
3.4 InnoDB 잠금 사이
RocksDB와 InnoDB의 차이점에 대해 말하자면, 업데이트 시나리오의 경우 RocksDB는 여전히 스냅샷을 읽는 반면 InnoDB는 현재 버전을 읽으므로 동작에 차이가 있습니다. 예를 들어 RC 격리 수준의 범위 업데이트 시나리오에서 트랜잭션은 1,000개의 레코드를 업데이트해야 하므로 잠금이 검색되고 잠겨 있으므로 999번째 레코드를 검색하면 이 키의 시퀀스를 찾을 수 있습니다. 스캔된 스냅샷(다른 트랜잭션에 의해 업데이트된 이 키)보다 크면 이번에는 스냅샷 다시 획득이 트리거되고 이 스냅샷을 기반으로 최신 키 값을 가져옵니다. InnoDB에는 이러한 문제가 없습니다. 현재 읽기 및 스캔 프로세스를 통해 999번째 레코드가 업데이트되면 InnoDB는 최신 레코드를 직접 볼 수 있습니다. 이 경우 RocksDB와 InnoDB에서 본 결과는 동일합니다. 또 다른 경우에는 새로 삽입된 키도 스캔 범위에 있다고 가정하면 이 키의 시퀀스는 의심할 여지없이 스캔된 스냅샷보다 크므로 이 키는 스캔 프로세스 중에 필터링되어 존재하지 않습니다. 충돌 감지라고 불리는 이 키는 발견되지 않습니다. 업데이트 과정에서 ID가 1과 900인 두 개의 레코드가 삽입되었습니다. 마지막으로 900번째 레코드가 보이지 않아 업데이트할 수 없었습니다. InnoDB의 경우 현재 읽고 있기 때문에 새로 삽입된 ID 900의 레코드를 보고 업데이트할 수 있다는 점이 InnoDB와의 차이점이다.
업데이트가 스냅샷을 기반으로 한다는 점 외에도 RocksDB는 잠금이 더 간결합니다. 특히 업데이트 프로세스 중에 열 업데이트에 고유 제약 조건이 포함되면 기본 키만 잠깁니다. , 잠금이 필요합니다. 일반 보조 인덱스는 잠글 필요가 없습니다. 이 목적은 고유 제약 조건 충돌을 방지하는 것입니다. 여기서 고유 제약 조건(기본 키 또는 고유 인덱스)이 업데이트되면 잠금이 필요합니다. InnoDB는 각 인덱스를 잠가야 합니다. 예를 들어 업데이트가 보조 인덱스를 기반으로 배치되면 보조 인덱스도 잠가야 합니다. 이러한 차이가 발생하는 이유는 InnoDB가 RR 격리 수준을 구현하기 때문입니다. 여기서 격리 수준에 대해 조금 이야기해 보겠습니다. 실제로 MySQL에서 정의하는 RR 격리 수준은 SQL 표준에서 정의하는 격리 수준과 다소 다릅니다. SQL 표준은 반복 불가능한 읽기 문제를 해결하기 위해 RR 격리 수준을 정의하고 팬텀 읽기 문제를 해결하기 위해 직렬화 가능 격리 수준을 정의합니다. 반복 불가능 읽기는 동일한 레코드의 값이 수정되지 않는다는 점을 강조하는 반면, 팬텀 읽기는 두 번의 읽기에 의해 반환되는 레코드 수가 고정되어 있으며 레코드 수를 늘리거나 줄이지 않는다는 점을 강조합니다. MySQL은 반복 불가능한 읽기 및 팬텀 읽기 문제를 해결하기 위해 RR 격리 수준을 정의하는 반면, InnoDB의 RR 격리 수준 구현은 GAP 잠금에 의존합니다. RocksDB는 스냅샷 기반 메커니즘이 새로 삽입된 레코드를 효과적으로 필터링할 수 있기 때문에 GAP 잠금을 지원하지 않습니다(고유한 제약 조건 검사만 지원하고 존재하지 않는 키는 잠급니다). 반면 InnoDB는 현재 읽기로 인해 간격 잠금을 통한 다른 삽입을 금지해야 합니다. 주로 잠금 간격 때문에 보조 인덱스도 잠궈야 합니다. 그렇지 않으면 현재 두 읽기의 결과가 다를 수 있습니다. 물론 RC 분할 수준의 경우 InnoDB 일반 보조 인덱스를 잠글 필요가 없습니다.
4.死锁检测算法
死锁检测采用DFS((Depth First Search,深度优先算法),基本思路根据加入等待关系,继续查找被等待者的等待关系,如果发现成环,则认为发生了死锁,当然在大并发系统下,锁等待关系非常复杂,为了将死锁检测带来的资源消耗控制在一定范围,可以通过设置deadlock_detect_depth来控制死锁检测搜索的深度,或者在特定业务场景下,认为一定不会发生死锁,则关闭死锁检测,这样在一定程度上有利于系统并发的提升。需要说明的是,如果关闭死锁,最好配套将锁等待超时时间设置较小,避免系统真发生死锁时,事务长时间hang住。死锁检测基本流程如下:
1.定位到具体某个分片,获取mutex
2.调用AcquireLocked尝试加锁
3.若上锁失败,则触发进行死锁检测
4.调用IncrementWaiters增加一个等待者
5.如果等待者不在被等待者map里面,则肯定不会存在死锁,返回
6.对于被等待者,沿着wait_txn_map_向下检查等待关系,看看是否成环
7.若发现成环,则将调用DecrementWaitersImpl将新加入的等待关系解除,并报死锁错误。
相关的数据结构:
class TransactionLockMgr { // 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, autovector<TransactionID>> wait_txn_map_; DecrementWaiters // IncrementWaiters // } struct TransactionOptions { bool deadlock_detect = false; //是否检测死锁 int64_t deadlock_detect_depth = 50; //死锁检测的深度 int64_t lock_timeout = -1; //等待锁时间,线上一般设置为5s int64_t expiration = -1; //持有锁时间, }
위 내용은 RocksDB 잠금 메커니즘의 예에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











CSS 리플로우와 리페인트는 웹페이지 성능 최적화에 있어 매우 중요한 개념입니다. 웹 페이지를 개발할 때 이 두 개념이 어떻게 작동하는지 이해하면 웹 페이지의 응답 속도와 사용자 경험을 향상시키는 데 도움이 될 수 있습니다. 이 기사에서는 CSS 리플로우 및 리페인트의 메커니즘을 자세히 살펴보고 구체적인 코드 예제를 제공합니다. 1. CSS 리플로우란 무엇입니까? DOM 구조에서 요소의 가시성, 크기 또는 위치가 변경되면 브라우저는 CSS 스타일을 다시 계산하고 적용한 다음 다시 레이아웃해야 합니다.

PHP 언어가 점점 대중화되면서 개발자는 점점 더 많은 클래스와 함수를 사용해야 합니다. 프로젝트 규모가 커지면 모든 종속성을 수동으로 도입하는 것은 실용적이지 않습니다. 이때 코드 개발 및 유지 관리 프로세스를 단순화하려면 자동 로딩 메커니즘이 필요합니다. 자동 로딩 메커니즘은 런타임에 필요한 클래스와 인터페이스를 자동으로 로드하고 수동 클래스 파일 도입을 줄일 수 있는 PHP 언어의 기능입니다. 이를 통해 프로그래머는 코드 개발에 집중할 수 있으며, 지루한 매뉴얼 수업 도입으로 인한 오류와 시간 낭비를 줄일 수 있습니다. PHP에서는 일반적으로

RocksDB는 Facebook RocksDB의 오픈 소스 버전인 고성능 스토리지 엔진입니다. RocksDB는 부분 정렬, 슬라이딩 윈도우 압축 등의 기술을 사용하며 클라우드 스토리지, 인덱싱, 로그, 캐싱 등 다양한 시나리오에 적합합니다. 실제 프로젝트에서는 일반적으로 RocksDB 캐싱 기술을 사용하여 프로그램 성능을 향상시킵니다. 다음에서는 RocksDB 캐싱 기술과 그 응용 프로그램을 자세히 소개합니다. 1. RocksDB 캐싱 기술 소개 RocksDB 캐싱 기술은 고성능 캐싱 기술입니다.

제목: Golang 변수의 저장 위치와 메커니즘에 대한 심층 탐구. 클라우드 컴퓨팅, 빅데이터 및 인공지능 분야에서 Go 언어(Golang)의 적용이 점차 증가함에 따라 이에 대한 심층적인 탐구가 특히 중요합니다. Golang 변수의 저장 위치와 메커니즘에 대한 심층적인 이해. 이 기사에서는 Golang의 메모리 할당, 저장 위치 및 관련 변수 메커니즘에 대해 자세히 설명합니다. 특정 코드 예제를 통해 독자가 Golang 변수가 메모리에 저장되고 관리되는 방식을 더 잘 이해할 수 있도록 도와줍니다. 1.Golang 변수의 메모리

RocksDB로 MySQL 살펴보기: 보다 효율적인 데이터 저장 및 검색 개요: 인터넷 산업의 급속한 발전으로 인해 데이터 크기와 액세스 로드도 증가하고 있습니다. 기존 관계형 데이터베이스는 대규모 데이터 스토리지와 높은 동시 읽기 및 쓰기를 처리할 때 성능 병목 현상에 직면하는 경우가 많습니다. 이 문제를 해결하기 위해 새로운 스토리지 엔진 RocksDB가 탄생했습니다. 이 기사에서는 RocksDB를 사용하여 MySQL을 탐색하여 데이터 저장 및 검색의 이점을 보여주고 코드 예제를 통해 이를 검증합니다. 큰 괴조

Go 언어(Golang이라고도 함)는 동시성 및 가비지 수집 메커니즘과 같은 기능을 갖춘 Google에서 개발한 효율적인 프로그래밍 언어입니다. 이 글에서는 Go 언어의 가비지 수집 메커니즘과 그 원리, 구현 방법, 코드 예제를 자세히 설명합니다. 1. 가비지 수집 원리 Go 언어의 가비지 수집 메커니즘은 "mark-clear" 알고리즘을 통해 구현됩니다. 프로그램이 실행되는 동안 Go 런타임은 힙에서 액세스할 수 있는(표시된) 개체와 액세스할 수 없는 개체, 즉 가비지 데이터(삭제해야 함)를 추적합니다.

PHP의 암시적 변환 메커니즘 분석 PHP 프로그래밍에서 암시적 변환은 유형 변환을 명시적으로 지정하지 않고 PHP가 자동으로 한 데이터 유형을 다른 데이터 유형으로 변환하는 프로세스를 의미합니다. 암시적 변환 메커니즘은 프로그래밍에서 매우 일반적이지만 예상치 못한 버그가 발생할 수도 있습니다. 따라서 강력한 PHP 코드를 작성하려면 암시적 변환 메커니즘의 원리와 규칙을 이해하는 것이 매우 중요합니다. 1. 정수와 부동 소수점 유형 간의 암시적 변환 PHP에서는 정수와 부동 소수점 유형 간의 암시적 변환이 매우 일반적입니다. 정수일 때

지식의 대중화: JS 캐싱 메커니즘의 5가지 중요한 개념을 이해합니다. 프론트엔드 개발에서는 JavaScript(JS) 캐싱 메커니즘이 매우 핵심적인 개념입니다. 캐싱 메커니즘을 이해하고 올바르게 적용하면 웹 페이지의 로딩 속도와 성능이 크게 향상될 수 있습니다. 이 기사에서는 JS 캐싱 메커니즘의 다섯 가지 중요한 개념을 소개하고 해당 코드 예제를 제공합니다. 1. 브라우저 캐시 브라우저 캐시는 귀하가 처음으로 웹페이지를 방문할 때 브라우저가 웹페이지의 관련 리소스(예: JS 파일, CSS 파일, 사진 등)를 저장한다는 의미입니다.
