동일한 입력 매개변수에 대해 항상 동일한 결과를 생성하는 경우 "DETERMINISTIC"인 것으로 간주되고 그렇지 않은 경우 "NOT DETERMINISTIC" 것으로 간주됩니다. 기본값은 NOT DETERMINISTIC입니다. 함수가 "DETERMINISTIC"임을 선언하려면 DETERMINISTIC을 명시적으로 지정해야 합니다.

 

MySQL은 DETERMINISTIC으로 선언된 루틴에 비결정적(NOT DETERMINISTIC) 결과를 생성하는 문이 없는지 확인하지 않습니다. 루틴을 잘못 선언하면 결과나 성능에 영향을 미칠 수 있습니다. 비결정적 루틴을 DETERMINISTIC으로 선언하면 옵티마이저가 잘못된 실행 계획을 선택하여 예기치 않은 결과가 발생할 수 있습니다. DETERMINISTIC 루틴을 NON DETERMINISTIC으로 선언하면 성능이 저하될 수 있습니다.

 

동일한 입력 매개 변수에 대해 항상 동일한 결과를 생성하는 경우, DETERMINISTIC로 간주되고 그렇지 않으면 NOT DETERMINISTIC으로 간주됩니다.

 

MySQL의 Stored procedure나 Function이 NOT DETERMINISTIC으로 정의되면 Mysql은 이 Stored routine의 결과값이 시시각각 달라진다고 가정하고, 비교가 실행되는 레코드마다 이 Stored routine을 매번 새로 호출해서 비교를 실행하게 됩니다.

즉, 함수 호출의 결과값이 Cache되지 않고, 비교되는 레코드 건수만큼 함수 호출을 발생하는 것입니다.

 

테스트 수행

- 테이블생성

- 테이블 데이터 생성(1백만건), 코드테이블 생성(함수를 수행해서 코드값을 return 할 수 있도록, 1000건)

- 코드별 균등하게 데이터 생성(case1: 코드 별로 1,000건 정도, case2: 코드종류 1건만)

- 펑션 생성(determistic, non-determistic)

- 수행 결과 비교

- 수행시간, profiling, 자원사용량?

 

코드테이블 생성 및 데이터 생성

-- 코드테이블 생성
create table code_tab(code int, code_name varchar(20));
alter table code_tab add constraint pk_code_tab primary key (code);

-- 코드데이터 생성
insert into code_tab(code, code_name)
WITH RECURSIVE cre_code(code, code_name) AS
(
 select 1 code, cast('code-1' as char(50))
 union all
 select code + 1, concat('code-', code) from cre_code where code < 1000
)
select * From cre_code;

transaction 테이블 생성 및 데이터 생성

-- transaction 테이블 생성
create table transaction_tab(tid int, tid_name varchar(20), code int);
alter table transaction_tab add constraint pk_transaction_tab primary key (tid);

-- transaction 데이터 생성
INSERT INTO TRANSACTION_TAB(TID, TID_NAME, CODE)
WITH RECURSIVE cre_tid(tid, tid_name, code) AS
(
 SELECT 1 tid, cast('transaction-1' AS CHAR(50)), 999
 UNION ALL
 SELECt /*+ SET_VAR(cte_max_recursion_depth = 2M) */ tid + 1, CONCAT('transaction-', tid), 999 FROM cre_tid where tid < 1000000
)
SELECT * FROM cre_tid;

 

stored function 생성 - DETERMISTIC, NON-DETERMISTIC

-- 코드값을 return 하는 stored function
DELIMITER $$
CREATE FUNCTION getcodename(v_code varchar(20)) RETURNS VARCHAR(20)
  DETERMINISTIC
BEGIN
declare v_codename varchar(20);
  select code_name into v_codename from code_tab where code = v_code ;
  return v_codename;
END $$
DELIMITER ;

수행시간 검증 

- Stored Function 호출, Scalar Subquery, Join 비교

-- function 호출
explain analyze
select *, getcodename(code) from transaction_tab 
where tid > 10000 and tid < 21000;
--------------------------------------------------------------------------------------
-> Filter: ((a.tid > 10000) and (a.tid < 21000))  (cost=101289.41 rows=110728) (actual time=4.361..475.477 rows=10999 loops=1)
     -> Table scan on A  (cost=101289.41 rows=996748) (actual time=0.022..422.806 rows=1000000 loops=1)
  ==> 수행시간 : 4.891 sec 
     
-- scalar subquery
explain analyze
select *, (select code_name from code_tab where code = A.code) from transaction_tab A 
where tid > 10000 and tid < 21000;
--------------------------------------------------------------------------------------
3	364	16:43:37	explain analyze
 select A.*, (select B.code_name from code_tab B where B.code = A.code) from transaction_tab A 
 where A.tid > 10000 and A.tid < 21000	1 row(s) returned	5.078 sec / 0.000 sec
 ==> 수행시간 : 5.078 sec 
 
-- join query
explain analyze
select *, B.code_name from transaction_tab A, code_tab B 
where A.tid > 10000 and A.tid < 21000
and   A.code = B.code;
--------------------------------------------------------------------------------------
-> Inner hash join (a.`code` = b.`code`)  (cost=1227191.04 rows=123006) (actual time=5.196..444.114 rows=10999 loops=1)
     -> Filter: ((a.tid > 10000) and (a.tid < 21000))  (cost=120.92 rows=11073) (actual time=4.628..442.106 rows=10999 loops=1)
        ...
==> 수행시간 : 0.469 sec

DETERMISTIC Stored Function 호출과 Scalar Subquery 그리고 JOIN 쿼리 비교 시

DETERMISTIC Stored Function 호출과 Scalar Subquery의 소요시간과 실행계획은 동일하였고 미세하게 Strored Function 수행 시간이 빨랐다. 그러나, JOIN 쿼리가 상대적으로 더 좋은 수행 속도를 보여주었다.(HASH JOIN을 수행하기 때문)

JOIN Query를 사용할 수 있으면 최대한 JOIN 쿼리를 사용하도록 한다.

 

code_table의 code 컬럼에 인덱스 생성 시(또는 Primary key 지정 시) 3개의 수행속도는 거의 비슷했다.

 

데이터와 테스트 방법에 따라 결과가 상이할 수 있으니 실제 적용 시에는 더 많은 Case로 테스트 해보는 것이 좋겠다.

 

NON-DETERMISTIC Function은 대부분의 경우 성능이 떨어지기 때문에 테스트하지 않는다. 

 

 

'MySQL' 카테고리의 다른 글

MySQL Lock 발생  (0) 2023.09.22
MySQL - Delimiter  (0) 2023.04.10
MySQL 8.0 connect by  (0) 2023.03.21
GCP-Google Cloud Platform-프로젝트 구축  (0) 2022.03.04
mysql /*! */ 의미  (0) 2022.03.02

MySQL 버전 : 8.0.32

 

MySQL recursive SQL 에 대해 설명

재귀 공통 테이블 식은 자체 이름을 참조하는 하위 쿼리가 있는 식입니다. 예를 들어 아래와 같은 형식입니다.

WITH RECURSIVE cte (n) AS
(
  SELECT 1
  UNION ALL
  SELECT n + 1 FROM cte WHERE n < 5
)
SELECT * FROM cte;

재귀 CTE(Common Table Expressions)의 구조는 다음과 같습니다.

  • WITH 절의 CTE가 자신을 참조하는 경우 WITH 절은 WITH RECURSIVE로 시작해야 합니다.
  • 재귀 CTE 하위 쿼리에는 UNION ALL 또는 UNION [DISTINCT]로 구분되는 두 부분이 있습니다.
SELECT ...      -- return initial row set
UNION ALL
SELECT ...      -- return additional row sets

첫 번째 SELECT는 CTE에 대한 초기 행을 생성하며 CTE 이름을 참조하지 않습니다. 두 번째 SELECT는 추가 행을 생성하고 FROM 절에서 CTE 이름을 참조하여 재귀합니다. 두 번째 SELECT가 새 행을 생성하지 않으면 재귀가 종료됩니다. Recursive CTE는 비재귀적 SELECT 부분과 재귀적 SELECT 부분으로 구성됩니다.

 

  • CTE의 컬럼타입은 비재귀적 SELECT 부분의 컬럼타입이며 모두 null을 허용합니다.
  • 비재귀 부분과 재귀 부분이 UNION [DISTINCT]로 구분되면 중복 행이 제거됩니다. 이는 무한 루프를 피하기 위해 전이적 폐쇄를 수행하는 쿼리에 유용합니다.
  • 재귀 부분의 각 반복은 이전 반복에서 생성된 행에 대해서만 작동합니다.

다음 쿼리는 재귀적 CTE 하위 쿼리의 초기 행 집합을 생성하는 비재귀적 부분입니다.

SELECT 1

 

CTE 하위의 아래 쿼리는 재귀 부분입니다.

SELECT n + 1 FROM cte WHERE n < 5

해당 SELECT는 이전 행 집합의 n 값보다 하나 더 큰 새 값을 가진 행을 생성합니다. 첫 번째 반복은 초기 행 세트(1)에 대해서 작동하고 1+1=2를 생성합니다. 두 번째 반복은 첫 번째 반복의 행 집합(2)에서 작동하고 2+1=3을 생성합니다. n이 5보다 작지 않을 때까지 계속됩니다.

CTE의 재귀 부분이 비재귀 부분보다 열에 대해 더 길은 값을 생성하는 경우 데이터 잘림을 방지하기 위해 비재귀 부분의 열을 확장해야 할 수 있습니다.

WITH RECURSIVE cte AS
(
  SELECT 1 AS n, 'abc' AS str
  UNION ALL
  SELECT n + 1, CONCAT(str, str) FROM cte WHERE n < 3
)
SELECT * FROM cte;

nonstrict SQL 모드에서는 위의 SQL문은 다음을 출력합니다.

비재귀적 SELECT가 열의 길이를 결정하기 때문에 str 열 값은 모두 'abc'입니다. 결과적으로 재귀 SELECT에 의해 생성된 더 길은 str 값이 잘립니다.

+------+------+
| n    | str  |
+------+------+
|    1 | abc  |
|    2 | abc  |
|    3 | abc  |
+------+------+

 

strict SQL모드에서는 이 명령문은 오류를 발생합니다.

ERROR 1406 (22001): Data too long for column 'str' at row 1

이 문제를 해결하려면 명령문이 잘림이나 오류를 생성하지 않도록 비재귀 SELECT에서 CAST()를 사용하여 str 열의 길이를 크게 만들어야 합니다.

WITH RECURSIVE cte AS
(
  SELECT 1 AS n, CAST('abc' AS CHAR(20)) AS str
  UNION ALL
  SELECT n + 1, CONCAT(str, str) FROM cte WHERE n < 3
)
SELECT * FROM cte;

 

이제 명령문은 잘림 없이 다음 결과를 생성합니다.

+------+--------------+
| n    | str          |
+------+--------------+
|    1 | abc          |
|    2 | abcabc       |
|    3 | abcabcabcabc |
+------+--------------+

일부 구문 제약 조건은 재귀 CTE 하위 쿼리 내에서 적용됩니다.

재귀 SELECT 부분은 다음 구문을 포함하지 않아야 합니다.

  • Aggregate functions such as SUM()
  • Window functions
  • GROUP BY
  • ORDER BY
  • DISTINCT

이러한 제약 조건은 재귀 CTE의 비재귀 SELECT 부분에는 적용되지 않습니다.

재귀 SELECT 부분은 sub 쿼리가 아닌 FROM 절에서만 한 번만 CTE를 참조해야 합니다. CTE 이외의 테이블을 참조하고 CTE와 조인할 수 있습니다.

 

Limiting Common Table Expression Recursion

재귀 CTE의 경우 재귀 SELECT 부분에 재귀를 종료하는 조건이 포함되어야 합니다. 런어웨이 재귀 CTE를 방지하기 위한 개발 기법으로 실행 시간을 제한하여 강제 종료할 수 있습니다.

  • cte_max_recursion_depth 시스템 변수는 CTE의 재귀 수준에 대한 제한을 적용합니다. 이 변수 ​​값보다 더 많은 수준을 반복하는 모든 CTE의 실행을 종료합니다.
  • max_execution_time 시스템 변수는 현재 세션 내에서 실행되는 SELECT 문에 대한 실행 제한 시간을 적용합니다.
  • MAX_EXECUTION_TIME 옵티마이저 힌트는 나타나는 SELECT 문에 대해 쿼리당 실행 제한 시간을 적용합니다.

재귀 CTE가 재귀 실행 종료 조건 없이 실수로 작성되었다고 가정합니다.

 

WITH RECURSIVE cte (n) AS
(
  SELECT 1
  UNION ALL
  SELECT n + 1 FROM cte
)
SELECT * FROM cte;

-- cte_max_recursion_depth=1000 설정 시 오류 발생 -- 
ERROR 3636 (HY000): Recursive query aborted after 1001 iterations. 
Try increasing @@cte_max_recursion_depth to a larger value.

기본적으로 cte_max_recursion_depth 값은 1000이므로 CTE가 1000 수준을 초과하여 재귀하면 종료됩니다. 

 

MySQL 8.0.19부터 재귀 쿼리 내에서 LIMIT를 사용하여 가장 바깥쪽 SELECT에 반환할 최대 행 수를 지정할 수도 있습니다. 예를 들면 다음과 같습니다.

WITH RECURSIVE cte (n) AS
(
  SELECT 1
  UNION ALL
  SELECT n + 1 FROM cte LIMIT 900
)
SELECT * FROM cte;

 

Recursive Common Table Expression Examples

Hierarchical Data Traversal

재귀 공통 테이블 표현식은 계층 구조에 유용합니다. 아래의 예시를 살펴봅니다. 최상위 직원(CEO)의 관리자 ID는 NULL(관리자 없음)입니다.

CREATE TABLE employees (
  id         INT PRIMARY KEY NOT NULL,
  name       VARCHAR(100) NOT NULL,
  manager_id INT NULL,
  INDEX (manager_id),
FOREIGN KEY (manager_id) REFERENCES employees (id)
);

INSERT INTO employees VALUES
(333, "Yasmina", NULL),  # Yasmina is the CEO (manager_id is NULL)
(198, "John", 333),      # John has ID 198 and reports to 333 (Yasmina)
(692, "Tarek", 333),
(29, "Pedro", 198),
(4610, "Sarah", 29),
(72, "Pierre", 29),
(123, "Adil", 692);

각 직원의 관리 체인(즉, CEO에서 직원으로의 경로)이 포함된 조직도를 생성하려면 재귀 CTE를 사용합니다.

아래 예제는 사번ID의 체인경로를 조회한다. 이름으로 하고 싶은 경우에는 id 대신 name 을 사용하면 된다.

WITH RECURSIVE employee_paths (id, name, path) AS
(
  SELECT id, name, CAST(id AS CHAR(200))
    FROM employees
    WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, CONCAT(ep.path, '->', e.id)
    FROM employee_paths AS ep JOIN employees AS e
      ON ep.id = e.manager_id
)
SELECT * FROM employee_paths ORDER BY path;
 
+------+---------+--------------------+
| id   | name    | path               |
+------+---------+--------------------+
|  333 | Yasmina | 333                |
|  198 | John    | 333->198           |
|   29 | Pedro   | 333->198->29       |
| 4610 | Sarah   | 333->198->29->4610 |
|   72 | Pierre  | 333->198->29->72   |
|  692 | Tarek   | 333->692           |
|  123 | Adil    | 333->692->123      |
+------+---------+--------------------+

 

MySQL recursive SQL로 데이터 생성하기

아래와 같은 WITH RECURSIVE SQL로 임시 데이터를 생성할 수 있습니다.

임시 데이터를 입력할 테이블을 생성합니다.

CREATE TABLE cte_populated
(
  id INT NOT NULL PRIMARY KEY,
  val varchar(20)
) ENGINE = InnoDB;

 

INSERT 문장과 WITH RECURSIVE 문장으로 임시 데이터를 입력합니다. 아래 예제는 1부터 2000000까지 2000000건의 데이터를 입력하는 문장입니다. 이를 응용하여 다양하게 데이터를 생성할 수 있습니다.

INSERT cte_populated(id, val)
WITH  RECURSIVE int_seq AS (
SELECT 1 AS id, cast('test1' as char(200)) as pass
UNION ALL
SELECT id + 1, concat('test',id)
FROM int_seq
WHERE id  < 2000000)
SELECT /*+ SET_VAR(cte_max_recursion_depth = 3M) */ id,  pass as id FROM int_seq;

/*+ SET_VAR(cte_max_recursion_depth = 3M) */ 힌트는 서버에 cte_max_recursion_depth 파라미터의 기본값이 1000으로 설정되어 있어 depth가 1000 이상인 경우 오류가 발생합니다. 이럴 경우 1000 건 이상 입력 시 힌트를 사용하여 원하는 만큼의 데이터를 생성할 수 있습니다. 3M에서 'M'은 백만입니다.

 

 

AWS Lambda를 사용하여 Amazon RDS 중지 및 시작

 

일반적인 개발 환경에서 개발 및 테스트 데이터베이스는 일과시간(9:00 ~ 18:00) 동안 사용되며 사용하지 않을 때는 유휴 상태입니다. 그러나 이 유휴 시간 동안에도 컴퓨팅 및 스토리지에 대한 비용이 청구됩니다.

 

비용을 줄이기 위해 Amazon RDS 인스턴스를 일시적으로 중지 할 수 있습니다 . 인스턴스가 중지된 동안에는 스토리지 및 백업에 대한 요금만 부과되며 DB 인스턴스 시간에 대한 요금은 부과되지 않습니다. 중지된 인스턴스는 7일 후에 자동으로 시작됩니다.

본 문서는 컴퓨팅 비용을 절감하기 위해 유휴 데이터베이스를 중지 및 시작하도록 AWS Lambda 및 Amazon EventBridge를 사용하는 방법을 설명합니다.

 

솔루션 개요

AWS Lambda는 서버를 관리하지 않고도 코드를 실행할 수 있는 컴퓨팅 서비스입니다. 

Amazon EventBridge는 이벤트를 생성하고 이에 대한 응답으로 특정 작업을 할당할 수 있는 간단한 규칙을 사용합니다.

Amazon RDS 인스턴스에 대한 컴퓨팅 비용은 시간 단위로 청구됩니다. 

오랜 시간 동안 유휴 상태로 유지되는 개발 또는 테스트 환경에서 프로비저닝된 데이터베이스 인스턴스는 Lambda 함수 및 EventBridge 규칙을 사용하여 야간에 자동으로 중지되고 업무 시간 전에 시작될 수 있습니다.

 

다음 그림은 Lambda함수와 EvnentBridge를 이용한 RDS 자동 중지/기동 아키텍처입니다.

전체적인 구성 단계

  1. 리소스를 프로비저닝
    • RDS 인스턴스용 태그(태그를 이용한 시작/중지 Lambda)
    • Lambda에 대한 AWS Identity and Access Management(IAM) 정책 및 역할
    • 데이터베이스를 중지하고 시작하는 두 개의 Lambda 함수
  2. Lambda 함수를 트리거하는 Amazon EventBridge 규칙을 생성

 

전제 조건

다음이 필요합니다.

  • Amazon RDS에 대한 관리자 액세스 권한이 있는 AWS 계정
  • RDS 인스턴스

 

리소스 프로비저닝

태그, Lambda에 대한 IAM 정책 및 역할, 데이터베이스를 중지하거나 시작하도록 예약하는 Lambda 함수를 생성하는 방법을 설명합니다.

태그 만들기

DB 인스턴스 생성 시 또는 인스턴스 생성 후 인스턴스에 태그를 할당할 수 있습니다. 아래와 같이 태그를 할당합니다.

  1. Amazon RDS 콘솔에서 태그를 추가할 데이터베이스와 해당 데이터베이스 내의 인스턴스를 선택
  2. 인스턴스 세부 정보 아래의 태그 탭에서 태그 추가를 선택
  3. 태그 키 DEV-TEST에 를 입력
  4.  에 Auto-Shutdown를 입력
  5. 추가를 선택

데이터베이스를 중지하는 Lambda 함수 생성

데이터베이스를 중지하고 시작하기 위해 호출할 수 있는 두 개의 Lambda 함수를 생성합니다. 

먼저 중지 기능을 하는 Lambda 함수를 생성합니다.

  1. Lambda 콘솔의 탐색 창에서 함수를 선택합니다.
  2. 함수 만들기를 선택합니다 .
  3. 함수 이름 에 stoprds를 입력합니다 .
  4. 런타임 에서 Python 3.10을 선택합니다 .
  5. 실행 역할 에서 기존 역할 사용을 선택합니다 .
  6. 기존 역할 에서 생성한 역할을 선택합니다( rdsLambda).
  7. 함수 만들기를 선택합니다 .
  8. 함수 세부 정보 페이지에서 함수 코드로 이동합니다.
  9. 샘플 코드를 삭제하고 다음을 입력합니다.
    # this Code will help to schedule stop the RDS databasrs using Lambda
    # Yesh 
    # Version -- 2.0
    
    import boto3
    import os
    import sys
    import time
    from datetime import datetime, timezone
    from time import gmtime, strftime
    
    def shut_rds_all():
        region=os.environ['REGION']
        key=os.environ['KEY']
        value=os.environ['VALUE']
    
        
        client = boto3.client('rds', region_name=region)
        response = client.describe_db_instances()
        v_readReplica=[]
        for i in response['DBInstances']:
            readReplica=i['ReadReplicaDBInstanceIdentifiers']
            v_readReplica.extend(readReplica)
        
        for i in response['DBInstances']:
    #The if condition below filters aurora clusters from single instance databases as boto3 commands defer to stop the aurora clusters.
            if i['Engine'] not in ['aurora-mysql','aurora-postgresql']:
    #The if condition below filters Read replicas.
                if i['DBInstanceIdentifier'] not in v_readReplica and len(i['ReadReplicaDBInstanceIdentifiers']) == 0:
                    arn=i['DBInstanceArn']
                    resp2=client.list_tags_for_resource(ResourceName=arn)
    #check if the RDS instance is part of the Auto-Shutdown group.
                    if 0==len(resp2['TagList']):
                        print('DB Instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
                    else:
                        for tag in resp2['TagList']:
    #If the tags match, then stop the instances by validating the current status.
                            if tag['Key']==key and tag['Value']==value:
                                if i['DBInstanceStatus'] == 'available':
                                    client.stop_db_instance(DBInstanceIdentifier = i['DBInstanceIdentifier'])
                                    print('stopping DB instance {0}'.format(i['DBInstanceIdentifier']))
                                elif i['DBInstanceStatus'] == 'stopped':
                                    print('DB Instance {0} is already stopped'.format(i['DBInstanceIdentifier']))
                                elif i['DBInstanceStatus']=='starting':
                                    print('DB Instance {0} is in starting state. Please stop the cluster after starting is complete'.format(i['DBInstanceIdentifier']))
                                elif i['DBInstanceStatus']=='stopping':
                                    print('DB Instance {0} is already in stopping state.'.format(i['DBInstanceIdentifier']))
                            elif tag['Key']!=key and tag['Value']!=value:
                                print('DB instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
                            elif len(tag['Key']) == 0 or len(tag['Value']) == 0:
                                print('DB Instance {0} is not part of auroShutdown'.format(i['DBInstanceIdentifier']))
                elif i['DBInstanceIdentifier'] in v_readReplica:
                    print('DB Instance {0} is a Read Replica. Cannot shutdown a Read Replica instance'.format(i['DBInstanceIdentifier']))
                else:
                    print('DB Instance {0} has a read replica. Cannot shutdown a database with Read Replica'.format(i['DBInstanceIdentifier']))
    
        response=client.describe_db_clusters()
        for i in response['DBClusters']:
            cluarn=i['DBClusterArn']
            resp2=client.list_tags_for_resource(ResourceName=cluarn)
            if 0==len(resp2['TagList']):
                print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
            else:
                for tag in resp2['TagList']:
                    if tag['Key']==key and tag['Value']==value:
                        if i['Status'] == 'available':
                            client.stop_db_cluster(DBClusterIdentifier=i['DBClusterIdentifier'])
                            print('stopping DB cluster {0}'.format(i['DBClusterIdentifier']))
                        elif i['Status'] == 'stopped':
                            print('DB Cluster {0} is already stopped'.format(i['DBClusterIdentifier']))
                        elif i['Status']=='starting':
                            print('DB Cluster {0} is in starting state. Please stop the cluster after starting is complete'.format(i['DBClusterIdentifier']))
                        elif i['Status']=='stopping':
                            print('DB Cluster {0} is already in stopping state.'.format(i['DBClusterIdentifier']))
                    elif tag['Key'] != key and tag['Value'] != value:
                        print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
                    else:
                        print('DB Instance {0} is not part of auroShutdown'.format(i['DBClusterIdentifier']))
    
    def lambda_handler(event, context):
        shut_rds_all()
  10. 저장을 선택합니다 .
    위의 람다 함수는 환경 변수로 전달되는 3개의 매개변수(REGION, KEY, VALUE)가 필요합니다. REGION은 RDS 인스턴스가 현재 실행 중인 위치이고 KEY 및 VALUE는 이전 단계에서 자동 종료가 필요한 인스턴스에 대해 연결한 태그입니다. RDS 인스턴스에 연결한 값은 환경 변수와 정확히 일치해야 합니다. 아래 단계에 따라 입력하십시오.
  11. '구성' 탭으로 이동하여 '환경 변수'를 선택합니다. EDIT를 클릭하고 아래와 같이 환경 변수를 추가합니다.
  12. 테스트를 선택하여 기능을 테스트합니다.

    테스트 이벤트 구성 페이지가 열립니다.
  13. 이벤트 이름 에 를 입력합니다 stop.
  14. 생성을 선택합니다 .
  15. 테스트를 다시 선택하여 기능을 테스트합니다.

테스트는 함수에 지정된 태그로 데이터베이스를 중지합니다. 함수 세부 정보 페이지에서 함수가 성공한 것을 확인할 수 있습니다.

데이터베이스를 시작하는 Lambda 함수 생성

동일한 단계를 반복하여 라는 시작 함수를 만듭니다 rdsstart. 다음 코드를 사용하십시오.

# this Code will help to schedule start the RDS databasrs using Lambda
# Yesh 
# Version -- 2.0

import boto3
import os
import sys
import time
from datetime import datetime, timezone
from time import gmtime, strftime

def start_rds_all():
    region=os.environ['REGION']
    key=os.environ['KEY']
    value=os.environ['VALUE']
    client = boto3.client('rds', region_name=region)
    response = client.describe_db_instances()

    v_readReplica=[]
    for i in response['DBInstances']:
        readReplica=i['ReadReplicaDBInstanceIdentifiers']
        v_readReplica.extend(readReplica)
    
    for i in response['DBInstances']:
#The if condition below filters aurora clusters from single instance databases as boto3 commands defer to start the aurora clusters.
        if i['Engine'] not in ['aurora-mysql','aurora-postgresql']:
#The if condition below filters Read replicas.
            if i['DBInstanceIdentifier'] not in v_readReplica and len(i['ReadReplicaDBInstanceIdentifiers']) == 0:
                arn=i['DBInstanceArn']
                resp2=client.list_tags_for_resource(ResourceName=arn)
#check if the RDS instance is part of the Auto-Shutdown group.
                if 0==len(resp2['TagList']):
                    print('DB Instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
                else:
                    for tag in resp2['TagList']:
                        if tag['Key']==key and tag['Value']==value:
                            if i['DBInstanceStatus'] == 'available':
                                print('{0} DB instance is already available'.format(i['DBInstanceIdentifier']))
                            elif i['DBInstanceStatus'] == 'stopped':
                                client.start_db_instance(DBInstanceIdentifier = i['DBInstanceIdentifier'])
                                print('Started DB Instance {0}'.format(i['DBInstanceIdentifier']))
                            elif i['DBInstanceStatus']=='starting':
                                print('DB Instance {0} is already in starting state'.format(i['DBInstanceIdentifier']))
                            elif i['DBInstanceStatus']=='stopping':
                                print('DB Instance {0} is in stopping state. Please wait before starting'.format(i['DBInstanceIdentifier']))
                        elif tag['Key']!=key and tag['Value']!=value:
                            print('DB instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
                        elif len(tag['Key']) == 0 or len(tag['Value']) == 0:
                            print('DB Instance {0} is not part of autoShutdown'.format(i['DBInstanceIdentifier']))
            elif i['DBInstanceIdentifier'] in v_readReplica:
                print('DB Instance {0} is a Read Replica.'.format(i['DBInstanceIdentifier']))
            else:
                print('DB Instance {0} has a read replica. Cannot shutdown & start a database with Read Replica'.format(i['DBInstanceIdentifier']))

    response=client.describe_db_clusters()
    for i in response['DBClusters']:
        cluarn=i['DBClusterArn']
        resp2=client.list_tags_for_resource(ResourceName=cluarn)
        if 0==len(resp2['TagList']):
            print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
        else:
            for tag in resp2['TagList']:
                if tag['Key']==key and tag['Value']==value:
                    if i['Status'] == 'available':
                        print('{0} DB Cluster is already available'.format(i['DBClusterIdentifier']))
                    elif i['Status'] == 'stopped':
                        client.start_db_cluster(DBClusterIdentifier=i['DBClusterIdentifier'])
                        print('Started Cluster {0}'.format(i['DBClusterIdentifier']))
                    elif i['Status']=='starting':
                        print('cluster {0} is already in starting state.'.format(i['DBClusterIdentifier']))
                    elif i['Status']=='stopping':
                        print('cluster {0} is in stopping state. Please wait before starting'.format(i['DBClusterIdentifier']))
                elif tag['Key'] != key and tag['Value'] != value:
                    print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
                else:
                    print('DB Instance {0} is not part of autoShutdown'.format(i['DBClusterIdentifier']))

def lambda_handler(event, context):
    start_rds_all()

위의 람다 함수는 환경 변수로 전달되는 3개의 매개변수(REGION, KEY, VALUE)가 필요합니다. REGION은 RDS 인스턴스가 현재 실행 중인 위치이고 KEY 및 VALUE는 이전 단계에서 자동 종료가 필요한 인스턴스에 대해 연결한 태그입니다. RDS 인스턴스에 연결한 값은 환경 변수와 정확히 일치해야 합니다. 아래 단계에 따라 입력하십시오.

'구성' 탭으로 이동하여 '환경 변수'를 선택합니다. EDIT를 클릭하고 아래와 같이 환경 변수를 추가합니다.

함수 테스트는 다음 출력을 제공해야 합니다.

두 기능을 모두 생성하고 테스트한 후 필요에 따라 이러한 기능을 트리거하는 EventBridge 규칙을 생성할 수 있습니다.

Amazon EventBridge 규칙 생성

Amazon EventBridge 규칙은 태그가 지정된 데이터베이스를 중지하거나 시작하기 위해 생성한 함수를 트리거합니다. 이 게시물에서는 일정에 따라 트리거하도록 구성합니다.

  1. EventBridge 콘솔의 탐색 창에 있는 Events 에서 Rules 를 선택합니다 . 규칙 만들기 섹션 에서 아래와 같이 규칙의 이름을 정의합니다.
  2. Define Pattern 섹션 에서 Schedule을 선택하고 Cron 표현식을 클릭한 후 0 23 ? * MON-FRI *아래와 같이 Enter를 누르십시오(이 cron 표현식은 월요일부터 금요일까지 GMT 오후 11시에 데이터베이스를 중지합니다).
  3. 이벤트 버스 선택 섹션 에서 AWS 기본 이벤트 버스를 선택하고 선택한 이벤트 버스에서 규칙을 활성화합니다(기본적으로 활성화됨).
  4. 대상 선택 섹션 에서 첫 번째 드롭다운 상자에서 Lambda 함수를 선택합니다 .
  5. Function 에서 생성한 정지 함수( stoprds)를 선택합니다.
  6. 입력한 세부 정보를 검토하고 페이지 하단에서 만들기를 선택하여 규칙을 만듭니다.
    EventBridge 규칙은 이제 stoprds예약된 시간에 Lambda 함수를 트리거합니다.
  7. rdsstart이 단계를 반복하여 원하는 예약 시간에 Lambda 함수를 트리거하는 규칙을 생성합니다 .

요약

이 게시물에서는 사용하지 않을 때 개발 및 테스트 환경에서 RDS 데이터베이스를 중지하고 시작하도록 Lambda 함수를 예약하는 방법을 보여주었습니다. Lambda를 사용하여 RDS DB 인스턴스의 시작 및 종료를 자동화함으로써 조직은 컴퓨팅 비용을 더욱 절감하고 지속적으로 실행할 필요가 없는 데이터베이스 환경의 관리를 간소화할 수 있습니다.

이 솔루션을 시도하고 AWS Lambda  및  Amazon Relational Database Service 사용의 모든 이점을 활용하는 것이 좋습니다 . AWS Systems Manager를 사용하여 Amazon RDS 중지 및 시작을 수행할 수도 있습니다. 블로그 게시물 " AWS Systems Manager를 사용하여 Amazon RDS 중지 및 시작 예약 "을 확인하십시오.

질문이나 요청 사항이 있으면 언제든지 댓글로 문의해 주세요.

 

 

AUTO_INCREMENT 사용

속성 AUTO_INCREMENT은 행에 대한 고유한 식별자를 생성하는 데 사용할 수 있습니다.

CREATE TABLE animals (
     id MEDIUMINT NOT NULL AUTO_INCREMENT,
     name CHAR(30) NOT NULL,
     PRIMARY KEY (id)
);

INSERT INTO animals (name) VALUES
    ('dog'),('cat'),('penguin'),
    ('lax'),('whale'),('ostrich');

SELECT * FROM animals;

다음을 반환합니다.

+----+---------+
| id | name    |
+----+---------+
|  1 | dog     |
|  2 | cat     |
|  3 | penguin |
|  4 | lax     |
|  5 | whale   |
|  6 | ostrich |
+----+---------+

AUTO_INCREMENT 열에 지정된 값이 없으므로 MySQL이 자동으로 시퀀스 번호를 할당합니다.

NO_AUTO_VALUE_ON_ZERO SQL 모드가 활성화되지 않은 경우 열에 명시적으로 0을 할당하여 시퀀스 번호를 생성할 수 있습니다 . 예를 들어:

INSERT INTO animals (id,name) VALUES(0,'groundhog');

컬럼이 NOT NULL로 선언된 경우에도 NULL을 컬럼에 할당하여 시퀀스 번호를 생성할 수도 있습니다.

예를 들어:

INSERT INTO animals (id,name) VALUES(NULL,'squirrel');

AUTO_INCREMENT 컬럼에 다른 값을 삽입하면 컬럼이 해당 값으로 설정되고 다음 자동 생성 값은 가장 큰 컬럼값부터 나오도록 시퀀스가 ​​설정됩니다. 예를 들어:

INSERT INTO animals (id,name) VALUES(100,'rabbit');
INSERT INTO animals (id,name) VALUES(NULL,'mouse');
SELECT * FROM animals;
+-----+-----------+
| id  | name      |
+-----+-----------+
|   1 | dog       |
|   2 | cat       |
|   3 | penguin   |
|   4 | lax       |
|   5 | whale     |
|   6 | ostrich   |
|   7 | groundhog |
|   8 | squirrel  |
| 100 | rabbit    |
| 101 | mouse     |
+-----+-----------+

기존 AUTO_INCREMENT 컬럼값을 업데이트하면 AUTO_INCREMENT 시퀀스도 재설정됩니다.

AUTO_INCREMENT 초기값을 1이 아닌 값으로 시작하려면 다음과 같이 CREATE TABLE또는 ALTER TABLE로 해당 값을 설정합니다.

mysql> ALTER TABLE tbl AUTO_INCREMENT = 100;
 

 

MyISAM 노트

MyISAM 테이블의 경우 복합 인덱스의 첫번째 컬럼이 아닌 보조컬럼으로 지정할 수 있습니다 . 이 경우 AUTO_INCREMENT 컬럼에 대해 생성된 값은 MAX(auto_increment_column) + 1 WHERE prefix=given-prefix 으로 계산됩니다 . 데이터를 정렬된 그룹에 넣을 때 유용합니다. 

 

CREATE TABLE animals (
    grp ENUM('fish','mammal','bird') NOT NULL,
    id MEDIUMINT NOT NULL AUTO_INCREMENT,
    name CHAR(30) NOT NULL,
    PRIMARY KEY (grp,id)
) ENGINE=MyISAM;

INSERT INTO animals (grp,name) VALUES
    ('mammal','dog'),('mammal','cat'),
    ('bird','penguin'),('fish','lax'),('mammal','whale'),
    ('bird','ostrich');

SELECT * FROM animals ORDER BY grp,id;

아래와 같이 grp컬럼별로 auto_increment 값이 리턴됩니다.

+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| fish   |  1 | lax     |
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| mammal |  3 | whale   |
| bird   |  1 | penguin |
| bird   |  2 | ostrich |
+--------+----+---------+

MyISAM 테이블의 경우( AUTO_INCREMENT 컬럼이 복합 인덱스의 일부인 경우) 그룹에서 가장 큰 값의 행을 삭제하면 AUTO_INCREMENT 값이 재사용됩니다. 즉 grp='mamman' 이고 id=3인 데이터를 삭제한 후 grp='mammal' 데이터를 입력하면 id는 다시 3을 재사용합니다.

 

 

AUTO_INCREMENT 컬럼이 선두컬럼인 인덱스가 있는 경우 MySQL은 단일 시퀀스 값을 생성합니다. 예를 들어, animals테이블에 인덱스 PRIMARY KEY (grp, id) 및 INDEX (id)가 있는 경우 MySQL은 grp별 시퀀스가 ​​아니라 단일 시퀀스를 생성합니다.

 
+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| bird   |  3 | penguin |
| fish   |  4 | lax     |
| mammal |  5 | whale   |
| bird   |  6 | ostrich |
+--------+----+---------+

AWS Cloud9이란 코드 작성, 실행 및 디버깅을 위한 클라우드 IDE입니다.
AWS Cloud9을 사용하면 브라우저만으로 코드를 작성, 실행 및 디버그할 수 있습니다. AWS Cloud9을 사용하면 코드 편집기, 통합 디버거 및 미리 구성된 AWS CLI가 있는 내장 터미널을 사용할 수 있습니다. 

 

작동 방식

신규 Amazon EC2 인스턴스에 AWS Cloud9 환경을 생성하거나 SSH를 통해 각자의 Linux 서버에 연결합니다. AWS Cloud9 환경을 생성하면 브라우저 내에서 코드 편집기, 통합 디버거 및 사전 구성된 AWS CLI가 있는 터미널에 액세스할 수 있습니다.


AWS Cloud9 대시보드를 사용하면 각각 특정 프로젝트에 대한 사용자 지정 도구, 런타임 및 파일을 포함하는 다양한 AWS Cloud9 환경을 생성하고 전환할 수 있습니다.

*** CloudFormation 으로 구성한다.

Benefits and features

 

Code with just a browser

AWS Cloud9을 사용하면 데스크톱 IDE를 설치하거나 유지 관리할 필요 없이 브라우저만으로 애플리케이션을 작성, 실행 및 디버깅할 수 있습니다.

 

Code together in real time

AWS Cloud9을 사용하면 코드 협업이 쉬워집니다. 몇 번의 클릭만으로 팀과 개발 환경을 공유하고 프로그램을 함께 페어링할 수 있습니다.

 

Start new projects quickly

AWS Cloud9 EC2 환경은 40개 이상의 프로그래밍 언어용 도구와 함께 사전 패키징되어 제공되므로 몇 분 안에 인기 있는 애플리케이션 스택에 대한 코드 작성을 시작할 수 있습니다.

 

Build serverless applications with ease

AWS Cloud9은 서버리스 애플리케이션 개발을 위한 환경을 제공합니다. 리소스를 쉽게 정의하고, 디버그하고, 코드의 로컬 실행과 원격 실행 간에 전환할 수 있습니다.

 

What can I do with AWS Cloud9?

  • Working with code in several programming languages and the AWS Cloud Development Kit (AWS CDK).
  • Working with code in a running Docker container.
  • Using online code repositories.
  • Collaborating with others in real time.
  • Interacting with various database and website technologies.
  • Targeting AWS Lambda, Amazon API Gateway, and AWS Serverless Applications.
  • Taking advantage of other AWS products such as Amazon Lightsail, AWS CodeStar, and AWS CodePipeline.

AWS Cloud9을 사용하면 소프트웨어를 코딩, 빌드, 실행, 테스트, 디버그 및 릴리스할 수 있습니다. 

  • 여러 프로그래밍 언어 및 AWS Cloud Development Kit(AWS CDK)의 코드로 작업
  • 실행 중인 Docker 컨테이너에서 코드 작업.
  • 온라인 코드 저장소 사용.
  • 다른 사람들과 실시간으로 협업
  • 다양한 데이터베이스 및 웹 사이트 기술과 상호 작용
  • AWS Lambda, Amazon API Gateway 및 AWS 서버리스 애플리케이션을 대상
  • Amazon Lightsail, AWS CodeStar 및 AWS CodePipeline과 같은 다른 AWS 제품을 활용

 

시작하기

Rancher Desktop

 

데스크톱의 컨테이너 관리 및 Kubernetes
Mac, Windows 및 Linux용 오픈 소스 데스크톱 애플리케이션입니다. Rancher Desktop은 데스크톱에서 Kubernetes 및 컨테이너 관리를 실행합니다. 실행하려는 Kubernetes 버전을 선택할 수 있습니다. containerd 또는 Moby(dockerd)를 사용하여 컨테이너 이미지를 빌드, 푸시, 풀 및 실행할 수 있습니다. 빌드한 컨테이너 이미지는 레지스트리 없이 Kubernetes에서 즉시 실행할 수 있습니다.

1. 소개하기

MySQL Shell for VS Code는 MySQL 데이터베이스 및 MySQL 데이터베이스 서비스(RDS 등)와 SQL을 실행할 수 있도록 합니다.

 

MySQL Shell for VS Code는 MySQL Shell을 Visual Studio Code 개발 워크플로에 직접 통합합니다.

MySQL Shell Console은 노트북 인터페이스 스타일의 편집기 내에서 MySQL Shell을 제공합니다. GUI 콘솔을 사용하면 AdminAPI, X DevAPI 및 ShellAPI를 포함하여 MySQL Shell의 대부분의 기능을 사용하여 InnoDB 클러스터, InnoDB ClusterSet 및 InnoDB ReplicaSet를 배포 및 관리할 수 있습니다.

DB Notebooks는 데이터베이스와 대화식으로 작업할 수 있는 방법을 제공합니다. 노트북 편집기 내에서 SQL(포함된 결과 포함)에서 JavaScript 또는 TypeScript로 전환하여 데이터를 쿼리, 조작 및 시각화할 수 있습니다. 

다음 표는 MySQL Shell의 기능과 이를 지원하는 편집기를 나타냅니다. 필요한 기능에 따라 MySQL Shell 기능을 사용하면 됩니다.

MySQL 셸 기능 MySQL 셸 콘솔(셸 세션) DB 노트북 SQL 스크립트, TS 스크립트, JS 스크립트
지원되는 언어 SQL, 파이썬, 자바스크립트 SQL(MySQL 및 SQLite), TypeScript, JavaScript SQL( .sql), TypeScript( .ts), JavaScript( .js)
대화형 코드 실행 아니요
배치 코드 실행
지원되는 API AdminAPI, X DevAPI 및 ShellAPI 해당 없음 해당 없음
X 프로토콜 지원
유용 아니요 아니요
API 명령줄 통합 해당 없음 해당 없음 해당 없음
출력 형식 아니요 아니요 아니요
로깅 및 디버그
글로벌 세션 해당 없음 해당 없음

 

2. 시작하기

2.1 Set Up MySQL Shell for VS Code

2.2 MySQL Shell for VS Code Components

2.3 Settings

2.4 Notebook Editors

2.5 Run Embedded SQL

2.1 VS Code용 MySQL 셸 설정

Visual Studio Code 에서 사용하기 위해 VS Code용 MySQL Shell을 설치하는 방법과 시스템에 MySQL Shell이 ​​설치되어 있지 않은 경우 MySQL Shell 인스턴스를 활성화하는 방법을 설명합니다.

MySQL Shell은 별도로 설치할 수 있습니다.

VS Code 확장용 MySQL Shell은 Microsoft Windows, Linux 및 macOS 플랫폼에서 사용할 수 있습니다. 

VS Code 확장 파일용 MySQL Shell 다운로드

VS Code용 MySQL Shell은 Visual Studio Marketplace 에서 설치하거나 다운로드할 수 있습니다 .

 

VS Code용 MySQL 셸 확장 설치

  1. Visual Studio Marketplace 항목 에서 설치를 클릭합니다 .
  2. Visual Studio Code를 열고 보기 메뉴 에서 모양을 선택합니다 . 열린 목록에서 다음을 활성화합니다.
    • 사이드 바 표시
    • 상태 표시줄 표시
    • 활동 표시줄 표시
  3. 활동 표시줄에서 확장 프로그램 아이콘을 클릭하여 측면 표시줄에서 보기를 엽니다.
  4. EXTENSIONS: MARKETPLACE 검색 에서 를 입력합니다 "MySQL Shell for VS Code".
  5. VS Code용 MySQL Shell 항목에서 설치를 클릭합니다 .
  6. 설치를 완료하라는 메시지가 표시되면 Visual Studio Code를 다시 시작합니다.

MySQL을 얻는 방법

MySQL 서버

MySQL 데이터베이스 서비스

MySQL 데이터베이스 서비스 시작하기를 참조하십시오 .

Flyway

Flyway는 오픈 소스 데이터베이스 마이그레이션 도구입니다.

Migrate, Clean, Info, Validate, Undo, Baseline 및 Repair의 7가지 기본 명령을 기반으로 합니다.

마이그레이션은 SQL 또는 Java로 작성될 수 있고, 다양한 데이터베이스를 지원합니다.

 

Why database migrations?

Shiny라는 프로젝트가 있고 Shiny DB라는 데이터베이스에 연결하는 Shiny Soft라는 소프트웨어가 있다고 가정합니다.

대부분의 프로젝트에서는 다음과 같은 과정으로 소프트웨어나 DB를 관리할 것입니다.

개발자의 개발 환경, 단위테스트 환경, 통합테스트 환경, 운영 환경 등으로 구성될 것입니다.

위와 같은 환경에서 software는 버전관리, 형상관리, 배포관리 등이 잘 관리되고 있습니다.

하지만 DB의 변경관리는 상대적으로 잘 관리되고 있지 않습니다.

많은 프로젝트가 수동으로 SQL 스크립트에 의존하여 DB를 관리하고 있습니다.

  • 이 시스템에서 데이터베이스는 어떤 상태입니까?
  • 이 스크립트가 이미 적용되었거나 적용되지 않았습니까?
  • 프로덕션의 빠른 수정이 나중에 테스트에 적용되었습니까?
  • 새 데이터베이스 인스턴스를 어떻게 설정합니까?

 

데이터베이스 마이그레이션 툴(flyway)은 이러한 질문에 대한 대안을 줄 수 있습니다.

  • 처음부터 데이터베이스 다시 만들기
  • 데이터베이스가 어떤 상태인지 항상 명확
  • 현재 버전의 데이터베이스에서 최신 버전으로 마이그레이션

 

How Flyway works

Flyway가 비어있는 데이터베이스와 연결되어 있다고 가정합니다.

flyway는 flyway_schema_history 테이블을 찾으려고 시도하고, 없을 경우 flyway_schema_history 테이블을 생성합니다. 

이 테이블은 데이터베이스의 상태를 추적하는 데 사용됩니다.
그 직후 Flyway는 마이그레이션을 위해 Sql 또는 Java로 작성된 파일(파일 시스템 또는 애플리케이션의 클래스 경로)을 스캔합니다.

마이그레이션은 버전번호를 기준으로 정렬되고 순서대로 적용됩니다.

여기서 마이그레이션이란 표현은 일련의 변경단위로 생각하시면 됩니다. 예를 들면 테이블 2개 생성, 3개 변경 등이 한 묶음인 경우 이를 하나의 마이그레이션으로 보시면 됩니다.

각 마이그레이션이 적용될 때 flyway_schema_history 데이터도 업데이트됩니다.

<flyway_schema_history>

installed_rank version description type script checksum installed_by installed_on execution_time success
1 1 Initial Setup SQL V1__Initial_Setup.sql 1996767037 axel 2016-02-04 22:23:00.0 546 true
2 2 First Changes SQL V2__First_Changes.sql 1279644856 axel 2016-02-06 09:18:00.0 127 true

메타데이터와 초기 상태가 설정되었으므로 이제 최신 버전으로의 마이그레이션을 진행할 수 있습니다.

Flyway는 마이그레이션을 위해 애플리케이션의 파일 시스템 또는 클래스 경로를 다시 한 번 스캔합니다. 마이그레이션은 스flyway_schema_history 의 정보를 기준으로 버전 번호가 현재 버전보다 낮거나 같으면 무시됩니다.

 

보류 마이그레이션은 아직 적용되지 않은 마이그레이션입니다.

마이그레이션은 버전 번호별로 정렬되고 순서대로 실행됩니다.

 

flyway_schema_history 테이블이  업데이트됩니다.

installed_rank version description type script checksum installed_by installed_on execution_time success
1 1 Initial Setup SQL V1__Initial_Setup.sql 1996767037 axel 2016-02-04 22:23:00.0 546 true
2 2 First Changes SQL V2__First_Changes.sql 1279644856 axel 2016-02-06 09:18:00.0 127 true
3 2.1 Refactoring JDBC V2_1__Refactoring   axel 2016-02-10 17:45:05.4 251 true

DDL 또는 DML에 관계없이 데이터베이스를 변경시켜야 할 때마다 현재 버전보다 높은 버전 번호로 새 마이그레이션을 생성하면 됩니다. 다음에 Flyway가 시작되면 이를 찾아서 데이터베이스를 업그레이드합니다.

 

 

First Steps: Command-line

Flyway Command-line 도구를 시작하고 실행하는 방법을 설명합니다. 

사전준비

Flyway 명령줄 도구를 다운로드하고 압축을 풉니다. Community, Teams, Enterprise 버전이 있습니다. 본 문서에서는 Windows용 Community 버전을 다운로드 합니다.

다운로드 : downloading the Flyway Command-line Tool 

Flyway 설정

Flyway를 다운로드하여 압축해제한 디렉토리로 이동합니다.

이동한 디렉토리 하위의 conf 디렉토리에 있는 flyway.conf 파일을 아래와 같이 설정합니다. 

기본적으로 내장되어 있는 H2 DB의 환경설정 부분입니다. 각자의 DB에 맞는 환경 설정을 하면 됩니다.

jdbc로 DB와 연결되므로 사용하는 DB에 맞는 jdbc 드라이버를 flyway 홈디렉토리 밑에 drivers 디렉토리에 가져다 놓으면 됩니다.

flyway.url=jdbc:h2:file:./foobardb
flyway.user=SA
flyway.password=

 

참고) mysql DBdml flywaydb 데이터베이스에 접속하는 경우

flyway.url=jdbc:mysql://<ip_address>:3606
flyway.schemas=flyway
flyway.user="username"
flyway.password="******"

Baseline

데이터베이스의 기준을 설정합니다.

baseline은 flyway의 기준을 지정하는 명령어입니다.

 

flyway baseline 명령어를 실행하면 아래 내용을 볼수 있습니다.

"Successfully baselined schema with version: 1" 이 있는데 이는 flyway migrate 의 version을 최초 1로 설정한 것입니다.

flyway-9.17.0> flyway baseline
Flyway Community Edition 9.17.0 by Redgate
See release notes here: https://rd.gt/416ObMi

Database: 
Creating Schema History table `flyway`.`flyway_schema_history` with baseline ...
Successfully baselined schema with version: 1

위의 그림처럼 flyway_schema_history 테이블을 생성하고 version 1 의 baseline을 설정합니다.

flyway info를 수행하면  관련 정보를 볼 수 있습니다.

flyway-9.17.0> flyway info

Flyway Community Edition 9.17.0 by Redgate
See release notes here: https://rd.gt/416ObMi

+----------+---------+-----------------------+----------+---------------------+----------+----------+
| Category | Version | Description           | Type     | Installed On        | State    | Undoable |
+----------+---------+-----------------------+----------+---------------------+----------+----------+
|          | 1       | << Flyway Baseline >> | BASELINE | 2023-04-28 13:51:24 | Baseline | No       |
+----------+---------+-----------------------+----------+---------------------+----------+----------+

A Flyway report has been generated here: report.html

 

만약, migrate할 sql 파일명을 V1__ 로 시작되는 파일명으로 작성했을 경우, version이 1로 동일하기 때문에 V1__ 로 시작하는 파일의 sql은 수행되지 않습니다. 그러므로 아래 명령어와 같이 -baselineVersion=0 으로 지정하는 것이 좋습니다.

 

flyway-9.17.0> flyway baseline -baselineVersion=0

## 실행결과
Flyway Community Edition 9.17.0 by Redgate
See release notes here: https://rd.gt/416ObMi

Database: jdbc:mysql://dept-ssgd-aurora-an2-dev.cluster-cktyv4o7rx0c.ap-northeast-2.rds.amazonaws.com:3606/flyway (MySQL 8.0)
Creating Schema History table `flyway`.`flyway_schema_history` with baseline ...
Successfully baselined schema with version: 0


flyway-9.17.0> flyway info

## 실행결과
Flyway Community Edition 9.17.0 by Redgate
See release notes here: https://rd.gt/416ObMi

+----------+---------+-----------------------+----------+---------------------+----------+----------+
| Category | Version | Description           | Type     | Installed On        | State    | Undoable |
+----------+---------+-----------------------+----------+---------------------+----------+----------+
|          | 0       | << Flyway Baseline >> | BASELINE | 2023-04-28 13:51:24 | Baseline | No       |
+----------+---------+-----------------------+----------+---------------------+----------+----------+

A Flyway report has been generated here: report.html

 

 

 

baseline 재설정
마이그레이션이 많은 경우 기본 마이그레이션을 재설정하는 것이 좋습니다. 이렇게 하면 오래되고 관련이 없을 수 있는 많은 스크립트를 처리하는 오버헤드를 줄일 수 있습니다.

마이그레이션 생성

마이그레이션 파일은 /sql 디렉터리에 "V + (version표기) + __ + ... + .sql" 형식으로 파일을 생성합니다.

여기서 대문자 "V"는 version이라는 단어를 의미하고 "version표기"은 version 정보입니다.

version형식은 일반적인 표기와 동일합니다. 예를 들면 1.1.1 등의 형식입니다. migrate은 버전형식의 오름차순으로 수행합니다.

 

migrate 파일형식 : (V + version표기+ __filename.sql)

  • V : version
  • version표기 : 예시) 1, 1.1, 2, 2.1 등
  • __ : 연속된 언더바 2개
  • filename : 임의의 파일명
  • .sql = 파일확장자

예시 : V1__Create_person_table.sql 

 

V1__Create_person_table.sql 이라는 첫 번째 마이그레이션을 만듭니다. V1 다음의 __는 언더바가 2개입니다. 반드시 2개이어야 하며 V는 반드시 대문자를 써야 합니다. 만든 파일을 flyway 홈디렉토리 및에 sql 디렉토리에 위치시킵니다.

create table PERSON (
    ID int not null,
    NAME varchar(100) not null
);

데이터베이스 마이그레이팅

Flyway 마이그레이션을 수행합니다. 

주의할 점은 baseline을 설정하지 않고 flyway migrate 명령어를 수행할 때 데이터베이스에 table이 존재하면 "non-empty schema" 오류가 발생합니다.

스키마에 테이블이 존재할 경우에는 flyway baseline 또는  flyway migrate -baselineOnMigrate=true 명령어를 수행해야 합니다. baselineOnMigrate=true 옵션은 baseline을 설정하고 migrate을 수행합니다.

단, baseline을 수행할 경우에는 version 1의 BASELNE이 설정되므로 "V1__"으로 시작하는 sql 파일이 존재할 경우에 동일한 버전 오류가 발생하니 sql파일명을 V1 이상의 버전으로 설정해야 합니다.

또는 flyway migrate -baselineOnMigrate=true -baselineVersion=0 명령어를 수행하여 baseline의 version을 0으로 설정합니다.

flyway-9.16.3> flyway migrate

 

모든 것이 잘 되었다면 다음 출력이 표시됩니다.

Flyway Community Edition 9.16.3 by Redgate
See release notes here: https://rd.gt/416ObMi

Database: jdbc:h2:file:./foobardb (H2 2.1)
Schema history table "PUBLIC"."flyway_schema_history" does not exist yet
Successfully validated 1 migration (execution time 00:00.014s)
Creating Schema History table "PUBLIC"."flyway_schema_history" ...
Current version of schema "PUBLIC": << Empty Schema >>
Migrating schema "PUBLIC" to version "1 - Create person table"
Successfully applied 1 migration to schema "PUBLIC", now at version v1 (execution time 00:00.022s)

두번째 마이그레이션 추가

 /sql 디렉토리에 V2__Add_people.sql 두 번째 마이그레이션을 추가합니다.

insert into PERSON (ID, NAME) values (1, 'Axel');
insert into PERSON (ID, NAME) values (2, 'Mr. Foo');
insert into PERSON (ID, NAME) values (3, 'Ms. Bar');

 

Flyway를 실행하여 데이터베이스를 마이그레이션합니다.

flyway-9.16.3> flyway migrate

아래와 같은 결과가 표시됩니다.

Flyway Community Edition 9.16.3 by Redgate
See release notes here: https://rd.gt/416ObMi

Database: jdbc:h2:file:./foobardb1 (H2 2.1)
Successfully validated 2 migrations (execution time 00:00.019s)
Current version of schema "PUBLIC": 1
Migrating schema "PUBLIC" to version "2 - Add people"
Successfully applied 1 migration to schema "PUBLIC", now at version v2 (execution time 00:00.026s)

Flyway 실행정보를 보려면 flyway info 명령어를 수행합니다. flyway 버전, type, 수행시간, 수행상태 등의 수행 내역을 보여줍니다.

 

 

+ Recent posts