Exception & Error
- Exception
- 프로그램이 핸들링 할 수 있는 경우.
- 처리가 가능.
- checked exception ( compile time )
- 실행하기 이전 예측 가능.
- 파일 확인할 때 에러, SQL 관련.
- 이클립스, 인텔리제이 등 IDE가 컴파일 할 때 체크 해줌.
- unchecked exception ( runtime )
- 실행해야 알 수 있음.
- 실행 시 객체 생성이 없는 경우 ( NullPointer ), Array 싸이즈가 안 맞는 에러, 잘못된 인덱스 접근 등.
- Error
- 런타임에서 실행 시 발생되며 전부 예측 불가능한 Unchecked Error
- 핸들링이 불가능해 수정 없이 회복, 처리가 불가능하다. 대표적으로 Out Of Memory, Stack Overflow 문제.
Exception 핸들링
- try - catch block
- 블럭을 설정해 Exception 처리.
- throws
- 메소드 선언부에서 사용.
- 메소드를 불러오는 메소드에 책임 전가.
- Exception 이 생기면 던짐.
- throw ( 인위적 Exception 발생 )
- Exception 이 필요한 상황에 개발자가 발생 시킴.
- try - catch 나 throws 를 사용해야 한다.
[Java] HashMap vs HashTable
Map
자바에서 제공하는 HashMap과 Hashtable은 Map 인터페이스를 상속받아 구현되어 데이터를 키와 값으로 관리하는 자료구조이다.
큰 특징으로는 키(Key)가 데이터를 추출할 때 구분자로 활용하는 방식을 취하는데 이는 리스트 인터페이스와 같은 자료구조보다 탐색에 있어 더 높은 효율을 기대할 수 있다.
HashMap과 Hashtable의 차이점은 동기화와 반환값이다.
동기화
HashMap의 경우 동기화를 지원하지 않는다.
반면 다중 스레드 환경에서 Hashtable은 동기화를 지원하기 때문에 실행 환경에 따라 구분하여 사용하면 된다.
하지만 한 자바 관련 서적에 의하면 Vector의 상위호환(?)개념인 ArrayList의 사용을 권장하듯 새로운 버전인 HashMap을 활용하고 동기화가 필요한 시점에서는 Java 5부터 제공하는 ConcurrentHashMap을 사용하는 것이 더 좋은 방법이라 표현한다.
추가로 속도적인 측면에서도 구형이라 할 수 있는 Hashtable은 동기화 처리라는 비용때문에 HashMap에 비해 더 느리다고 한다.
반환값
HashMap은 저장된 요소들의 순회를 위해 Fail-Fast Iterator를 반환한다.(ConcurrentHashMap의 경우에는 Fail-Safe Iterator)
Hashtable은 같은 경우 Enumeration을 반환한다.
여기서 Enumeration과 Iterator는 컬렉션에 저장된 요소를 접근하는데 사용되는 인터페이스이다.
Enumeration은 컬렉션 프레임워크 이전에 사용되던 인터페이스로 Iterator의 사용을 권장한다.
Iterator엔 remove() 메소드가 추가되었고 메소드 네이밍이 간략화되었다.
그리고 다른 스레드(lock된 상황에서)에서 해당 자료에 요소를 수정(삽입, 삭제, 수정 등)이 발생하면 ConcurrentModificationException을 발생시켜 일관성을 보장한다.
이를 Fail-Fast Iterator라 한다.
ConcurrentHashMap의 경우 Map의 복사본을 참조하는 Iterator를 반환하며 다시 반환받은 시점에 Map에 수정이 있을 경우 해당 Iterator는 반영되지 않는다. 고로 ConcurrentModificationException또한 발생하지 않는다. 이는 약한 일관성(Weakly Consistent)를 제공하지만 다중 스레드상황에서 해당 Map의 무결성(?)을 보장한다.
추가로 ListIterator가 있는데 이는 단방향만을 제공하는 Iterator의 기능을 향상시킨 것이다.
Iterator it = list.iterator(); it.next();
와 같이 사용된다면
ListIterator li = list.listIterator(); li.next(); li.previous();
와 같이 활용할 수 있다.
다만 List 인터페이스를 상속한 컬렉션에서만 사용가능하다.
DB 트랜잭션(Transaction)
트렌잭션이란?
데이터베이스의 상태를 변화시키기 위해 수행하는 작업 단위
상태를 변화시킨다는 것 → SQL 질의어를 통해 DB에 접근하는 것
- SELECT - INSERT - DELETE - UPDATE
작업 단위 → 많은 SQL 명령문들을 사람이 정하는 기준에 따라 정하는 것
예시) 사용자 A가 사용자 B에게 만원을 송금한다. * 이때 DB 작업 - 1. 사용자 A의 계좌에서 만원을 차감한다 : UPDATE 문을 사용해 사용자 A의 잔고를 변경 - 2. 사용자 B의 계좌에 만원을 추가한다 : UPDATE 문을 사용해 사용자 B의 잔고를 변경 현재 작업 단위 : 출금 UPDATE문 + 입금 UPDATE문 → 이를 통틀어 하나의 트랜잭션이라고 한다. - 위 두 쿼리문 모두 성공적으로 완료되어야만 "하나의 작업(트랜잭션)"이 완료되는 것이다. `Commit` - 작업 단위에 속하는 쿼리 중 하나라도 실패하면 모든 쿼리문을 취소하고 이전 상태로 돌려놓아야한다. `Rollback`
즉, 하나의 트랜잭션 설계를 잘 만드는 것이 데이터를 다룰 때 많은 이점을 가져다준다.
트랜잭션 특징
-
원자성(Atomicity)
트랜잭션이 DB에 모두 반영되거나, 혹은 전혀 반영되지 않아야 된다.
-
일관성(Consistency)
트랜잭션의 작업 처리 결과는 항상 일관성 있어야 한다.
-
독립성(Isolation)
둘 이상의 트랜잭션이 동시에 병행 실행되고 있을 때, 어떤 트랜잭션도 다른 트랜잭션 연산에 끼어들 수 없다.
-
지속성(Durability)
트랜잭션이 성공적으로 완료되었으면, 결과는 영구적으로 반영되어야 한다.
Commit
하나의 트랜잭션이 성공적으로 끝났고, DB가 일관성있는 상태일 때 이를 알려주기 위해 사용하는 연산
Rollback
하나의 트랜잭션 처리가 비정상적으로 종료되어 트랜잭션 원자성이 깨진 경우
transaction이 정상적으로 종료되지 않았을 때, last consistent state (예) Transaction의 시작 상태) 로 roll back 할 수 있음.
상황이 주어지면 DB 측면에서 어떻게 해결할 수 있을지 대답할 수 있어야 함
Transaction 관리를 위한 DBMS의 전략
이해를 위한 2가지 개념 : DBMS의 구조 / Buffer 관리 정책
- DBMS의 구조
크게 2가지 : Query Processor (질의 처리기), Storage System (저장 시스템)
입출력 단위 : 고정 길이의 page 단위로 disk에 읽거나 쓴다.
저장 공간 : 비휘발성 저장 장치인 disk에 저장, 일부분을 Main Memory에 저장

- Page Buffer Manager or Buffer Manager
DBMS의 Storage System에 속하는 모듈 중 하나로, Main Memory에 유지하는 페이지를 관리하는 모듈
Buffer 관리 정책에 따라, UNDO 복구와 REDO 복구가 요구되거나 그렇지 않게 되므로, transaction 관리에 매우 중요한 결정을 가져온다.
- UNDO
필요한 이유 : 수정된 Page들이 **Buffer 교체 알고리즘에 따라서 디스크에 출력**될 수 있음. Buffer 교체는 transaction과는 무관하게 buffer의 상태에 따라서, 결정됨. 이로 인해, 정상적으로 종료되지 않은 transaction이 변경한 page들은 원상 복구 되어야 하는데, 이 복구를 undo라고 함.
-
2개의 정책 (수정된 페이지를 디스크에 쓰는 시점으로 분류)
steal : 수정된 페이지를 언제든지 디스크에 쓸 수 있는 정책
- 대부분의 DBMS가 채택하는 Buffer 관리 정책
- UNDO logging과 복구를 필요로 함.
¬steal : 수정된 페이지들을 EOT (End Of Transaction)까지는 버퍼에 유지하는 정책
- UNDO 작업이 필요하지 않지만, 매우 큰 메모리 버퍼가 필요함.
- REDO
이미 commit한 transaction의 수정을 재반영하는 복구 작업
Buffer 관리 정책에 영향을 받음
-
Transaction이 종료되는 시점에 해당 transaction이 수정한 page를 디스크에 쓸 것인가 아닌가로 기준.
FORCE : 수정했던 모든 페이지를 Transaction commit 시점에 disk에 반영
transaction이 commit 되었을 때 수정된 페이지들이 disk 상에 반영되므로 redo 필요 없음.
¬FORCE : commit 시점에 반영하지 않는 정책
transaction이 disk 상의 db에 반영되지 않을 수 있기에 redo 복구가 필요. (대부분의 DBMS 정책)
트랜잭션 격리 수준(Transaction Isolation Level)
Isolation level
트랜잭션에서 일관성 없는 데이터를 허용하도록 하는 수준
Isolation level의 필요성
데이터베이스는 ACID 특징과 같이 트랜잭션이 독립적인 수행을 하도록 한다.
따라서 Locking을 통해, 트랜잭션이 DB를 다루는 동안 다른 트랜잭션이 관여하지 못하도록 막는 것이 필요하다.
하지만 무조건 Locking으로 동시에 수행되는 수많은 트랜잭션들을 순서대로 처리하는 방식으로 구현하게 되면 데이터베이스의 성능은 떨어지게 될 것이다.
그렇다고 해서, 성능을 높이기 위해 Locking의 범위를 줄인다면, 잘못된 값이 처리될 문제가 발생하게 된다.
- 따라서 최대한 효율적인 Locking 방법이 필요함!
Isolation level 종류
SELECT 문장이 수행되는 동안 해당 데이터에 Shared Lock이 걸리지 않는 계층
사용자1이 A라는 데이터를 B라는 데이터로 변경하는 동안 사용자2는 아직 완료되지 않은(Uncommitted) 트랜잭션이지만 데이터B를 읽을 수 있다데이터베이스의 일관성을 유지하는 것이 불가능함
Read Uncommitted (레벨 0)
- 트랜잭션에 처리중이거나, 아직 Commit되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용함
SELECT 문장이 수행되는 동안 해당 데이터에 Shared Lock이 걸리는 계층
Commit이 이루어진 트랜잭션만 조회 가능
SQL 서버가 Default로 사용하는 Isolation Level임
사용자1이 A라는 데이터를 B라는 데이터로 변경하는 동안 사용자2는 해당 데이터에 접근이 불가능함
Read Committed (레벨 1)
- 트랜잭션이 수행되는 동안 다른 트랜잭션이 접근할 수 없어 대기하게 됨
트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock이 걸리는 계층
다른 사용자는 트랜잭션 영역에 해당되는 데이터에 대한 수정 불가능
Repeatable Read (레벨 2)
- 트랜잭션이 범위 내에서 조회한 데이터 내용이 항상 동일함을 보장함
트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock이 걸리는 계층
다른 사용자는 트랜잭션 영역에 해당되는 데이터에 대한 수정 및 입력 불가능
Serializable (레벨 3)
- 완벽한 읽기 일관성 모드를 제공함
선택 시 고려사항
Isolation Level에 대한 조정은, 동시성과 데이터 무결성에 연관되어 있음
동시성을 증가시키면 데이터 무결성에 문제가 발생하고, 데이터 무결성을 유지하면 동시성이 떨어지게 됨
레벨을 높게 조정할 수록 발생하는 비용이 증가함
낮은 단계 Isolation Level을 활용할 때 발생하는 현상들
-
Dirty Read
커밋되지 않은 수정중인 데이터를 다른 트랜잭션에서 읽을 수 있도록 허용할 때 발생하는 현상
어떤 트랜잭션에서 아직 실행이 끝나지 않은 다른 트랜잭션에 의한 변경사항을 보게되는 경우
-
Non-Repeatable Read
한 트랜잭션에서 같은 쿼리를 두 번 수행할 때 그 사이에 다른 트랜잭션 값을 수정 또는 삭제하면서 두 쿼리의 결과가 상이하게 나타나는 일관성이 깨진 현상
-
Phantom Read
한 트랜잭션 안에서 일정 범위의 레코드를 두 번 이상 읽었을 때, 첫번째 쿼리에서 없던 레코드가 두번째 쿼리에서 나타나는 현상
트랜잭션 도중 새로운 레코드 삽입을 허용하기 때문에 나타나는 현상임