사전 Oracle
구현체

Oracle

gabury1고친 사람 github-actions[bot]

Oracle 은 데이터를 여러 표에 나눠 담아 두고 필요할 때 꺼내 쓰게 해 주는 데이터베이스 서버입니다. 같은 이름의 회사가 만들어 파는 상용 제품입니다. 멈추면 안 되는 큰 업무 시스템에서 오래 쓰여 왔습니다. 실무에서 그냥 「오라클」이라고 하면 대개 이 데이터베이스를 가리킵니다.

쉽고 빠른 이해

Oracle 은 데이터를 표에 담아 두고 질의를 받으면 꺼내 주는 서버입니다. 은행의 계좌 잔액과 거래 내역 같은 데이터가 이런 서버에 들어갑니다.

여러 사람이 같은 데이터를 동시에 고치고 읽어도 값이 꼬이면 안 됩니다. 서버가 갑자기 꺼져도 확정된 거래는 남아야 합니다. 이 일을 애플리케이션이 직접 하기는 어렵습니다. 그래서 데이터베이스 서버에 맡깁니다.

어떻게 도는가:

  1. 서버를 켜면 메모리와 일꾼 프로세스가 뜹니다. 데이터는 디스크의 파일에 있습니다
  2. 바꾼 내용은 먼저 변경 기록 파일에 적고 확정합니다. 데이터 파일은 나중에 모아서 씁니다
  3. 바꾸기 전 값을 따로 남겨 둡니다. 읽는 쪽은 남겨 둔 바꾸기 전 값을 읽으므로 쓰는 쪽을 기다리지 않습니다

대가가 있습니다. 돈을 내고 쓰는 제품이라 소스가 닫혀 있습니다. 설정할 것이 많아 전담 관리자를 두는 곳이 많습니다. Oracle 에만 있는 기능에 기대면 다른 데이터베이스로 옮기기 어렵습니다. 그래서 새로 시작하는 작은 서비스는 무료 데이터베이스를 고르는 일이 많습니다.

상세

Oracle 은 관계형 데이터베이스 서버입니다. 데이터를 행과 열로 된 테이블에 담습니다. 넣고 꺼낼 때는 SQL(Structured Query Language, 구조화 질의 언어)을 씁니다. 서버를 따로 띄워 두면 애플리케이션이 네트워크로 붙어 질의를 보냅니다.

제품의 정식 이름은 Oracle Database 입니다. 만든 회사의 이름도 Oracle 입니다. 이 회사는 MySQL 과 Java 도 관리합니다. 그래서 「Oracle 을 쓴다」가 데이터베이스 얘기인지 회사가 내놓은 다른 제품 얘기인지는 앞뒤를 보고 가립니다.

소프트웨어 테스트에서 말하는 테스트 오라클은 이름만 같은 다른 말입니다. 테스트 결과가 맞는지 판정하는 기준을 가리킵니다.

인스턴스와 데이터베이스

Oracle 은 서버를 두 덩어리로 갈라 부릅니다. 디스크에 남는 파일 묶음이 데이터베이스입니다. 서버를 켜면 뜨는 메모리와 프로세스 묶음이 인스턴스입니다.

인스턴스가 데이터베이스 파일을 열어야 질의를 받을 수 있습니다. 서버를 끄면 인스턴스는 사라지고 파일만 남습니다.

다른 제품에서는 둘을 뭉뚱그려 「데이터베이스 서버」라고 부르곤 합니다. 갈라 부르는 까닭은 뒤의 「여러 서버로 늘리는 구성」에서 드러납니다.

데이터베이스 쪽 파일은 세 종류입니다.

파일 담는 것
데이터 파일 테이블과 인덱스의 실제 데이터
제어 파일 데이터베이스를 이루는 파일들의 목록과 상태
리두 로그 파일 바꾼 내용의 기록. 아래 「리두 로그」 소절에서 다룹니다

셋 가운데 사용자의 데이터가 들어 있는 것은 데이터 파일뿐입니다. 나머지 둘은 데이터베이스를 안전하게 열고 되살리는 데 씁니다.

Oracle 은 데이터 파일을 일정한 크기의 조각으로 나눠 읽고 씁니다. 이 조각이 데이터 블록입니다. 행 하나를 읽을 때도 그 행이 든 블록을 통째로 읽어 옵니다.

인스턴스 쪽 메모리의 큰 몫은 SGA(System Global Area, 시스템 전역 영역)입니다. 서버에 붙은 애플리케이션 연결 모두가 함께 쓰는 공유 메모리입니다.

SGA 에는 버퍼 캐시가 들어 있습니다. 디스크에서 읽은 블록을 담아 두는 곳입니다. 같은 블록을 다시 읽을 때 디스크까지 가지 않으려고 둡니다.

인스턴스에는 사용자의 요청과 따로 뒤에서 도는 백그라운드 프로세스가 여럿 있습니다. 메모리에서 바뀐 블록을 데이터 파일에 내려 쓰는 프로세스와 리두 로그를 쓰는 프로세스가 대표적입니다.

flowchart TD
    APP["애플리케이션"] -->|SQL| I
    subgraph I["인스턴스 · 켜면 뜨고 끄면 사라진다"]
        SGA["SGA · 버퍼 캐시"]
        BG["백그라운드 프로세스"]
    end
    subgraph D["데이터베이스 · 디스크에 남는다"]
        DF["데이터 파일"]
        CF["제어 파일"]
        RL["리두 로그 파일"]
    end
    DF -->|읽은 블록을 담아 둔다| SGA
    BG -->|바뀐 블록을 내려 쓴다| DF
    BG -->|바꾼 내용을 적는다| RL

그림에서 애플리케이션은 인스턴스와만 이야기합니다. 파일을 직접 만지는 것은 인스턴스의 프로세스들입니다.

리두 로그

트랜잭션은 여러 변경을 묶어 다 되거나 다 안 되게 하는 단위입니다. 커밋은 그 묶음을 확정하는 명령입니다. 커밋이 끝났다고 답한 뒤에는 서버가 꺼져도 그 변경이 남아야 합니다.

Oracle 은 이를 위해 바꾼 내용을 먼저 리두 로그에 적습니다. 리두 로그는 「어느 블록을 어떻게 바꿨나」를 순서대로 적은 기록입니다. 커밋은 이 기록이 디스크에 닿으면 끝납니다. 바뀐 데이터 블록은 메모리에 둔 채 나중에 모아서 씁니다.

블록을 바로 쓰지 않는 것은 디스크가 움직이는 방식 때문입니다. 바뀐 블록은 파일 여기저기에 흩어져 있습니다. 커밋마다 쓰면 디스크가 여러 곳을 오가야 합니다. 로그는 끝에 이어 붙이기만 하면 됩니다.

서버가 갑자기 꺼지면 다음에 켤 때 리두 로그를 다시 읽습니다. 아직 데이터 파일에 안 들어간 변경을 로그대로 채워 넣습니다. 이 방식은 Oracle 만의 것이 아닙니다. WAL(Write-Ahead Logging, 선행 기록 로그)이라는 이름으로 여러 데이터베이스가 씁니다.

언두와 읽기 일관성

Oracle 은 행을 바꿀 때 바꾸기 전 값을 따로 남깁니다. 이 옛 값의 기록이 언두입니다. 트랜잭션을 롤백해 없던 일로 되돌릴 때 이 값을 씁니다.

언두는 읽기에도 쓰입니다. 누군가 행을 고치는 중이어도 다른 연결에서 보낸 조회는 기다리지 않습니다. 조회는 그 SQL 문장(조회 한 건)이 시작한 시점의 값을 봅니다. 그 뒤에 바뀐 블록을 만나면 언두로 옛 값을 다시 만들어 읽습니다.

오후 3시에 시작한 집계 조회가 3시 1분에 바뀐 잔액을 만났다고 해 보겠습니다. 조회는 언두에서 3시 시점의 잔액을 꺼내 합산합니다. 그래서 결과는 3시 한 시점의 모습으로 맞아떨어집니다. 한 조회가 한 시점의 값만 보는 이 성질이 읽기 일관성입니다.

옛 버전을 들고 있다가 읽는 쪽에 내주면 읽기와 쓰기가 서로 막지 않습니다. 이런 방식을 넓게 부르는 이름이 MVCC(Multi-Version Concurrency Control, 다중 버전 동시성 제어)입니다. Oracle 의 언두도 이 방식의 하나입니다.

격리 수준은 동시에 도는 트랜잭션이 서로의 변경을 얼마나 보는지 정하는 단계입니다. Oracle 의 기본값은 READ COMMITTED 입니다. 커밋된 값만 읽는다는 뜻입니다. Oracle 에서는 SQL 문장마다 그 문장이 시작한 시점에 커밋돼 있던 값을 봅니다.

언두를 담는 공간에는 끝이 있습니다. 공간이 모자라면 오래된 언두부터 덮어씁니다. 아주 오래 도는 조회가 이미 덮인 옛 값을 찾으면 옛 값을 못 만들어 실패합니다. 이때 나는 오류가 ORA-01555 「snapshot too old」입니다.

사용자와 스키마

Oracle 에서는 사용자를 만들면 같은 이름의 스키마가 함께 생깁니다. 스키마는 테이블 같은 객체를 담는 이름 공간입니다. 사용자 APP 이 만든 테이블 ORDERS 의 전체 이름은 APP.ORDERS 가 됩니다.

MySQL 에서 말하는 데이터베이스는 테이블을 묶는 이름 공간입니다. 앞에서 본 디스크의 파일 묶음과는 다른 말이고, Oracle 로 치면 스키마에 해당합니다.

MySQL 에서는 데이터를 가르고 싶으면 이 데이터베이스를 하나 더 만듭니다. Oracle 에서는 같은 일을 대개 사용자를 하나 더 만들어 합니다. 처음 넘어온 개발자가 「데이터베이스를 만들어 달라」고 하면 관리자가 계정을 내주는 것도 이 때문입니다.

테이블스페이스

Oracle 은 테이블을 어느 파일에 넣을지 곧바로 고르게 하지 않습니다. 사이에 테이블스페이스라는 논리 단위를 둡니다. 테이블은 테이블스페이스에 속합니다. 테이블스페이스는 데이터 파일 하나 이상으로 이루어집니다.

이렇게 하면 저장 공간을 늘릴 때 테이블 정의를 건드리지 않습니다. 관리자는 테이블스페이스에 데이터 파일을 하나 더 붙이면 됩니다. 테이블이 보는 논리 구조와 디스크의 물리 구조가 떨어져 있는 것입니다.

PL/SQL

Oracle 에는 PL/SQL(Procedural Language/SQL)이라는 전용 언어가 들어 있습니다. SQL 에 변수와 조건문, 반복문을 더한 언어입니다. PL/SQL 로 짠 저장 프로시저는 데이터베이스 안에 저장되고 서버에서 바로 돕니다.

저장 프로시저를 쓰면 네트워크 왕복이 줄어듭니다. 행 수천 건을 애플리케이션으로 가져와 하나씩 고치고 다시 보내면 왕복이 수천 번 생깁니다. 서버 안의 프로시저로 돌리면 호출 한 번이면 됩니다.

오래된 업무 시스템에서는 핵심 업무 규칙이 PL/SQL 로 짜여 있기도 합니다. 이런 시스템은 다른 데이터베이스로 옮기기 어렵습니다.

다른 데이터베이스에서 넘어오면 걸리는 차이

MySQL 이나 PostgreSQL 을 쓰던 개발자가 Oracle 에서 처음 부딪히는 차이가 둘 있습니다. 빈 문자열을 다루는 방식과 행 하나짜리 시스템 테이블입니다.

Oracle 은 길이가 0 인 문자열을 NULL 로 저장합니다. NULL 은 값이 비어 있다는 뜻이 아니라 값이 없다는 표시입니다. 그래서 '' 를 넣은 행은 빈 문자열로 찾아도 안 나옵니다.

아래는 빈 테이블 t 에 행 하나를 넣은 뒤 두 가지로 세어 본 것입니다.

SQL
INSERT INTO t (name) VALUES ('');

SELECT COUNT(*) FROM t
 WHERE name IS NULL;   -- 1
SELECT COUNT(*) FROM t
 WHERE name = '';      -- 0

둘째 조건은 name = NULL 과 같아집니다. NULL 과 비교하면 참도 거짓도 아닌 「알 수 없음」이 나옵니다. 그래서 조건을 만족하는 행이 하나도 없습니다.

Oracle 코드에는 SELECT SYSDATE FROM DUAL 같은 줄이 자주 보입니다(SYSDATE 는 현재 날짜와 시각을 돌려주는 함수). DUAL 은 행이 딱 하나 든 시스템 테이블입니다. Oracle 의 SELECT 문법은 FROM 절을 요구해 왔습니다. 그래서 테이블과 무관한 값을 계산할 때도 FROM 뒤를 DUAL 로 채웁니다.

여러 서버로 늘리는 구성

서버 한 대로는 두 가지를 못 막습니다. 그 서버가 죽으면 서비스가 멈춥니다. 서버가 놓인 건물이 통째로 망가지면 데이터도 잃습니다. Oracle 에는 두 경우를 막는 구성이 따로 있습니다.

Oracle RAC(Real Application Clusters)는 인스턴스 여러 개가 데이터베이스 하나를 함께 여는 구성입니다. 앞에서 인스턴스와 데이터베이스를 갈라 부른 까닭이 여기 있습니다. 데이터 파일은 모든 서버가 함께 보는 공유 디스크에 둡니다. 서버마다 인스턴스를 하나씩 띄웁니다.

flowchart TD
    subgraph S1["서버 1"]
        I1["인스턴스 1"]
    end
    subgraph S2["서버 2"]
        I2["인스턴스 2"]
    end
    subgraph DISK["공유 디스크"]
        DB["데이터베이스 파일"]
    end
    I1 --> DB
    I2 --> DB

서버 1이 죽어도 인스턴스 2가 같은 파일로 요청을 계속 받습니다. 두 인스턴스가 같은 블록을 고치려 하면 서로 연락해 순서를 맞춥니다.

Oracle Data Guard는 멀리 떨어진 곳에 데이터베이스 사본을 하나 더 두는 구성입니다. 원본 쪽의 리두 로그를 사본 쪽으로 보내 그대로 다시 적용합니다. 원본이 있는 곳이 망가지면 사본을 원본으로 올려 서비스를 잇습니다. 이렇게 한쪽이 멈췄을 때 다른 쪽이 넘겨받는 일을 페일오버라고 합니다.

RAC 는 서버 한 대의 고장을 견디게 합니다. 파일은 한 벌이라 디스크가 망가지면 함께 멈춥니다. Data Guard 는 파일을 두 벌로 두어 디스크나 건물 단위의 장애를 견디게 합니다.

포기한 것

Oracle 은 한 제품 안에 큰 시스템이 필요로 하는 기능을 많이 넣는 쪽을 골랐습니다. 그 대가로 내려놓은 것이 셋 있습니다.

포기한 것 무엇이 곤란해지나
공개된 소스와 무료 사용 쓰려면 라이선스를 사야 합니다. 일부 기능은 본체와 따로 값을 매겨 팝니다
설정할 것이 적은 설치와 운영 메모리 영역·프로세스·파일 종류가 많아 정할 것이 많습니다
다른 데이터베이스로 옮기기 PL/SQL·빈 문자열 처리처럼 Oracle 에만 있는 동작에 기대면 코드가 여기에 묶입니다

둘째 줄 때문에 Oracle 을 쓰는 곳은 전담 DBA(Database Administrator, 데이터베이스 관리자)를 두는 일이 많습니다. 셋째 줄처럼 한 제품에 묶여 빠져나오기 어려운 상태를 벤더 종속이라고 부릅니다.

이 대가를 치르고 얻는 것은 앞에서 본 구성들입니다. 읽기 일관성, RAC, Data Guard, PL/SQL 이 한 벤더 안에서 맞춰져 나옵니다. 문제가 생기면 벤더의 기술 지원을 받습니다.

쓰는 곳

은행·통신·공공기관처럼 오래전에 지은 큰 업무 시스템에서 자주 보입니다. 이런 시스템은 멈추면 안 되고 데이터를 잃으면 안 됩니다. 그래서 RAC 와 Data Guard 같은 구성을 한 벤더에게서 받는 쪽을 골라 왔습니다.

새로 시작하는 작은 서비스는 라이선스 비용이 없는 PostgreSQL 이나 MySQL 을 고르는 일이 많습니다. 기존 Oracle 시스템을 이런 데이터베이스로 옮기는 작업도 흔합니다. 이때 가장 큰 일감이 PL/SQL 과 Oracle 전용 문법을 바꾸는 일입니다.

Java 백엔드는 JDBC(Java Database Connectivity) 드라이버로 Oracle 에 붙습니다. Oracle 이 내놓은 드라이버를 의존성에 넣고 접속 주소를 적으면 됩니다. 그 위의 커넥션 풀과 ORM(Object-Relational Mapping, 객체-관계 매핑)은 다른 데이터베이스를 쓸 때와 같습니다.

관련 항목

Oracle 서버를 이루는 구성 요소

인스턴스 · SGA · PGA · 버퍼 캐시 · 백그라운드 프로세스 · 데이터 파일 · 제어 파일 · 리두 로그 · 데이터 블록 · 테이블스페이스

Oracle 이 트랜잭션과 동시성을 지키는 수단

트랜잭션 · 커밋 · 롤백 · WAL · 언두 · 읽기 일관성 · MVCC · 격리 수준 · 락

Oracle 에서 쓰는 언어와 객체

SQL · PL/SQL · 저장 프로시저 · 트리거 · 시퀀스 · 스키마 · 테이블 · DUAL · NULL

Oracle 을 여러 서버로 늘리고 지키는 구성

Oracle RAC · Oracle Data Guard · Oracle GoldenGate · 공유 디스크 · 클러스터 · 복제 · 페일오버

Oracle 에 애플리케이션이 붙는 수단

JDBC · Java · 커넥션 풀 · ORM · Hibernate

Oracle 을 쓸 때 치르는 비용

라이선스 · DBA · 벤더 종속 · 데이터베이스 마이그레이션

Oracle 과 같은 역할을 두고 겨루는 데이터베이스

PostgreSQL · MySQL · MariaDB · Microsoft SQL Server · IBM Db2 · SQLite

Oracle 과 이름이 겹치는 이웃

Oracle Corporation · Oracle JDK · Oracle Coherence · 테스트 오라클

Oracle 이 속하는 상위 분류

데이터베이스 · 관계형 데이터베이스 · 데이터베이스 관리 시스템 · OLTP

다른 이름: Oracle Database · 오라클 · 오라클 데이터베이스