본문 바로가기

DB/Oracle

[Oracle] Chapter 6. 커넥션과 서버 프로세스의 생성

6.1 애플리케이션에서의 접속을 왜 배워야 하는가?

 

그 이유는 애플리케이션에서 접속하는 것을 최적화하는 것만으로도 데이터베이스의 성능을 더욱 끌어올릴 수 있고, 애플리케이션에서 피해야만 하는 코딩 방식을 이해하는 데도 도움이 되기 때문이다. 오라클은 애플리케이션 서버를 사용한 시스템이나 클라이언트/서버 형태(클라이언트/서버(CS, Client/Server): 시스템 사용자가 사용하는 클라이언트 장비(주로 PC)에서 작동하는 클라이언트 프로그램을 '클라이언트;라고 부르며, 클라이언트에게 서비스를 제공하는 서버 장비에서 작동하는 서버 프로그램을 '서버'라고 부른다. 이 둘이 합쳐져서 클라이언트/서버를 구성한다.)의 시스템에서도 많이 사용되고 있다. 죽 오라클과 '오라클을 사용하는 애플리케이션'이 같은 서버 위에 있는 경우는 드물며, 애플리케이션(오라클 클라이언트)과 오라클이 네트워크를 통해서 통신하는 경우가 많다. 그러므로 접속 설정으로 인한 장애가 쉽게 발생하지만, 간단한 장애일 때는 아키텍처를 이해하고 있다면 대부분 쉽게 해결할 수 있다.

 

이번 장에서는 유닉스 환경을 기준으로 살펴보겠다. 따라서 설명하는 내용 중 일부는 윈도우에서 운영하는 오라클에는 해당되지 않을 수 있다. 단 윈도우용 오라클과 유닉스용 오라클의 내부 구조는 대부분 동일하지만, 유닉스 쪽 프로세스의 동작이 살펴보기에 좀 더 편하다. 그러므로 유닉스 환경을 먼저 살펴보는 것이 윈도우 환경의 오라클을 이해하기 위한 지름길이라고 생각한다.

 

6.2 오라클의 접속 동작

 

6.2.1 소켓의 동작 이미지

 

오라클은 TCP/IP의 소켓(socket)을 네트워크 통신 수단으로 사용하고 있다. 그래서 소켓이 어떻게 동작하는지를 머리속에 이미지로 떠올릴 수 있어야 한다. 소켓을 사용하면 마치 전화처럼 다른 장비에 있는 프로그램과 통신할 수 있다. 장비 안에는 프로그램(프로세스)이 작동하고 있고, 그 프로그램이 수화기에 해당하는 소켓을 가지고 있는 모습을 머리속에서 그려주면 된다.

 

한번 소켓을 만들어 두면 소켓을 읽고 쓰기만 해도 송수신을 구현할 수 있으므로 프로세스의 측면에서 보면 편리한 기능이라고 할 수 있다. 네트워크의 드라이버와 OS의 라이브러리가 송수신을 수행하며, 네트워크 안에는 여러 개의 소켓이 존재한다. 그러면 어떻게 해야 원하는 대상에 틀리지 않고 연결할 수 있을까? 소켓은 '주소(address)'와 '포트(port)번호'라고 불리는 번호의 조합으로 식별할 수 있으며, 어떻게 연결하는지를 동작과 함께 설명하겠다.

 

1. 서버: 특정 포트 번호로 연락이 오는 것을 기다리고 있다(listen 상태). 전화를 기다리는 것에 해당된다.

2. 클라이언트: 연결하고 싶은 상대 장비의 주소와 포트 번호를 향해 송신한다(전화를 거는 것에 해당한다)

3. 서버: 응답한다. 그 후 몆 번 송수신을 하고 난 후에 연결(커넥션)이 확립된다.

 

중요한점은 연락이 오기만을 기다리고 있는 프로세스가 존재한다는 점과 연결할 떄는 송신 측에서 '주소'와 '기다리고 있는 포트'를 반드시 지정해야 한다는 점이다.

 

6.2.2 오라클에서 소켓의 동작

 

오라클에서도 소켓을 사용하고 있는 이상, 위와 같이 동작한다. 오라클에서는 수신을 기다리는 프로세스를 '리스너(listener)'라고 부른다(서버 프로세스가 아니다). 리스너로 접속하려는 프로세스는 업무 애플리케이션의 프로세스이다. 

 

6.2.3 커넥션 처리 1: 리스너 기동

 

커넥션 처리를 창고 회사 오라클에 비유하면서 상세히 설명하겠다. 리스너는 창고 회사 오라클의 접수 데스크이다. listener.ora 파일은 접수 데스크가 가지고 있는 '회사의 대표 번호' 및 '내선 전화번호부'이며, 하나의 리스너는 여러 개의 데이터베이스에 안내할 수 있다. 물론, 일반적인 상황에서는 하나의 리스너가 데이터베이스 한 개를 담당하게 한다.

 

1. DB서버: 자신이 listen해야 할 포트 번호 등을 listener.ora에서 확인한 뒤 listen을 시작함

- listener.ora 파일(리스너의 설정 파일)

2. APP서버: tnsnames.ora 파일을 확인해서 리스너의 주소나 포트 번호, 서비스 이름 등을 확인함. 확인한 내용을 사용해서 연결(커넥션)을 확립시킴

-  tnsnames.ora(단축 다이얼에 해당)

3. DB서버: 소켓이 확립되면 서버 프로세스를 생성함. 소켓은 서버 프로세스에게 전달해줌. 단, 일부 OS에서는 소켓을 공유하는 경우도 있음

 

-- 아직은 서비스명을 자세히 설명해도 이해하기 어려우니, '일반적으로 데이터베이스를 식별하기 위해 사용하는 이름'이라고 생각하자. 또한, JDBC의 Thin 드라이버는 접속 관련 설정을 tnsnames.ora를 사용하지 않고 직접 기술한다.

 

오라클은 기본적으로 리스너의 포트 번호로 1521을 사용하지만, 다른 애플리케이션과 충돌이 발생했을 때는 다른 번호를 사용해도 상관없다. 리스너의 설정(최근에는 주로 GUI 도구(NET Configuration Assistant, NETCA)를 사용해서 리스너나 오라클 클라이언트의 설정을 수행한다. 이번에는 내부 동작을 소개하고 싶어서 설명하지 않았지만, 실제로 설정할 떄는 도구가 편하다.)이 끝나면, lsnrctl이라는 도구를 사용해서 리스너를 기동한다(리스트 6.1). 리스너가 자신이 안내해야 하는 데이터베이스를 인식하는 방법으로는 listener.ora 파일에 기록되어 있는 설정을 읽거나, 데이터베이스가 자동으로 등록하는 방법이 있다. 일반적으로는 사용이 간단한 자동 등록을 선택해서 사용한다. 이로써 접수 데스크 직원이 출근해서 업무를 시작할 준비가 되어있다. 고객이 언제 전화를 걸어오더라도 업무를 수행할 수 있다.

 

리스트 6.1 리스너의 기동

OS 프롬프트> lsnrctl start  -- 기본 리스너를 기동시키는 명령어

LSNRCTL for Linux: Version 18.0.0.X.X - Production on XX-XXX-2022 18:16:23

Copyright (c) 1991, 2017, Oracle. All rights reserved.

Starting /<ORACLE_HOME>/bin/tnslsnr: please wait...

TNSLSNR for Linux: Version 18.0.0.0.0 - Production
System parameter file is /<ORACLE_HOME>/network/admin/listener.ora -- 리스너를 정의해둔 listener.ora 파일의 위치
Log messages written to /<ORACLE_BASE>/tnslsnr/hostname/listener/alert/log.xml
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=XXXX)(PORT=1521))) -- 이 호스트(host)의 해당 포트로 listen하고 있다는 것을 의미
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
.
.
<중략>
.
.
The command completed successfully -- 이 메세지가 나오면 명령어가 성공했다는 것을 의미

6.2.4 커넥션 처리 2: 애플리케이션에서의 커넥션

 

다음으로 업무 애플리케이션 측에서의 커넥션이다. 업무 애플리케이션 안에서 연결하기 위한 명령이 실행되거나, SQL*Plus에서 connect 명령어를 실행한 순간에 데이터베이스에 연결된다. 여기서는 먼저 데이터베이스에 연결할 때 필요한 정보를 오라클 클라이언트에 전달할 필요가 있으며, 그 정보를 '커넥션 디스크립터(connection descriptor)'라고 부른다. 커넥션 디스크립터 안에는 '주소는 XXX이며, 포트가 XXX이고, 서비스 이름이 XXXX....' 같은 정보가 포함되어 있다. 연결할 떄마다 정보를 입력할 수는 없으므로, 일반적으로 tnsnames.ora에 커넥션 디스크립터를 작성해놓고 커넥션 식별자(별칭)를 커넥션 디스크립터마다 붙인다. 그래서 연결할 때는 해당 커넥션 식별자를 오라클 클라이언트에 전달하기만 하면 된다. 이를 전화로 비유하면 '단축 다이얼'이다. 일반적으로 오라클 클라이언트는 tnsnames.ora의 커넥션 디스크립터 정보를 사용해서 리스너와 클라이언트 사이에 소켓을 생성하고, 리스너에게 '이 데이터베이스와 통신하고 싶어'라고 연락한다. 다음의 리스트 6.2에는 tnsnames.ora의 예를, 리스트 6.3에서는 애플리케이션에서 연결하는 예를 소개한다.

 

리스트 6.2 tnsnames.ora의 예

ORA18C = -- 커넥션 식별자 : 임의의 이름으로 설정할 수 있으며, net service name이라고도 부름
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP) (HOST = XXXX) (PORT = 1521)) -- 소켓을 생성하는 데 필요한 정보
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl) -- 리스너에게 전달하기 위한 정보
    )
  ) -- 커넥션 디스크립터

리스트 6.3 애플리케이션에서 오라클에 연결하기 위한 명령의 예

SQL*Plus일 떄
SQL*Plus의 프롬프트> CONNECT scott/tiger@ora18c -- 커넥션 식별자가 적혀 있음 (대/소문자는 구별하지 않음)

PRO*C일 때
EXEC SQL CONNECT :username IDNETIFIED BY :password USING :dbstring ;
-- username, password : 계정명과 비밀번호를 입력하는 변수, dbstring : 커넥션 식별자를 입력하는 변수

JDBC OCI 드라이버일 때
OracleDataSource ods = new OracleDataSource();
ods.setURL("jdbc:oracle:oci:@ORA18C"); -- 커넥션 식별자가 적혀 있음
ods.setUser("scott");
ods.setPassword("tiger");
Connection conn = ods.getConnection();

JDBC Thin 드라이버일 때
OracleDataSource ods = new OracleDataSource();
ods.setURL("jdbc:oracle:thin:@//XXXXX:1521/orcl); -- Thin드라이버는 tnsnames.ora를 사용하지 않으므로 접속하기 위한 정보를 직접 적음
ods.setUser("scott");
ods.setPassword("tiger");
Connection conn = ods.getConnection();

※JDBC OCI 드라이버는 오라클이 제공하는 JDBC 드라이버의 일종으로, 오라클 클라이언트와 함께 설치되어야 하는 드라이버이다.
JDBC Thins 드라이버는 오라클이 제공하는 JDBC 드라이버의 일종으로, 자바로 만들어져 있으며
따로 오라클 클라이언트를 설치할 필요가 없다(드라이버 파일을 복사하는 것으로 충분함)

6.2.5 커넥션 처리 3: 서버 프로세스의 생성

 

마지막 과정은 서버 프로세스를 생성하고 소켓을 인계받는 것이다. 소켓을 생성하면 리스너가 그대로 SQL 처리를 해도 될 것처럼 보이지만, 한번 SQL 처리를 시작하면 요청받는 SQL을 처리하느라 다른 처리를 할 수 없게 되므로 전담 영업 담당자인 서버 프로세스를 생성해서 SQL 처리를 즉시 인계한다. 서버 프로세스의 생성을 비유해서 말하자면, 창고 회사 영업 담당자의 출근이다. 이 부분(영업 담당자의 출근은 고객에게 요청이 온 시점에 한다)이 현실에서의 회사와는 다른 점이다. 하지만 서버 프로세스의 생성을 '출근'에 비유한 것이 오히려 적절하다는 생각이 들 정도로 서버 프로세스의 생성은 큰 작업이다.

먼저 OS상에서 프로세스를 생성해야 하며(일반적으로도 프로세스를 생성하는 것은 처리가 무겁다고 알려져 있다), 서버 프로세스가 사용할 수 있는 공유 메모리를 확보해야한다. 또한, 서버 프로세스용 전용 메모리(PGA)도 확보해야 한다. 그 외에 데이터베이스 내부의 처리도 여러 가지가 남아 있다. 따라서 서버 프로세스를 한번 생성 하는 데는 가벼운 SQL문을 처리할 때 사용하는 CPU 시간보다 몆 배에서 몆십 배 많은 CPU 시간을 사용한다. 그럼에도 불구하고 최근에 경험한 프로젝트에서는 애플리케이션을 개발하면서 서버 프로세스의 생성이 필요한 물리 커넥션을 만들고 종료하기를 반복하는 형태로 개발하는 형태로 개발하는 사례를 볼 수 있었다.(리스트 6.4) 여러분은 이렇게 하지 않도록 주의하자.

리스너는 서버 프로세스 생성이 끝나면 소켓을 서버 프로세스에 인계한다. 창고 회사로 비유해서 설명해보면 회사의 대표 전화번호로 받은 전화를 접수 데스크가 담당자에게 돌려주는 과정과 같다. 리스너가 인계한 후부터 서버 프로세스와 오라클 클라이언트는 직접 송수신하므로 리스너는 자유로워진다. 이것은 이 책의 1장에서 나열했던 오라클을 이해하기 위한 키워드 중 하나인 '병렬 처리를 가능케 하고 높은 처리량을 실현한다'에 해당한다.

 

리스트 6.4 접속과 종료를 반복하는 예

... 업무 처리 코드 ...

Connection conn = ods.getConnection(); -- 업무 처리를 하는 도중 데이터베이스의 데이터가 필요해져,
...executeQuery("select ...");         -- 데이터베이스에 접속한 후 데이터를 가져옴.
conn.close();                          -- 그 후에 커넥션을 닫아버림

... 업무 처리 코드 ...

Connection conn = ods.getConnection(); -- 또 데이터베이스의 데이터가 필요해져서 다시 한번 접속한 후 데이터를 가져옴
...executeQuery("select ...");
conn.close();

... 업무 처리 코드 ...

커넥션을 닫지 않고 계속 사용한다면, SQL보다 부하가 몆십 배나 높은 커넥션 처리를 하지 않아도 됨. 이런 식으로 사용하는 것은 시스템의 응답 시간을 나쁘게 만들고 있다고 말해도 과언이 아님. 단, 6.5절에 설명하는 '커넥션 풀'이라고 불리는 기술을 사용하고 있다면, 애플리케이션에서 커넥션하고 있는 것처럼 보여도 실제로 물리 커넥션을 만들지 않고(서버 프로세스도 생성되지 않음) 처리할 수 있으므로 문제가 되지는 않음

 

Tip

서버 프로세스를 생성하는 것은 무겁다?

 

리스너를 경유해서 오라클에 접속할 때, 리스너가 서버 프로세스를 생성하고 클라이언트와의 커넥션을 확립한다(전용 서버 접속일 떄). 프로세스를 생성하기 위해서는 여러 처리가 필요하다. 먼저 OS에서 새로운 메모리를 할당받아야 하며, 메모리 공간을 오라클용으로 초기화한다. 또한, SGA에 접근할 수 있도록 각종 설정을 수행한다. 그 후에는 오라클을 정상적으로 사용할 수 있도록 파라미터를 읽어오거나 초기화를 위한 SQL을 수행한다.

클라이언트에서 수행되는 일반적인 OLTP 시스템의 대부분은 간결한 SQL문으로 이루어져 있으며, 접속 처리에 비하면 매우 짧은 시간만 사용하고 끝나는 경우가 많다. 리눅스 장비(CPU: core i5/2.6GHz)에서 시험해본 결과, 하나의 커넥션을 생성하는 데는 0.048초 정도가 걸렸다. 0.049초라는 시간은 어떻게 보면 별거 아니라고 생각할 수 있지만, TPS(Transactions Per Second)가 수백~수만에 이르는 시스템에서 SQL을 실행할 때마다 0.048초가 필요하다면 결코 작은 시간이라고 할 수 없다. 그런 관점으로 생각해보면 커넥션을 생성하는 작업은 매우 무거운 처리라는 것을 알 수 있다.

 

6.3 접속 동작의 확인

 

6.3.1 tnsnames.ora 파일을 사용하지 않으면 어떻게 되는가?

 

tnsnames.ora 파일의 커넥션 식별자는 단축 다이얼과 같다고 설명했다. 그러면 정말로 단축 다이얼인지 아닌지를 시험해보자. 리스트 6.5는 tnsnames.ora 파일을 사용하지 않고 글자 길이가 긴 커넥션 디스크립터를 직접 작성해서 연결해본 예이다. 일반적으로는 커넥션 디스크립터를 일일이 작성해서 사용하는 것은 번거로우므로 리스트 6.6과 같이 단축 다이얼로 작성되어 있는 tnsnames.ora 파일을 사용한다. 빈번하게 접속하지 않는 환경이라면 리스트 6.7과 같은 EZCONNECT(Eazy Connect)라는 방법을 사용하는 것도 가능하다. 하지만 tnsnaes.ora에 등록을 해두는 편이 훨씬 간편하므로 빈번하게 접속해야 하는 환경은 tnsnames.ora파일을 사용하자.

 

리스트 6.5 tnsnames.ora 파일을 사용하지 않고 접속할 때

SQL> connect scott/tiger@(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = XXXX)
(PORT = 1521))(CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = orcl)))
Connected. -- 이렇게 길이가 긴 내용을 매번 작성해서 사용하기는 번거로우므로, 단축 다이얼에
-- 해당하는 커넥션 식별자를 tnsnames.ora에 적어 놓고 사용함

리스트 6.6 tnsnames.ora 파일을 사용해서 접속할 때

SQL> connect scott/tiger@ORA18C -- 커넥션 식별자

tnsnames.ora 설정
=============================================================
ORA18C = -- 커넥션 식별자
 (DESCRIPTION =
  (ADDRESS = (PROTOCOL = TCP)(HOST = XXXX)(PORT = 1521))
  (CONNECT_DATA =
   (SERVER = DEDICATED)
   (SERVICE_NAME = orcl)
  ) -- 커넥션 디스크립터
=============================================================

리스트 6.7 EZCONNECT를 사용한 접속 예

SQL> connect scott/tiger@XXXX:1521/orcl -- <호스트명(주소)>:<포트>/<서비스명>

리스트 6.8 tnsnames.ora 파일에 해당하는 데이터가 없을 때

$ sqlplus scott/tiger@XXXX

SQL*Plus: Release XX.X.
... 중략 ...
  ERROR:
ORA-12154: TNS:could not resolve the connect identifier specified
-- '기술된 커넥션 식별자를 알 수 없다'라고 말하고 있음. 간단히 말하면, tnsnames.ora 파일에서 찾을 수 없다는 것

6.3.2 데이터베이스 서버의 동작

 

tnsnames.ora 파일의 설정이 끝나서 접속을 시도(다이얼을 돌려 보려고)했지만 에러가 발생했다(리스트 6.9) 아무래도 리스너가 기동하지 않았던 것 같아서 리스너를 기동해보았지만 또 에러가 발생했다(리스트 6.10).

 

리스트 6.9 리스너가 기동하지 않았을 때

$ sqlplus scott/tiger@XXXX

SQL*Plus: Release XX.X.
... 중략 ...
ERROR:
ORA-12541: TNS:no listener -- 지정한 포트로 listen하고 있는 리스너가 없었으므로
-- 'no listener'라고 표시함. 이 메세지가 나타났을 때는 리스너가 기동하지 않았다든가 잘못된 주소를 지정한 경우가 많음

리스트 6.10 리스너가 가진 데이터베이스의 정보와 일치하지 않을 때

$ sqlplus scott/tiger@XXXX

SQL*Plus: Release XX.X.
... 중략 ...
ERROR:
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
-- 리스너가 커넥션 디스크립터에서 요청받은 서비스(여기서는 대부분 데이터베이스에 해당)를 찾을 수 없다고
-- 응답하고 있음. 요청이 리스너까지는 도달했다는 것을 의미함

이렇듯 데이터베이스에 접속하지 못하는 장애는 현장에서 빈번하게 일어난다. 장애가 발생했을 때를 위해서라도 무엇이 어떤 역활을 가지고 있고, 어떤 식으로 작동하며, 어디에 정보가 기록되어 있는지를 꼭 알아 두자. 그러면 어디에서 문제가 발생했는지 쉽게 알아낼 수 있다. 또한, 에러 번호를 토대로 매뉴얼을 찾아보는 대처 방법도 적혀 있으므로 그 부분도 참고하자.

 

Tip

데이터베이스에 접속하지 못할 때 확인해볼 만한 실수들

 

데이터베이스에 접속하려고 하면 'ORA-12154: TNS xxxxxx'라는 에러가 발생하면서 접속하지 못하는 경우가 있다. 에러의 원인은 여러 가지가 있겠지만 그중에서도 '설정 실수'인 경우가 의외로 많다.

 

- 접속할 호스트명(IP)을 잘못 입력했다

tnsnames.ora 파일을 보면 접속할 호스트의 IP를 'HOST = 192.168.56.xxx(통신할 수 없는 호스트)' 부분에서 확인할 수 있다. ping 명령어를 사용해서 호스트와 통신이 가능한지를 확인하자.

 

- tnsnames.ora가 올바르지 않은 경로에 위치하고 있다.

오라클은 기본적으로 $ORACLE_HOME/network/admin 경로의 파일을 확인한다. 올바른 경로에 tnsnames.ora 파일이 있는지를 확인하자. TNS_ADMIN 환경 변수를 설정하여 tnsnames.ora의 경로를 임의로 변경할 수도 있다.

 

- tnsnames.ora에 정의된 내용과 다른 커넥션 식별자를 사용해서 접속을 시도했다

sqlplus scott/tiher@ORA17C  <- 'ORA18C의 오타이며 tnsnames.ora에는 존재하지 않는다' 명령어의 커넥션 식별자가 tnsnames.ora에 제대로 존재하고 있는지를 확인하자.

 

- 리스너가 기동되어 있지 않다

접속할 호스트의 리스너가 기동되어 있는지 여부를 확인하자

 

- 리스너에 서비스명을 등록하지 않았다

리스너가 기동한 직후에는 서비스명이 자동으로 등록되지 않을 떄가 있다. 1분 정도 기다리거나, 데이터베이스에서 서비스명 등록 명령어를 실행한다.

 

이외에도 '전원이 켜져 있지 않다', '랜선이 뽑혀 있었다'와 같은 기본적인 문제가 원인일 때도 있다. 에러가 발생하더라도 조급해하지 말고 하나씩 원인을 구별해 분석하는 것이 중요하다.

 

6.4 정지나 리스너의 상태 확인

 

지금까지 설명하지 않았던 각 프로세스의 정지나 리스너의 상태 확인에 관해 간단하게 설명하겠다.

일반적으로 애플리케이션에서 접속을 종료하는 처리(close나 disconnect)를 수행하면 서버 프로세스도 함께 종료(프로세스가 없어진다)된다.

그러면 리스너는 어떻게 정지할까? 리스너는 lsnrctl의 stop 명령어를 사용해 정지할 수 있다. 또한, lsnrctl의 status 명령어로 현재의 리스너 기동 상태나 listen하고 있는 포트의 번호, 보유하고 있는 데이터베이스의 정보 등을 알 수 있으므로 데이터베이스에 접속할 수 없는 장애가 발생할 때 활용하자.

 

칼럼

접속을 강제로 중지하는 명령어

 

오라클은 대부분의 환경에서 TCP/IP를 사용한다. TCP/IP에서 에러가 반환되지 않으면 오라클은 문제가 발생한 것을 눈치채지 못할 수 있다. 예를 들어, 오라클 클라이언트가 이미 비정상 종료되었음에도 서버 프로세스는 데이터베이스에 Lock을 소유한 채로 클라이언트에서 연락이 오기만을 기다리는 경우가 있다. 또한, 처리 도중에 테이블에 Lock을 걸어둔 채로 사용자가 식사를 하러 나갔던 사례도 있다. 이런 행위들이 쌓이면 애플리케이션 전체를 멈추게 할 수 있다.

그래서 이럴 때는 일반적으로 다음의 명령어로 접속을 끊고 Lock을 해제하든가, 인스턴스를 재기동하여 대처한다.

alter system kill session ...;

'어쩔 수 없을 떄는 alter system kill session을 사용해서 세션(클라이언트와 오라클 간의 연결)을 끊을 수 있다'라고 기억해두자. 또한, 끊어졌다는 사실을 빠르게 검출할 수 있도록 자동으로 검출하는 TCP/IP의 설정도 추천한다. 덧붙여 다음 절에서 살펴볼 커넥션 풀의 사용할 떄는 한 개의 커넥션이 끊어지면 모든 커넥션이 다시 접속하는 경우도 있으므로 주의하자.

 

6.5 성능을 개선하기 위해서는?

 

커넥션을 맺는 것과 서버 프로세스를 생성하는 것은 오라클에게는 무거운 작업이라는 것을 설명했다. 일너 힘든 작업을 '병렬 처리를 가능케 하고 높은 처리량을 실현한다'를 지키면서 개선할 수는 없을까?

여기서 힌트가 되는 것이 '현실의 실제 시스템에서 서버 프로세스는 긴 시간 동안 아무 것도 하지 않는다'라는 점이다. 수십, 수백 개의 서버 프로세스가 가동 중인 시스템도 드물지는 않지만, 대부분의 서버 프로세스는 가동 시간 중 일부만이 SQL을 처리 한다. 또한 클라이언트와 일대일로 대응하여 서버 프로세스가 존재하고 있는 이상, 서버 프로세스의 수를 줄이는 것(애플리케이션을 줄인다)은 매우 곤란할 수 있다. 그러면 실제 영업 담당자처럼 서버 프로세스 한 명이 여러 개의 클라이언트를 담당하게 하면 어떨까? 하지만 이것은 큰 문제가 따른다. 서버 프로세스는 SQL을 처리하는 프로세스이므로 한번 SQL 처리를 시작하면 끝날 떄까지 다른 작업은 하지 않는다는 점이다. 만약 클라이언트가 한 명이라면 문제가 되진 않겠지만, 서버 프로세스 한 명이서 많은 클라이언트를 담당해야 할 때 요청을 동시에 받으면 대처할 수 없게된다. 이래서는 '병렬 처리를 가능케 하고 높은 처리량을 실현한다'를 지켰다고 말할 수 없다.

그래서 효율을 높이기 위한 방법 중 하나로 여러 서버 프로세스가 여러 클라이언트의 SQL을 처리할 수 있도록 한다면 어떻게 될까? 바꿔서 말하면 '서버 프로세스 몆개를 풀로 구성해두고 여러 애플리케이션이 자신이 쓰고 싶을 때만 풀에서 하나를 꺼내 사용한다'는 구조라면 대부분이 대기 상태이던 서버 프로세스의 가동률을 높일 수 있을 것이다. 이러한 구성을 위해서 오라클의 '공유 서버 구성'이나 ;커넥션 풀(연결 풀)이라고 불리는 구성을 사용할 수 있는데, 최근엔 커넥션 풀을 사용하는 일이 많으므로 이를 간단히 소개하겠다.

 

애플리케이션 서버: 우리는 쓰고 싶을 때만 커넥션을 사용한다. 누가 어떤 커넥션을 사용할지를 정하지는 않았다. 커넥션을 점유하지 않으므로 커넥션의 수가 적어도 문제가 없다.

 

DB 서버: 저는 커넥션 풀을 사용하지 않았던 떄와 다른 점은 느끼지 못하고 있다. 또한 클라이언트가 그떄마다 다르다는 것을 눈치채지 못했고 같은 사람이라 생각하고 서비스 했다.

 

※ 서버 프로세스가 애플리케이션의 스레드에 비해 별로 없다는 점에 주목!!

- 커넥션을 모두가 돌려 쓴 덕분에 효율이 높아졌다. 접속하는 횟수가 줄어들었기에 성능도 좋고, 커넥션 수가 줄었기 떄문에 서버 프로세스가 사용하는 메모리도 적어졌으니 좋은 일만 가득한 거 같다.

 

칼럼

'scott'과 'tiger' 이야기

 

이 책에서도 샘플 계정으로 scott/tiger를 볼 수 있듯이, scott/tiger는 전 세계에 널리 알려졌다. 이 이름에는 어떤 유래가 있는 것일까? 실은 scott은 초기에 오라클을 개발하고 있었던 개발자의 이름(Bruce Scott)이다. 그리고 tiger는 그가 기르고 있던 고양이의 이름이라고 한다. 가끔 운영 환경의 계정과 비밀번호에 scott/tiger를 사용하고 있는 DBA들이 보이는데, 이렇게 많은 사람이 유추해 낼 수 있는 비밀번호는 사용하지 않도록 하자.

 

6.6 요약

 

- 데이터베이스에 접속하기 위해서는 데이터베이스 서버의 주소와 리스너의 포트 번호가 필요하다.

- tnsnames.ora라는 파일 안에 접속하기 위한 정보를 기록해둔다(JDBC Thin 드라이버는 제외)

- 리스너라고 하는 프로세스가 접속 요청을 받으며, 서버 프로세스의 생성도 같이 수행한다.

- listener.ora라는 파일 안에 리스너의 설정(포트 번호 등)을 기록해둔다.

- 서버 프로세스의 생성은 매우 무거운 작업이므로 가능하면 줄이는 것이 좋다.

 

Q. 리스너를 정지했을 떄 기존 커넥션은 어떻게 될까? 그리고 SQL의 처리는 가능한건가?

 

A. 기존의 커넥션에는 아무런 영향도 없으며, SQL처리도 문제없이 수행된다. '6.2.5 커넥션 처리 3: 서버 프로세스의 생성'에서 설명한 것처럼, 리스너는 소켓을 즉시 서버 프로세스에 인계하고(또는 소켓을 공유하고) 이후에는 서버 프로세스와 오라클 클라이언트가 직접 주고받는다. 따라서 리스너는 기존 커넥션과 관계가 없다. 지금도 '리스너를 정지하면 SQL의 처리가 한번에 정지된다'고 생각하는 분들이 많은 듯하지만, 그런 일은 발생하지 않는다.