Stored Object Access Control

CREATE PROCEDURE 및 CREATE FUNCTION에는 CREATE ROUTINE 권한이 필요합니다.

DEFINER 절이 있는 경우 필요한 권한은 사용자(user) 값에 따라 다릅니다.

DEFINER 및 SQL SECURITY 절은 루틴(procedure or function, trigger, view) 실행 시에 액세스 권한을 확인할 때 사용할 보안 컨텍스트를 지정합니다.

 

저장 프로그램(프로시저, 함수, 트리거 및 이벤트) 및 뷰는 사용하기 전에 정의되며 참조 시 해당 권한을 결정하는 보안 컨텍스트 내에서 실행됩니다. 저장 객체의 실행에 적용 가능한 권한은 DEFINER 속성 및 SQL SECURITY 특성에 의해 제어됩니다.

  • The DEFINER Attribute
  • The SQL SECURITY Characteristic
  • Examples
  • Orphan Stored Objects
  • Risk-Minimization Guidelines

The DEFINER Attribute

저장 객체에는 MySQL 계정을 지정하는 DEFINER 속성을 포함할 수 있습니다. DEFINER 속성이 생략된 경우 객체 정의자는 객체를 만든 사용자입니다.
다음 규칙은 저장된 객체에 대한 DEFINER 속성으로 지정할 수 있는 계정을 결정합니다.

  • SET_USER_ID 권한(더 이상 사용되지 않는 SUPER 권한)이 있으면, 모든 계정을 DEFINER 속성으로 지정할 수 있습니다. 계정이 존재하지 않으면 경고가 뜹니다. 저장 객체 DEFINER 속성을 SYSTEM_USER 권한이 있는 계정으로 설정하려면 SYSTEM_USER 권한이 있어야 합니다.
  • 허용되는 계정은 CURRENT_USER()로 지정된 자신의 계정입니다. DEFINER를 다른 계정으로 설정할 수 없습니다.

존재하지 않는 DEFINER 계정으로 저장 객체를 생성하면 고아 객체가 생성되어 잘못된 결과를 초래할 수 있습니다.

 

The SQL SECURITY Characteristic

저장 루틴(프로시저 및 함수) 및 뷰의 경우 정의에는 개체가 정의자 컨텍스트에서 실행되는지 호출자 컨텍스트에서 실행되는지 지정하기 위해 DEFINER 또는 INVOKER 값이 있는 SQL SECURITY 특성이 포함될 수 있습니다. 정의에서 SQL SECURITY 특성을 생략하는 경우 기본값은 정의자(DEFINER) 컨텍스트입니다.

트리거 및 이벤트에는 SQL SECURITY 특성이 없으며 항상 정의자 컨텍스트에서 실행됩니다. 
정의자(DEFINER) 및 호출자(INVOKER) 보안 컨텍스트는 다음과 같은 차이가 있습니다.

  • 정의자 보안 컨텍스트에서 실행되는 저장된 객체는 DEFINER 속성으로 명명된 계정의 권한으로 실행됩니다. 이러한 권한은 호출하는 사용자의 권한과 완전히 다를 수 있습니다. 호출자는 개체를 참조할 수 있는 권한(예: 저장 프로시저를 호출하기 위한 EXECUTE 또는 뷰에서 선택하기 위한 SELECT)이 있어야 하지만 개체 실행 중에는 호출자의 권한이 무시되고 DEFINER 계정 권한만 중요합니다. DEFINER 계정에 권한이 거의 없는 경우 개체가 수행할 수 있는 작업이 이에 따라 제한됩니다. DEFINER 계정에 높은 권한이 있는 경우(예: 관리 계정) 개체는 누가 호출하든 상관없이 강력한 작업을 수행할 수 있습니다.
  • 호출자 보안 컨텍스트에서 실행되는 저장된 루틴 또는 뷰는 호출자에게 권한이 있는 작업만 수행할 수 있습니다. 

Examples

아래 프로시저는 정의자(DEFINER) 보안 컨텍스트에서 실행하기 위해 SQL SECURITY DEFINER로 선언된 프로시저입니다.

CREATE DEFINER = 'definer_user'@'%' PROCEDURE p1()
SQL SECURITY DEFINER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;

p1 프로시저에 대한 EXECUTE 권한이 있는 모든 사용자는 CALL 문으로 호출할 수 있습니다. 그러나 p1이 실행될 때 정의자 보안 컨텍스트에서 실행되므로 DEFINER 속성으로 명명된 계정인 'admin'@'localhost'의 권한으로 실행됩니다. 이 계정 은 p1에 대한 EXECUTE 권한과 본문 내에서 참조되는 테이블 t1에 대한 UPDATE 권한이 있어야 합니다.

 

아래 프로시저는 SQL SECURITY 특성이 INVOKER라는 점을 제외하면 p1과 동일한 프로시저입니다.

CREATE DEFINER = 'definer_user'@'%' PROCEDURE p2()
SQL SECURITY INVOKER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;

p1과 달리 p2는 호출자 보안 컨텍스트에서 실행되므로 DEFINER 속성 값에 관계없이 호출한 사용자의 권한으로 실행됩니다. 호출자에게 p2에 대한 EXECUTE 권한이나 테이블 t1에 대한 UPDATE 권한이 없으면 p2가 실패합니다.

 

예제1) DEFINER는 프로시저에 대한 EXECUTE와 테이블에 대한 SELECT 권한만 있고, INVOKER는 프로시저에 대한 EXECUTE와 테이블에 대한 SELECT, UPDATE 권한이 있을 경우 p2프로시져 INVOKER가 프로시저 수행하면 정상수행되지만 DEFINER 가 수행하면 권한 오류가 발생합니다.

CREATE DEFINER = 'definer_user'@'%' PROCEDURE p2()
SQL SECURITY INVOKER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;

 

DEFINER가 프로시저를 수행했을 경우 UPDATE 권한이 없다는 오류가 발생합니다.

 

ERROR 1142 (42000): UPDATE command denied to user 'definer_user'@'localhost' for table 't1'

INVOKER가 프로시저를 수행했을 경우 프로시저가 정상적으로 수행됩니다.

Query OK, 2 rows affected (0.02 sec)

예제2) 이번에는 위 예제와 반대로 INVOKER는 프로시저에 대한 EXECUTE와 테이블에 대한 SELECT 권한만 있고, DEFINER는 프로시저에 대한 EXECUTE와 테이블에 대한 SELECT, UPDATE 권한이 있을 경우 p1프로시져 수행 시 DEFINER와 INVOKER는 정상 수행합니다. 프로시저를 정의한 DEFINER가 권한이 있기 때문입니다.

CREATE DEFINER = 'definer_user'@'%' PROCEDURE p1()
SQL SECURITY DEFINER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;

 

DEFINER 또는 INVOKER 가 프로시저를 수행했을 경우 프로시저가 정상적으로 수행됩니다.

Query OK, 2 rows affected (0.02 sec)

[참고사항]

프로시저 수행 권한을 줄 때 주의합니다.

특정 프로시저에 권한을 줄 때는 

GRANT EXECUTE ON PROCEDURE 형식을 사용하고

GRANT EXECUTE ON PROCEDURE db_name.procdure_name TO 'user_name'@'%';

특정 DB의 전체 프로시저에 대한 수행 권한을 줄 때는

GRANT EXECUTE ON db_name.* TO user_name 형식으로 줍니다.

GRANT EXECUTE ON db_name.* TO 'user_name'@'%';

 

DEFINER=CURRENT_USER() 로 설정

DEFINER 절을 생략하면 기본 정의자는 CREATE PROCEDURE 또는 CREATE FUNCTION 문을 실행하는 사용자입니다. 이는 DEFINER = CURRENT_USER를 명시적으로 지정하는 것과 같습니다.

 

*** 테스트 스크립트 ***

-- definer 계정 생성 및 권한 설정
create user 'definer_user'@'%' identified by 'definer_user';
grant select, update on userdb.* to 'definer_user'@'%';
grant execute on userdb.* to 'definer_user'@'%';

-- invoker 계정 생성 및 권한 설정
create user 'invoker_user'@'%' identified by 'invoker_user';
grant select on userdb.* to 'invoker_user'@'%';
grant execute on userdb.* to 'invoker_user'@'%';

-- SQL SECURITY를 DEFINER로 설정
delimiter //
CREATE DEFINER = 'definer_user'@'%' PROCEDURE p1()
SQL SECURITY DEFINER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;
//

-- SQL SECURITY를 INVOKER로 설정
delimiter //
CREATE DEFINER = 'definer_user'@'%' PROCEDURE p2()
SQL SECURITY INVOKER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;
//


-- DEFINER를 current_user로 설정
delimiter //
CREATE DEFINER = current_user PROCEDURE p3()
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;
//

 

MySQL DEFINER 정의 관련 정보 조회

SELECT DISTINCT DEFINER FROM INFORMATION_SCHEMA.EVENTS;
SELECT DISTINCT DEFINER FROM INFORMATION_SCHEMA.ROUTINES;
SELECT DISTINCT DEFINER FROM INFORMATION_SCHEMA.TRIGGERS;
SELECT DISTINCT DEFINER FROM INFORMATION_SCHEMA.VIEWS;

 

 

프로시져 (Stored procedure) 권한 문장

CREATE
DEFINER = { user | CURRENT_USER } 
PROCEDURE sp_process_ranking()
SQL SECURITY { DEFINER | INVOKER }
BEGIN
  ...
  ...
END;

 

함수 (Stored function) 생성 문장

CREATE
DEFINER = { user | CURRENT_USER } 
FUNCTION fn_get_usercount() RETURN INT UNSIGNED
SQL SECURITY { DEFINER | INVOKER }
BEGIN
  ...
  ...
END;

 

뷰 권한 문장

CREATE or REPLACE
DEFINER = { user | CURRENT_USER } 
SQL SECURITY { DEFINER | INVOKER }
VIEW view_name
AS SELECT ...

 

동일한 입력 매개변수에 대해 항상 동일한 결과를 생성하는 경우 "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 데이터베이스 서비스 시작하기를 참조하십시오 .

+ Recent posts