1. Data type
비고 : oracle
| Datatype | 설 명 |
|---|---|
| VARCHAR2(size) | size 만큼 2000개까지 문자열 데이터 저장. 가변형. |
| CHAR(size) | size 만큼 255개 문자까지 문자열 데이터 저장. 고정형. |
| NUMBER | 실수형 데이터 저장 NUMBER(p,s) |
| NUMBER(p,s) | 숫자데이터를 저장. p는 최대자리수, s는 소수점 이하 자리수 |
| DATE | BC 4712년 1월 1일부터 AD 4712년 12월 31일까지의 날짜를저장 * 기본 날짜 형식은 'DD-MON-YY' 임 |
| LONG | 2GB의 문자까지 문자열 데이터를 저장. |
| 비고사항 | * CHAR과 VARCHAR2의 사용 - 데이터의 update가 잦은 칼럼은 char형은 씀. - 한 칼럼에서 고정된 길이의 데이터를 입력할 경우는 char형, 가변형길이의 데이터를 입력할 경우는 varchar를 사용. |
| Datatype | 설 명 |
|---|---|
| VARCHAR2(size) | 가변길이 문자열. 자신의 실제길이로 저장됨. |
| CHAR(size) | 고정 문자열, 최대값은 8000 |
| NCHAR(n) | 고정길이 유니코드 문자셋 저장. CHAR는 1바이트를 사용함에비해, NCHAR는 2바이트를 사용. 최대 4000. |
| NVARCHAR(n) | 가변길이 유니코드 문자셋. |
| TEXT(n) | 최대 2GB까지의 고정길이 문자열 저장 |
| NTEXT(n) | 큰 가변 길이 문자 데이터 저장. TEXT는 1바이트, NTEXT는 2바이트사용. |
| INT | 정수값 표현. 4바이트. |
| SMALLINT | 정수값 표현. 2바이트(-32768~32767 |
| TINYINT | 음이 아닌 정수값 표현. 1바이트(0~255) |
| DECIMAL(p,[s]) | 고정 소수점 값. |
| NUMBER(p,[s]) | DECIMAL과 동의어. |
| REAL | 부동소수점 값 |
| FLOAT[(p)] | 부동소수점 값 |
| MONEY | 화폐값을 표현 하는 데 사용. 8바이트 |
| SMALLMONEY | MONEY와 같지만, 4바이트 사용. |
| DATETIME | 4바이트의 정수값으로 저장되는 DATE와 TIME을 저장. |
| SMALLDATETIME | 2바이트의 정수값으로 저장되는 DATE와 TIME을 저장 |
비고 : Informix
| Datatype | 설 명 |
|---|---|
| VARCHAR(a,b) | 가변길이 문자열. 자신의 실제길이로 저장됨. Max 255 |
| CHAR(size) | 고정 문자열, 최대값은 32767 byte |
| TEXT | 최대 2GB까지의 고정길이 문자열 저장 blobspace공간을 만들어저장하는 것이 IO향상됨 |
| Byte | 최대 2Gb까지의 바이너리 데이터 저장blobspace공간을 만들어별도로 저장 |
| INT | 정수값 표현. 4바이트. |
| SMALLINT | 정수값 표현. 2바이트(-32768~32767 |
| SERIAL | 서버에 의해 자동적으로 1씩 증가 내부적 int와 동일 |
| DECIMAL(p,[s]) | 고정 소수점 값. ? 숫자형은 모두 decimal로 하는 것이 좋다.엔진에서 decimal로 처리 |
| REAL | 부동소수점 값 |
| FLOAT | 16자리 부동소수점 값 |
| SMALLFLOTE | 8자리 부동 소수점 |
| MONEY | 화폐값을 표현 하는 데 사용. Decimal 형태로 저장되며 $와 소수점뒤에 두자리 표시 DBMONY=’\’;export DBMONEY |
| DATETIME INTERVAL | 4바이트의 정수값으로 저장되는 DATE와 TIME을 저장. |
| DATE | 2바이트의 정수값으로 저장되는 DATE만 저장 DBDATE=y4md/;exportDBDATE |
SQL3 type : Informix 9.x, oracle 8.x이상 버젼에서 비정형 데이더를저장, 검색하기 위한 확장형 타입과 ORDBMS의 특징인 object type을지원한다.
- clob : 문자열 데이터를 저장
- blob : 바이너리 데이터를 저장
- list, multiset, set : 한 컬럼에 여러 데이터 저장
- named row type, unnamed row type : type을 상속받아 테이블을생성할 수 있음
- user define function 및 user define type을 생성
- cast 함수를 이용하여 type의 확장지원
- Informix : lvarchar,int8,Serial8 type 새롭게 지원
2. 데이터 저장 공간
비고 : oracle
- 데이터베이스(oracle instance당 한 개가 존재) >테이블스페이스(system, nonsystem tablespace로 나누어 장치간 적절히스토리지를 분배해야 함, 물리적인 공간) > 세그먼트 ( 테이블세그먼트, 인텍스 세그먼트, 임시세그먼트, 롤백세그먼트 :tablespace에 있는 여러 datafile에 걸처서 생성할 수 있음)>datafile(물리적 공간) > 익스텐트( 연속된 블록의 집합 ) >데이터 블록 (가장 작은단위의 저장공간)
비고 : MS-SQL
- 데이터 베이스 ( system database : master,tempdb,msdb,model userdatabase로 나누며 user database는 여러 개 생성할 수 있음 ) > 주데이터 파일( .mdf ), 보조 데이터 파일( .ndf), 로그 데이터 파일 (.ldf ) > 익스텐트 > 블럭
비고 : Informix
- dbspace ( system, nonsystem dbspace로 구별하여 생성해야 함 ) >chunk (물리적인 데이터 공간) > database ( 한 dbspace에 여러 개의데이터베이스 생성 가능 ) >chunk(물리적은 공간 cook device, rawdevice로 구성할 수 있음) > tablespace ( extent들의 연속된 공간 )> extent > page
3. 테이블 정보 보기 및 sql query tool
비고 : oracle-sqlplus를 이용해서 server에 connection
- SELECT * FROM tab;
- DESC table_name
- Enterprise Manager를 이용해서 비주얼 볼 수 있음
비고 : Informix-dbaccess를 이용해서 볼 수 있음.
- Dbaccess -> 메뉴에서 table -> database선택 ->table선택.
- Dbshcema 툴을 이용
4. SQL 문 이용 가이드
4.1 NULL
비고 : oracle,Informix 동일* 이용할 수도 없고, 값이 할당되어 있지도 않고, 알려져 있지않은
또는 적용불가능한 값을 의미.
* 0이나 공백(space)와 같은 것이 아님.
* NULL 값을 포함한 산술 표현식 결과는 NULL임.
NVL 함수 이용.
* NULL 값은 NVL함수를 이용하여 다른 값으로 대치할 수 있음.
* 사용가능한 datatype은 날짜, 문자 및 숫자형.
* 구문 : NVL(expr1, expr2)
expr1 : 널값을 포함할 수 있는 값이거나 표현식
expr2 : 널값을 전환하여 사용할 값
예 : NVL(hiredate, '01-JAN-95')
NVL(job, 'FRESH MAN')
NVL(comm, 1000)
권장 사항 : nvl함수를 자주 쓰는 것은 성능에 걸림돌이 될 수 있음.따라서 컬럼에 공백이 들어가지 않도록 하는 것이 성능향상에좋다.table을 생성할 때 default값을 주어 nvl 함수대신 default값을가지고 구분할 수 있도록 한다.
4.2 합성문자.
비고 : oracle,Informix 동일* 칼럼과 문자 또는 칼럼과 다른 칼럼을 연결
* 두 개의 수직바(||)에 의해 이루어짐.
* 문자 표현식의 결과에 의해 새로운 칼럼이 생성됨.
SELECT ename||ename
FROM emp;
* Literal 이란 SELECT 리스트에 포함된 문자, 표현식 또는숫자임.
* 날짜 및 문자값은 단일 인용부호로 둘러쌈.
SELECT job||' is job of '||ename FROM emp;
4.3 조건절 사용
비고 : oracle* 비교 연산자 : =, >, >=, <, <=
* SQL 연산자
- between .....and... : 두 값사이의 값
- in(list) : 리스트 중 하나와 같은 값을 가지는 값
- like : 문자 패턴과 일치하는 값
- is null : 널값인 것
* 논리 연산자 : and, or, not ( 우선순위 and -> or )
* 부정 연산자
- 비교 연산자 : !=(VAX,UNIX, PC), ^=(IBM), <>(all OS)
- SQL 연산자 : not between....and...., not in(list), not like, isnot null
비고 : Informix
- 부정 비교 연산자는 <>를 쓴다.
- Like는 %만을 이용하여 검색 가능하며, matches는 *만을이용한다.
4.4 함수
비고 : oracle구문
* function_name(column|expr, [arg1, arg2,....])
- function_name : 함수의 이름
- column : 데이터베이스에 존재하는 칼럼명
- expr : 어떤 문자열 또는 계산된 표현식
- arg1, arg1,... : 함수에 의해 사용될 인수
* 함수는 데이터 값을 조작함.
* 인수를 받아들여 한 개의 값을 return
* 각각의 행에 적용됨.
* 행당 하나의 결과를 돌려줌
* datatype을 바꾸어 줌.
* 중첩하여 사용하는 것이 가능함.
문자함수
* LOWER(column/expr) : 알파벳 문자를 소문자로 바꾸어 줌.
* UPPER(column/expr) : 알파벳 문자를 대준자로 바꾸어 줌.
* SUBSTR(column/expr,m[, n]) : 문자값 중에서 m위치에서 n 문자길이에해당하는 문자를 돌려줌. Informix 에서는 substr을 써야만 index를탄다.
* LENGTH(column/expr) : 문자개수를 값으로 돌려줌.
* NVL(expr1, expt2) : 첫번째 값이 널이면 두번째 값으로 바꾸어줌.
예)SELECT ename, job, deptno FROM emp WHERE lower(job) ='clerk';
* LTRIM(‘ lee’); 공백을 왼쪽으로 밀어준다.
*RTRIM(‘Lee ‘);
숫자함수
* ROUND(column/expr, n) : column/expr의 값을 소수점 n자리까지반올림. n이 없으면 소수점은 없어지고, 음수라면 소수점의왼쪽자리만큼 반올림됨.
* TRUNC(column/expr, n) : column/expr의 값을 소수점 n자리까지 버림n이 없으면 소수점은 없어지고, 음수라면 소수점의 왼쪽자리만큼버림.
* MOD(m, n) : m값을 n으로 나누고 남은 나머지를 return
날짜함수
* date + number : 날수를 날짜에 더함. 결과는 date.
* date - number : 날짜에서 날수를 뺌. 결과는 date.
* date - date : 날짜에서 날짜를 뺌. 결과는 날수.
* date + date/24 : 시간을 날짜에 더함. 결과는 date
* MONTHS_BETWEEN(date1, date2) : 두 날짜 사이의 달수를찾아줌.
* ADD_MONTHS(date, n) : 날짜에 n달을 추가
* NEXT_DAY(date, 'char') : date 다음의 첫번째 해당요일('char')일자.
* LAST_DAY(date) : date를 포함하고 있는 달의 마지막날
예)select ename,(sysdate-hiredate)/7 weeks from emp where deptno =20
oder by week;
전환함수
* 숫자 또는 날짜값을 fmt 모델을 사용하여 varchar2 문자열로바꿈.
* TO_CHAR(date, 'fmt')
- fmt 형식모델은 단일 인용부호로 에워싸야 하고, 대소문자 구별이있음.
- 형식 모델은 어떠한 날짜 형식 요소도 포함할 수 있고,
날짜값은 콤마에 의해 구분됨.
- 결과에 일, 월의 이름은 자동적으로 공백으로 덧붙여 짐.
- 공백 및 선행제로를 없애기 위해서는 'fm'을 사용
* TO_CHAR(number, 'fmt') - 대소문자 구별
- 9 : 숫자위치(9의 수는 폭을 결정)
- 0 : 0을 나타냄
- $ : 달러기호 예) to_char(sal,’fm$9,999,999’)
- . : 위치에 소수점(999.999 -- > 123.2)
- , : 위치에 콤마 (999,999,999 -- > 1,234)
예) select ename, to_char(hiredate, ‘fmDD “of” Month YYYY’)start_date from emp
where hiredate like ‘%87’;
soctt 19 of April 1987 ß 결과값
* TO_NUMBER 함수
to_number(char) 예) to_number(to_char(empno))
* TO_DATE 함수
to_date(char[, ‘fmt’]) 예)to_date(‘1981/05/10’,’yyyy/mm/dd’);
그룹함수
* 그룹당 하나의 결과를 return
* 그룹함수들은 SELECT 절 및 HAVING 절에 쓸 수 있음.
* SELECT 문장에서 GROUP BY 절은 행들을 작은 그룹으로 나눔.
* HAVING 절은 그룹을 제한함.
그룹함수의 종류
* AVG() : 널값을 제외한 행의 평균값
* COUNT() : 행의 개수. expr은 널값이 아닌 것을, '*'를 사용하면 중복및 널값을 갖는 것도 포함.
* MAX() : 최대값 : 어떤 데이터 type도 쓴다.
* MIN() : 최소값: “
* STDDEV() : 널값을 제외한 표준편차
* SUM() : 널값을 제외한 합계
* VARIANCE() : 널값을 제외한 분산
비고 : Informix
| 함수명 | 설명 | Ver. | ||
|---|---|---|---|---|
| 날자함수 | DATE | DATE(char1) | 비날자형 값을 날자로 변환 | ALL |
| DAY | DAY(d1) | 날자형값중 일을 구함 | ALL | |
| MONTH | MONTH(d1) | 날자형값중 월을 구함 | ALL | |
| WEEKDAY | WEEKDAY(d1) | 날자형값중 요일을 구함 | ALL | |
| YEAR | YEAR(d1) | 날자형값중 년을 구함 | ALL | |
| EXTEND | EXTEND(d1 | 날자형값을 지정한 길이로 조정함 | ALL | |
| MDY | 비날자형 값을 날자로 변환 | ALL | ||
| 산술함수 | ABS | ABS(n) | n의 절대값 구함 | ALL |
| MOD | MOD(m,n) | m을 n으로 나눈 나머지 구함 | ALL | |
| POW | POW(m) | 값의 n제곱 구함 | ALL | |
| ROOT | ROOT(m) | 제곱근 구함 | ALL | |
| ROUND | ROUND(m,n) | m의 값을 n자리수 위치 기준으로 반올림 | ALL | |
| SQRT | ALL | |||
| TRUNC | TRUNC(m,n) | m의 값을 n자리수 위치 기준으로 반올림 | ALL | |
| 로그함수 | EXP | ALL | ||
| LOGN | ALL | |||
| LOG10 | ALL | |||
| 삼각함수 | SIN | SIN 함수 | ALL | |
| COS | COS 함수 | ALL | ||
| TAN | TAN 함수 | ALL | ||
| ASIN | ALL | |||
| ACOS | ALL | |||
| ATAN | ALL | |||
| ATAN2 | ALL | |||
| String 함수 | LENGTH | LENGTH(char1) | Charter 길이 구하기 | ALL |
| TRIM | TRIM(char1) | 공백 자르기 | ALL | |
| 집계함수 | COUNT | COUNT(*) | 건수 구하기 | ALL |
| AVG | AVG(m) | 평균 구하기 | ALL | |
| MIN | MIN(n) | 최소값 구하기 | ALL | |
| MAX | MAX(m) | 최대값 구하기 | ALL | |
| SUM | SUM(n) | 합 구하기 | ALL | |
| RANGE | RANGE(m) | 최대값과 최소값 차 구하기 | ALL | |
| STDEV | STDEV(n) | 표준편차 구하기 | ALL | |
| VARIANCE | VARIANCE(m) | 분산 구하기 | ALL | |
| 기타함수 | DBINFO | DBINFO(KEYWORD) | ALL | |
| TODAY | 오늘날자 | ALL | ||
| CURRENT | 현재 날자+ 시분초 | ALL | ||
| USER | 사용자 이름 | ALL | ||
| SITENAME | HOST명 구하기 | ALL | ||
| DBSERVER NAME |
INFORMIXSERVER 명 구하기 | ALL | ||
| NVL | NVL(m,n) | m값을 검사 NULL 이면 n값을 Return | 7.3Y | |
| DECODE | DECODE(c1,a1,A1,b1, B1,c1,C1,D1) |
Multi-Case 문 | 7.3Y | |
| CASE | SELECT 문에서 CASE 비교 | 7.3Y | ||
| INITCAP | INITCAP(char1) | 첫글자 대문자 | 7.3Y | |
| UPPER | UPPER(char1) | 대문자로 | ALL | |
| LOWER | LOWER(char1) | 소문자로 | ALL | |
| REPLACE | REPLACE(char1,search_string[,replacement_string]) | search_string 의 각 문자를 대응하는 replacement_string 으로치환 . replacement_string이 미지정이면 serach_string 을 제거 |
7.3Y | |
| SUBSTR | SUNSTR(char,m[n] | char의 m번째 문자부터 n개의 문자를 취한다 . | 7.3Y | |
| LPAD | LPAD(char1,n,[char2]) | Char1의 왼쪽에 Char2를 덧붇여 전체가 n문자로 되도록한다 .Char2가 미지정이면 blank 적용 | 7.3Y | |
| RPAD | RPAD(char1,n,[char2]) | Char1의 오른쪽에 Char2를 덧붇여 전체가 n문자로 되도록한다 .Char2가 미지정이면 blank 적용 | 7.3Y | |
| SPL로 제공되는 함수 | CEIL | n 보다 크거나 같은 최소 정수 | ||
| NEXT_DAY | ||||
4.5 Having절의 사용
Having 절에 조건을 줌으로서 그룹을 제한할 수 있다.예) job이 vp로 시작하지 않는 사람들을 job별로 급여의 합계를구한다음 급여합계가 5000이상인 job과 그 합계를 구하라.
Select job,sum(sal) payroll from emp
Where job not like ‘VP%’ Group by job having sum(sal) >5000;
4.6 JOIN
Join을 쓰는 목적은 Cartesian product를 막고 정규화 된 테이블 간데이터를 가져올 때.비고 : 오라클
- equi join
- non-equijoin
- self join
- outer join : 정상적으로 조인 조건을 만족하지 못하는 행들을 보기위해
4.7 SubQuery
* subquery는 다른 SQL 문장안에 존재하는 SELECT 구문을 말함.* 주 질의가 실행되기 전에 일단 한번 먼저 실행되어 그 결과는 주질의의 조건으로 사용됨.
* subquery에는 ORDER BY 절을 사용할 수 없음.
SELECT select_list
FROM tables
WHERE expr operator (SELECT select_list FROM table);
오라클은 where 조건절 및 from절에도 subquery ( inline View )를실행할 수 있지만 informix는 inline view가 지원되지 않는다.
4.8 HINT 사용
비고 : Informix* SQL을 어떠한 방법으로 실행할 것인가를 결정을 optimizer에게 지시
- index, join 방법을 지정할 수 있다.
- INDEX, AVOID_INDEX, FULL, AVOID_FULL
- ORDERED : join의 대상 테이블에 대한 join순서를 테이블 순서대로하도록 한다.
- USE_NL,AVOID_NL,USE_HASH, AVIOD_HASH : NL은 nested loop join을말함.
- FIRST_ROWS, ALL_ROWS (default)
- FIRST N 질의 결과 중 n개의 row를 가져옴.
- select --+ first_rows name,age from employee e,department
4.9 ROWID
* 각 행의 주소를 반환하며, 반환되는 데이터는 ROWID 데이터타입을가짐.* ROWID는 행에 접근하는 가장 빠른 방법임.
* 어떻게 테이블의 데이터 행들이 저장되었는 지 보여주고, 한 테이블의각 행에 유일성을 줌.
Select rowid,ename from emp;
4.10 ROWNUM
비고 : 오라클* ROWNUM을 이용하여 반환되는 행의 수를 제한할 수 있음
select ename,sal from ( select ename, sal from emp group bysal,ename)
where rownum <= 5;
top n [percent]구문
* 행의 개수를 쓰거나 percent 구문을 이용
예) select top 4 with ties employeeID, LastName, HireDate
4.11 테이블 생성
ansi 표준으로 만드는 것이 좋음.비고 : Oracle: subquery를 이용한 테이블 생성방법
* As subquery를 이용하여 테이블 생성
* subquery에서의 컬럼수와 생성하는 컬럼이 일치해야 함.
* 컬럼명과 default 값, 무결성 제약조건만이 subquery를 이용해생성됨
* NOT NULL 만이 복사됨
예) create table emp_20 AS select empno,emame,hiredate,job
from emp where deptno = 20; -- 데이터 까지 복제..
비고 :Informix :
테이블을 DataBase가 저장된 space외에 다른 space에 생성할 수 있을뿐만 아니라 컬럼까지도 다른 space에 저장할 수 있다.
예) create table aaa ( a int, b char(10) in space2);
성능 향상 법 : 모든 DBMS에서 테이블을 생성시 테이블 저장량을예측하여, extend size 및 저장할 곳을 적절히 분산하여 생성해야 한다.또한 index 저장 할 곳도 각 DBMS성격에 맞게 구축해야 한다.
테이블이 대용량일 경우는 분할 하여 저장하는 것이 좋다.
-분할 방법 :
- 가급적 remainder를 쓰지 않는다.
- 여러 디스크에 분산하여 설계한다.
- Expressino의 조건은 가급적 숫자형 type( date포함 )하는 것이좋다.
- 테이블 전체에 거처 조회가 된다면 round robin방법을 쓰는 것이좋다.
- 인텍스는 fragment하지 않고 별도의 dbspace 한곳에 저장한다.
- 100만 건 이상의 테이블을 고려 대상으로 한다.
- round robin방법 : 컬럼이 insert시 dbs1,dbs2,dbs3에 순차적으로저장
- expression 방법 : 조건을 주어 조건에 맞는 space에 저장
- hash 방법 : DBMS의 function을 이용해 space에 저장
4.12 인덱스 생성 조건
* 컬럼이 WHERE 절이나 join 조건에 자주 사용되어야 함* 컬럼이 넓은 범위의 값을 가지고 있어야 함
* 컬럼이 null value를 많이 가지고 있어야 함
* 두 개나 그 이상의 컬럼이 같이 WHERE절이나 join조건에 자주사용되어야 함
* 테이블이 매우 크고, 대부분의 질의결과가 행들중 10~15%만을가져오는 것이 좋음
* 많은 인덱스는 항상 질의속도를 높이지는 않음
* 테이블이 만약 다음과 같은 조건이라면 인덱스를 생성하는 것이 좋지않음
- 테이블이 너무 작음
- 컬럼이 질의에서 조건절에 자주 사용되지 않음
- 대부분의 질의결과가 10~15% 이상의 행들을 결과로 가져옴
- 테이블의 변경이 자주 일어나는 경우
4.13 시스템 권한
비고 : Oracle권한부여
* 80개 이상의 권한이 있음
* DBA(Database Administrator)는 상위 레벨의 시스템 권한을가짐
- 새로운 사용자 생성 : CREATE USER(다른 사람에게 유저를 생성할 수있는 권한을
줄 수도 있음)
- 사용자 삭제 : DROP USER
- 테이블 삭제 : DROP ANY TABLE(어떤 사람의 테이블도 삭제할 수있음)
- 테이블 백업 : BACKUP ANY TABLE(export utility를 이용하여 어떤사람의 테이블도
백업할 수 있음)
* DBA는 CREATE USER 명령을 통해 사용자를 생성함
예) CREATE USER user_name IDENTIFIED BY password;
ALTER USER user_name IDENTIFIED BY password;
* 사용자를 생성하면 DBA는 GRANT 명령을 실행하여 사용자에게 권한을부여할 수 있음
* 사용자는 다음의 시스템 권한을 가짐
- CREATE SESSION : 시스템에 접속할 수 있도록 함.
- CREATE TABLE : 테이블을 생성할 수 있도록 함
- CREATE SEQUENCE : 시퀀스를 생성할 수 있도록 함
- CREATE VIEW : 뷰를 생성할 수 있도록 함
- CREATE PROCEDURE : 프로시져를 생성할 수 있도록 함
* 구문
GRANT privilege [,privilege,....] TO user_name [, user....];
Role
* Role이란 사용자에게 줄 수 있는 연관있는 권한들을 모아둔 그룹을말함
* role을 사용함으로써 사용자에게 권한을 주고 다시 회수하는 것을쉽게 다룰 수 있음
* revoke 명령어를 사용
비고 : Informix
비고 : SQL
* user 관리는 두가지 방법을 제공함
Database에 생성 방법과 system유저를 이용하는 방법
처음 설치 시 user생성 방법을 물어보며, default admin은 sapasswd는없음.
4.14 load, unload
5. 인포믹스의 무결성 기능
5.1. 자료의 무결성 (ISOLATION)
5.2 Locking
1. items 테이블을 unload한다.
items 테이블의 모든 행을 삭제한다.
items 테이블을 load한다.
각각의 경우 items 테이블을 검색해 보고 생성된 자료 파일도 확인해본다.
2. 두 명의 사용자가 나누어 실습한다.
데이터베이스를 생성한 사용자(DBA)가 다음 각각의 SQL문을 실행할때
다른 사용자는 데이터베이스를 connect해 본다.
SQL 1 > revoke dba from public
revoke resource from public
revoke connect from public
3. DBA가 데이터베이스에 connect권한을 부여한 후
다른 사용자는 다음 SQL문을 실행시켜 본다.
SQL 2 > create index idx_cname on customer(담당자명);
lock table orders in exclusive mode;
unlock table orders;
4. DBA가 데이터베이스에 resource 권한을 부여한 후
다른 사용자는 다음 SQL문을 실행시켜 본다.
SQL 3 > create index idx_cname on customer(담당자명);
lock table orders in exclusive mode;
unlock table orders;
5. 다음 SQL을 실행한 상태에서 다른 사용자가 데이터베이스를 선택해본다.
SQL 4> database user## exclusive;
6. 데이터베이스를 새로 connect하고 다음과 같은 SQL을 실행해본다.
| user 1 | user 2 |
| drop database user## ; create database user## with log; grant connect to public; begin work; create table test (a date); insert into test values (today); lock table test in exclusive mode; rollback work; select * from test; |
database user##; select * from test; set lock mode to wait 10; select * from test; set isolation to dirty read; select * from test; |
6.Informix DBMS SQL 성능 향상 기법
서론
- Set Explain 의 출력을 해석하는 방법
- Informix Optimizer의 역할
- 성능향상을 위한 방법
- Root Dbspace 에는 최소한의 시스템 정보만 포함할 것
- UNIX File system 공간을 큰 정렬 파일로 채우면 UNIX 프로세스에서오류가능성
- 논리LOG를 LONG TRANSACTION으로 채우지 말것 : LOG TRX주의
6.1. OPTIMIZER
- 주어진 질의를 실행하기 전 가장 좋은 최선의 경로를 찾는 INFORMIX엔진의 한 부분
- SET EXPLAIN ON : OPTIMIZER가 데이타에 접근하는데 선택한 경로를나타냄
- SYSTEM CATALOG의 정보를 바탕으로 결정
- 질의에 사용되는 테이블의 행수 :systables.nrows
- 데이타에 사용되는 페이지수와 색인에 사용되는 페이지수 :systables.npused
- Column값의 uniqueness : sysconstraints
- 색인존재여부 :sysindexes
- 데이타가 색인과 같은 순서 즉, cluster index인지 여부:sysindexes.clust
- root node에서 leafnode 까지 색인의 레벨수
- 각 column에서 두번째로 큰 값과 두번째로 작은 값. optimizer는 이정보로 값의 범위를 대략적으로 알아낼 수 있슴 : syscolumns.colmin,colmax
- 테이블 조인 순서 결정
- Sequential scan의 수행 여부
- 임시 테이블의 작성 여부
- 색인 사용 여부 결정
- 통계의 정확도
- 엔진이 최적화를 수행하는 시기
- 질의가 갑자기 느려질 때
6.2. 개발 시 고려사항
- 개발하는 동안 잘 실행되는질의가 실제적용에 들어가면옵티마이저가 완전히 다른 경로를 선택할 수 있슴--> 실제환경의데이타베이스와 비슷한 크기의 시험데이타베이스에서 같은 데이타로질의를 실행
- 단순 명료한 시스템 설계에 충분한 시간을 갖는다.
- Optimizer는 항상 최적의 query plan을 세우는 것은 아니다. 따라서optimizer가 좋은 역할을 하도록 factor - hint 기능 사용 ( index,full scan method)를 부여한다.
- ESQL/C 코딩시 onconfig 파라메터의 FET_BUF_SIZE를 32767로 늘려놓아 커서 fetch 사이즈를 늘려 놓는다.
- 코딩 시 prepare 구문을 사용하여 SQL을 이용한다.
- prepare 시점에 SQL문장에 대하여 미리 parsing되고 query plan이생성된다.
- Execute 시점에는 처리해야 할 값만 넘겨주면 즉시 실행된다.
6.3. 옵티마이저 제어
- SET OPTIMIZATION HIGH : 엔진이 모든 엑세스 경로를 검사
- SET OPTIMIZATION LOW : 초기 단계에서 가능성이 적은 옵션을제거하여 최적화 시간을 줄임.그러나 최적의 경로가 될 수 있는엑세스경로가 초반에 제거되어 버릴 수 있슴
- Onconfig 환경 파일의 OPTCOMPIND 파라메터 고려
OPTCOMPIND=0 : INDEX사용
OPTCOMPIND=1 : OPTIMIZER가 2로 설정되었을 때처럼 작동.
단REPEATABLE READ가 선택되면 OPTCOMPIND가 0으로 설정되었을 때
처럼 작동
6.4. 옵티마이저를 위한 데이타분산
칼럼에서 데이타표본을 채취하여테이블 영역에 관한 정보를 다양한BIN에 저장하는 것으로 이루어짐 : 대형 테이블을 다룰 경우 유용한정보가 됨. 또한 UPDATE STATISTICS명령으로 각 칼럼 분포 정보를 생성할 수 있슴- UPDATE STATISTICS HIGH FOR :테이블의 모든 행 평가. 느린 대신정확함
- UPDATE STATISTICS MEDIUM FOR :데이타 표본만 추출하여 실행
- UPDATE STATISTICS LOW FOR : 데이타 분포 정보를 얻지 않음
6.5. UPDATE STATISTICS 실행 계획
- UPDATE STATISTICS문을 모든 테이블에 실행
- 복합 색인의 첫번째 칼럼이나 질의의 한 부분으로 사용된 모든칼럼에는 UPDATE STATISTICS HIGH문을 실행하거나 테이블이 클 경우에는update statistics medium resolution 0.0598;을 실행한다.
- 복합 색인의 첫번째 칼럼이 아닌 모든 칼럼에 대해서 UPDATESTATISTICS LOW를 실행
- UPDATE STATISTICS단계를 완료하면 DBSCHEMA UTILITY의 다음OPTION을 사용하여 데이타분포정보를 확인 할 수 있슴
- 위의 내용은 fragmentation 전략에 유용하게 사용할 수 있슴
- 대형 정적 테이블에 대해서는 UPDATE STATISTICS를 재실행할 필요가없슴
6.6. SQL 질의 품질보증과 최적화
- 큰 테이블에서는 순차적 검색을 하지 않는 것이 좋음
- 임시 정렬FILE이 크게 생성되도록 하는 질의는 사용하지 말 것
- Correlated Subquery를 사용
- 서로 다른 칼럼에서 사용된 OR문은 옵티마이저의 색인 사용을방해하므로 색인이 있고 질의 계획에서 옵티마이저가 순차스캔을선택하면 UNION문의 사용을 고려할 것
- 질의 초기에 가능하면 많은 행을 제거할 것
가 추가로 제공되므로 알맞은 테이블이 먼저 제거됨. INDEX SCAN이항상최선
의 방법은 아님
- 자료형을 변환하고 문자 칼럼을 비교하는 것. 가능하면 칼럼의자료형을 숫자형으로 바꿀 것. 조인 칼럼이 문자형이면 매 행마다 한바이트씩 비교함.
- OR, LIKE, MATCH, 함수(MONTH, DAY, LENGTH등), 부정표현(!=’NOUN’), 첫 문자를 제외한 하위 열 검색(POSTCODE[4,5] >10)등은 INDEX를 사용하지 못함
- LOGGING된 데이타베이스에서 LONG TRANSACTION을 실행하지 말것,LONG TRANSACTION은 논리로그를 채워 데이타베이스를 손상시킬 위험이있슴
- 필요한 칼럼만 선택할 것. FRONT END와 BACK END간의 통신이 줄고I/O도 줄어듬. 가능한 한 “SELECT *” 은 사용하지 말 것.
- 데이타의 일정부분 집합이 WHERE 술어로 다시 선택되면 임시테이블을 사용할 것. 검색하고자 하는 테이블이 매우 클 경우 모든테이블을 임시 테이블로 선택하고 임시 테이블에서 재검색을 할것.
- 옵티마이저가 가장 좋은 경로를 선택하도록 임시 테이블을 사용함.큰 테이블에서 임시 테이블로 행을 선택하고 임시 테이블을 나머지테이블에 조인하면 됨
- 임시 테이블에 색인 사용
- 임시 테이블에 UPDATE STATISTICS사용
- 임시 테이블을 만들 경우 WITH NO LOG를 사용. 논리 로그에 쓰는오버헤드가 없어지므로 성능이 향상됨. 임시 테이블에 LONGTRANSACTION을 만들 가능성이 없어짐
act.sa_handler = SigHandler;
act.sa_flags = 0;
sigemptyset( &act.sa_mask );
sigaction( SIGCHLD, &act, NULL );
void SigHandler( int signo )
{
pid_t pid;
while ((pid = waitpid(-1, 0, WNOHANG)) > 0);
return;
}
시간이 걸리는 처리(대량 메일 송신이나 데이터 베이스 전체 삭제등)를
브라우저에서 실향하고 싶을 때나 필요성이 있을 겁니다. 그럴 경우에
일반적인 방법으로 처리하면 이 처리가 종료될 때까지 유저가 브라우저를
사용할 수 없게 됩니다(IE 의 지구마크가 계속 돌아가는 상태).
이 때에는 유저가 브라우저에 대하여 어떤 조작을 하였을 경우의 작동에
대해서 보장이 되지 않습니다. 또, 일정한 시간이 경과하면 접속(런)
타임아웃이 되는 경우가 있습니다.
이러한 상태를 피하기 위해 브라우저를 바로 풀어주고 시간이 걸리는
처리는 백그라운드에서 처리(뒷구멍에서 호박씨까기 상태)해주는 방법이
필요하게 됩니다. 주로 대량 메일을 송신하는 경우에 많이 사용되리라고
생각됩니다.
Perl 에서는 fork 함수를 사용해서 같은 프로그램안에 백그라운드에서
처리할 부분을 기술할 수 있지만 PHP 의 경우에는 fork 함수가 없으므로
백그라운드처리하게 할때 먼저 별도의 프로그램을 만들어 놓고, system
함수 혹은 exec 함수를 불러내는 형식이 됩니다. 당연한 이야기 이지만
백그라운드에서 처리된 결과를 얻는 것은 불가능합니다(메인 처리는
백그라운드 처리 전에 먼저 종료 되었기 때문에). 그래서 결과적으로
exec 를 사용해도 system 을 사용해도 똑 같은 결과가 됩니다.
system 함수나 exec 함수의 리퍼런스를 보면 다음과 같은 내용이 있습니다.
(이 함수를 사용하여 프로그램실행을 해서 백그라운드에서 실행한 상태로
두고 싶을 때에는 프로그램의 출력을 파일 혹은 다른 출력 스트림에
리다이렉트해줄 필요가 있는 점에도 추의해야 됩니다. 그렇지 않으면
PHP는 그 프로그램의 실행이 종료될 때까지 풀어주지 않고 붙들고 있을
것입니다(Hang).)
이 문장의 의미는 다음과 같이 기술하라는 의미입니다.
system("/home/your/bgprog $arg1 $arg2 > /dev/null &");
여기에서 bgprog 은 백그라운드에서 실행하고자 하는 프로그램입니다.
어떤 언어로 기술되어도 상관이 없습니다. 혹은 위의 예와 같이 메인
처리에서 인수를 건네 주고 싶은 경우에는 bgprog 쪽에서 정확하게 받아
처리할 수 있도록 해주지 않으면 안됩니다.
[> /dev/null] 가 [리다이렉트] 에 해당하는 기술입니다. /dev/null
라고 하는 것은 [쓰레기통] 으로 생각하면 됩니다. 모든 출력결과를
버립니다. 마지막의 & 는 백그라운드에서 실행을 하라는 의미입니다.
이 주변의 자세한 내용은 UNIX 쉘 관련 참고서를 보면 있습니다.
[PHP는 그 프로그램이 종료 될때까지 Hang됩니다] 라는 의미는 IE 의
지구 마크가 게속 돌고 있는 상태를 의미합니다.
그럼 실험을 해보겠습니다. 브라우저의 상태에 주의해서 봅시다.
bgprog 에 해당하는 프로그램을 다음과 같은 내용으로 만들어 보겠습니다.
간단히 수면제 멱여서 10 초동안 잠재우는 프로그램입니다.
여기에서는 쉘스크립으로 만들겠습니다.
sleep.sh
sleep 10
다음은 위의 프로그램을 불러낼 PHP 스크립을 만들겠습니다.
<?
system("/bin/sh /home/your/sleep.sh > /dev/null &");
echo "OK.";
?>
위의 페이지를 출력하면 순간적으로 OK 가 출력이 됩니다.
10초 이내에 ps 코맨드를 확인하면 OK가 출력이 된후에도
sleep.sh 가 실행이 되고 있는 상태가 됩니다.
그럼 아래와 같이 일반적인 방법으로 변경하면 어떻게 될까요?
<?
system("/bin/sh /home/your/sleep.sh &");
echo "OK.";
?>
10초가 지난 후에 OK가 출력이 될 것입니다.
그 동안에 브라우저는 목빠지게 처리 결과를 기다리고 있는
상태로 있다는 것을 확인 할 수 있을 겁니다.
|
내맘대로 정의한 Win 32 API 함수 PostMessage() GetKeyState() CClientDC dc; GetStyle() SetWindowLong() ZeroMemory() SetWindowPos() GetCursorPos() GetParent() SetCaputre() AfxGetMainWnd() pCmdUI->Enable() pCmdUI->SetText() pCmdUI->SetCheck() GetMenu()->GetSubmenu() 메뉴객체->AppendMenu() 메뉴객체->DeleteMenu() 툴바객체.CreateEx() 툴바객체.LoadToolBar() 상태바객체.SetPaneInfo() 상태바객체.SetPaneText() CSDI_SequenceApp() CSDI_SequenceApp()::InitInstance() AddDocTemplate() ProcessShellCommand() CMainFrame::PreCreateWindow() CSDI_SequenceApp:Run() 다이얼로그 관련 함수 메시지 함수
기타 함수 |
| 트레이를 이용할때 창이 없어지면 트레이에 있다는 것을 나타내고 싶은데... 그냥 없어져 버리면 트레이에 남아있는것을 알기가 쉽지 않게되지요. 이때, 가장 좋은방법이 트레이쪽으로 최소화되는거처럼 보이는 것일텐데..... DrawAnimatedRects라는 함수를 이용하면 caption bar가 날아가는 것이 그려진답니다. (마치 minimize될때 작업창쪽으로 줄여지는 거처럼 보이는거 처럼요.) 아래 세개의 함수를 이용하시면 그런 그림을 그려줄것이구요. 이 함수는 정말 그려주는 거 밖에 안하니까.... ShowWindow(SW_HIDE)나 ShowWindow(SW_SHOWNORMAL)을 이용하심 윈도우도 보이게 될겁니다. 머하면 그냥 윈도우를 생성하구 삭제해두 되구요. 예를들면.. AniMinimizeToTray(m_hWnd); ShowWindow(SW_HIDE); //또는 ::ShowWindow(m_hWnd, SW_HIDE); 이려나... 이러면 tray쪽으로 없어지는거 처럼 보이져. 참고로.. 이 함수들은 제 프로그램에서 static member함수로 되어있던 놈들입니다. 그럼 즐프하시길~~ /////////////////////////////////////////////////////////////////////////////////////// //hwnd가 tray로 날아가는 그림을 그려준다. void AniMinimizeToTray(HWND hwnd) { RECT rectTo, rectFrom; ::GetWindowRect(hwnd, &rectFrom); _GetTrayWndRect(&rectTo); DrawAnimatedRects(hwnd, IDANI_CAPTION, &rectFrom, &rectTo); } //tray에서 hwnd의 원래위치로 날아가는 그림을 그려준다. void AniMaximiseFromTray(HWND hwnd) { RECT rectFrom; GetTrayWndRect(&rectFrom); WINDOWPLACEMENT wndpl; ::GetWindowPlacement(hwnd, &wndpl); //최소화된 상태에서 죽으면 GetWindowRect로 안됨 DrawAnimatedRects(hwnd, IDANI_CAPTION, &rectFrom, &wndpl.rcNormalPosition); } //tray의 위치를 가져온다. void GetTrayWndRect(RECT *pRect) { HWND hwndTaskBar=::FindWindow(_T("Shell_TrayWnd"), NULL); if (hwndTaskBar){ HWND hwndTray=::FindWindowEx(hwndTaskBar, NULL, _T("TrayNotifyWnd"), NULL); if (hwndTray) ::GetWindowRect(hwndTray, pRect); else{ //tray부분을 못찾으면 task바의 구석탱이를 그렇다고 믿게 하자. ::GetWindowRect(hwndTaskBar, pRect); pRect->left=pRect->right-20; pRect->top=pRect->bottom-20; } } else{ //task바를 못찾으면 그냥 화면 하단부 int nWidth = GetSystemMetrics(SM_CXSCREEN); int nHeight = GetSystemMetrics(SM_CYSCREEN); SetRect(pRect, nWidth-40, nHeight-20, nWidth, nHeight); } } |
((CEdit*)GetDlgItem(IDC_NAME_EDIT))->SetLimitText(8);
((CEdit*)GetDlgItem(IDC_DST_EDIT))->SetLimitText(13);
안녕하세요.
마지막으로 덤프 파일을 WinDbg 를 이용하여 분석해 보도록 하겠습니다.
지난 글에서
!analyze - v 명령을 이용하여 덤프를 분석해 보았었는데요.
우선 그 결과를 살펴보겠습니다.
0:000> !analyze -v
*** WARNING: Unable to verify checksum for CrashSample.exe
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
FAULTING_IP:
advapi32!RegSetValueExA+b9
77f5ec75 8078ff00 cmp byte ptr [eax-1],0 : 문제의 명령어를 가리키고 있습니다.
EXCEPTION_RECORD: ffffffff -- (.exr ffffffffffffffff)
ExceptionAddress: 77f5ec75 (advapi32!RegSetValueExA+0x000000b9)
ExceptionCode: c0000005 (Access violation) : 예외 상황 코드이네요. 접근 위반 이로군요.
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00001243
Attempt to read from address 00001243 : 00001243 주소를 읽으려고 했답니다.
DEFAULT_BUCKET_ID: APPLICATION_FAULT
PROCESS_NAME: CrashSample.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - "0x%08lx"
READ_ADDRESS: 00001243
BUGCHECK_STR: ACCESS_VIOLATION
LAST_CONTROL_TRANSFER: from 00401378 to 77f5ec75 : 실행 제어가 마지막으로 옮겨간 것에 대한 내용이네요.
STACK_TEXT: : 아래부터 스택 상황을 보여줍니다.
0012f89c 00401378 00000740 00403020 00000000 advapi32!RegSetValueExA+0xb9
0012f8bc 73d124c0 004022d0 00000111 0012f8fc CrashSample!CCrashSampleDlg::OnButton1+0x48 [E:\work\CrashSample\CrashSampleDlg.cpp @ 119] : 제가 만든 부분이 보이는군요.
0012f8cc 73d123bf 0012fe94 000003e8 00000000 mfc42!_AfxDispatchCmdMsg+0x82
0012f8fc 73d7dead 000003e8 00000000 00000000 mfc42!CCmdTarget::OnCmdMsg+0x10a
0012f920 73d13244 000003e8 00000000 00000000 mfc42!CPropertySheet::OnCmdMsg+0x1d
0012f970 73d11bf1 00000000 006111ea 0012fe94 mfc42!CWnd::OnCommand+0x53
0012f9f0 73d11b9b 00000111 000003e8 006111ea mfc42!CWnd::OnWndMsg+0x2f
0012fa10 73d11b05 00000111 000003e8 006111ea mfc42!CWnd::WindowProc+0x24
0012fa70 73d11a58 0012fe94 00000000 00000111 mfc42!AfxCallWndProc+0x91
0012fa90 73da847d 00360eba 00000111 000003e8 mfc42!AfxWndProc+0x36
0012fabc 77cf8724 00360eba 00000111 000003e8 mfc42!AfxWndProcBase+0x39
0012fae8 77cf8806 73da8444 00360eba 00000111 user32!InternalCallWinProc+0x28
0012fb50 77cfb88b 00000000 73da8444 00360eba user32!UserCallWinProcCheckWow+0x150
0012fb8c 77cfb8f3 007cc170 007cb068 000003e8 user32!SendMessageWorker+0x4a5
0012fbac 77d2fe95 00360eba 00000111 000003e8 user32!SendMessageW+0x7f
0012fbc4 77d2658d 007cd9b0 00000000 007cd9b0 user32!xxxButtonNotifyParent+0x41
0012fbe0 77d0783f 00149f9c 00000001 00000000 user32!xxxBNReleaseCapture+0xf8
0012fc64 77d1b06a 007cd9b0 00000202 00000000 user32!ButtonWndProcWorker+0x6df
0012fc84 77cf8724 006111ea 00000202 00000000 user32!ButtonWndProcA+0x5d
0012fcb0 77cf8806 77d1b01e 006111ea 00000202 user32!InternalCallWinProc+0x28
0012fd18 77cf89bd 00000000 77d1b01e 006111ea user32!UserCallWinProcCheckWow+0x150
0012fd78 77cf8a00 004030bc 00000000 0012fdac user32!DispatchMessageWorker+0x306
0012fd88 77d0e0a7 004030bc 004030bc 004030c4 user32!DispatchMessageW+0xf
0012fdac 77d1c6bb 00360eba 007cd9b0 004030bc user32!IsDialogMessageW+0x572
0012fdcc 73d26795 00360eba 004030bc 0012fe94 user32!IsDialogMessageA+0xfd
0012fddc 73d1a76e 004030bc 73d2675a 004030bc mfc42!CWnd::IsDialogMessageA+0x31
0012fde4 73d2675a 004030bc 004030bc 00360eba mfc42!CWnd::PreTranslateInput+0x29
0012fdf4 73d113aa 004030bc 004030bc 0012fe94 mfc42!CDialog::PreTranslateMessage+0x96
0012fe04 73d11351 00360eba 004030bc 00403088 mfc42!CWnd::WalkPreTranslateTree+0x21
0012fe18 73d11248 004030bc 00000000 0012fe94 mfc42!CWinThread::PreTranslateMessage+0x31
0012fe28 73d26b99 00000004 0012fe94 0012fe88 mfc42!CWinThread::PumpMessage+0x2a
0012fe4c 73d26a2e 00000004 00403088 00403088 mfc42!CWnd::RunModalLoop+0xd9
0012fe88 004010f3 00403088 00402338 00000001 mfc42!CDialog::DoModal+0xe8
0012ff00 73d1cf74 00000000 001423ad 00000000 CrashSample!CCrashSampleApp::InitInstance+0x43 [E:\work\CrashSample\CrashSample.cpp @ 71]
0012ff10 004017e9 00400000 00000000 001423ad mfc42!AfxWinMain+0x49
0012ff24 00401704 00400000 00000000 001423ad CrashSample!WinMain+0x15 [appmodul.cpp @ 30]
0012ffc0 7c816fd7 00000000 7c94d496 7ffd4000 CrashSample!WinMainCRTStartup+0x134
0012fff0 00000000 004015d0 00000000 78746341 kernel32!BaseProcessStart+0x23
STACK_COMMAND: ~0s; .ecxr ; kb : ~0s; .ecxr ; kb 이 명령어를 command 창에 입력하란 얘기네요.
FAULTING_THREAD: 00000954 : 문제가 발생한 쓰레드의 아이디 입니다.
FOLLOWUP_IP:
CrashSample!CCrashSampleDlg::OnButton1+48 [E:\work\CrashSample\CrashSampleDlg.cpp @ 119]
00401378 8b542400 mov edx,dword ptr [esp]
FAULTING_SOURCE_CODE: : 소스 코드 까지도 보여주고 있습니다.
115:
116: RegSetValueEx(RegHandle, REG_VALUENAME_DEBUGGER , 0 , REG_SZ ,
117: (LPBYTE)pCrashPointer , 0x10 );
118:
> 119: RegCloseKey( RegHandle ); : return 될 부분을 가리키고 있죠. 우리는 이 바로 윗 라인에서 에러가 발생했을거라 추측할 수 있습니다.
120:
121: return;
122:
123:
124: }
SYMBOL_STACK_INDEX: 1
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: CrashSample
IMAGE_NAME: CrashSample.exe
DEBUG_FLR_IMAGE_TIMESTAMP: 47730c41
SYMBOL_NAME: CrashSample!CCrashSampleDlg::OnButton1+48
FAILURE_BUCKET_ID: ACCESS_VIOLATION_CrashSample!CCrashSampleDlg::OnButton1+48
BUCKET_ID: ACCESS_VIOLATION_CrashSample!CCrashSampleDlg::OnButton1+48
Followup: MachineOwner
---------
예, WinDbg 의 분석 결과를 대충 살펴보니, 잘못된 메모리를 읽으려고 시도했던것 같습니다.
일단 WinDbg 가 시키는대로 다음의 명령을 입력해 보겠습니다.
~0s; .ecxr ; kb
나오는 결과는 위에서 보는것과 그렇게 큰 차이가 없네요. ( 생략하겠습니다. )
현재의 스택 프레임은 advapi32!RegSetValueExA 함수의 것으로 맞추어져 있습니다.
이건 우리가 만든게 아니죠.
일단 우리가 만든 함수의 스택 프레임으로 가보겠습니다.
다음의 명령을 입력하면, 각 스택의 번호를 확인할 수 있습니다.
kn
0:000> kn
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr
00 0012f880 004013f4 advapi32!RegSetValueExA+0xb9
01 0012f8b0 73d124c0 CrashSample!CCrashSampleDlg::OnButton1+0x5c [E:\work\CrashSample\CrashSampleDlg.cpp @ 115]
02 0012f8c0 73d123bf mfc42!_AfxDispatchCmdMsg+0x82
03 0012f8f0 73d7dead mfc42!CCmdTarget::OnCmdMsg+0x10a
04 0012f914 73d13244 mfc42!CPropertySheet::OnCmdMsg+0x1d
제가 만든 루틴의 스택 번호가 01 이라는걸 알 수 있네요.
이 스택을 현재 프레임으로 맞춰보겠습니다.
.frame 1
예, 맞춰졌습니다. 소스 폴더까지 등록해 놓으셨으면, 소스 코드까지도 보여줄 것 같습니다.
일단 지역 변수를 살펴 보겠습니다.
dv
0:000> dv
this = 0x0012fe8c
pCrashPointer = 0x00001234 ""
Ret = 0x00000000
RegHandle = 0x00000740
예, pCrashPointer 라는 지역 변수가 0x1234 라는걸 확인할 수 있습니다.
여기까지만 보아도 어떤 문제가 있었는지 감을 잡을 수 있을겁니다.
( 최적화 옵션으로 인해서 지역 변수가 깔끔하게 나타나지 않아서 프로젝트 세팅 부분에서 최적화 옵션을 Disable 로 설정하였습니다. )
이 덤프 분석에서는
Attempt to read from address 00001243
가 문제의 원인이 되고 있습니다.
왜 1234 를 넣었는데, 1243 을 읽으려고 했는지를 조금만 더 살펴 보도록 하죠.
WinDbg 의 View 메뉴에서
Disassembly (Alt + 7)
메뉴를 선택해 봅니다.
그리고 새로나온 디스어셈블 창의 offset 부분에
ExceptionAddress: 77f5ec75
예외 발생 지점인 77f5ec75 를 입력해 봅시다.
77f5ec3d 0f84f9840200 je advapi32!RegSetValueExA+0x58 (77f8713c)
77f5ec43 8d45cc lea eax,[ebp-34h]
77f5ec46 8945d8 mov dword ptr [ebp-28h],eax
77f5ec49 668345cc02 add word ptr [ebp-34h],2
77f5ec4e 0f84fa840200 je advapi32!RegSetValueExA+0x77 (77f8714e)
77f5ec54 8b4518 mov eax,dword ptr [ebp+18h]
77f5ec57 8945dc mov dword ptr [ebp-24h],eax
77f5ec5a 3bc3 cmp eax,ebx
77f5ec5c 0f84e6000000 je advapi32!RegSetValueExA+0x12a (77f5ed48)
77f5ec62 837d1401 cmp dword ptr [ebp+14h],1
77f5ec66 0f85c8000000 jne advapi32!RegSetValueExA+0xa4 (77f5ed34)
77f5ec6c 8b751c mov esi,dword ptr [ebp+1Ch]
77f5ec6f 3bf3 cmp esi,ebx
77f5ec71 760c jbe advapi32!RegSetValueExA+0xe1 (77f5ec7f)
77f5ec73 03c6 add eax,esi
77f5ec75 8078ff00 cmp byte ptr [eax-1],0 ds:0023:00001243=??
네 디스어셈블된 명령어 들이 쭈욱 보입니다.
친절하게도 eax-1 의 주소가 ds:0023:00001243 이 값이고, 이는 ?? 이렇게 표현할 수 없는 메모리 주소(Page Fault)라고 알려주네요.
eax 에 어떻게 저런 값이 들어갔는지를 뚫어져라 살펴보니... ( 빨간색 칠을 한 부분 )
인자값으로 받은 0x1234 에서 0x10 (이것도 인자로 입력하였죠) 만큼의 길이 를 더해서(그리고 -1) 이 값이 널( 0 ) 값인지를 체크하고 있는것 같네요.
우리는 advapi32.dll 의 RegSetValueExA 함수 내부에 인자로 받은 문자열의 끝부분을 NULL 체크하는 루틴이 있을거 같다고 추측할 수 있습니다.
정리해보면, 우리가 0x1234 라는 잘못된 주소값을 넘겨주었기 때문에,
RegSetValueEx 라는 API 함수 내부에서 이 잘못된 메모리에 접근하려다가 문제가 발생했다고 결론내릴 수 있을것 같습니다.
이렇게 해서 간단하게 덤프 파일을 분석하는 방법에 대해 살펴보았습니다.
사실 디어셈블까지 하지 않더라도, 디버깅 심벌 파일을 세팅해놓고, !analyze -v 명령을 입력하는 것만으로도
많은 경우의 에러상황을 분석할 수 있습니다.
안녕하세요.
저번 글에 이어서 덤프 파일을 분석하는 환경을 구축하는 방법에 대해 정리해 보도록 하겠습니다.
일단 예제로 사용할 런타임 오류를 일으키는 프로그램을 하나 간단하게 만들어 보았습니다.
#define REG_PATH_AEDEBUG "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"
#define REG_VALUENAME_DEBUGGER "Debugger"
void CCrashSampleDlg::OnButton1()
{
HKEY RegHandle = NULL;
LPDWORD Ret = 0;
char * pCrashPointer;
pCrashPointer = (char*)0x1234;
RegCreateKeyEx(HKEY_LOCAL_MACHINE , REG_PATH_AEDEBUG , 0 , NULL ,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL , &RegHandle, Ret);
RegSetValueEx(RegHandle, REG_VALUENAME_DEBUGGER , 0 , REG_SZ ,
(LPBYTE)pCrashPointer , 0x10 );
RegCloseKey( RegHandle );
return;
}
보시는 바와 같이 잘못된 메모리 0x1234 의 메모리를 읽어서 레지스트리에 저장하려고 하다가 오류가 발생하는 소스 입니다.
저번 글에 소개한 drwtsn32.exe 를 시스템 기본 디버거로 설정하고,
위의 소스를 컴파일한 실행 파일을 윈도우 탐색기에서 실행해 봅니다.
그리고 Button1 을 눌러서 위의 루틴을 실행해서 오류를 일으켜 봅니다.
그러면 닥터 왓슨이 해당 프로세스의 덤프를 생성해서 설정된 위치에 저장해 둘 것입니다.
( drwtsn32.exe 를 실행해 보시면, 덤프 생성 위치를 확인할 수 있습니다. )
이제 분석할 덤프 파일을 확보하였습니다.
저번 글에 말씀드린 Generate Debug Info 옵션이 체크되어 있는 상태로 컴파일 하셨다면,
디버깅 심벌 파일 또한 이미 만들어져 있을 것입니다. ( 위의 예제에선 CrashSample.pdb 파일 입니다. )
이제 이 덤프 파일을 분석할 도구가 필요합니다.
저는 windbg 를 선택하였습니다.
http://www.microsoft.com/whdc/devtools/debugging/default.mspx
위의 링크에서 windbg 를 다운로드 받고, 설치할 수 있습니다.
설치가 완료되었다면, WinDbg 를 실행해 보겠습니다.
WinDbg 를 실행하면, File -> Open Crash Dump 메뉴를 이용하여 조금 전에 생성한 dmp 파일을 열어봅니다.
이제 덤프 파일을 분석할 준비가 되었는데요.
본격적인 분석을 하기 위해선, 운영체제의 디버깅 심벌이 필요합니다.
WinDbg 의 Command 창에서 다음과 같이 입력해 보겠습니다.
.symfix+ c:\MySymbol
이때, c:\MySymbol 이라는 디렉토리가 생성되어 있어야 하며,
이 디렉토리에는 WinDbg 가 필요한 심벌 파일을 다운 받아서 저장하게 됩니다.
그리고 File -> Symbol File Path 메뉴를 선택하여, 우리가 분석할 프로그램의 pdb 파일도 등록을 합니다.
여기에선 CrashSample.pdb 가 존재하는 디렉토리를 선택해 주시면 됩니다. ( browse 버튼을 이용하여, 추가해 주십시요. )
디버그 버전으로 컴파일 하셨으면 debug 폴더를, 릴리즈 버전으로 컴파일 하셨으면, release 폴더를 선택해주시면 됩니다.
또한 File -> Source File Path 메뉴를 선택하여, 소스가 들어있는 폴더도 적당히 선택해 줍니다.
이렇게 디버깅 심벌 파일들의 설정이 끝났으면, 분석을 해보도록 하겠습니다.
다음 명령을 command 창에 입력해 주십시요.
!analyze -v
이렇게 입력을 했더니, 저는 다음과 같은 결과가 나왔네요.
0:000> !analyze -v
*** WARNING: Unable to verify checksum for CrashSample.exe
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
FAULTING_IP:
advapi32!RegSetValueExA+b9
77f5ec75 8078ff00 cmp byte ptr [eax-1],0
EXCEPTION_RECORD: ffffffff -- (.exr ffffffffffffffff)
ExceptionAddress: 77f5ec75 (advapi32!RegSetValueExA+0x000000b9)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00001243
Attempt to read from address 00001243
DEFAULT_BUCKET_ID: APPLICATION_FAULT
PROCESS_NAME: CrashSample.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - "0x%08lx"
READ_ADDRESS: 00001243
BUGCHECK_STR: ACCESS_VIOLATION
LAST_CONTROL_TRANSFER: from 00401378 to 77f5ec75
STACK_TEXT:
0012f89c 00401378 00000740 00403020 00000000 advapi32!RegSetValueExA+0xb9
0012f8bc 73d124c0 004022d0 00000111 0012f8fc CrashSample!CCrashSampleDlg::OnButton1+0x48 [E:\work\CrashSample\CrashSampleDlg.cpp @ 119]
0012f8cc 73d123bf 0012fe94 000003e8 00000000 mfc42!_AfxDispatchCmdMsg+0x82
0012f8fc 73d7dead 000003e8 00000000 00000000 mfc42!CCmdTarget::OnCmdMsg+0x10a
0012f920 73d13244 000003e8 00000000 00000000 mfc42!CPropertySheet::OnCmdMsg+0x1d
0012f970 73d11bf1 00000000 006111ea 0012fe94 mfc42!CWnd::OnCommand+0x53
0012f9f0 73d11b9b 00000111 000003e8 006111ea mfc42!CWnd::OnWndMsg+0x2f
0012fa10 73d11b05 00000111 000003e8 006111ea mfc42!CWnd::WindowProc+0x24
0012fa70 73d11a58 0012fe94 00000000 00000111 mfc42!AfxCallWndProc+0x91
0012fa90 73da847d 00360eba 00000111 000003e8 mfc42!AfxWndProc+0x36
0012fabc 77cf8724 00360eba 00000111 000003e8 mfc42!AfxWndProcBase+0x39
0012fae8 77cf8806 73da8444 00360eba 00000111 user32!InternalCallWinProc+0x28
0012fb50 77cfb88b 00000000 73da8444 00360eba user32!UserCallWinProcCheckWow+0x150
0012fb8c 77cfb8f3 007cc170 007cb068 000003e8 user32!SendMessageWorker+0x4a5
0012fbac 77d2fe95 00360eba 00000111 000003e8 user32!SendMessageW+0x7f
0012fbc4 77d2658d 007cd9b0 00000000 007cd9b0 user32!xxxButtonNotifyParent+0x41
0012fbe0 77d0783f 00149f9c 00000001 00000000 user32!xxxBNReleaseCapture+0xf8
0012fc64 77d1b06a 007cd9b0 00000202 00000000 user32!ButtonWndProcWorker+0x6df
0012fc84 77cf8724 006111ea 00000202 00000000 user32!ButtonWndProcA+0x5d
0012fcb0 77cf8806 77d1b01e 006111ea 00000202 user32!InternalCallWinProc+0x28
0012fd18 77cf89bd 00000000 77d1b01e 006111ea user32!UserCallWinProcCheckWow+0x150
0012fd78 77cf8a00 004030bc 00000000 0012fdac user32!DispatchMessageWorker+0x306
0012fd88 77d0e0a7 004030bc 004030bc 004030c4 user32!DispatchMessageW+0xf
0012fdac 77d1c6bb 00360eba 007cd9b0 004030bc user32!IsDialogMessageW+0x572
0012fdcc 73d26795 00360eba 004030bc 0012fe94 user32!IsDialogMessageA+0xfd
0012fddc 73d1a76e 004030bc 73d2675a 004030bc mfc42!CWnd::IsDialogMessageA+0x31
0012fde4 73d2675a 004030bc 004030bc 00360eba mfc42!CWnd::PreTranslateInput+0x29
0012fdf4 73d113aa 004030bc 004030bc 0012fe94 mfc42!CDialog::PreTranslateMessage+0x96
0012fe04 73d11351 00360eba 004030bc 00403088 mfc42!CWnd::WalkPreTranslateTree+0x21
0012fe18 73d11248 004030bc 00000000 0012fe94 mfc42!CWinThread::PreTranslateMessage+0x31
0012fe28 73d26b99 00000004 0012fe94 0012fe88 mfc42!CWinThread::PumpMessage+0x2a
0012fe4c 73d26a2e 00000004 00403088 00403088 mfc42!CWnd::RunModalLoop+0xd9
0012fe88 004010f3 00403088 00402338 00000001 mfc42!CDialog::DoModal+0xe8
0012ff00 73d1cf74 00000000 001423ad 00000000 CrashSample!CCrashSampleApp::InitInstance+0x43 [E:\work\CrashSample\CrashSample.cpp @ 71]
0012ff10 004017e9 00400000 00000000 001423ad mfc42!AfxWinMain+0x49
0012ff24 00401704 00400000 00000000 001423ad CrashSample!WinMain+0x15 [appmodul.cpp @ 30]
0012ffc0 7c816fd7 00000000 7c94d496 7ffd4000 CrashSample!WinMainCRTStartup+0x134
0012fff0 00000000 004015d0 00000000 78746341 kernel32!BaseProcessStart+0x23
STACK_COMMAND: ~0s; .ecxr ; kb
FAULTING_THREAD: 00000954
FOLLOWUP_IP:
CrashSample!CCrashSampleDlg::OnButton1+48 [E:\work\CrashSample\CrashSampleDlg.cpp @ 119]
00401378 8b542400 mov edx,dword ptr [esp]
FAULTING_SOURCE_CODE:
115:
116: RegSetValueEx(RegHandle, REG_VALUENAME_DEBUGGER , 0 , REG_SZ ,
117: (LPBYTE)pCrashPointer , 0x10 );
118:
> 119: RegCloseKey( RegHandle );
120:
121: return;
122:
123:
124: }
SYMBOL_STACK_INDEX: 1
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: CrashSample
IMAGE_NAME: CrashSample.exe
DEBUG_FLR_IMAGE_TIMESTAMP: 47730c41
SYMBOL_NAME: CrashSample!CCrashSampleDlg::OnButton1+48
FAILURE_BUCKET_ID: ACCESS_VIOLATION_CrashSample!CCrashSampleDlg::OnButton1+48
BUCKET_ID: ACCESS_VIOLATION_CrashSample!CCrashSampleDlg::OnButton1+48
Followup: MachineOwner
---------
다음 글에서는 간단하게 WinDbg 명령어를 이용하여 오류를 분석해 보도록 하겠습니다.
이올린에 북마크하기
PREV