이 글에서는 데이터베이스 트랜잭션 및 잠금 관련 지식을 자세히 소개합니다. 주로 일부 개념적인 내용은 지루해 보일 수 있지만 자격을 갖춘 프로그래머로서 이러한 개념을 숙지해야 합니다. 이 이론적 지식은 사람의 내적 힘과 같습니다. 우리가 일반적으로 코딩할 때 내적, 외적 기술을 모두 연습하고 서로 격려해야 무술 고수의 수준에 도달할 수 있습니다. 좋아요, 더 이상 고민하지 말고 시작해 보겠습니다.
데이터베이스 트랜잭션
트랜잭션 경계
트랜잭션 시작 경계(begin)
트랜잭션 끝 경계(commit): 트랜잭션을 제출하고 완료 후 영구 저장합니다. 트랜잭션 데이터베이스 상태에 따라 업데이트됩니다.
비정상적인 트랜잭션 종료 경계(롤백): 트랜잭션을 취소하고 데이터베이스를 트랜잭션 실행 전의 초기 상태로 되돌린다.
MySQL.exe 프로그램을 시작할 때마다 별도의 데이터베이스 연결이 이루어집니다. 각 데이터베이스 연결에는 현재 트랜잭션 모드를 나타내는 전역 변수 autocommit이 있습니다. 여기에는 두 가지 선택적 값이 있습니다.
0: 수동 커밋 모드를 나타냅니다.
1: 자동 제출 모드를 나타내며 기본값은
이 값을 보고 수정할 수 있습니다.
데이터베이스 트랜잭션(ACID)의 네 가지 특성:
원자성: 트랜잭션은 원자적 연산 단위이며, 데이터를 수정하거나 모두 실행하거나 또는 전혀 실행하지 않습니다.
일관성: 트랜잭션이 시작되고 완료될 때 데이터가 일관성을 유지해야 합니다.
격리: 데이터베이스 시스템은 외부 동시 작업의 영향을 받지 않는 "독립적인" 환경에서 트랜잭션이 실행되도록 특정 격리 메커니즘을 제공합니다.
내구성: 트랜잭션이 완료된 후 데이터는 영구적이며 시스템 장애가 발생하더라도 유지될 수 있습니다.
트랜잭션 격리 수준
데이터베이스 트랜잭션 격리 수준은 한 트랜잭션이 다른 트랜잭션의 중간 결과를 읽을 수 있는지 여부에 관한 것입니다.
Read Uncommitted(커밋되지 않은 콘텐츠 읽기)
이 격리 수준에서 모든 트랜잭션은 커밋되지 않은 다른 트랜잭션의 실행 결과를 볼 수 있습니다. 이 격리 수준은 성능이 다른 수준보다 그다지 좋지 않기 때문에 실제 응용 프로그램에서는 거의 사용되지 않습니다. 커밋되지 않은 데이터를 읽는 것을 더티 읽기(dirty read)라고도 합니다.
읽기 커밋됨
이것은 대부분의 데이터베이스 시스템에 대한 기본 격리 수준입니다(MySQL 기본값은 아님). 이는 격리의 간단한 정의를 충족합니다. 트랜잭션은 커밋된 트랜잭션에 의해 변경된 내용만 볼 수 있습니다. 이 격리 수준은 소위 반복 불가능한 읽기도 지원합니다. 동일한 트랜잭션의 다른 인스턴스가 이 인스턴스를 처리하는 동안 새로운 커밋을 가질 수 있으므로 동일한 선택이 다른 결과를 반환할 수 있기 때문입니다.
반복 읽기(재읽기)
이것은 MySQL의 기본 트랜잭션 격리 수준으로, 동일한 트랜잭션의 여러 인스턴스가 동시에 데이터를 읽을 때 동일한 데이터 행을 볼 수 있도록 보장합니다. 그러나 이론적으로 이는 또 다른 까다로운 문제, 즉 팬텀 읽기(Phantom Read)로 이어집니다. 간단히 말하면, 팬텀 읽기는 사용자가 특정 범위의 데이터 행을 읽을 때 다른 트랜잭션이 해당 범위에 새 행을 삽입하는 것을 의미합니다. 사용자가 해당 범위의 데이터 행을 다시 읽으면 새로운 행이 있음을 알게 됩니다. " 좋아요. InnoDB와 Falcon 스토리지 엔진은 MVCC(Multiversion Concurrency Control) 메커니즘을 통해 이 문제를 해결합니다.
격리 수준이 높을수록 데이터의 완전성과 일관성이 보장되지만 동시성 성능에 미치는 영향은 더 커집니다.
대부분의 애플리케이션에서는 데이터베이스 시스템의 격리 수준을 커밋된 읽기로 설정하는 것을 효과적으로 고려할 수 있습니다. 그러면 더티 읽기를 방지하고 동시성 성능이 향상됩니다. 반복 불가능한 읽기, 낭비된 읽기 및 유형 II 업데이트 손실과 같은 동시성 문제가 발생하더라도 이러한 문제가 발생할 수 있는 개별 상황에서는 비관적 잠금 및 낙관적 잠금을 사용하여 애플리케이션에서 이러한 문제를 제어할 수 있습니다.
현재 실행 중인 트랜잭션을 추가합니다. 현재 트랜잭션이 없으면 새 트랜잭션을 시작합니다. Spring 운영 데이터베이스의 기본 트랜잭션 전파 동작은 propagation_required입니다.
PROPAGATION_SUPPORTS
현재 거래 중인 경우 거래로 실행됩니다. 더 이상 거래가 아닌 경우 비거래로 실행됩니다.
PROPAGATION_MANDATORY
트랜잭션 내에서 실행되어야 합니다. 즉, 상위 트랜잭션에서만 호출할 수 있습니다. 그렇지 않으면 예외가 발생합니다.
PROPAGATION_REQUIRES_NEW
현재 거래를 일시 중지하고 새 거래를 시작합니다.
PROPAGATION_NOT_SUPPORTED
는 트랜잭션 내에서 실행할 수 없으며, 트랜잭션 내에서 실행되면 예외가 발생합니다.
PROPAGATION_NESTED
상위 트랜잭션이 커밋되면 상위 트랜잭션이 롤백됩니다.
행 수준 잠금
페이지 수준: 엔진 BDB는 한 번에 인접한 레코드 그룹을 잠급니다.
테이블 수준: MyISAM 엔진은 전체 테이블을 잠그는 것으로 이해되며 동시에 읽을 수는 있지만 쓸 수는 없습니다.세 가지 잠금의 특징은 대략 다음과 같이 요약할 수 있습니다.
1) 테이블 수준 잠금: 낮은 오버헤드, 빠른 잠금, 큰 잠금 세분성, 가장 높은 잠금 충돌 가능성 및 가장 낮은 동시성 .2) 행 수준 잠금: 오버헤드가 높고 잠금이 느릴 수 있습니다. 잠금 세분성은 가장 작고 잠금 충돌 가능성도 가장 낮으며 동시성도 가장 높습니다.
3) 페이지 잠금: 비용과 잠금 시간은 테이블 잠금과 행 잠금 사이에 있습니다. 교착 상태는 테이블 잠금과 행 잠금 사이에 발생하며 동시성은 평균입니다.
여기서 주로 이야기하는 것은 행 수준 잠금입니다. 일반적으로 플래시 세일 시스템에서는 플래시 세일 중에 재고가 매우 중요한 데이터이기 때문에 행 수준 잠금을 사용합니다. 데이터베이스 테이블을 생성하는 중입니다. 다음 설정이 나타날 수 있습니다:
ENGINE = InnoDB AUTO_INCREMENT=10 DEFAULT CHARACTER SET = utf8 comment='用户表
InnoDB中两种模式的行级锁:
1)共享锁:允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
( Select * from table_name where ……lock in share mode)
2)排他锁:允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和 排他写锁。(select * from table_name where…..for update)
为了允许行锁和表锁共存,实现多粒度锁机制;同时还有两种内部使用的意向锁(都是表锁),分别为意向共享锁和意向排他锁。
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
注意:InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。
行级锁的优缺点
行级锁定的优点:
当在许多线程中访问不同的行时只存在少量锁定冲突。
回滚时只有少量的更改。
可以长时间锁定单一的行。
行级锁定的缺点:
比页级或表级锁定占用更多的内存。
当在表的大部分数据上使用时,比页级或表级锁定速度慢,因为你必须获取更多的锁。如果你在大部分数据上经常进行GROUP BY操作或
者必须经常扫描整个表,比其它锁定明显慢很多。
hibernate中通过行级锁实现的悲观锁。
一些例子:
假设有个表单products ,里面有id跟name二个栏位,id是主键。
1: 明确指定主键,并且有此条记录,执行row lock。若查无此记录,无lock。
SELECT * FROM products WHERE id='3' FOR UPDATE;SELECT * FROM products WHERE id='3' and name="cat" FOR UPDATE;
2: 无主键,执行table lock。
SELECT * FROM products WHERE name='Mouse' FOR UPDATE;
3: 主键不明确,table lock。
SELECT * FROM products WHERE id<>'3' FOR UPDATE;
注意: FOR UPDATE仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效。此外,如果A与B都对表id进行查询但查询不到记录,则A与B在查询上不会进行row锁,但A与B都会获取排它锁,此时A再插入一条记录的话则会因为B已经有锁而处于等待中,此时B再插入一条同样的数据则会抛出Deadlock found when trying to get lock; try restarting transaction。然后释放锁,此时A就获得了锁而插入成功。
以上就是MySQL中的事务与锁的内容,更多相关内容请关注PHP中文网(www.php.cn)!