>  기사  >  데이터 베이스  >  mysql 팬텀리딩 해결방법

mysql 팬텀리딩 해결방법

WBOY
WBOY앞으로
2023-06-02 19:13:241631검색

트랜잭션 격리 수준(tx_isolation)

mysql에는 4가지 수준의 트랜잭션 격리가 있습니다. 각 수준에는 문자 또는 숫자가 있습니다.

읽어보세요 -UNCOMMITTED0123
더티 읽기, 반복 불가능 읽기, 팬텀 읽기에 문제가 있습니다 읽기 제출 READ-COMMITTED
더티 읽기 문제를 해결하려면 다음과 같은 문제가 있습니다. 반복 불가능 읽기 및 팬텀 읽기 문제 반복 읽기 REPEATABLE-READ
mysql 기본 레벨은 더티 읽기, 반복 불가능 읽기 및 팬텀 읽기 문제를 해결합니다. MMVC 메커니즘을 사용하여 반복 읽기 구현 Serialization SERIALIZABLE
더티 읽기, 반복 불가능 읽기, 팬텀 읽기를 해결하여 트랜잭션 보안을 보장하지만 완전히 직렬 실행으로 성능이 가장 낮습니다

다음 명령을 사용하여 글로벌/세션의 트랜잭션 격리 수준을 보거나 설정할 수 있습니다.

mysql> SELECT @@global.tx_isolation, @@tx_isolation;
+-----------------------+------------------+
| @@global.tx_isolation | @@tx_isolation   |
+-----------------------+------------------+
| REPEATABLE-READ       | READ-UNCOMMITTED |
+-----------------------+------------------+
1 row in set (0.00 sec)

# 设定全局的隔离级别 设定会话 global 替换为 session 即可 把set语法温习一下
# SET [GLOABL] config_name = 'foobar';
# SET @@[session.|global.]config_name = 'foobar';
# SELECT @@[global.]config_name;

SET @@gloabl.tx_isolation = 0;
SET @@gloabl.tx_isolation = 'READ-UNCOMMITTED';

SET @@gloabl.tx_isolation = 1;
SET @@gloabl.tx_isolation = 'READ-COMMITTED';

SET @@gloabl.tx_isolation = 2;
SET @@gloabl.tx_isolation = 'REPEATABLE-READ';

SET @@gloabl.tx_isolation = 3;
SET @@gloabl.tx_isolation = 'SERIALIZABLE';

phantom reading

우선 팬텀 리딩이 무엇인지 이해해야 합니다. 인터넷에 많은 블로그 게시물이 있는 것 같습니다. 팬텀리딩에 대한 설명은 잘 생각해보면 알 수 있을 것 같습니다. 뒤집기의 예는 비차단 IO를 비동기 IO와 동일시하는 블로그 게시물과 같으며 실제로 두 가지를 차용했습니다. Non-Blocking IO는 비동기 IO가 아닌 동기 IO 모드입니다. 대중의 오해는 '수정'되었으니 다시 본론으로 돌아가자.

팬텀 읽기는 RU/RC/RR 수준에 나타납니다. SERIALIZABLE은 팬텀 읽기를 제거합니다. 그러나 더티 읽기 및 반복 불가능한 읽기는 여전히 RU/RC에 존재하므로 RR 수준에서 팬텀 읽기를 연구하고 다른 것을 제외합니다. . 간섭.

참고: RR 수준에서 팬텀 판독 가능성이 있지만 레코드에 X 잠금을 수동으로 추가하는 방법을 사용하여 팬텀 판독을 제거할 수도 있습니다. SERIALIZABLE은 모든 트랜잭션에 X 잠금을 추가하여 팬텀 읽기를 방지하지만 많은 시나리오에서 비즈니스 SQL에는 팬텀 읽기 위험이 없습니다. SERIALIZABLE을 사용하면 트랜잭션의 절대적인 보안을 보장할 수 있지만 성능상 불필요한 손실이 많이 발생합니다. 따라서 RR에서 비즈니스 요구에 따라 잠금 여부를 결정할 수 있으며, 팬텀 읽기의 위험이 있으면 잠그지 않습니다. 이는 트랜잭션 보안과 성능을 모두 갖습니다. 그렇기 때문에 mysql의 기본 격리 수준인 RR이 트랜잭션 격리 수준이므로 팬텀 읽기에 대한 올바른 이해가 필요합니다.

팬텀 읽기 오류에 대한 이해: 팬텀 읽기는 트랜잭션 A가 서로 다른 데이터 세트를 얻기 위해 두 가지 선택 작업을 수행하는 경우, 즉 선택 1은 10개의 레코드를 얻고 선택 2는 11개의 레코드를 얻는 경우라고 합니다. 이는 실제로 팬텀 읽기가 아닙니다. 이는 반복 불가능한 읽기 유형으로, R-U R-C 수준에서만 발생하지만 mysql의 기본 RR 격리 수준에서는 발생하지 않습니다.

팬텀 읽기에 대한 좀 더 일반적인 이해는 다음과 같습니다.

팬텀 읽기는 두 번의 읽기로 얻은 결과 세트가 다르다는 것을 의미하지 않습니다. 팬텀 읽기의 초점은 특정 선택 작업의 결과로 표시될 수 없습니다. 후속 사업 운영을 지원합니다. 보다 구체적으로 말하면, 특정 레코드가 존재하는지 여부를 선택하고, 존재하지 않는 경우 레코드를 삽입할 준비를 합니다. 그러나 삽입 실행 시 해당 레코드가 이미 존재하므로 이때는 팬텀 읽기를 수행할 수 없습니다. 발생합니다.

다음은 MySQL 팬텀 읽기에 대한 보다 생생한 시나리오입니다(Zhihu에 대한 내 답변에서 빌려옴).

table users: id primary key

Transaction T1

mysql 팬텀리딩 해결방법


Transaction T2

mysql 팬텀리딩 해결방법

step1 T1: SELECT * FROM `users ` WHERE` id` = 1;
step2 T2: INSERT INTO `users` VALUES (1, 'big cat');
step3 T1: INSERT INTO `users` VALUES (1, 'big cat');
step4 T1: SELECT * FROM `users` WHERE `id` = 1;

T1: 기본 트랜잭션, 테이블에 ID가 1인 레코드가 있는지 감지하고 없으면 이를 삽입합니다. 이는 우리가 기대하는 일반적인 비즈니스 논리입니다.

T2: 간섭 트랜잭션, 목적은 T1의 정상적인 트랜잭션 실행을 방해하는 것입니다.

RR 격리 수준에서는 step1과 step2가 정상적으로 실행되지만 step3은 T1의 비즈니스에 대해 실행이 실패합니다. T1이 step1에 있기 때문입니다. 읽기 데이터 상태는 후속 비즈니스 작업을 지원할 수 없습니다. T1: "젠장, 방금 읽은 결과가 이런 작업을 지원할 수 있어야 하는데 지금은 왜 할 수 없습니까?" T1은 이를 믿을 수 없어 4단계를 다시 실행했고, setp1이 읽은 결과가 동일하다는 것을 발견했습니다(RR의 MMVC 메커니즘). 이때 T1이 아무리 읽어도 id=1인 레코드는 찾을 수 없으나 읽어서 존재하지 않는다고 판단한 이 레코드는 삽입할 수 없음이 틀림없다. T2 ), T1의 경우 팬텀으로 읽습니다. 실제로 RR은 행을 수동으로 추가하여 팬텀 읽기를 피할 수도 있습니다. 현재 레코드가 존재하지 않더라도(예: id=1이 존재하지 않음) 현재 트랜잭션은 레코드 잠금을 획득합니다(InnoDB의 행 잠금이 인덱스를 잠그기 때문) , 레코드 엔터티가 존재하는지 여부는 중요하지 않으며 존재하는 경우 행 X 잠금을 추가합니다. 존재하지 않으면 다음 키 잠금 간격 X 잠금을 추가합니다. 다른 트랜잭션은 이 인덱스에 레코드를 삽입할 수 없으므로 팬텀 읽기가 제거됩니다.

SERIALIZABLE 격리 수준에서는 step1이 실행될 때 행(X) 잠금/갭(X) 잠금이 암시적으로 추가되므로 step2는 차단되고 step3은 정상적으로 실행되며 T1이 제출된 후에도 T2는 계속 실행할 수 있습니다. (기본 키 충돌 실행 실패) T1의 경우 성공적인 차단으로 T2가 종료되어 T1의 경우 조기 판독 결과가 후속 비즈니스를 지원할 수 있습니다.

所以 mysql 的幻读并非什么读取两次返回结果集不同,而是事务在插入事先检测不存在的记录时,惊奇的发现这些数据已经存在了,之前的检测读获取到的数据如同鬼影一般。

这里要灵活的理解读取的意思,第一次select是读取,第二次的 insert 其实也属于隐式的读取,只不过是在 mysql 的机制中读取的,插入数据也是要先读取一下有没有主键冲突才能决定是否执行插入。

不可重复读侧重表达 读-读,幻读则是说 读-写,用写来证实读的是鬼影。

RR级别下防止幻读

RR级别下只要对 SELECT 操作也手动加行(X)锁即可类似 SERIALIZABLE 级别(它会对 SELECT 隐式加锁),即大家熟知的:

# 这里需要用 X锁, 用 LOCK IN SHARE MODE 拿到 S锁 后我们没办法做 写操作
SELECT `id` FROM `users` WHERE `id` = 1 FOR UPDATE;

如果 id = 1 的记录存在则会被加行(X)锁,如果不存在,则会加 next-lock key / gap 锁(范围行锁),即记录存在与否,mysql 都会对记录应该对应的索引加锁,其他事务是无法再获得做操作的。

这里我们就展示下 id = 1 的记录不存在的场景,FOR UPDATE 也会对此 “记录” 加锁,要明白,InnoDB 的行锁(gap锁是范围行锁,一样的)锁定的是记录所对应的索引,且聚簇索引同记录是直接关系在一起的。

mysql 팬텀리딩 해결방법

id = 1 的记录不存在,开始执行事务:
step1: T1 查询 id = 1 的记录并对其加 X锁
step2: T2 插入 id = 1 的记录,被阻塞
step3: T1 插入 id = 1 的记录,成功执行(T2 依然被阻塞中),T1 提交(T2 唤醒但主键冲突执行错误)
T1事务符合业务需求成功执行,T2干扰T1失败。

SERIALIZABLE级别杜绝幻读

在这个层面上,我们不必对 SELECT 操作进行显式加锁,因为InnoDB会自动加锁以确保事务的安全性,但是这会导致性能较低

mysql 팬텀리딩 해결방법

step1: T1 查询 id = 2 的记录,InnoDB 会隐式的对齐加 X锁
step2: T2 插入 id = 2 的记录,被阻塞
step3: T1 插入 id = 2 的记录,成功执行(T2 依然被阻塞中)
step4: T1 成功提交(T2 此时唤醒但主键冲突执行错误)
T1事务符合业务需求成功执行,T2干扰T1失败。

위 내용은 mysql 팬텀리딩 해결방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제