스프링 @트랜잭션 - 격리, 전파
누군가 절연 및 전파 매개변수가 무엇에 사용되는지 설명할 수 있는가?@Transactional
실제 사례를 통한 주석 작성
기본적으로 내가 그들의 기본값을 언제 그리고 왜 변경해야 하는지 선택해야 한다.
좋은 질문이야, 비록 사소한 대답은 아니지만.
전파
거래가 서로 어떻게 관련되는지 규정한다.일반 옵션:
REQUIRED
코드는 항상 트랜잭션에서 실행된다.새 트랜잭션을 만들거나 사용 가능한 경우 트랜잭션을 다시 사용REQUIRES_NEW
코드는 항상 새로운 거래에서 실행된다.현재 트랜잭션이 있는 경우 해당 트랜잭션을 일시 중단하십시오.
에 대한 기본값@Transactional
이다REQUIRED
그리고 이것은 종종 당신이 원하는 것이다.
격리
트랜잭션 간의 데이터 계약을 정의한다.
ISOLATION_READ_UNCOMMITTED
더러워진 읽기를 허용한다.ISOLATION_READ_COMMITTED
는 허용되지 지저분한 읽기는 허용되지 않는다.ISOLATION_REPEATABLE_READ
두 번 같은 거래에서 행을 두 번 읽으면 결과는 항상 같을 것이다.ISOLATION_SERIALIZABLE
모든 트랜잭션을 순차적으로 수행한다.
레벨이 다르면 다중 스레드 애플리케이션에서 성능 특성이 다르다.나는 네가 더러운 읽기 개념을 이해한다면 좋은 선택을 할 수 있을 것이라고 생각해.
기본값은 차이 데이터베이스마다 다를 수 있다.예를 들어, 마리아DB는REPEATABLE READ
.
더티한 읽기가 발생할 수 있는 경우의 예:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
따라서 정상적인 디폴트(이러한 디폴트를 청구할 수 있는 경우)는 다음과 같을 수 있다.ISOLATION_READ_COMMITTED
이미 중인 수 것으로, 이 값은 propagation인 다다이즘의 되어 있다.REQUIRED
만약 당신의 어플리케이션이 다른 필요를 가지고 있다면 당신은 거기서 일할 수 있다.
입력 시 항상 새로운 트랜잭션이 생성될 수 있는 실질적인 예provideService
루틴 및 종료 시:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
우리가 대신 사용했었더라면REQUIRED
루틴을 입력할 때 트랜잭션이 이미 열려 있으면 트랜잭션이 열린 상태로 유지된다.또한 a의 결과도 주의하십시오.rollback
여러 실행이 동일한 거래에 참여할 수 있기 때문에 달라질 수 있다.
테스트로 동작을 쉽게 검증할 수 있으며, 결과가 전파 수준에 따라 어떻게 다른지 확인할 수 있다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {
private @Autowired TransactionManager transactionManager;
private @Autowired FooService fooService;
@Test
public void testProvideService() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);
// assert repository values are unchanged ...
}
전파 수준:
REQUIRES_NEW
: 우리는 예상할 수 있다.fooService.provideService()
자체적인 하위 트랜잭션을 만든 이후 롤백되지 않았다.REQUIRED
: 모든 것이 롤백되고 백업 스토어는 변동이 없을 것으로 예상할 수 있다.
전파_요구 = 0; 방법 M1에 대해 DataSourceTransactionObject T1이 이미 시작된 경우. 다른 방법 M2 Transaction Object가 필요한 경우, 새로운 트랜잭션 개체가 생성되지 않는다.M2에도 같은 물체 T1이 사용된다.
전파_필수화 = 2; 방법은 트랜잭션 내에서 실행되어야 한다.진행 중인 기존 거래가 없을 경우 예외가 발생한다.
전파_요구_NEW = 3; 방법 M1에 대해 DataSourceTransactionObject T1이 이미 시작되어 진행 중인 경우(방법 M1 실행).만약 다른 방법 M2가 실행을 시작한다면, T1은 M2에 대한 새로운 DataSourceTransactionObject T2를 가진 방법 M2의 기간 동안 중단된다. M2는 자신의 트랜잭션 컨텍스트 내에서 실행된다.
전파_NOT_Supported = 4; 방법 M1에 대해 DataSourceTransactionObject T1이 이미 시작된 경우. 다른 방법 M2가 동시에 실행되는 경우.그러면 M2는 트랜잭션 컨텍스트 내에서 실행되지 않아야 한다.T1은 M2가 끝날 때까지 정지한다.
전파_NEVERSION_NEVENE = 5; 트랜잭션 컨텍스트에서 실행되는 방법이 없음.
격리 수준:그것은 거래가 다른 동시 거래의 활동에 의해 얼마나 영향을 받을 수 있는지에 관한 것이다.a는 여러 테이블의 데이터를 일관된 상태로 유지하도록 일관성을 지원한다.데이터베이스의 행 및/또는 테이블을 잠그는 것을 포함한다.
다중 트랜잭션의 문제
시나리오 1. T1 트랜잭션이 다른 동시 트랜잭션 T2에 의해 작성된 표 A1의 데이터를 읽는 경우.T2가 롤백되는 과정에서 T1이 획득한 데이터는 유효하지 않은 데이터가 된다.예: a=2는 원본 데이터다.만약 T1이 t2에 의해 쓰여진 a=1을 읽는다면.T2 롤백 시, a=1은 DB에서 a=2로 롤백된다.그러나 지금은 T1이 a=1을 가지고 있지만 DB 테이블에서는 a=2로 변경된다.
시나리오2.T1 거래에서 표 A1의 데이터를 읽는 경우.다른 동시 트랜잭션(T2)이 표 A1의 데이터를 업데이트하는 경우.그러면 T1이 읽은 데이터는 표 A1과 다르다.T2는 표 A1의 데이터를 업데이트했기 때문이다. 예를 들어, T1이 a=1을 읽고 T2가 a=2를 업데이트한 경우.그럼 a!=b.
시나리오 3.T1 트랜잭션이 일정 수의 행으로 테이블 A1의 데이터를 읽는 경우.다른 동시 트랜잭션(T2)이 테이블 A1에 더 많은 행을 삽입하는 경우.T1이 읽은 행의 수는 표 A1의 행과 다르다.
시나리오 1은 더티 리딩이라고 불린다.
시나리오 2는 반복 불가능한 읽기라고 불린다.
시나리오 3은 팬텀 읽기라고 불린다.
그러므로 격리 수준은 시나리오 1, 시나리오 2, 시나리오 3을 예방할 수 있는 확장이다.당신은 잠금을 구현함으로써 완전한 격리 수준을 얻을 수 있다.그것은 동일한 데이터에 대한 동시 읽기 및 쓰기를 방지하는 것이다.하지만 그것은 성능에 영향을 미친다.격리 수준은 애플리케이션에 얼마나 많은 격리가 필요한지에 따라 달라진다.
INCLATION_READ_UNCOMMITED: 아직 커밋되지 않은 변경 내용을 읽을 수 있도록 허용.시나리오 1, 시나리오 2, 시나리오 3으로 어려움을 겪고 있다.
INCLATION_READ_COMMITED: 커밋된 동시 트랜잭션의 읽기 허용.시나리오 2와 시나리오 3으로 인해 문제가 발생할 수 있다.왜냐하면 다른 트랜잭션들이 데이터를 갱신하고 있을 수 있기 때문이다.
격리_반복 가능_READE: 동일한 필드의 다중 읽기는 스스로 변경되기 전까지 동일한 결과를 산출할 것이다.시나리오 3으로 인해 문제가 발생할 수 있다.왜냐하면 다른 트랜잭션들이 데이터를 삽입하고 있을 수 있기 때문이다.
격리_시리얼라이징: 시나리오 1, 시나리오 2, 시나리오 3은 절대 일어나지 않는다.그것은 완전한 고립이다.그것은 완전 잠금을 수반한다.그것은 잠금 때문에 공연에 영향을 미친다.
다음을 사용하여 테스트할 수 있다.
public class TransactionBehaviour {
// set is either using xml Or annotation
DataSourceTransactionManager manager=new DataSourceTransactionManager();
SimpleTransactionStatus status=new SimpleTransactionStatus();
;
public void beginTransaction()
{
DefaultTransactionDefinition Def = new DefaultTransactionDefinition();
// overwrite default PROPAGATION_REQUIRED and ISOLATION_DEFAULT
// set is either using xml Or annotation
manager.setPropagationBehavior(XX);
manager.setIsolationLevelName(XX);
status = manager.getTransaction(Def);
}
public void commitTransaction()
{
if(status.isCompleted()){
manager.commit(status);
}
}
public void rollbackTransaction()
{
if(!status.isCompleted()){
manager.rollback(status);
}
}
Main method{
beginTransaction()
M1();
If error(){
rollbackTransaction()
}
commitTransaction();
}
}
분리 및 전파를 위해 서로 다른 값을 사용하여 결과를 디버깅하고 볼 수 있다.
각 매개 변수에 대한 충분한 설명은 다른 답변에 의해 제공된다. 그러나 실제 예를 들어, 다음과 같이 다양한 전파 옵션의 목적을 명확히 하는 것이 있다.
Suppose you're in charge of implementing a 가입 서비스 in which a confirmation e-mail is sent to the user. You come up with two service objects, one for 등록 the user and one for 보내기 e-mails, which the latter is called inside the first one. For example something like this:/* Sign Up service */
@Service
@Transactional(Propagation=REQUIRED)
class SignUpService{
...
void SignUp(User user){
...
emailService.sendMail(User);
}
}
/* E-Mail Service */
@Service
@Transactional(Propagation=REQUIRES_NEW)
class EmailService{
...
void sendMail(User user){
try{
... // Trying to send the e-mail
}catch( Exception)
}
}
당신은 두 번째 서비스가 FEST_NEW 형식의 전파라는 것을 알았을 수 있고, 더욱이 그것이 예외를 발생시킬 가능성이 있다(SMTP 서버 다운, 잘못된 이메일 또는 다른 이유).당신은 데이터베이스나 다른 것들로부터 사용자 정보를 제거하는 것과 같은 전체 프로세스가 롤백되는 것을 원하지 않을 것이다. 따라서 당신은 별도의 거래에서 두 번째 서비스를 호출한다.
Back to our example, this time you are concerned about the database security, so you define your DAO classes this way:/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
// some CRUD methods
}
DAO 객체, 즉 DB에 대한 잠재적인 액세스가 생성될 때마다 실시간 트랜잭션이 존재해야 한다는 것을 암시하는, 서비스 내부에서 통화가 이루어졌음을 확신시킬 필요가 있다. 그렇지 않으면 예외가 발생한다.따라서 전파는 필수의 유형이다.
격리 수준은 한 트랜잭션에 의한 일부 데이터 저장소의 변경사항이 다른 동시 트랜잭션에 어떤 영향을 미치는지, 또한 변경된 데이터가 다른 트랜잭션에 어떻게 그리고 언제 사용 가능하게 되는지를 정의한다.Spring 프레임워크를 사용하여 트랜잭션을 정의할 때 동일한 트랜잭션이 실행될 격리 수준도 구성할 수 있다.
@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {
}
READ_UNCOMMITED 분리 수준은 트랜잭션은 다른 트랜잭션에 의해 커밋되지 않은 데이터를 읽을 수 있다고 명시한다.
READ_COMMITED 분리 수준은 트랜잭션이 아직 다른 트랜잭션에 의해 커밋되지 않은 데이터를 읽을 수 없다고 명시한다.
REFITABLE_READ 분리 수준은 트랜잭션이 데이터베이스에서 하나의 레코드를 여러 번 읽는 경우 모든 읽기 작업의 결과가 항상 동일해야 한다고 명시한다.
직렬화 가능한 격리 수준은 모든 격리 수준 중에서 가장 제한적이다.트랜잭션은 모든 레벨(읽기, 범위, 쓰기 잠금)에서 잠금으로 실행되기 때문에 마치 직렬화된 방식으로 실행된 것처럼 나타난다.
전파는 논리적 거래나 물리적 거래에서 사업방식이 어떻게 캡슐화되어야 하는지를 결정하는 능력이다.
스프링 요구 동작은 현재의 콩 방법 실행 컨텍스트에서 이미 열린 거래가 있는 경우에도 동일한 거래가 사용됨을 의미한다.
필요_NEW 동작은 컨테이너에 의해 항상 새로운 물리적 트랜잭션이 생성됨을 의미한다.
중첩 동작은 중첩된 봄철 트랜잭션을 동일한 물리적 트랜잭션을 사용하게 하지만 내부 트랜잭션도 외부 트랜잭션과 독립적으로 롤백할 수 있도록 중첩된 호출 사이의 저장 지점을 설정한다.
필수 동작은 기존의 열린 트랜잭션이 이미 존재해야 함을 명시한다.예외는 컨테이너에 의해 던져진다.
NEVE 동작은 기존의 열린 거래가 이미 존재하지 않아야 한다고 명시한다.거래가 존재할 경우 예외는 컨테이너에 의해 던져진다.
NOT_Supported 동작은 어떤 거래의 범위 밖에서 실행될 것이다.열린 트랜잭션이 이미 존재하면 일시 중지된다.
Support 동작은 열린 거래가 이미 존재하는 경우 거래의 범위에서 실행될 것이다.이미 오픈된 거래가 없다면, 그 방법은 어쨌든 거래되지 않는 방식으로 실행될 것이다.
트랜잭션은 데이터베이스가 있는 작업 단위를 나타낸다.자체 txns(또는 txn 없음)를 가지는 다중 서비스에서의 트랜잭션 행동을 트랜잭션 전파라고 한다.트랜잭션 격리는 두 트랜잭션이 동일한 데이터베이스 엔티티에 동시에 작용하는 경우 데이터베이스 상태를 정의한다.
TransactionDefinition
스프링 호환 트랜잭션 속성을 정의하는 인터페이스.@Transactional
주석에는 메서드 또는 클래스에 대한 트랜잭션 속성이 설명된다.
@Autowired
private TestDAO testDAO;
@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {
// Interact with testDAO
}
전파(재생산) : 거래간 관계에 사용된다.(자바 간 스레드 통신에 필요)
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| value | Propagation | Description |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| -1 | TIMEOUT_DEFAULT | Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
| 0 | PROPAGATION_REQUIRED | Support a current transaction; create a new one if none exists. |
| 1 | PROPAGATION_SUPPORTS | Support a current transaction; execute non-transactionally if none exists. |
| 2 | PROPAGATION_MANDATORY | Support a current transaction; throw an exception if no current transaction exists. |
| 3 | PROPAGATION_REQUIRES_NEW | Create a new transaction, suspending the current transaction if one exists. |
| 4 | PROPAGATION_NOT_SUPPORTED | Do not support a current transaction; rather always execute non-transactionally. |
| 5 | PROPAGATION_NEVER | Do not support a current transaction; throw an exception if a current transaction exists. |
| 6 | PROPAGATION_NESTED | Execute within a nested transaction if a current transaction exists. |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
격리 : 격리는 데이터베이스 트랜잭션의 AMD(원자성, 일관성, 격리, 내구성) 속성 중 하나이다.격리는 거래 무결성이 다른 사용자와 시스템에 어떻게 보이는지를 결정한다.리소스 잠금(즉, 동시성 제어)에 사용되며, 지정된 지점에서 하나의 트랜잭션만 리소스에 액세스할 수 있는지 확인하십시오.
잠금 인식: 격리 레벨은 잠금이 유지되는 기간을 결정한다.
+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode | Read | Insert | Update | Lock Scope |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED | uncommitted data | Allowed | Allowed | No Lock |
| READ_COMMITTED (Default) | committed data | Allowed | Allowed | Lock on Committed data |
| REPEATABLE_READ | committed data | Allowed | Not Allowed | Lock on block of table |
| SERIALIZABLE | committed data | Not Allowed | Not Allowed | Lock on full table |
+---------------------------+-------------------+-------------+-------------+------------------------+
읽기 인식: 다음과 같은 3가지 주요 문제가 발생한다.
- 더티 리드 : 다른 tx(트랜잭션)에서 커밋되지 않은 데이터를 읽는다.
- 반복할 수 없는 읽기: 읽기 커밋됨
UPDATES
다른 tx에서 - 팬텀 읽기: 읽기 커밋됨
INSERTS
및/또는DELETES
에서.
다양한 종류의 읽기가 포함된 분리 수준:
+---------------------------+----------------+----------------------+----------------+
| Isolation Level Mode | Dirty reads | Non-repeatable reads | Phantoms reads |
+---------------------------+----------------+----------------------+----------------+
| READ_UNCOMMITTED | allows | allows | allows |
| READ_COMMITTED (Default) | prevents | allows | allows |
| REPEATABLE_READ | prevents | prevents | allows |
| SERIALIZABLE | prevents | prevents | prevents |
+---------------------------+----------------+----------------------+----------------+
당신은 거의 절대로 사용하고 싶지 않다.Read Uncommited
사실이 아니므로ACID
순응하는 Read Commmited
디폴트 출발지가 좋다. Repeatable Read
보고, 롤업 또는 집계 시나리오에서만 필요할 수 있다.포함된 많은 DB, postgres가 Repeatable Read를 실제로 지원하지 않으므로Serializable
대신에 Serializable
당신이 알고 있는 어떤 일에도 유용하다 다른 일과는 완전히 독립적으로 일어나야 한다; 그것을 생각하라.synchronized
자바어로직렬화 가능성과 연계됨REQUIRES_NEW
전파의
나는 사용한다REQUIRES
"서비스" 수준 기능뿐만 아니라 UPDATE 또는 DELETE 쿼리를 실행하는 모든 기능에 대해.에는 SELECT를 하는 것이 DAO 레하의 경우하이다SUPPORTS
TX가 이미 시작된 경우(즉, 서비스 기능에서 호출되는 경우) TX에 참여한다.
거래 격리와 거래 전파는 관련이 있지만 분명히 매우 다른 두 개념이다.두 경우 모두 선언적 트랜잭션 관리 또는 프로그램 트랜잭션 관리를 사용하여 클라이언트 경계 구성요소에서 기본값을 사용자 정의한다.각 격리 수준과 전파 속성에 대한 자세한 내용은 아래 참조 링크에서 확인할 수 있다.
두 개 이상의 실행 중인 트랜잭션/데이터베이스 연결의 경우, 한 트랜잭션의 쿼리에 의해 수행되는 변경사항이 다른 트랜잭션의 쿼리에 미치는 영향/보여지는 시기 및 방법그것은 또한 이 거래의 변화를 다른 거래와 분리하기 위해 어떤 종류의 데이터베이스 레코드 잠금을 사용할 것인가와 관련이 있다.이는 일반적으로 트랜잭션에 참여하는 데이터베이스/리소스에 의해 구현된다.
.
주어진 요청/처리에 대한 엔터프라이즈 애플리케이션에는 작업을 수행하는 데 관여하는 많은 구성 요소가 있다.이 구성요소 중 일부는 각 구성요소와 그것의 하위 구성요소에서 사용될 거래의 경계(시작/종료)를 표시한다.이 요소들의 거래상 경계에서, 거래는 각 요소들이 거래에 참여할 것인지 또는 참여하지 않을 것인지, 그리고 통화 요소가 이미 거래를 만들거나 시작하지 않았을 경우 어떻게 되는지를 명시한다.이것은 Java EE Transaction Attributes와 동일하다.이는 일반적으로 클라이언트 트랜잭션/연결 관리자가 구현한다.
참조:
나는 도망쳤다.outerMethod
method_1
그리고method_2
다른 전파 모드와 함께.
아래는 다양한 전파 모드에 대한 출력이다.
아우터 메서드
@Transactional
@Override
public void outerMethod() {
customerProfileDAO.method_1();
iWorkflowDetailDao.method_2();
}
방법_1
@Transactional(propagation=Propagation.MANDATORY)
public void method_1() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "XXX");
session.save(entity);
System.out.println("Method - 1 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
방법_2
@Transactional()
@Override
public void method_2() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "CCC");
session.save(entity);
int i = 1/0;
System.out.println("Method - 2 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
-
- OuterMethod - 트랜잭션 없음
- 방법_1 - 전파.필수) -
- 방법_2 - 트랜잭션 주석만
- 출력: method_1은 기존 트랜잭션이 없다는 예외 발생
-
- OuterMethod - 트랜잭션 없음
- 메서드_1 - 트랜잭션 주석만
- 방법_2 - 전파.필수)
- 출력: method_2는 기존 트랜잭션이 없다는 예외 발생
- 출력: method_1은 데이터베이스에서 레코드를 유지한다.
-
- OuterMethod - 트랜잭션 포함
- 메서드_1 - 트랜잭션 주석만
- 방법_2 - 전파.필수)
- 출력: method_2는 데이터베이스에서 레코드를 유지한다.
- 출력: method_1은 데이터베이스에서 레코드를 유지한다. -- 여기서 Main Outer 기존 트랜잭션은 method 1과 2에 모두 사용된다.
-
- OuterMethod - 트랜잭션 포함
- 방법_1 - 전파.필수)
- 방법_2 - 트랜잭션 주석만 및 예외 발생
- 출력: 데이터베이스에서 기록이 유지되지 않음은 롤백 완료됨을 의미한다.
-
- OuterMethod - 트랜잭션 포함
- 방법_1 - 전파.필요_NEW)
- 방법_2 - 전파.필요_NEW) 및 1/0 예외 발생
- 출력: method_2는 예외를 발생하므로 method_2 레코드가 유지되지 않는다.
- 출력: method_1은 데이터베이스에서 레코드를 유지한다.
- 출력: method_1에 대한 롤백 없음
여기에 추가할 수 있는 항목:
@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {
public Customer getDetail(String customername) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateCustomer(Customer customer) {
// do something
}
}
다음과 같이 사용할 수 있다.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}
당신은 또한 이것을 사용할 수 있다:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}
참조URL: https://stackoverflow.com/questions/8490852/spring-transactional-isolation-propagation
'programing' 카테고리의 다른 글
Java에서 동기화되지 않도록 하시겠습니까? (0) | 2022.05.06 |
---|---|
SID 대신 서비스 이름을 사용하여 Oracle에 연결하는 방법 (0) | 2022.05.06 |
C / C++ 컴파일러 경고: 코드를 모두 정리하여 제거하시겠습니까, 아니면 그대로 두십니까? (0) | 2022.05.06 |
해당 계산된 속성 방법 내에서 계산된 속성의 현재 값을 읽는 방법? (0) | 2022.05.06 |
다중 페이지 애플리케이션 VueJs (0) | 2022.05.05 |