MYSQL Function
- function vs index optimizaion
DETERMINISTIC VS NOT DETERMINISTIC
- MySQL Server의 stroed Function은 반드시 둘 중 하나의 속성을 가지게 됨
- DETERMINISTIC
- 한글로 확정적이라 해석 됨
- 입력이 동일하면 언제 실행하든지 관계없이 동일 한 출력
- 함수의 호출 시점마다 참조하는 테이블 데이터가 달라지는 경우도 생각해 볼 수 있음
- 여기서 입력이라하면 함수의 인자뿐 아니라 함수가 참조하는 데이터도 같이 포함 한다고 이해하자
- DETERMINISTIC 펑션이라 하더라도 참조하는 테이블이 데이터가 달라지면
- 입력이 달라지므로 출력이 달라짐
- ex ) 오늘 가입한 사용자의 수를 가져오는 함수를 만들었을 때
- stored function이 실행되는 도중에도 사용자의 가입은 꼐속 진행되기때문에
- 이 함수는 호출할때마다 결과가 달라질 수 도 있다
- 하지만 이 때 사용자 테이블의 레코드가 달라지는 것도 입력이 달라지는 것으로 해석하기 때문에
- MYsql 서버에서 셀럭터를 포함해서 하나의 statement는 반드시 시작 시점의 스냅샷을 보도록 구현 됨
- 그래서 mysql 서버에서 실행되는 query 문장 하나는 동일한 데이터 상태를 보게 됨
- 그래서 하나의 문장내에서 stored function이 여러번 호출된다고 하더라도 테이블의 데이터는
- 해당 시점의 스냅샷을 보기 때문에 함수의 인자만 동일하다면 입력이 달라지지 않는 것으로 볼 수 있다.
- 지금까지 설명한 것과 반대로 입력이 동일하더라도 호출되는 시점에 따라서 결과가 달라질 수 있는
- 비확정적 이라하고 NOT DETERMINISTIC 키워드 로 정의
정리
확정적 함수
- 같은 입력을 넣으면 항상 같은 결과가 나옴
- 예를 들어, 숫자를 두 배로 만드는 함수는 항상 같은 결과
- 주의할 점은, '입력'이라고 할 때 함수에 직접 넣는 값뿐만 아니라 함수가 사용하는 테이블의 데이터도 포함된다
- MySQL은 하나의 쿼리 문장이 실행될 때, 그 순간의 데이터 상태를 '사진 찍듯이' 저장해둡니다. 이걸 '스냅샷'이라고 해요
- 같은 쿼리 안에서 함수를 여러 번 호출해도, 항상 같은 데이터를 보게 됨
- function 1 , function 2 함수 정의
SELECT * FROM tab WHERE id=func1();
SELECT * FROM tab WHERE id=func2();
SELECT @func1_called , @func2_called;
@func1_called = 3
@func2_called = 12
CREATE
DEFINER=root@ 'localhost'
FUNCTION func1() RETURNS INTEGER
DETERMINISTIC SQL SECURITY INVOKER
BEGIN
SET @func1_called = INNULL(@func1_called,0) +1;
RETURN 1;
END ;;
CREATE
DEFINER=root@ 'localhost'
FUNCTION func2() RETURNS INTEGER
NOT DETERMINISTIC SQL SECURITY INVOKER
BEGIN
SET @func2_called = INNULL(@func2_called,0) +1;
RETURN 1;
END ;;
- 확정 함수와 비확정 함수 정의
- 변수의 값은 단순히 1씩 증가하는 함수
- tab이라는 테이블에서 id 컬럼의 값이 fun1 과 fun2 함수의 결과 값이랑 동일한 레코더 찾는 쿼리 실행해보자
- 두 쿼리 실행 결과 값 중요 X 쿼리 실행 결과
- func1_called 와 fun2_called 가 어떻게 바뀌었는지 중요
- 각 3 / 12번 호출된걸 알수 있다
- 호출횟수가 많다? 그만큼 많이 실행 됬다 -> 함수가 사용된 쿼리 처리시 실행ㅅ히간보다 cpu 또는 메모리 사용량이한 4배정도 더 많이 소요됬다.
정리
- func1: 확정적 함수 (Deterministic) / func2: 비확정적 함수 (Not Deterministic)
- 두 함수는 거의 똑같아요. 호출될 때마다 각각의 카운터(@func1_called, @func2_called)를 1씩 증가시키고, 항상 1을 반환
SELECT FROM tab WHERE id=func1();
SELECT FROM tab WHERE id=func2();
- 각 몇번 호출 되었나? 3 / 12
- func2(비확정적 함수)가 func1(확정적 함수)보다 4배나 더 많이 호출되었다는 거
- func2를 사용한 쿼리가 더 많은 시간과 자원(CPU, 메모리 등)을 사용했다는 걸 의미
- 비확정적 함수(Not Deterministic)는 확정적 함수(Deterministic)보다 더 자주 호출될 수 있으며, 이는 쿼리 성능에 큰 영향을 미칠 수 있다
DETERMINISTIC 여부에 따른 작동 차이
-
두 함수의 차이가 발생하는 원인을 찾아 보자
-
확정 함수 func1 첫번재 쿼리는 기본키 타입을 const 타입으로 접근 함
-
테이블의 레코드를 1건만 읽었다는 것 의미 매우 빠르게 처리되었다는 것을 의미
-
실행 계획 하단의 노트에 명시된 query에서도 where 절의 func1 함수 호출이 없어진것을 알 수 있따
-
이는 최적화 되어서 함수 호출 부분 자체가 사라짐의미
-
두번째 쿼리는 실행계획 타입이 all 이라는것을 알수 있다
-
이거는 쿼리가 풀테이블 스캔으로 처리되었다는 것을 의미
-
실행 계획 하단의 노트에 표시된 쿼리에서 func2 함수 호출코드가 남아 있다.
-
이번 실행 계획에서는 확정되지함수가 풀스캔을 하는 심각한 문제를 가지고 있는 것 확인 가능
-
쿼리 실행 계획이 인덱스를 사용하지 못하고 풀 테이블 스캔으로 바뀌었기 때문 (호출 횟수가 많이 높은 이유)
정리
확정적 함수(func1) 사용 쿼리
- 데이터베이스가 아주 똑똑하게 동작
- 단 한 번의 검색으로 원하는 데이터를 찾았어요 (const 타입)
- 함수 호출 부분을 아예 없애버렸어요. 이걸 '최적화'
비확정적 함수(func2) 사용 쿼리
- 데이터베이스가 조금 둔하게 동작
- 모든 데이터를 하나하나 다 뒤짐 (ALL 타입, 풀 테이블 스캔)
- 함수를 계속 호출하면서 검색
왜 이렇게 다를까?
- 확정적 함수는 결과를 예측할 수 있어서 데이터베이스가 똑똑하게 처리
- 비확정적 함수는 결과를 예측할 수 없어서 데이터베이스가 모든 경우를 다 확인
NOT DETERMINISTIC 최적화 이슈
- 왜 이런 문제가 생기지?
- 실행하는 시점에 따라서 다른 값을 반환할 수 있다는 것 때문에 이 모든 문제가 발생함
- EX) 레코드 건수가 10건인 테이블에서 WHERE 조건절이 ID 값인
- stored function 호출 결과값과 동일한 레코드를 찾으려고 하는 것 인데
- Not Determinisic 속성의 fuynction2 함수는 레코더를 매번 비교할 때마다
- fuction2 함수 결과값이 매번 달라짐
- 그래서 MySql 서버의 옵티마이저는 function2함수의 결과 값을 상수로 취급하지 못함
- 그래서 테이블의 레코더를 한 건씩 읽어서 비교할 때마다 function2 함수를 실행해서
- 결과 값을 매번 새롭게 가져와서 비교를 수행하게 됨
- Not Deteministic 함수를 where 조건절의 비교값으로
- 사용하게 되면 Mysql 서버는 해당 컬럼에 인덱스가 있다하더라도 인덱스가 있더라도 인덱스 최적화 못해 풀 테이블 스캔으로 처리됨
Not Deterministic 효과
- mysql 서버에서는 not Deterministic 속성이 Stored function에만 적용되는게 아님
- mysql 서버는 다양한 built-in 함수들이 있음 (Not Deterministic 으로 정의된 함수들 속성을 상속받음)
- RAND() , UUID() , SYSDATE(), NOW()
- 위 예시들은 where조건절에 사용되면 비교되는 모든 컬럼의 인덱스를 효율적으로 사ㅓ용하지 못하게 되므로 똑같이 주의 필요
Not Deterministic 예외
- now 와 sysdate함수는 조금 예외적인 성격을 가지고 있음
- 위 예 제는 tab 테이블에서 10건의 레코드만 줘야 하는데 now 함수 의 결과값과
- sysdate 결과값 가져오는 쿼리
- slip 함수를 이용해서 레코드 끈 단위로 1 초씩 슬립하도록 쿼리를 조금 변경함
- now 함수의 결과값은 모두 동일함 쿼리가 시작된 시점만 반환
- sysdate는 첫번재는 now함수와 동일한 결과값을 반환하지만
- 그뒤로는 슬립 패턴 1초씩 뒤로 밀려있음
- 둘다 not Deterministic 이긴 하지만
- now 함수는 하나의 문장안에서 deferministic처럼 인식되도록 구현되있음
- 반면 sysdate는 not deterministic처럼 레코드 끝단위로 매번 새로 호출됨
- 그 결과 매번 결과값이 달라짐
- 그래서 create_at = now() (인덱스 적용) 쿼리 최적 실행 가능
- sysdate 결과값과 비교하는 경우 full table scan을 한다
- 의외로 이 둘의 차이를 잘모르는 개발자가 많음
- sysdate-is-now 설정을 mysql 서버에서 옵션을 넣어주면 now 함수와 같은방식으로 실행되게함
mysql stored function 사용시 주의사항
- mysql server에서 stored function 을 not deterministic 이 기본 속성
- stored function 생성할때 deterministic 옵션을 별도로 설정 해줘야 됨
- Stored Function 정의시 많이 실수하는 부분인 define 부분과
- Sql security 속성도 정확히 이해하고 명시하는걸 권장
정리
NOT DETERMINISTIC 최적화 이슈:
NOT DETERMINISTIC 함수는 실행할 때마다 다른 결과를 반환할 수 있다
- 이로 인해 MySQL 옵티마이저가 효율적으로 쿼리를 최적화하지 못함
- 예를 들어, WHERE 절에서 NOT DETERMINISTIC 함수를 사용하면 인덱스를 사용할 수 없게 되어 전체 테이블 스캔이 발생
NOT DETERMINISTIC 효과
- 이 속성은 Stored Function뿐만 아니라 일부 내장 함수(RAND(), UUID(), SYSDATE(), NOW() 등)에도 적용
- 이런 함수들을 WHERE 절에 사용하면 인덱스를 효율적으로 사용하지 못함
NOT DETERMINISTIC 예외
- NOW()와 SYSDATE() 함수는 약간 다르게 동작
- NOW()는 쿼리 시작 시점의 시간을 반환하고, 쿼리 내에서 일관된 값을 유지
- SYSDATE()는 매 호출마다 현재 시간을 반환
- 따라서 NOW()는 인덱스를 사용할 수 있지만, SYSDATE()는 전체 테이블 스캔을 유발
MySQL Stored Function 사용 시 주의사항
- MySQL에서 Stored Function은 기본적으로 NOT DETERMINISTIC
- DETERMINISTIC으로 설정하려면 함수 생성 시 명시적으로 지정 해야 함
- DEFINER와 SQL SECURITY 속성도 정확히 이해하고 설정해야 함