mysql采用默认事务隔离级别REPEATABLE-READ;
然后我分别执行以下内容:
1、
2、使用jdbc访问数据库:
3、这时候打印结果,发现线程进入阻塞,一直卡在这里
也就是说我开启的另一个事务仍然能读取到数据,只是在最后执行executeUpdate的时候才被锁定不能执行
4、命令行执行commit
5、结果命令行操作被jdbc操作替换掉,数据改为4。
这不就是说REPEATABLE-READ仍然出现了丢失更新,而且没有行锁定吗?
事务与并发到底是什么区别,我在测试多线程执行以上jdbc操作时,发现最后得到的结果总是不对。例我开十个线程,每个都对数据+1,结果可能只加了5.所以在并发情况下怎么做才能保证数据的安全?
PHPz2017-04-17 17:57:17
1만 증가하면 number의 값은 select를 통해 얻어지지 않으며, select로 얻은 값은 이전 값일 수 있으므로 업데이트 테스트 세트 number = number + 1로 대체합니다. 여기서 id = ? . 여기에 있는 번호는 최신 번호입니다. 이 방법을 사용하면 업데이트 선택의 전체 직렬 성능 손실을 방지할 수 있습니다.
怪我咯2017-04-17 17:57:17
REPEATABLE-READ는 팬텀 읽기를 유발할 수 있습니다
절대적인 보안을 보장하려면 격리 수준을 SERIALIZABLE로만 설정할 수 있습니다
이렇게 하면 모든 트랜잭션은 순차적으로만 실행될 수 있으며 자연스럽게 동시성으로 인해 문제는 없지만 성능이 많이 저하됩니다.
성능 저하를 원하지 않지만 잘못되지 않도록 제어하고 싶다면 이제 더 일반적인 접근 방식은 최신 버전 제어를 사용하는 것입니다.
필드를 UpdateVersion으로 유지합니다. 수정 시 updateversion도 매개변수로 전달됩니다. 예를 들어 조건문에 where id=? and update_version = ?
을 추가합니다. 물론 set에는 update_version+1이 필요합니다.
한 번에 한 사람만 한 버전을 업데이트할 수 있도록 제어할 수 있습니다.
PHP中文网2017-04-17 17:57:17
실험을 통해 나는 내 문제를 어느 정도 이해했음을 발견했습니다.
이전에는 REPEATABLE-READ 수준과 SERIALIZABLE 수준이 업데이트 손실 문제를 해결할 수 있다고 생각했지만 실제로는 그렇지 못한 트랜잭션 격리 수준에 대한 잘못된 정보에 오해를 받았습니다.
mysql의 select ... from table; 문은 격리 수준에 관계없이 차단되지 않기 때문에
격리 수준에서 쓰기 잠금(배타적 잠금)은 데이터를 업데이트할 때 릴리스만 기다리므로 여러 스레드가 동시에 숫자=3을 읽을 수 있으므로 이를 기반으로 수정하면 필연적으로 업데이트가 손실됩니다.
그래서 해결책은 비관적 잠금 또는 낙관적 잠금 메커니즘을 프로그램에 추가하는 것입니다.
비관적 잠금은 업데이트를 위해 선택 ...을 사용합니다.
이때 트랜잭션 B의 읽기 작업은 실행되지 않습니다. 트랜잭션 A의 커밋이 완료되어야만 트랜잭션 B가 계속 실행될 수 있으며 이는 순차적으로 하나씩 실행되는 것과 같습니다.
낙관적 잠금은 일반적으로 버전 제어나 타임스탬프 제어에 사용됩니다.
으아아아이때 트랜잭션 B는 version=1이 더 이상 존재하지 않음을 발견했습니다. 트랜잭션 A가 먼저 실행을 완료하고 데이터베이스를 업데이트하여 버전 필드를 2로 설정했기 때문입니다. 이로 인해 트랜잭션 제출이 실패하게 되므로 다음 작업을 수행해야 합니다. 프로그램의 이상 상황을 판단하는 방법은 다음과 같습니다.
위 내용은 현재 제가 알고 있는 내용입니다. MySQL 자체에서 지원하는 MVCC(Multiple Version Concurrency Control)에 대해서는 아직 어떻게 사용하는지 모르겠습니다. 낙관적 잠금의 예외 처리를 어떻게 구현하는지 모르겠습니다. . 특정 비즈니스 시나리오를 기반으로 할 수 있습니다.
怪我咯2017-04-17 17:57:17
간단한 Select
이라면 단순 질의 연산으로 간주되어 정지되지 않습니다.
특정 행에 대해 트랜잭션을 수행하려면 Select
시 먼저 Select ... For Update
형식을 사용해야 합니다. For Update
을 사용하면 선택한 행이 잠깁니다. 다른 거래가 수행되면 해당 거래의 Select ... For Update
에서 일시 중지되고 계속하기 전에 현재 거래가 완료될 때까지 기다립니다. 이렇게 하면 쿼리와 쓰기 사이에 중개자가 없습니다.