Mysql 데이터베이스 InnoDB 엔진은 행 수준 잠금을 지원합니다. 즉, 테이블의 특정 데이터 행에 대해 잠금 작업을 수행할 수 있다는 의미입니다. 잠금 작업의 영향은 다음과 같습니다. 하나의 트랜잭션이 테이블의 특정 행에 대해 잠금 작업을 수행하는 경우. , 다른 트랜잭션도 동일한 행에 잠금 작업을 수행해야 하므로 두 번째 트랜잭션의 잠금 작업이 차단될 수 있습니다. 일단 차단되면 두 번째 트랜잭션은 첫 번째 트랜잭션이 완료(커밋 또는 롤백)될 때까지만 대기할 수 있습니다. 또는 시간 초과.
이 기사에서는 주로 행 잠금의 잠금 범위에 초점을 맞춰 InnoDB의 행 잠금과 관련된 개념을 소개합니다.
어떤 종류의 SQL 문이 잠길까요?
어떤 종류의 자물쇠를 추가해야 하나요?
잠금 문으로 잠길 행은 무엇인가요?
위에서 InnoDB의 행 수준 잠금에 대해 간략하게 소개했습니다. 후속 검증 부분을 이해하려면 몇 가지 배경 지식을 추가해야 합니다. 해당 지식을 아주 잘 알고 계시다면 바로 검증 부분으로 넘어갈 수 있습니다.
InnoDB 엔진은 다음과 같은 7가지 유형의 잠금을 사용합니다.
공유 및 배타적 잠금(공유 및 배타적 잠금)
의도 잠금(의도 잠금)
기록 잠금
간격 잠금
다음 키 잠금
삽입 의도 잠금
자동 입력 C Locks
이 문서는 주로 공유 및 Exclusive Locks, Record Locks, Gap Locks 및 Next-Key Locks. 다른 유형의 잠금 장치에 관심이 있다면 여기서는 자세히 설명하지 않겠습니다.
공유 잠금(S 잠금)과 배타적 잠금(X 잠금)의 개념은 많은 프로그래밍 언어에 등장했습니다. 먼저 MySQL에서 이 두 가지 잠금의 영향을 설명하겠습니다.
트랜잭션이 특정 데이터 행에 S 잠금을 추가하면 다른 트랜잭션도 해당 행에 S 잠금을 추가할 수 있지만 해당 행에는 추가할 수 없습니다. X 잠금을 추가합니다.
트랜잭션이 특정 데이터 행에 X 잠금을 추가하면 다른 트랜잭션은 해당 행에 S 잠금이나 X 잠금을 추가할 수 없습니다.
공유 잠금과 배타적 잠금 사이의 상호 배타적 관계를 계속 설명하려면 기본 매트릭스 테이블을 사용하십시오.
X | 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
그림에서 S는 공유 잠금을 나타내고 X는 독점 잠금을 나타냅니다. 0은 잠금 호환성을 나타내고 1은 잠금 충돌이 차단되지 않음을 나타냅니다. 테이블에서 볼 수 있듯이 트랜잭션이 배타적 잠금을 추가하면 다른 트랜잭션은 잠금을 기다려야 합니다. 여러 공유 잠금은 서로를 차단하지 않습니다. 1.2 레코드 잠금, 갭 잠금, 다음 키 잠금이 세 가지 유형의 잠금은 모두 잠금 범위를 설명하므로 함께 설명합니다. 다음 정의는 공식 MySQL 문서
정의는 모두 색인 레코드를 언급합니다. 왜? 행 잠금과 인덱스의 관계는 무엇입니까? 실제로 InnoDB는 테이블의 인덱스를 검색하거나 스캔하여 잠금 작업을 완료합니다. InnoDB는 만나는 모든 인덱스 데이터에 공유 잠금 또는 배타적 잠금을 추가합니다. 따라서 행 수준 잠금(행 수준 잠금)을 인덱스 레코드 잠금(인덱스 레코드 잠금)이라고 부를 수 있습니다. 행 수준 잠금은 행에 해당하는 인덱스에 추가되기 때문입니다. 세 가지 유형의 자물쇠의 잠금 범위는 서로 다르며 점차 확장됩니다. 다양한 잠금의 잠금 범위를 간략하게 설명하기 위해 예를 들어 보겠습니다. 테이블 t의 인덱스 열에 3, 5, 8, 9의 4가지 숫자 값이 있다고 가정합니다. 공식 문서에 따르면 잠금 범위는 다음과 같습니다. 세 개의 잠금은 다음과 같이 결정됩니다.
마지막으로 간격 잠금에 대해 세 가지 사항을 추가해야 합니다:
select ... from lock in. 공유 모드 문: 이 문의 차이점 일반적인 select 문은 공유 모드의 잠금이 마지막에 추가된다는 것입니다. 문자 그대로의 의미로 볼 때 이는 잠긴 읽기 문이고 잠금 유형은 공유 잠금(읽기 잠금)입니다. 검색된 모든 인덱스 레코드에 추가되지만 고유 인덱스의 유일한 행을 검색하면 next-key가 인덱스 레코드 잠금으로 다운그레이드됩니다.
더 이상 고민하지 말고 이 문서의 SQL 문 확인 부분을 살펴보겠습니다. 1. 테스트 환경데이터베이스: MySQL 5.6.35 2. 검증 시나리오2.1 시나리오 1테이블 생성: CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(8) NOT NULL, PRIMARY KEY (`id`), KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 데이터 삽입: INSERT INTO `user` (`id`, `name`) VALUES ('1', 'a'); INSERT INTO `user` (`id`, `name`) VALUES ('3', 'c'); INSERT INTO `user` (`id`, `name`) VALUES ('5', 'e'); INSERT INTO `user` (`id`, `name`) VALUES ('7', 'g'); INSERT INTO `user` (`id`, `name`) VALUES ('9', 'i'); 먼저 SQL 문의 템플릿을 실행합니다:
Re 단계의 지명 5개 값, 관찰 결과:
결과를 관찰한 결과, SQL 문 接下来我们确定next-key锁中哪部分是索引记录锁哪部分是间隙锁。 执行SQL语句的模板:
替换步骤5中name的值,观察结果:
因为间隙锁只会阻止insert语句,所以同样的索引数据, 观察执行结果可知,d和f为间隙锁,e为索引记录锁。 结论:通过两条SQL,我们确定了对于辅助索引name在查询条件为
5
이고 name='e' 가 (c, e], (e, g)일 때 보조 인덱스 이름에 대한 잠금 범위를 확인했습니다. 그 중: 🎜🎜🎜🎜 SQL 문으로 스캔한 인덱스 레코드 e에 인덱스 레코드 잠금 [e]를 추가합니다. 🎜🎜🎜🎜 e 앞의 간격을 잠그고 c 사이의 데이터(c, e)에 간격 잠금을 추가합니다. and e. 🎜🎜🎜🎜처음 두 개는 다음 키 잠금(c, e]를 구성합니다. 🎜🎜🎜🎜e 뒤의 간격(e, g)도 잠겨 있다는 점에 주목할 가치가 있습니다. 🎜🎜🎜🎜세심함 여기 독자들은 테스트 데이터에 간격 경계 데이터 c와 g가 없다는 것을 발견했을 수도 있습니다. 다음으로 간격 경계 값을 테스트하겠습니다. 🎜🎜🎜🎜🎜Step 🎜🎜client 1🎜🎜client 2. 🎜🎜 🎜🎜🎜🎜1🎜🎜begin;🎜🎜--🎜🎜🎜🎜2🎜🎜SELECT * FROM user where name='e' for update;🎜🎜--🎜🎜🎜🎜3🎜🎜- 시작하다; 🎜🎜🎜🎜4🎜🎜--🎜🎜INSERT INTO `user` (`id`, `name`) VALUES (#{id}, #{name});🎜🎜🎜🎜5🎜🎜rollback;🎜 🎜- -🎜🎜🎜🎜6🎜🎜--🎜🎜롤백;🎜🎜🎜🎜5단계에서 id 및 name 값을 바꾸고 결과를 관찰합니다.
如果先不看
接下来我们解释一下 client 1
실행 결과
替换步骤5中name的值,观察结果:
由测试结果可知,只有 通过SQL语句我们验证了,对于唯一索引列加锁,间隙锁失效, 2.3 场景三场景一和场景二都是在查询条件等于的情况下做出的范围判断,现在我们尝试一下其他查询条件,看看结论是否一致。 借用场景一的表和数据。 建表: CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(8) NOT NULL, PRIMARY KEY (`id`), KEY `index_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 插入数据: INSERT INTO `user` (`id`, `name`) VALUES ('1', 'a'); INSERT INTO `user` (`id`, `name`) VALUES ('3', 'c'); INSERT INTO `user` (`id`, `name`) VALUES ('5', 'e'); INSERT INTO `user` (`id`, `name`) VALUES ('7', 'g'); INSERT INTO `user` (`id`, `name`) VALUES ('9', 'i'); 执行SQL语句的模板:
替换步骤5中name的值,观察结果:
这个结果是不是和你想象的不太一样,这个结果表明 我们执行以下的SQL语句执行计划: explain select * from user where name>'e' for update; 执行结果: +----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ | 1 | SIMPLE | user | index | index_name | index_name | 26 | NULL | 5 | Using where; Using index | +----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ 1 row in set (0.00 sec) 如果你的结果与上面不同先执行一下 通过观察SQL语句的执行计划我们发现,语句使用了 接下来我们再制造一组数据。 CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(8) NOT NULL, `age` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `index_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 插入数据: INSERT INTO `user` (`id`, `name`,`age`) VALUES ('1', 'a','15'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('3', 'c','20'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('5', 'e','16'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('7', 'g','19'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('9', 'i','34'); 这张表和前表的区别是多了一列非索引列 我们再执行一下同样的SQL语句执行计划: explain select * from user where name>'e' for update; 执行结果: +----+-------------+-------+-------+---------------+------------+---------+------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------------+---------+------+------+-----------------------+ | 1 | SIMPLE | user | range | index_name | index_name | 26 | NULL | 2 | Using index condition | +----+-------------+-------+-------+---------------+------------+---------+------+------+-----------------------+ 1 row in set (0.00 sec) 是不是和第一次执行结果不同了, 因为
接下来我们逐一测试: 首先测试验证了next-key锁范围,执行SQL语句的模板:
替换步骤5中name的值,观察结果:
下面验证next-key锁中哪部分是间隙锁,哪部分是索引记录锁,执行SQL语句的模板:
替换步骤5中name的值,观察结果:
接下来验证对
替换步骤5中id的值,观察结果:
最后我们验证
替换步骤5中id的值,观察结果:
注意7和9是索引记录锁记录锁。 观察上面的所有SQL语句执行结果,可以验证 2.4 场景四我们通过场景三验证了普通索引的范围查询语句加锁范围,现在我们来验证一下唯一索引的范围查询情况下的加锁范围。有了场景三的铺垫我们直接跳过扫描全部索引的情况,创建可以扫描范围记录的表结构并插入相应数据测试。 建表: CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(8) NOT NULL, `age` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `index_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 插入数据: INSERT INTO `user` (`id`, `name`,`age`) VALUES ('1', 'a','15'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('3', 'c','20'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('5', 'e','16'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('7', 'g','19'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('9', 'i','34'); 和场景三表唯一不同是 SQL语句 我们通过SQL验证我们的结论,执行SQL语句的模板:
5단계에서 name 값을 바꾸고 결과를 관찰합니다.
다음 키 잠금 중 어느 부분이 간격 잠금이고, 어떤 부분이 인덱스 레코드 잠금인지 확인해 보겠습니다. SQL 문 실행을 위한 템플릿:
到目前为止所有实验结果和场景三完全一样,这也很好理解,毕竟场景四和场景三只是辅助索引 -- begin;
7과 9는 인덱스 레코드 잠금 레코드 잠금입니다 . 결과를 보면 고유 인덱스에 대한 범위 쿼리는 일반 인덱스에 대한 범위 쿼리와 유사하지만 유일한 차이점은 보조 인덱스가 상위 및 하위 범위의 경계 값과 같을 때 기본 키에 간격 잠금이 추가되지 않는다는 것입니다. 고유 인덱스 범위 쿼리 잠금 범위:
결론InnoDB 엔진은 스캔한 인덱스 레코드에 해당 잠금을 추가합니다. "시나리오 1"을 통해 일반 인덱스 레코드 스캔의 잠금 범위를 명확히 했습니다. 일반 인덱스 레코드에 대한 잠금 범위 수에는 제한이 없습니다. "시나리오 2"를 통해 고유 인덱스 레코드(또는 기본 키)를 검색하기 위한 잠금 범위를 결정했습니다. "시나리오 4"를 통해 스캔 인덱스 레코드(또는 기본 키) 수에 관계없이 잠금 범위를 유추할 수 있습니다. 두 SQL 문이 서로 잠겨 있는지 여부를 확인하기 위해 실제 응용 프로그램에서 유연하게 사용할 수 있습니다. 여기서도 인덱스의 쿼리 조건은 당연하게 받아들일 수 없으며, 실행 계획을 기반으로 인덱스가 최종적으로 스캔하는 레코드 수를 판단해야 하는 경우가 많습니다. 잠금 범위를 이해하는 데 편차가 발생합니다. Remarks참고 1: 트랜잭션 격리 수준이 SERIALIZABLE인 경우 일반 select 문은 문 실행 중에 검색된 인덱스에 다음 키 잠금도 추가합니다. 명령문이 고유 인덱스를 스캔하는 경우 다음 키 잠금이 인덱스 레코드 잠금으로 다운그레이드됩니다. 관련 권장 사항: sql 파일 실행 시 mysql 오류 오류: 알 수 없는 저장소 엔진'InnoDB 해결 방법 MySQL 시작 시 InnoDB 엔진이 비활성화된 경우 수행할 작업 MySQL 저장소 엔진 MyISAM 및 InnoDB 비교 |
위 내용은 MySQL 데이터베이스 InnoDB 엔진의 행 수준 잠금 잠금 범위에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!