최대 절전 모드 및 데이터베이스 잠금 1. 왜 잠금을 사용합니까?
잠금 장치가 존재하는 이유를 파악하려면 먼저 트랜잭션 개념을 이해해야합니다.
트랜잭션은 데이터베이스에서 일련의 관련 작업이며 산성 특성이 있어야합니다.
일반적으로 사용되는 관계형 데이터베이스 RDBMS는 이러한 트랜잭션의 특성을 구현합니다. 그들 중에는 원자력,
일관성과 지속성은 로깅을 통해 보장됩니다. 격리는 오늘날 우리가 우려하는 잠금 장치에 의해 달성되므로 잠금 메커니즘이 필요합니다.
자물쇠가없고 격리에 대한 통제가 없다면 어떤 결과가 발생할 수 있습니까?
최대 절전 모드의 예를 살펴 보겠습니다. 두 스레드는 각각 두 개의 트랜잭션 작업을 시작합니다. tb_account 테이블의 동일한 데이터 행은 col_id = 1입니다.
패키지 com.cdai.orm.hibernate.annotation; java.io.serializable import; javax.persistence.column import; javax.persistence.entity import; javax.persistence.id import; javax.persistence.table import; @Entity @table (name = "tb_account") 공개 클래스 계정 시리얼이 가능성 {private static final long serialversionuid = 5018821760412231859L; @id @column (name = "col_id") private long id; @column (이름 = "col_balance") 개인 장기 잔액; 공개 계정 () {} 공개 계정 (긴 ID, 긴 잔액) {this.id = id; this.balance = 균형; } public long getId () {return id; } public void setId (long id) {this.id = id; } public long getBalance () {return Balance; } public void setBalance (긴 잔액) {this.balance = 밸런스; } @override public String toString () {return "account [id =" + id + ", balance =" + balance + "]; }} com.cdai.orm.hibernate.Transaction 패키지; import org.hibernate.session; import org.hibernate.sessionfactory; import org.hibernate.Transaction; import org.hibernate.cfg.annotationconfiguration; com.cdai.orm.hibernate.annotation.account 가져 오기; 공개 클래스 DirtyRead {public static void main (String [] args) {Final SessionFactory SessionFactory = New AnnotationConfiguration (). AddFile ( "Hibernate/Hibernate.cfg.xml"). 구성 (). AddPackage ( "com.cdai.orm.hibernate.annotation"). AddAnnotatedClass (Account.Class). buildSessionFactory (); 스레드 t1 = new Thread () {@override public void run () {세션 세션 1 = sessionFactory.Opensession (); 트랜잭션 tx1 = null; try {tx1 = session1.begintransaction (); System.out.println ( "T1- TRASACTION 시작"); Thread.sleep (500); 계정 계정 = (계정) 세션 1.get (Account.Class, New Long (1)); System.out.println ( "T1- 밸런스 =" + account.getBalance ()); Thread.sleep (500); account.setBalance (ac System.out.println ( "T1- 변경 잔액 :" + account.getBalance ()); tx1.commit (); System.out.println ( "T1- 커밋 거래"); Thread.sleep (500); } catch (예외 e) {e.printstacktrace (); if (tx1! = null) tx1.rollback (); } 마침내 {session1.close (); }}}}; // 3. run 트랜잭션 2 스레드 t2 = new Thread () {@override public void run () {session session2 = sessionfactory.opensession (); 트랜잭션 tx2 = null; try {tx2 = session2.begintransaction (); System.out.println ( "T2- TRASACTION 시작"); Thread.sleep (500); 계정 계정 = (Acc System.out.println ( "t2 -balance =" + account.getBalance ()); Thread.sleep (500); account.setBalance (ac System.out.println ( "T2- 변경 밸런스 :" + account.getBalance ()); tx2.commit (); System.out.println ( "T2- 커밋 거래"); Thread.sleep (500); } catch (예외 e) {e.printstacktrace (); if (tx2! = null) tx2.rollback (); } 마침내 {session2.close (); }}}}; t1.start (); t2.start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } catch (InterruptedException e) {}} system.out.println ( "T1과 T2는 모두 죽었습니다."); SessionFactory.close (); }} 트랜잭션 1은 COL_BALANCE를 100으로 줄이고 트랜잭션 2는이를 100으로 줄이고 최종 결과는 0 또는 200 일 수 있으며 트랜잭션 1 또는 2의 업데이트는 손실 될 수 있습니다. 로그 출력은 또한 이것을 확인합니다, 트랜잭션 1 및 2
로그 교차 프린트.
T1- TrasactionT2 시작 - TrasactionHibernate 시작 : col1_0_0_, col1_0_0_, col2_0_0_ as tb_account ac TB_ACCOUNT ACCHOTT0_ 여기서 Account0_.COL_ID =? T1- 밸런스 = 100T2- 밸런스 = 100T2- 변경 밸런스 : 0T1- 밸런스 변경 : 200hibernate : TB_ACCOUNT SET COL_BALANCE =? 여기서 col_id =? hibernate : tb_account set col_balance =? 여기서 col_id =? t1- 커밋 트랜잭션 T2- Commit TransactionBoth T1 및 T2가 죽었습니다.
격리는 신중한 고려가 필요한 문제이며 자물쇠를 이해해야한다는 것을 알 수 있습니다.
2. 얼마나 많은 유형의 자물쇠가 있습니까?
공통은 공유 잠금 장치, 업데이트 잠금 및 독점 잠금 장치입니다.
1. 공유 잠금 : 데이터 운영을 읽는 데 사용되어 다른 트랜잭션을 동시에 읽을 수 있습니다. 거래가 SELECT 명세서를 실행할 때
데이터베이스는 읽기 데이터를 잠그기 위해 공유 잠금 장치를 트랜잭션에 자동으로 할당합니다.
2. 독점 잠금 : 데이터를 수정하기 위해 다른 트랜잭션을 읽거나 수정할 수 없습니다. 트랜잭션이 삽입을 실행할 때
업데이트 및 삭제가 업데이트되면 데이터베이스가 자동으로 할당됩니다.
3. 업데이트 잠금 : 거래 1 및 2 보유 공유 잠금 장치와 같은 업데이트 작업 중에 공유 잠금으로 인한 교착 상태를 피하는 데 사용됩니다. 업데이트를 수행 할 때 트랜잭션은 먼저 업데이트 잠금을 획득 한 다음 업데이트 잠금을 독점 잠금으로 업그레이드하여 교착 상태를 피합니다.
또한 이러한 잠금 장치는 모두 데이터베이스의 다른 객체에 적용 할 수 있습니다.
데이터베이스 레벨 잠금, 테이블 레벨 잠금, 페이지 레벨 잠금, 키 레벨 잠금 및 로우 레벨 잠금과 같은.
따라서 많은 유형의 자물쇠가 있습니다. 완전히 마스터하고 많은 자물쇠를 유연하게 사용하는 것은 너무 어렵습니다. 우리는 DBA가 아닙니다.
무엇을해야합니까? 다행히도 잠금 장치는 일반 사용자에게 투명합니다. 데이터베이스는 적절한 잠금 장치를 자동으로 추가하고 적시에 다양한 잠금 장치를 자동으로 업그레이드하고 다운 그레이드합니다. 너무 사려 깊습니다! 우리가해야 할 일은 다른 비즈니스 요구에 따라 격리 수준을 설정하는 법을 배우는 것입니다.
3. 격리 수준을 설정하는 방법은 무엇입니까?
일반적으로 데이터베이스 시스템은 사용자가 선택할 수 있도록 4 개의 트랜잭션 격리 수준을 제공합니다.
1. 서신화 가능 : 두 트랜잭션이 동시에 동일한 데이터를 조작하면 거래 2가 중지하고 대기 할 수 있습니다.
2. Repeatable Read (반복 가능) : 트랜잭션 1은 트랜잭션 2에서 새로 삽입 된 데이터를 볼 수 있으며 기존 데이터에 대한 업데이트를 볼 수 없습니다.
3. Commited (커밋 된 데이터 읽기) : 트랜잭션 1은 트랜잭션 2에서 새로 삽입되고 업데이트 된 데이터를 볼 수 있습니다.
4. 커밋되지 않은 읽기 (커밋되지 않은 데이터 읽기) : 트랜잭션 1은 트랜잭션 2가 커밋되지 않은 삽입 및 업데이트 데이터를 볼 수 있습니다.
4. 응용 프로그램의 잠금
데이터베이스가 읽기 커미션 격리 수준을 채택하면 응용 프로그램에서 비관적 잠금 또는 낙관적 잠금을 사용할 수 있습니다.
1. 비관적 잠금 : 현재 트랜잭션 운영의 데이터에 다른 트랜잭션 액세스가 확실히 있다고 가정하므로, 애플리케이션에 독점 잠금이 사용되어 데이터 자원을 잠그는 데 비관적으로 지정합니다. MySQL 및 Oracle에서 다음 양식을 지원합니다.
업데이트하려면 ...
Select가 독점 잠금 잠금을 사용하여 쿼리의 레코드를 잠그십시오. 쿼리로의 다른 트랜잭션의 경우이 잠긴 데이터를 업데이트하거나 삭제하려면 트랜잭션이 끝날 때까지 기다려야합니다.
Hibernate에서는 록 모드를 전달할 수 있습니다. 이전 예제를 수정하십시오.
트랜잭션 1 및 2의 GET 메소드 호출에서 추가 잠금 모드 매개 변수가 전달됩니다. 로그에서 볼 수 있듯이 트랜잭션 1 및 2
더 이상 교차 실행이 아니며, 트랜잭션 2는 데이터를 읽기 전에 트랜잭션 1을 완료 할 때까지 기다릴 수 있으므로 최종 Col_balance 값은 올바른 100입니다.
com.cdai.orm.hibernate.Transaction 패키지; import org.hibernate.lockmode; import org.hibernate.session; import org.hibernate.sessionfactory; import org.hibernate.Transaction; com.cdai.orm.hibernate.annotation.account 가져 오기; com.cdai.orm.hibernate.annotation.annotationHibernate import; public class upgradelock {@suppresswarnings ( "감가 상각") public static void main (String [] args) {Final SessionFactory sessionFactory = AnnotationHibernate.createsessionFactory (); // 트랜잭션 실행 1 스레드 t1 = 새 스레드 () {@override public void run () {세션 세션 1 = sessionFactory.Opensession (); 트랜잭션 tx1 = null; try {tx1 = session1.begintransaction (); System.out.println ( "T1- TRASACTION 시작"); Thread.sleep (500); 계정 계정 = (Acc System.out.println ( "T1- 밸런스 =" + account.getBalance ()); Thread.sleep (500); account.setBalance (ac System.out.println ( "T1- 변경 잔액 :" + account.getBalance ()); tx1.commit (); System.out.println ( "T1- 커밋 거래"); Thread.sleep (500); } catch (예외 e) {e.printstacktrace (); if (tx1! = null) tx1.rollback (); } 마침내 {session1.close (); }}}; // 트랜잭션 실행 2 스레드 t2 = 새 스레드 () {@override public void run () {세션 세션 2 = sessionfactory.opensession (); 트랜잭션 tx2 = null; try {tx2 = session2.begintransaction (); System.out.println ( "T2- TRASACTION 시작"); Thread.sleep (500); 계정 계정 = (Acc System.out.println ( "t2 -balance =" + account.getBalance ()); Thread.sleep (500); account.setBalance (ac System.out.println ( "T2- 변경 밸런스 :" + account.getBalance ()); tx2.commit (); System.out.println ( "T2- 커밋 거래"); Thread.sleep (500); } catch (예외 e) {e.printstacktrace (); if (tx2! = null) tx2.rollback (); } 마침내 {session2.close (); }}}}; t1.start (); t2.start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } catch (InterruptedException e) {}} system.out.println ( "T1과 T2는 모두 죽었습니다."); SessionFactory.close (); }}T1- TrasactionT2 시작 - TrasactionHibernate 시작 : col1_0_0_, col1_0_0_, col2_0_0_ as tb_account ac tb_account account0_에서 col2_0_0_ (updlock, rowlock) where ac 여기서 col_id =? t2- 커밋 트랜잭션 ~ 밸런스 = 0t1- 밸런스 변경 : 100hibernate : TB_ACCOUNT SET COL_BALANCE =? 여기서 col_id =? t1- Commit TransactionBoth T1 및 T2가 죽었습니다.
Hibernate SQLServer 2005 용 SQL을 실행합니다.
코드 사본은 다음과 같습니다.
col1_0_0_, col1_0_0_.col_balance as tb_account ac
2. 낙관적 잠금 : 다른 트랜잭션에 의해 현재 트랜잭션 운영의 데이터에 동시에 액세스되지 않다고 가정하므로 데이터베이스의 격리 수준이 데이터베이스에 완전히 의존하여 잠금 작업을 자동으로 관리합니다. 낮은 확률로 발생할 수있는 동시성 문제를 피하기 위해 응용 프로그램에서 버전 제어를 채택하십시오.
최대 절전 모드에서 버전 주석을 사용하여 버전 번호 필드를 정의하십시오.
DirtyLock의 계정 개체를 AccountVersion으로 바꾸면 다른 코드는 변경되지 않으며 실행이 발생할 때 예외가 발생합니다.
com.cdai.orm.hibernate.Transaction 패키지; javax.persistence.column import; javax.persistence.entity import; javax.persistence.id import; javax.persistence.table import; javax.persistence.version import; @entity @table (name = "tb_account_version") public class accountversion {@id @column (name = "col_id") 개인 긴 ID; @column (이름 = "col_balance") 개인 장기 잔액; @version @column (name = "col_version") 개인 int 버전; public accountversion () {} public accountversion (긴 ID, 긴 잔액) {this.id = id; this.balance = 균형; } public long getId () {return id; } public void setId (long id) {this.id = id; } public long getBalance () {return Balance; } public void setBalance (긴 잔액) {this.balance = 밸런스; } public int getVersion () {return 버전; } public void setversion (int 버전) {this.version = 버전; }}로그는 다음과 같습니다.
T1- TrasactionT2 시작 - TrasactionHibernate 시작 : col1_0_0_, col1_0_0_, col2_0_0_, col2_0_0_, col3_0_0_ as as ac col1_0_0_, col2_0_0_, col2_0_0_, col2_0_0_, ac col_balance =?, col_version =? 여기서 col_id =? 및 col_version =? hibernate : 업데이트 tb_account_version set col_balance =?, col_version =? 여기서 col_id =? and col_version =? t1 -Commit Transaction2264 [Thread -2] Error org.hibernate.event.def.abstractflushingeventListener- 데이터베이스 상태를 sessionorg.hibernate.hibernate.hibernate.hibernate.hibernate.staleexception을 동기화 할 수 없습니다. 다른 거래에 의해 Row가 업데이트되거나 제한되었습니다 (또는 벤트 값 맵핑이 잘못되었다). [com.cdai.orm.hibernate.transaction.accountversion#1] at org.hibernate.persister.entity.abstractentitypersister.check (actractentitypersister.java:1934)의 org.hibernate.persister.abstractentitypersister.update.update org.hibernate.persister.entity.abstractentitypersister.updateorinsert (tracketentitypersister.java:2478) at org.hibernate.entity.entity.abstractentitypersister.update (acpractentitypersister.java:2805) at org.hibernate.action.entityupdateaction.execute (entityupdateaction.java:114) at org.hiberate.encqueue.execute (actionqueue.java:268) at org.hibernate.engine.actionqueue.executeactions (acturequeue.java.java:260) at org.hibernate.actqueue.executeactions (actionqueue.java:180) org.hibernate.event.def.abtractflushingeventlistener.performexecutions (AbstractFlushingEventListener.java:321) at org.hibernate.event.def.def.defaultflusheventListener.onflush (defaultflusheventListener.java:51) at org.hibernate.impl.sessionimpl.flush (sessionimpl.java:1206) at org.hibernate.implphlph (sessionimpl.managefflush) at org.hibernate.transaction.jdbctransaction.commit (com.cdai.orm.hibernate.transaction.versionlock $ 2.run (versionlock.java:93)의 Commit (jdbctransaction.java:137)은 T1과 T2가 죽었습니다.
낙관적 잠금은 제어를 위해 트랜잭션 격리를 완전히 실행하기 때문에 트랜잭션 1 및 2가 크로스 런을 실행하고 트랜잭션 1이 성공적으로 커밋되고 Col_version이 1으로 변경됩니다. 그러나 트랜잭션 2가 저지하면 Col_version이있는 데이터가 더 이상 발견 될 수 없으므로 예외는 발생했습니다.
최대 절전 모드 쿼리 방법의 비교
최대 절전 모드에는 세 가지 주요 쿼리 방법이 있습니다.
1.hql (최대 절전 모드 쿼리 언어)
SQL과 매우 유사하며 페이징, 연결, 그룹화, 집계 기능 및 하위 퀘스트와 같은 기능을 지원합니다.
그러나 HQL은 객체 지향적이며 관계형 데이터베이스의 테이블이 아닙니다. 쿼리 문은 도메인 객체를 방향으로 지정하기 때문에 HQL을 사용하면 크로스 플랫폼 혜택을 얻을 수 있습니다. 최대 절전 모드
다른 데이터베이스에 따라 다른 SQL 문으로 번역하는 데 자동으로 도움이됩니다. 이는 여러 데이터베이스 또는 데이터베이스 마이그레이션을 지원 해야하는 응용 프로그램에서 매우 편리합니다.
그러나 편리하게 진행되는 동안 SQL 문은 최대 절전 모드에 의해 자동으로 생성되므로 SQL 문의 효율성 최적화 및 디버깅에 도움이되지 않습니다. 데이터 양이 클 경우 효율성 문제가있을 수 있습니다.
문제가 있으면 조사하고 해결하는 것이 편리하지 않습니다.
2.QBC/QBE (기준/예에 의한 쿼리)
QBC/QBE는 쿼리 조건 또는 템플릿 객체를 조립하여 쿼리를 수행합니다. 이는 쿼리 조건의 많은 자유 조합에 대한 유연한 지원이 필요한 응용 프로그램에서 편리합니다. 같은 문제는 쿼리 문이 자유롭게 조립되기 때문에 문을 작성하는 코드는 길고 많은 분기 조건을 포함하여 최적화 및 디버깅에 매우 불편합니다.
3.sql
Hibernate는 또한 SQL을 직접 실행하는 쿼리 메소드도 지원합니다. 이 방법은 최대 절전 모드 대사의 이점을 희생하고 최상의 실행 효율성을 달성하기 위해 기본 SQL 문을 수동으로 작성합니다.
처음 두 가지 방법과 비교하여 최적화 및 디버깅이 더 편리합니다.
간단한 예제를 살펴 보겠습니다.
패키지 com.cdai.orm.hibernate.query; import java.util.arrays; Java.util.list 가져 오기; import org.hibernate.criteria; import org.hibernate.query; import org.hibernate.session; import org.hibernate.sessionfactory; import org.hibernate.cfg.annotationconfiguration; import org.hibernate.criterion.criterion; import org.hibernate.criterion.example; import org.hibernate.criterion.expression; com.cdai.orm.hibernate.annotation.account 가져 오기; public class BasicQuery {public static void main (String [] args) {sessionFactory sessionFactory = new AnnotationConfiguration (). AddFile ( "Hibernate/Hibernate.cfg.xml"). 구성 (). AddPackage ( "com.cdai.orm.hibernate.annotation"). AddAnnotatedClass (Account.Class). buildSessionFactory (); 세션 세션 = sessionfactory.opensession (); // 1.hql query query = session.createquery ( "A.ID = : id"로 계정에서); query.setlong ( "id", 1); 목록 결과 = query.list (); for (Object Row : result) {System.out.println (행); } // 2.qbc 기준 기준 = session.createCriteria (Account.Class); criteria.add (expression.eq ( "id", new Long (2)); 결과 = criteria.list (); for (Object Row : result) {System.out.println (행); } // 3.qbe 계정 예 = 새 계정 (); 예제 밸런스 (100); result = session.createCriteria (Account.Class). 추가 (example.create (example)). 목록(); for (Object Row : result) {System.out.println (행); } // 4.sql query = session.createsqlQuery ( "col_id desc의 tb_account order에서 Top 10 * 선택"); 결과 = query.list (); for (Object Row : result) {System.out.println (Arrays.toString ((Object []) row)); } session.close (); }}Hibernate : col1_0_, col1_0_.col_balance as col2_0_ as tb_account ac this_.col_id =? 계정 [id = 2, balance = 100] hibernate : col1_0_0_, col1_0_0_, col2_0_0_ as this_ this_ this_ this_ this_ this_ this_ this_.col_balance를 선택하십시오. col_id desc [2, 100] [1, 100]
로그에서 생성 된 SQL 문에 대한 Hibernate의 제어를 명확하게 볼 수 있습니다. 선택할 특정 쿼리 방법은 특정 응용 프로그램에 따라 다릅니다.