Redis에서는 특정 키의 값을 읽어서 일부 비즈니스 로직 처리를 수행한 후 읽은 값을 기준으로 새로운 값을 계산하고 다시 설정하는 상황이 종종 있습니다.
클라이언트 A가 키 값을 방금 읽은 후 클라이언트 B가 키 값을 수정하면 동시성 보안 문제가 발생합니다.
Redis 서버에 json 배열 [1, 2, 3]을 저장하는 test라는 키가 있다고 가정합니다.
클라이언트 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의 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 } } }
최종 "테스트" 내용은 다음과 같습니다.
WATCH 및 TRANACTION 명령을 사용하여 구현하는 것을 볼 수 있습니다. CAS 낙관적 잠금 일관성을 사용하는 데이터.
위 내용은 Redis가 낙관적 잠금을 사용하여 데이터 일관성을 보장하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!