> 데이터 베이스 > Redis > Redis가 낙관적 잠금을 사용하여 데이터 일관성을 보장하는 방법

Redis가 낙관적 잠금을 사용하여 데이터 일관성을 보장하는 방법

WBOY
풀어 주다: 2023-06-02 16:52:42
앞으로
782명이 탐색했습니다.

Scenario

Redis에서는 특정 키의 값을 읽어서 일부 비즈니스 로직 처리를 수행한 후 읽은 값을 기준으로 새로운 값을 계산하고 다시 설정하는 상황이 종종 있습니다.

클라이언트 A가 키 값을 방금 읽은 후 클라이언트 B가 키 값을 수정하면 동시성 보안 문제가 발생합니다.

문제 시뮬레이션

Redis 서버에 json 배열 [1, 2, 3]을 저장하는 test라는 키가 있다고 가정합니다.

Redis가 낙관적 잠금을 사용하여 데이터 일관성을 보장하는 방법

클라이언트 A와 클라이언트 B가 동시에 수정 사항에 액세스하는 상황을 시뮬레이션해 보겠습니다. 코드는 다음과 같습니다.

클라이언트 A:

class RedisClientA(username: String, password: String, host: String, port: Int) {
    val jedis: Jedis

    init {
        val pool = JedisPool(JedisPoolConfig(), host, port)
        jedis = pool.resource
        jedis.auth(username, password)
    }

    fun update(key: String) {
        val idStr = jedis.get(key)
        val idList = Json.decodeFromString<MutableList<Int>>(idStr)

        // 等待2秒,模拟业务
        TimeUnit.SECONDS.sleep(2L)

        idList.add(4)
        println("new id list: $idList")

        jedis.set(key, Json.encodeToString(idList))
    }

    fun getVal(key: String): String? {
        return jedis.get(key)
    }
}

fun main() {
    val key = "test"
    val redisClientA = RedisClientA("default", "123456", "127.0.0.1", 6379)
    redisClientA.update(key)
    val res = redisClientA.getVal(key)
    println("res: $res")
}
로그인 후 복사

클라이언트 B:

class RedisClientB(username: String, password: String, host: String, port: Int) {
    val jedis: Jedis

    init {
        val pool = JedisPool(JedisPoolConfig(), host, port)
        jedis = pool.resource
        jedis.auth(username, password)
    }

    fun update(key: String) {
        val idStr = jedis.get(key)
        val idList = Json.decodeFromString<MutableList<Int>>(idStr)

        idList.add(5)
        println("new id list: $idList")

        jedis.set(key, Json.encodeToString(idList))
    }

    fun getVal(key: String): String? {
        return jedis.get(key)
    }
}

fun main() {
    val key = "test"
    val redisClientB = RedisClientB("default", "123456", "127.0.0.1", 6379)
    redisClientB.update(key)
    val res = redisClientB.getVal(key)
    println("res: $res")
}
로그인 후 복사

클라이언트 A가 2초 동안 차단됩니다. , 시간이 많이 소요되는 비즈니스 로직 처리를 시뮬레이션하는 데 사용됩니다. 클라이언트 B는 처리 중에 "test"에 액세스하여 ID:5를 추가했습니다.

클라이언트 A가 시간이 많이 걸리는 비즈니스 로직 처리를 마치면 id:4가 추가되고 id:5가 덮어쓰여집니다.

"test"의 최종 내용은 다음과 같습니다.

Redis가 낙관적 잠금을 사용하여 데이터 일관성을 보장하는 방법

CAS로 데이터 일관성 보장

Redis의 WATCH 명령은 Redis 트랜잭션에 대한 확인 및 설정(CAS) 동작을 제공합니다. WATCHed 키가 모니터링되어 수정되었는지 여부가 검색됩니다. EXEC가 실행되기 전에 모니터링되는 개체 중 하나 이상이 수정되면 전체 트랜잭션이 취소되고 EXEC는 Null 재생을 반환하여 트랜잭션 실행 실패를 나타냅니다. 우리는 작업을 반복하고 이 기간 동안 새로운 경쟁이 없기를 바랄 뿐입니다. 이러한 형태의 잠금을 낙관적 잠금이라고 하며 이는 매우 강력한 잠금 메커니즘입니다.

그렇다면 CAS를 어떻게 구현할까요? RedisClientA의 update() 메소드에 있는 코드만 다음과 같이 수정하면 됩니다.

fun update(key: String) {
    var flag = true

    while (flag) {
        jedis.watch(key)

        val idStr = jedis.get(key)
        val idList = Json.decodeFromString<MutableList<Int>>(idStr)

        // 等待2秒,模拟业务
        TimeUnit.SECONDS.sleep(2L)

        val transaction = jedis.multi()
        idList.add(4)
        println("new id list: $idList")

        transaction.set(key, Json.encodeToString(idList))

        transaction.exec()?.let {
            flag = false
        }
    }

}
로그인 후 복사

최종 "테스트" 내용은 다음과 같습니다.

Redis가 낙관적 잠금을 사용하여 데이터 일관성을 보장하는 방법

WATCH 및 TRANACTION 명령을 사용하여 구현하는 것을 볼 수 있습니다. CAS 낙관적 잠금 일관성을 사용하는 데이터.

위 내용은 Redis가 낙관적 잠금을 사용하여 데이터 일관성을 보장하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:yisu.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿