Transaction Isolation Level이란 사용자에게 트랜잭션의 안정성을 보장해 주는 수준을 뜻한다.


4단계로 되어있으며, 각 단계 별로 안정성과 성능이 달라진다.



  • Read Uncommitted (가장 낮은 수준)
  • Read Committed
  • Repeatable Read
  • Serializable (가장 높은 수준)

실습용 테이블을 만들어서 살펴보자

CREATE TABLE TX_TEST(A INT, B CHAR(8), C TIMESTAMP);
언제 레코드가 바뀌는 지 정확히 알기 위해 TIMESTAMP 컬럼을 넣었다.

INSERT INTO TX_TEST (A, B) VALUES (1, 'A'), (2, 'B'), (3, 'C'), (4, 'D');
여러 레코드를 한 번에 넣을 수 있는 방법.

SELECT * FROM TX_TEST;





Isolation Level 변경하는 법
SET GLOBAL TRANSACTION ISOLATION LEVEL [LEVEL_NAME];


Isolation Level 확인하는 법

SELECT @@TX_ISOLATION;



GLOBAL 값을 변경하더라도, 세션의 Isolation Level이 변경되지 않을 수 있다.


Session에 대한 Level 변경하는 법

SET SESSION TRANSACTION ISOLATION LEVEL [LEVEL_NAME];




1. READ-UNCOMMITTED 인 경우


[세션 A에서]

START TRANSACTION;

UPDATE TX_TEST SET A = A + 1;


[세션 B에서]

SELECT * FROM TX_TEST;


세션 A에서 commit을 하지 않았음에도, transaction 중간 변경사항이 세션 B에서도 확인할 수 있게 되었다.

일반적인 데이터베이스의 transaction에서 이러한 일이 생겨서는 안된다.


만약 이 때 세션 B에서 UPDATE 문을 날리면 어떻게 될까?


UPDATE TX_TEST SET A = A + 5;

...


세션 A에서 TRANSACTION이 이루어지고 있기 때문에, 해당 TRANSACTION이 끝나기를 기다리게 된다.

(장시간 기다릴 경우 LOCK WAIT TIMEOUT EXCEEDED 오류가 난다)


[세션 A]

SELECT NOW(); ROLLBACK;


[세션 B]

SELECT * FROM TX_TEST;


현재 컬럼 C에 찍혀있는 TIMESTAMP 값은 해당 UPDATE 문을 날렸을 때의 TIMESTAMP이다.




2. READ-COMMITTED 인 경우


각 세션에 다음을 통해 Transaction Isolation Level을 read-committed로 변경하자.

SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;


[세션 A]

START TRANSACTION;

UPDATE TX_TEST SET A = A * 10;


SELECT * FROM TX_TEST;


[세션 B]

START TRANSACTION;

SELECT * FROM TX_TEST;


세션 A에서 날린 UPDATE 문의 결과를 세션 B에서는 아직 확인할 수 없다.



[세션 A]

COMMIT;


[세션 B]

SELECT * FROM TX_TEST;


세션 A의 TRANSACTION이 끝난 이후 세션 B에서도 업데이트 된 레코드를 읽을 수 있게 되었다.

하지만, 세션 B의 TRANSACTION이 진행 중인데 레코드의 값이 변경이 되는 것은 문제가 될 수 있다.




3. REPEATABLE-READ


각 세션에 다음을 통해 Transaction Isolation Level을 REPEATABLE READ로 변경하자.

SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;


[세션 A, B]

START TRANSACTION;


[세션 A]

UPDATE TX_TEST SET B = CONCAT(B, 'A');

SELECT * FROM TX_TEST;



[세션 B]

SELECT * FROM TX_TEST;



심지어 세션 A에서 COMMIT을 하고 난 이후에도,

SELECT * FROM TX_TEST;

가 된다.


결국 세션 B에서 COMMIT; 을 하고 난 뒤에, 세션 A에서 UPDATE를 한 결과 레코드들을 확인할 수 있게 된다.


만약 세션 B에서 TRANSACTION 중 UPDATE 문을 사용하면 어떻게 될까?

당연히 세션 A에서 TRANSACTION-UPDATE가 이루어지고 있기 때문에 LOCK이 걸려서 응답을 기다리게 된다.


만약 세션 A에서 COMMIT;을 하게 되면 A의 UPDATE 문과 B의 UPDATE 문의 결과를 모두 반영한 레코드를 각 세션들이 확인할 수 있게 된다.



4. SERIALIZABLE


각 세션에 다음을 통해 Transaction Isolation Level을 SERIALIZABLE로 변경하자.

SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;

SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;


[세션 A, B]

START TRANSACTION;


[세션 B]

SELECT * FROM TX_TEST;    [정상 작동]

UPDATE TX_TEST SET A = A + 1;    [LOCK이 걸림]


[세션 A]

UPDATE TX_TEST SET A = A + 1;    [DEADLOCK FOUND ERROR 발생]

심지어 세션 B에서 TRANSACTION-UPDATE를 하고 있는 경우에는, SELECT조차 실행이 되지 않는다.







저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 우너효

모든 함수는 prototype 프로퍼티를 갖는다. 이 프로퍼티는 해당 참조 타입의 인스턴스가 가져야 할 프로퍼티와 메서드를 담고 있는 객체이다. 이 객체는 생성자를 호출할 때 생성되는 객체의 문자 그대로 '프로토타입(원형)'이다. 프로토타입의 프로퍼티와 메서드는 객체 인스턴스 전체에서 공유된다는 점이 프로토타입의 장점이다. 객체 정보를 생성자에 할당하는 대신 다음과 같이 직접적으로 프로토타입에 할당할 수 있다.


function Person() {

}


Person.prototype.name = "Woonohyo";

Person.prototype.age = 27;

Person.prototype.job = "Software Engineer";

Person.prototype.sayName = function() {

alert(this.name);

};


var person1 = new Person();

person1.sayName();    // "Woonohyo"


var person2 = new Person();

person2.sayName();    // "Woonohyo"


alert(person1.sayName == person2.sayName);    // true


여기에서 프로퍼티들과 sayName() 메서드는 Person의 prototype 프로퍼티에 직접 추가되었고 생성자 함수는 비워 두었다. 비록 생성자 함수는 비어있지만, 생성자를 호출헤 만든 객체에도 프로퍼티와 메서드는 존재한다. 생성자 패턴과는 달리 프로퍼티와 메서드를 모든 인스턴스에서 공유하므로 person1과 person2는 같은 프로퍼티 집합에 접근하며 같은 sayName() 함수를 공유한다. 세부 동작에 대한 이해를 위해서는 ECMAScript의 프로토타입에 대해 알아야 한다.



- 프로토타입은 어떻게 동작하는가

함수가 생성될 때마다 prototype 프로퍼티 역시 특정 규칙에 따라 생성된다. 기본적으로 모든 프로토타입은 자동으로 contructor 프로퍼티를 갖는데 이 프로퍼티는 해당 프로토타입이 프로퍼티로서 소속된 함수를 가리킨다. 예를 들어 위의 코드에서 Person.prototype.constructor는 Person을 가리킨다. 다음에는 생성자에 따라 각종 프로퍼티와 메서드가 프로토타입에 추가된다.

커스텀 생성자를 정의하면해당 프로토타입은 단지 constructor 프로퍼티만 가지며 다른 메서드는 Object에서 상속한다. 생성자를 호출해서 인스턴스를 생성할 때마다 해당 인스턴스 내부에는 생성자의 프로토타입을 가리키는 포인터가 생성된다. ECMA-262 5판에서는 이 포인터를 [[Prototype]]이라고 부른다. 스크립트에서 [[Prototype]]에 접근하는 표준은 없지만 파이어폭스, 사파리, 크롬은 모두 __proto__ 라는 프로퍼티를 지원한다. 인스턴스와 직접 연결되는 것은 생성자의 프로토타입이지 생성자 자체가 아님을 이해해야 한다.


프로토타입은 contructor 프로퍼티를 갖고 기타 추가된 프로퍼티들도 가지게 된다. Person의 인스턴스인 person1과 person2는 Person.prototype을 가리키는 내부 프로퍼티를 가질 뿐 생성자와 직접 연결되지는 않는다. 이 인스턴스들에는 아무 프로퍼티나 메서드도 없는데 person1.sayName()이 동작하는 것도 객체 프로퍼티를 검색하는 메커니즘 때문이다. 

[[prototype]]은 구현 환경에 따라 접근 불가능할 수도 있지만 객체 사이에 프로토타입 연결이 존재하는지는 isPrototypeOf() 메서드를 통해 알 수 있다. 요약하면, isPrototypeOf()는 다음과 같이 [[Prototype]]이 자신을 호출하는 프로토타입을 가리킬 때 true를 반환한다.


alert(Person.prototype.isPrototypeOf(person1));    // true

alert(Person.prototype.isPrototypeOf(person2));    // true


이 코드에서 프로토타입의 isPrototypeOf() 메서드를 person1과 person2 모두에 대해 호출했다. 두 인스턴스는 모두 Person.prototype에 연결되므로 isPrototypeOf() 메서드는 true를 반환한다.

ECMAScript 5판에는 [[Prototype]]의 값을 반환하는 Object.getPrototypeOf() 라는 메서드가 추가 되었다. 다음의 코드를 보자.


alert(Object.getPrototypeOf(person1) == Person.prototype);    // true

alert(Object.getPrototypeOf(Person1).name);    // "Woonohyo"


첫번째 줄은 단순히 Object.getPrototypeOf()에서 반환 된 객체가 해당 객체의 프로토타입이 맞는지만 확인한다.

두번째 줄은 프로토타입의 name 프로퍼티 값인 "Woonohyo"를 가져온다.

이 메서드를 통해 프로토타입을 이용한 상속 구현을 할 수 있다.

(메서드 지원 브라우저: IE9+, Firefox 3.5+, Safari 5+, Opera 12+, Chrome)


객체에서 프로퍼티를 읽으려 할 때마다 해당 프로퍼티 이름으로 찾으려는 검색은 객체 인스턴스 자체에서 시작한다. 인스턴스에서 프로퍼티 이름을 찾으면 그 값을 반환하고, 그렇지 않으면 포인터를 프로토타입으로 올려서 검색을 계속한다. 프로퍼티를 프로토타입에서 찾으면 그 값을 반환한다. 따라서 person1.sayName()을 호출하면 두 단계가 발생한다. 첫 단계에서 자바스크립트 엔진은 person1 인스턴스에 sayName이라는 프로퍼티가 있는지 확인한다. 찾을 수 없기 때문에 person1의 프로토타입에서 sayName이 있는지 확인하고, 찾았으므로 프로토타입에 저장된 함수가 실행된다. person2.sayName()을 호출하면 똑같은 방식으로 검색하여 실행하고 같은 결과를 반환한다. 프로토타입은 이런 식으로 여러 객체 인스턴스 사이에서 프로퍼티와 메서드를 공유한다.


객체 인스턴스에서 프로토타입에 있는 값을 읽을 수는 있지만, 수정은 불가능하다. 프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면 해당 프로퍼티는 인스턴스에 추가되며 프로토타입까지 올라가지 않는다.


function Person() {

}


Person.prototype.name = "Woonohyo";

Person.prototype.age = 27;

Person.prototype.job = "Software Engineer";

Person.prototype.sayName = function() {

alert(this.name);

};


var person1 = new Person();

var person2 = new Person();


person1.name = "Wonbin";

alert(person1.name);    // "Wonbin" - 인스턴스에서

alert(person2.name);    // "Woonohyo" - 프로토타입에서


위 예제에서 person1의 name 프로퍼티에 새 값이 반영되었다. person1의 경우 person1.name에 접근하면 인스턴스에서 name 프로퍼티를 반환하고, person2의 경우 person2.name에 접근하면 인스턴스에서 찾지 못하므로 프로토타입을 검색해 name 프로퍼티를 찾는다.

즉, 객체 인스턴스에 프로퍼티를 추가하면 해당 프로퍼티는 프로토타입에 존재하는 프로퍼티를 '가리게' 된다. (프로토타입에 존재하는 동명의 프로퍼티에 대한 접근 차단)

심지어 프로퍼티를 null로 설정해도 인스턴스의 프로퍼티만 변경할 뿐 프로토타입 프로퍼티에 다시 연결되지 않는다. 다시 프로토타입 프로퍼티에 접근하기 위해서는 delete 연산자를 사용해야 한다.


function Person() {

}


Person.prototype.name = "Woonohyo";

Person.prototype.age = 27;

Person.prototype.job = "Software Engineer";

Person.prototype.sayName = function() {

alert(this.name);

};


var person1 = new Person();

var person2 = new Person();


person1.name = "Wonbin";

alert(person1.name);    // "Wonbin" - 인스턴스에서

alert(person2.name);    // "Woonohyo" - 프로토타입에서


delete person1.name;

alert(person1.name);    // "Woonohyo" - 프로토타입에서


hasOwnProperty("propertyName") - 프로퍼티가 인스턴스에 존재하는지 프로토타입에 존재하는지 확인할 수 있는 메서드. (true = 인스턴스, false = 프로토타입)

Object로부터 상속되는 메서드로, 다음과 같이 해당 프로퍼티가 객체 인스턴스에 존재할 때만 true를 반환한다.


function Person() {

}


Person.prototype.name = "Woonohyo";

Person.prototype.age = 27;

Person.prototype.job = "Software Engineer";

Person.prototype.sayName = function() {

alert(this.name);

};


var person1 = new Person();

var person2 = new Person();


alert(person1.hasOwnProperty("name"));    // false


person1.name = "Wonbin";

alert(person1.name);    // "Wonbin" - 인스턴스에서

alert(person1.hasOwnProperty("name"));    // true


alert(person2.name);    // "Woonohyo" - 프로퍼티에서

alert(person2.hasOwnProperty("name"));    // false


delete person1.name;

alert(person1.name);    // "Woonohyo" - 프로퍼티에서

alert(person1.hasOwnProperty("name"));    // false















저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 우너효

클로저 내부의 this 객체는 꽤 복잡하게 동작한다. this 객체는 런타임에서 함수가 실행 중인 컨텍스트에 묶인다. 즉 전역 함수에서 this는 스트릭트 모드가 아닐 때는 window, 스트릭트 모드에서는 undefined이며 함수가 객체 메서드로 호출되었을 때 this는 해당 객체이다. 하지만 클로저를 작성하는 방식으로 인해 이를 분명히 알기는 어렵다. 다음 코드를 보자.


var name = "The Window";


var object = {

name: "My Object",

getNameFunc: function() {

return function() {

return this.name;

};

}

};


alert(object.getNameFunc()());    // "The Window" (스트릭트 모드가 아닌 경우)


여기에서 name이라는 전역 변수와 객체를 함께 생성했는데 객체에도 name 프로퍼티가 존재한다. 객체의 getNameFunc() 메서드는 익명 함수를 반환하며 익명 함수는 this.name을 반환한다. getNameFunc()는 함수를 반환하므로 object.getNameFunc()()을 호출하면 getNameFunc()가 반환하는 함수를 즉시 호출하는 것이나 마찬가지이며 문자열을 얻는다. 반환 받은 문자열은 전역 name 변수의 값인 "The Window"이다. 익명 함수가 외부 스코프의 this 객체를 포함하지 않았던 이유는 무엇일까?


모든 함수는 호출되는 순간 자동으로 this와 arguments 두 특별한 변수를 갖게 됨을 상기하자. 내부 함수는 결코 외부 함수의 this와 arguments에 직접적으로 접근할 수 없다. 다음과 같이 외부 함수의 this 객체를 다른 변수에 저장해서 클로저가 이 변수에 접근하도록 하는 일은 가능하다.


var name = "The Window";


var object = {

name : "My Object",

getNameFunc : function() {

var that = this;

return function() {

 return that.name;

};

}

};


alert(object.getNameFunc()());    // "My Object"


굵게 처리한 두 줄로 인해 다른 결과가 나왔다. 익명 함수를 정의하기 전에 외부 함수의 this 객체를 that이란 변수에 할당했다. 이제 클로저를 정의하면 that은 외부 함수에 고유한 변수이므로 클로저는 이 변수에 접근이 가능하다. 함수가 반환한 뒤에도 that은 여전히 object에 묶여 있으므로 object.getNameFunc()()을 호출하면 "My Object"를 반환한다.



this의 값이 예상과 다른 특별한 경우가 몇 가지 있다. 이전 예제를 조금 수정한 다음 코드를 보자.


var name = "The Window";


var object = {

name : "My Object",

getName : function() {

return this.name;

}

};


getName() 메서드는 단순히 this.name의 값을 반환한다. object.getName()을 호출하는 다양한 방법과 그 결과를 보자.


object.getName();    // "My Object"

(object.getName)();    // "My Object"

(object.getName = object.getName)();    // "The Window" (단, 스트릭트 모드가 아닌 경우)


첫 번째 줄은 일반적인 방법으로 object.getName()을 호출했는데 this.name과 object.name은 같으므로 "My Object"를 반환했다. 

두 번째 줄은 object.getName을 호출하기 전에 괄호로 감쌌다. 함수를 참조한 것처럼 보이겠지만 object.getName과 (object.getName)은 같은 것으로 정의되어 있으므로 this 값은 그대로 유지된다. 

세 번째 줄에서는 먼저 할당한 후 그 결과를 호출했다. 할당 표현식의 값은 함수 자체이므로 this의 값이 유지되지 않기 때문에 "The Window"를 반환하게 된다. 

저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 우너효

자바스크립트에서 '익명 함수'와 '클로저'는 자주 잘못 혼용되곤 한다. '클로저'란 다른 함수의 스코프에 있는 변수에 접근 가능함 함수를 뜻한다. 다음의 코드를 보자.


function createComparisonFunction(propertyName) {

return function(object1, object2) {

var value1 = object1[propertyName];

var value2 = object2[propertyName];


if (value1 < value2) {

return -1;

} else if (value1 > value2) {

return 1;

} else {

return 0;

}

};

}


위 코드에서 Bold 처리 된 부분은 내부 함수(익명 함수)이면서 외부 함수의 변수(propertyName)에 접근한다. 내부 함수가 반환되어 다른 컨텍스트에서 실행되는 동안에도 propertyName에 접근할 수 있다. 이런 일이 가능한 것은 내부 함수의 스코프 체인에 createComparisonFunction()의 스코프가 포함되기 때문이다. 


좀 더 쉽게 이해하기 위해 함수를 처음 호출할 때 무슨 일이 일어나는 지 생각해보자.


함수를 호출하면 실행 컨텍스트와 스코프 체인이 생성된다. 함수의 활성화 객체는 arguments 및 이름 붙은 매개변수로 초기화된다. 외부 함수의 활성화 객체는 스코프 체인의 두 번째 객체이다. 이 과정이 포함 관계에 있는 함수에서 계속 발생하여 스코프 체인이 전역 실행 컨텍스트에서 종료될 때까지 이어진다.


함수를 실행하면 값을 읽거나 쓸 변수를 스코프 체인에서 검색한다. 다음 코드를 보자.


function compare(value1, value2) {

if (value1 < value2) {

return -1;

} else if (value1 > value2) {

return 1;

} else {

return 0;

}

}


var result = compare(5, 10);


위 코드는 전역 실행 컨텍스트에서 호출하는 compare()라는 함수를 만든다. compare()를 처음으로 호출하면 arguments, value1, value2를 포함한 활성화 객체가 만들어진다. 전역 실행 컨텍스트의 변수 객체는 this와 result, compare를 포함하는 compare() 실행 컨텍스트의 스코프 체인의 다음에 위치한다. 


실행 컨텍스트마다 변수를 나타내는 객체가 존재한다. 전역 컨텍스트의 변수 객체는 항상 존재하지만, compare()와 같은 로컬 컨텍스트 변수 객체는 함수를 실행하는 동안에만 존재한다. compare() 함수를 정의하면 스코프 체인이 생성되고 자바스크립트 인터프리터는 이를 전역 변수 객체와 함께 미리 읽어 함수 내부의 [[Scope]] 프로퍼티에 저장한다. 함수를 호출하면 실행 컨텍스트가 생성되며 함수의 [[Scope]] 프로퍼티에 들어 있는 객체를 복사하여 스코프 체인이 생성된다. 다음에는 변수 객체로 동작하기도 하는 활성화 객체가 생성되어 컨텍스트의 스코프 체인 맨 앞에 위치한다. 


위 예제에서 compare() 함수의 실행 컨텍스트가 스코프 체인 내에 변수 객체를 두 개, 즉 로컬 활성화 객체와 전역 변수 객체를 갖는다. 스코프 체인이란 변수 객체를 가리키는 포인터 목록이며 객체를 직접 포함하는 것은 아니다.


함수에서 변수에 접근할 때마다 스코프 체인에서 해당 이름의 변수를 검색한다. 함수 실행이 끝나면 로컬 활성화 객체는 파괴되고 메모리에는 전역 스코프만 남는다. 하지만, 클로저는 이와 다르게 동작한다.

다른 함수의 내부에 존재하는 함수는 외부 함수의 활성화 객체를 자신의 스코프 체인에 추가한다. 따라서 createComparisonFunction()(회부 함수) 내부에 있는 익명 함수의 스코프 체인에는 외부 함수의 활성화 객체를 가리키는 포인터가 존재하게 된다. 


외부 함수가 실행을 마치고 익명 함수를 반환하면 익명 함수의 스코프 체인은 외부 함수의 활성화 객체와 전역 변수 객체를 포함하도록 초기화된다. 이 때문에 익명 함수는 외부 함수의 변수 전체에 접근할 수 있게 된다. 한 가지 흥미로운 점은 외부 함수가 실행을 마쳤는데도 활성화 객체가 파괴되지 않는 점인데 아직 익명 함수의 스코프 체인에서 이를 참조하기 때문이다. 외부 함수가 실행을 마치면 실행 컨텍스트의 스코프 체인은 파괴되지만 활성화 객체는 익명 함수가 파괴될 때까지 메모리에 남는다.




















저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 우너효

프로시저

SQL의 기본 명령은 선언적 명령이다. (명령 후 결과)

즉, 데이터베이스에서 절차적 명령을 수행하기 위해서는 스토어드 프로그램을 이용해야 한다.


스토어드 프로그램의 종류

  • 스토어드 함수
    의미 그대로 함수. 날짜, 현재 사용자, 시간 등.
    SELECT 쿼리 문 안에 사용 가능.

  • 스토어드 프로시져
    더 다양한 기능을 제공.
    단, 쿼리 안에서 사용 불가능. 쿼리 외부에서 호출해야 함.

  • 트리거
    기존 프로그래밍 언어의 ‘이벤트 핸들러’와 유사.
    어떤 테이블에 레코드가 추가됐을 때 특정 작업을 수행하는 일 따위를 할 수 있음.

  • 이벤트 핸들러
    트리거와 유사하지만 다소 다름.


스토어드 프로시저의 장점

응용 프로그램의 성능 향상

네트워크 트래픽 감소
여러 개의 쿼리로 처리해야 할 작업을, 단일 쿼리로 처리할 수 있게 되기 때문이다.

보안성 향상
SQL Injection 원천 봉쇄

개발 업무의 구분


스토어드 프로시저의 단점

유지 보수가 어려워진다.
모든 코드가 프로그램 안에 들어있지 않기 때문.

GIT에서 관리가 어렵다.

롤백이 어렵다.

명령 자체의 성능이 감소한다.
특히 mysql에서 성능이 좋지 않다.


— 웹 분야에서 스토어드 프로시져의 사용은 점차 증가하고 있다.


스토어드 프로시져로 Hello World 작성하기

DELIMETER $$ // 구분자를 $$로 변경. ;으로 인한 종결 방지.

CREATE PROCEDURE SP_HELLO()

BEGIN

DECLARE STR CHAR(20) DEFAULT ‘POPI’;

SET STR = ‘HELLO, WORLD’;

SELECT STR;

END $$

DELIMETER ;


CALL SP_HELLO();


기본 변수 사용하기

DECLARE 자료형 [DEFAULT 기본값]

— 기본 변수는 프로시져 내에서만 유효함.


세션 변수

@로 시작하는 변수를 만들면, 세션 변수로 기존 프로그래밍 언어의 전역변수와 비슷한 역할을 하게 된다.

SET @name = ‘woonohyo’;

SELECT NAME INTO @name from user_list limit 1; // @name의 값이 바뀌게 된다.


기본 변수에 값 넣기

DELIMETER $$

CREATE PROCEDURE SP_TEST1()

BEGIN

DECLARE A INT;

SET A = 10;

SET A = A * 2;

SELECT A;

END $$

DELIMITER ;


CALL SP_TEST1();



SELECT INTO 사용하기

쿼리의 결과(스칼라 값)를 변수에 넣을 수 있다.

CREATE PROCEDURE SP_TEST2()

BEGIN

DECLARE A INT;

SELECT COUNT(UID) INTO A FROM USER_INFO;

SELECT A;

END $$

DELIMITER ;


결과값을 테이블에 넣기

CREATE TABLE TEST3(NUM INT);

INSERT INTO TEST3 VALUES(1);

CREATE PROCEDURE SP_TEST3()

BEGIN    

DECLARE A INT DEFAULT 1;

SELECT MAX(NUM) INTO A FROM TEST3;

SET A = A + 1;

INSERT INTO TEST3 VALUES (A);

SELECT * FROM TEST3;

END $$

DELIMITER ;

실행 할 때마다 TEST3의 값이 1씩 증가한다.


매개변수 사용하기

IN parameter: 입력에 사용

OUT parameter: 출력에 사용

 INOUT parameter: 입출력에 사용

CREATE TABLE TEST4 ( 

ID INT PRIMARY KEY AUTO_INCREMENT, 

NAME VARCHAR(20)

);


INSERT INTO TEST4 VALUES (NULL, ‘SHINJI’) (NULL, ‘ASUKA’) (NULL, ‘REI’) (NULL, ‘KAORU');

DELIMTER $$

CREATE PROCEDURE SP_TEST4(IN pname varchar(10))

BEGIN

DECLARE A INT DEFAULT 1;

SELECT * FROM TEST4 WHERE NAME = pname;

END $$

DELIMITER;


CALL SP_TEST4(‘



IF문 사용하기

IF <condition> THEN <statement>

[ELSE IF <condition> THEN <statement>]

[ELSE <statement>]

END IF


WHILE 문 사용하기

WHILE <condition> DO

<statement>

END WHILE



프로시저의 파라미터 중에는 INOUT이 있다. 이는 입/출력 전용임을 뜻한다. 



스토어드 프로시져 실습하기

DELIMITER $$

CREATE PROCEDURE SP_TEST1(IN num INT, INOUT ret INT)

BEGIN

DECLARE n INT;

// 거래 건수가 num 이상인 USER와

SELECT * FROM USER WHERE TRADE_NUM >= num;

// 거래 건수가 num 이상인 MARKET을 선택.

SELECT * FROM MARKET WHERE TRADE_NUM >= num;

SELECT COUNT(*) INTO n FROM USER WHERE TRADE_NUM > num;

SET ret = ret + n;

END $$

DELIMITER ;


SET @n = 10;

CALL SP_TEST1(2, @n); // SP_TEST1(2, 10) 으로 넣으면 INOUT에 상수를 넣었다는 에러 발생.

SELECT @n;



Statement의 생성은 다음과 같이 이루어진다.

CallableStatement cs = c.prepareCall(‘{CALL SP_TEST1(?,?)}’);


저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 우너효

02 트랜잭션 수준 읽기 일관성



(1) 트랜잭션 수준 읽기 일관성이란?


문장수준 읽기 일관성(Statement-Level Read Consistency)은 쿼리가 시작된 시점을 기준으로 데이터를 일관성 있게 읽어 들이는 것

트랜잭션 수준 읽기 일관성(Transaction-Level Read Consistency)은 트랜잭션이 시작된 시점을 기준으로 일관성 있게 데이터를 읽어 들이는 것

오라클은 완벽한 문장수준의 읽기 일관성을 보장하나 트랜잭션에 대해서는 기본적으로 보장하지 않는다. 



(2) 트랜잭션 고립화 수준


 트랜잭션 수준으로 읽기 일관성을 강화 하려면 트랜잭션 고립화 수준을 높여 주어야 한다. 

트랜잭션 고립화 수준을 조절하는 방법은 네가지 수준이 있다.

ANSI / ISO SQL standard(SQL92)에서 정의함


■ 레벨 0 ( = Read Uncommitted )

 - 트랜잭션에서 처리중인 / 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용

 - Dirty Read, Non-repeatable Read, Phantom Read 현상 발생

 - 오라클은 이 레벨을 지원하지 않음


■ 레벨 1 ( = Read Committed )

 - Dirty Read 방지 : 트랜잭션이 커밋되어 확정된 데이터만 읽는 것을 허용

 - 대부분의 DBMS가 기본모드로 채택하고 있는 일관성 모드

 - Non-Repeatable Read, Phantom Read 현상 발생

 - DB2, SQL Server, Sybase의 경우 읽기 공유 Lock을 이용해서 구현, 하나의 레코드를 읽을 때 Lock을 설정하고 해당 레코드에서 빠지는 순간 Lock해제

 - Oracle은 Lock을 사용하지 않고 쿼리시작 시점의 Undo 데이터를 제공하는 방식으로 구현


■ 레벨 2 ( = Repeatable Read )

 - 선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제하는 것을 불허함으로써 같은 데이터를 두번 쿼리했을 때 일관성 있는 결과를 리턴

 - Phantom Read 현상 발생

 - DB2, SQL Server의 경우 트랜잭션 고립화 수준을 Repeatable Read로 변경하면 읽은 데이터에 걸린 공유 Lock을 커밋할 때까지 유지하는 방식으로 구현

 - Oracle은 이 레벨을 명시적으로 지원하지 않지만 for update절을 이용해 구현 가능,

   SQL Server등에서도 for update 절을 사용할 수 있지만 커서를 명시적으로 선언할 때만 사용 가능함


■ 레벨 3 ( = Serializable Read )

 - 선행 트랜잭션이 읽은 데이터를 후행 트랜잭션이 갱신하거나 삭제하지 못할 뿐만 아니라 중간에 새로운 레코드를 삽입하는 것도 막아줌

 - 완벽한 읽기 일관성 모드를 제공

  

 트랜잭션 고립화 수준 조정기능을 위한 script

set tracsaction isolation level serializable;



낮은 단계의 트랜잭션 고립화 수준을 이용시 발생하는 현상 3가지

  • Dirty Read ( = Uncommitted Dependency )
  • Non-Repeatable Read ( = Inconsistent Analysis )
  • Phantom Read 


(3) Dirty Read ( = Uncommitted Dependency)


커밋되지 않은 수정 중인 데이터를 다른 트랜잭션에서 읽을 수 있도록 허용할때 발생

대부분 DBMS가 기본 트랜잭션 고립화 수준을 레벨1 로 설정하고 있어 Dirty Read는 발생하지 않음

 =  커밋된 데이터만 읽을 수 있도록 허용

SQL Server, Sybase등은 데이터를 읽을 때 공유 Lock을 사용,

갱신중인 레코드에는 배타적 Lock이 걸림, 이는 공유 Lock과는 호환되지 않아 갱신중인 레코드는 읽지 못함(Lock에 의한 동시성 저하 발생)

오라클은 다중 버전 읽기 일관성 모델을 채택함으로써 Lock을 사용하지 않고도 Dirty Read를 피해 일관성 있는 데이터 읽기가 가능 

 

(4) Non-Repeatable Read ( = Inconsistent Analysis)


 한 트랜잭션 내에서 같은 쿼리를 두번 수행 할 때 그 사이에 다른 트랜잭션이 값을 수정 또는 삭제함으로써

두 쿼리의 결과가 상이하게 나타나는 비일관성이 발생하는 것을 말함 


(5) Phantom Read


한 트랜잭션 안에서 일정 범위의 레코드를 두번 이상 읽을 때, 첫번째 쿼리에서 없던 레코드가 두번째 쿼리에서 나타나는 현상

이는 트랜잭션 도중 새로운 레코드가 삽입되는 것을 허용하기 때문에 나타나는 현상임.

Phantom Read현상을 방지하려면 트랜잭션 고립화 수준을 레벨3으로 올려 주어야 한다. 

고립화 수준을 높이면 데이터 일관성은 확보 되지만 동시성이 현격히 저하되는 결과를 초래함

오라클은 해당 쿼리의 SCN 확인 과정을 통해 질의 시점에 존재했던 데이터만을 대상으로 집계 수행을 하므로 동시성을 저하시키지 않으면서 일관성을 유지 

트랜잭션 고립화 수준을 높이면 일관성은 향상 되지만 더 넓은 범위의 Lock을 더 오랫동안 유지하는 방식으로 동시성을 저하한다.(그림2-2참조)

그러나 오라클은 트랜잭션 고립화 수준을 높이더라도 Lock을 사용하지 않으므로 동시성이 저하되지는 않는다.


 

참조


 

위 사용된 Image는 마소7월호에서 발췌하였습니다. 

문서정보


저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 우너효

목표

  • this 키워드가 어떻게 동작하는지 안다.
  • scope를 이해한다.
  • closure를 이해한다.

- What is scope?
scope는 변수의 유효 범위를 뜻한다. 

코드로 이해해보자.
---------------------------------
function myNum() {
var i = 0;
for ( var i = 0; i < 10; i++) {
}
console.log(i);
}
---------------------------------
위 함수에서 i는 0이 될까 10이 될까? 

정답은 10이다. 이유는 자바스크립트의 scope는 블록(block)이 아닌 함수(function)이기 때문이다.



다른 코드를 보자.


---------------------------------

i = 0;

function myNum() {

// var의 유무에 따라 결과는 다르게 나온다!

var i = 0;

for ( var i = 0; i < 10; i++) {

}

}

myNum();

console.log(i);

---------------------------------


i는 0이 나온다. 왜냐하면 전역변수 구간의 i와 myNum() 함수 내의 i는 서로 다르기 때문이다.

만약 var 없이 코딩을 하게 되면, 전역변수에 있는 i를 가져와서 수정이 이루어지기 때문에 값은 10이 나오게 된다.



그렇다면 var는 어떻게 쓰는 게 좋을까?


1. 중첩된 함수

---------------------------------

function mySNS() {

var name = "LINE";

return function() {

return name;

};

}

---------------------------------


---------------------------------

function mySNS() {

var name = "LINE";

return function() {

return function (prefix) {

return prefix + name;

}

};

}

mySNS()()("my sns is ");

---------------------------------


가장 안 쪽에 있는 함수는, 부모 함수들 중 name이라는 변수가 있는지 계속 찾게 된다.


---------------------------------

function mySNS() {

var name = "LINE";

function inner() {

var x = "inner";

}

inner();

console.log(x);

}

mySNS();

---------------------------------


결과값은 어떻게 될까? ---------------> x is not defined 라는 결과가 뜨게 된다.

왜냐하면 변수 x의 스코프는 inner() 함수 안 쪽에 불과하기 때문이다.



- 즉시실행함수

또 다른 스코프를 만드는 것. 


기본적인 형태는 다음과 같다.


(function() {


})();


---------------------------------

function mySNS() {

 var name = "LINE";

(function() {

var name2 = "KAKAO';

})();

console.log(name + " & " + name2);

}


mySNS();

---------------------------------

결과는 name2 is not defined가 된다. 왜냐하면, name2에 var를 사용하는 바람에 해당 변수의 스코프가 즉시실행함수 내부로 한정되기 때문이다.


만약 바깥에서도 name2 변수를 사용하고 싶으면 즉시실행함수 내부 변수에 var 키워드를 제거해주면 된다.


이외에도 즉시실햄함수는 주로 전역변수를 없애는 목적으로 자주 쓰인다.




- this

this란 context를 가리키는 변수이다. context는 해당 함수가 실행이 되는 공간이라고 생각하면 편하다.


함수 호출 관점에서 this를 이해해보자.

함수를 호출하는 순간에는 무조건 this가 생겨난다. 즉, 모든 함수는 그 함수가 실행되는 context가 항상 존재하게 된다는 의미이다.

기본적으로 함수의 context는 window이다.


---------------------------------

function mySNS() {

this.name = "LINE";

debugger;

return this.name;

}

---------------------------------

위의 함수를 호출했을 때, scope variable을 확인해보면

this: window 임을 알 수 있다.



method에서 this는 어떻게 될까?

---------------------------------

var mySNS = {

name : "LINE";

getName : function() {

debugger;

return this.name;

}

}

---------------------------------

mySNS.getName()을 했을 때, scope variable을 확인해보면

this:object 임을 알 수 있다.



constructor의 this

---------------------------------

function mySNS() {

this.name = "LINE";

this.getName = function() {

return this.name;

}

}

mySNS();

---------------------------------

name3; --> "LINE"

window.name3; --> "LINE"


즉, 변수가 window의 전역공간에 꽂히게 된다.


만약

new mySNS();

를 호출할 땐 어떻게 될까?


mySNS {name3: "LINE", getName3: function}

바로 객체가 하나 생성된다. 


즉, new 키워드를 통해 함수를 호출하면, 해당 함수 내의 this에 묶여있는 모든 속성을 다 담아서 반환하게 된다.

(this가 리턴된다고 생각하면 편하다)



call & apply의 this


-- 과제 --

1. call 함수를 공부하고 간단한 예제를 만들어보자.

2. call 함수에서의 this가 어떤 것을 가리키는지 예제코드에 주석으로 설명을 추가하자.

3. apply와 call의 차이점을 주석으로 설명하라.


-- 과제2 --

web화면에 '선식', '토마토', '마늘' 등을 만들고 해당 버튼을 누르면 console 화면을 통해 관련된 문구를 출력하도록 만들어라.

var obj = {

strTexts : " 은 정말 몸에 좋아요 ^^ ",

registerEvents : function() {

// do something

// strTexts는 this.strTexts로만 접근할 것.

}

};

obj.registerEvents();



저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 우너효

한 객체 A가 다른 객체 B를 참조할 경우, B의 변화에 따라 A가 영향을 받게 된다. 이를 의존성이라고 부른다.

객체지향프로그래밍의 경우 객체 A가 객체 B의 메서드를 사용하는 경우가 그러하다.


Dependency

<그림 1> 코드를 통해 보는 의존성


위의 코드를 보면 MovieFinder라는 객체는 MovieLister의 findAll()이라는 메서드를 사용함으로써 의존관계가 생성된다.


개발을 함에 있어 적절한 의존관계는 필수적이지만, 지나친 의존관계는 유연성을 떨어뜨려 향후 코드의 유지보수에 악영향을 끼치게 된다. 이를 해결하기 위해 예전부터 많은 시도가 있어 왔고, 오늘은 'Dependency Injection(의존성 주입)'라는 방법을 정리해본다.



성적을 입력하면, 그에 따라 A, B, C 등의 성적을 매겨주는 프로그램을 작성한다고 가정해보자.

대충 모양은 다음과 같이 될 것이다.

Lecture 클래스는 StandardGrader에 의존하여, 성적을 입력하면 그에 알맞은 결과를 얻을 수 있다.


하지만 만약 A, B, C처럼 일반적인 채점이 아닌 Pass/Fail의 경우로 바뀌면 다음과 같은 모습으로 바뀌어야 한다.


하지만 이러한 경우에 Lecture 클래스는 총 2개의 클래스에 의존관계를 갖게 된다. 이러한 의존관계를 흔히 커플링(Coupling)이라고도 표현을 하기도 한다. 위와 같은 구조를 계속 유지하게 될 경우에는 성적을 채점하는 방식이 늘어날수록 커플링, 즉 결합도가 높아질 수 밖에 없다.


이러한 결합도를 느슨하게 하기 위한 가장 일반적인 방법은 대표적으로 추상화가 있다.


Lecture 클래스와 Grader 클래스들간의 결합을 늦추기 위해, Grader들을 추상화 한 요소하고만 의존관계를 갖게끔 설정하는 것이다. 


일반적으로, 이러한 추상화를 위한 방법으로는

1. 추상 클래스 (Abstract Class)

2. 인터페이스 (Interface)

가 존재한다.

이를 통해 클래스에 대한 가정을 제거하고, 단지 행위에 대해서만 가정하게 되어 결합도를 낮출 수 있게 된다.


여기서는 인터페이스를 통해 결합도를 낮추는 그 과정에 대해서 좀 더 상세하게 살펴보기로 한다.

우선, 인터페이스는 간단하게 생각하면 어떠한 행위를 담고 있는 것이라고 생각하면 편하다. 위의 사례에서는 Grader들은 grade()라는 행위를 공통적으로 갖고 있으므로, 이를 인터페이스로 묶어낼 수 있다.


즉, Lecture의 입장에서는 Grade를 산출할 수 있는 동일한 인터페이스를 제공하는 다양한 객체와 협력을 할 수 있게 되는 것이다. 이를 통해 설계는 좀 더 유연해질 수 있고, 다양한 상황에서 재사용이 가능해지게 된다.

처음에 제시했던 모양처럼 Lecture가 특정 Grader 클래스와 의존관계를 맺고 있으면, 앞서 말한 장점들은 포기할 수 밖에 없게 된다. 한마디로 요약하자면, 의존관계는 구체적인 클래스가 아닌 행위에 대해 생성되어야 하는 것이다.


이를 구현하기 위해서는 '생성(Construction)'과 '사용(Use)'을 분리해야 한다.

Lecture 클래스가 Grader 인터페이스에 대해서는 '사용'에 대한 의존관계를 갖고, 구현클래스인 PassFailGrader나 StandardGrader에 대해서는 '생성'에 대한 의존관계를 갖게끔 재설계를 해야한다.


<그림> 분리 된 의존관계를 가진 Lecture 클래스


결합도가 많이 낮아졌지만, 여전히 Lecture 클래스는 구현 클래스들의 생성에 대한 의존관계를 가지고 있다.

Grader grader = new PassFailGrader();

와 같은 방식으로 Lecture 클래스 내에서 명시적으로 사용할 클래스를 지정해야 하므로 앞으로 귀찮아질 가능성이 높다.


이러한 생성 의존관계를 Lecture 클래스 밖으로 꺼낼 수 있는 방법이 있다. 바로 클라이언트에게 이 생성하는 작업을 넘겨버리는 것이다. 무엇이 클라이언트가 될 지 잘 와닿지 않는다면 테스트 코드를 생각하면 쉽다. 테스트 코드 단위에서, 어떠한 구현 클래스를 이용할지 정해서 Lecture 클래스에게 전달해주면, Lecture 클래스는 생성에 대해 고민을 할 필요도 없고 유연성도 좀 더 늘어나게 된다.


이 경우, 클라이언트에 의해 어떤 구현 클래스를 사용할지가 결정이 되면서 의존성이 생겨나게 되고, 이 의존성을 Lecture 클래스에게 강제로 사용하게끔 하는데 이게 바로 'Dependency Injection(의존성 주입)'인 것이다. 즉, Lecture 클래스가 사용하는 메서드의 의존관계가 클라이언트에 의해 강제로 주입당하게 되는 것이다.



<그림> Client로 인해 의존성 주입이 이루어지는 모습


코드로 표현하면 다음과 같이 된다.


위의 코드는 테스트 코드이다. Lecture 클래스를 생성할 때, 매개변수로 어떠한 구현 클래스를 사용할지 클라이언트가 결정하게 된다. 즉, Lecture의 클라이언트가 Lecture로 하여금 사용할 Grader를 생성하여 주입하게 되는 것이다.








저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 우너효


티스토리 툴바