참고 자료
RealMySQL 8.0 1권 - 백은빈, 이성욱 지음
해당 글은 책에서 나오는 내용을 나중에 편하게 보기 위해서 요약한 내용이다.
1. 사용자 식별
MySQL에서 사용자는 사용자의 계정뿐 아니라 사용자의 접속 지점(호스트명, 도메인, IP 주소)도 계정의 일부가 된다.
'svc_id'@'127.0.0.1'
아이디: svc_id
접속 지점: 127.0.0.1
해당 아이디는 MySQL 서버가 기동 중인 로컬 호스트에서 svc_id라는 아이디로 접속할 때만 사용 가능한 계정
만약 사용자 계정에 해당 계정만 있으면 다른 컴퓨터에서는 svc_id라는 아이디로 접속할 수 없다.
모든 외부 컴퓨터에서 접속이 가능한 사용자 계정
사용자 계정의 호스트 부분을 % 문자로 대체한다.
% 문자는 모든 IP(모든 호스트명)을 의미한다.
'svc_id'@'%'
동일한 아이디 식별
'svc_id'@'%' / 비번: 123
'svc_id'@'192.168.0.10' / 비번: abc
IP 주소가 192.168.0.10 PC에서 svc_id 로 로그인을 하면 MySQL 서버는
범위가 작은 ID인 'svc_id'@'192.168.0.10'인 계정을 통해 인증을 진행한다.
즉 IP가 명시된 계정 정보를 통해 사용자 인증을 한다.
만약 비번을 123으로 시도를 하면 접속이 거절될 것이다.(비밀번호가 틀려서)
그러니 사용자 계정을 생성할 때 동일한 ID명을 가진 계정을 생성하지 말자.
2. 사용자 계정 관리
2 - 1 시스템 계정과 일반 계정
MySQL8.0 부터 계정은 SYSTEM_USER 권한을 가지고 있느냐에 따라서
시스템 계정(System Account), 일반 계정(Regular Account)로 구분
시스템 계정은 데이터베이스 서버 관리자를 위한 계정
일반 계정은 데이터베이스를 사용하는 개발자를 위한 계정
시스템 계정의 특권
일반 계정을 관리(생성 삭제 및 권한 부여 및 제거)
다른 세션(Connection) 또는 그 세션에서 실행 중인 쿼리 강제 종료
스토어드 프로그램 생성 시 DEFINER를 타 사용자로 설정
DEFINER
DEFINER는 호출자(invoker)의 권한을 가지고 실행하는 게 아닌 해당 스토어드 프로그램의 생성자의 권한을 가지고
스토어드 프로그램의 내부의 기능을 실행할 수 있게 한다.
이는 호출자에게 직접적인 권한 부여를 안 하고도 스토어드 프로그램의 실행 동안은 DEFINER(생성자)의 권한을 통해 실행할 수 있게 한다.
일반 계정으로 스토어드 프로그램을 생성하면 DEFINER는 해당 스토어드 프로그렘을 생성한 계정으로 지정된다.
하지만 시스템 계정은 DEFINER를 타 사용자로 설정이 가능하다.
MySQL 내장 계정
MySQL에는 'root'@'localhost'를 제외한 3개의 계정이 있는데
내부적으로 각기 다른 목적으로 사용되므로 삭제하지 않도록 하자.
'mysql.sys'@'localhost'
기본적으로 내장된 sys 스키마의 객체(뷰나 함수, 프로시저)들의 DEFINER로 사용되는 계정
'mysql.session'@'localhost'
MySQL 플러그인이 서버로 접근할 때 사용되는 계정
'mysql.infoschema'@'localhost'
information_schema에 정의된 뷰의 DEFINER로 사용되는 계정
3개의 계정은 account_locked 칼럼 상태 즉 잠겨 있는 상태이므로 잠긴 계정을 풀지 않는 한
악의적인 용도로 사용할 수 없다.
2 - 2 계정 생성
MySQL 5.7 버전까지는 GRANT 명령으로 권한 부여와 동시에 계정 생성이 가능했다.
MySQL 8.0 버전부터는 계정의 생성은 CREATE USER 권한 부여는 GRANT 명령으로 구분한다.
계정을 생성할 때 사용하는 다양한 옵션
계정의 인증 방식과 비밀번호
비밀번호 관련 옵션(유효기간, 이력 개수, 재사용 불가 기간)
기본 역할(Role)
SSL 옵션
계정 잠금 여부
일반적으로 사용하는 CREATE USER 명령
mysql> CREATE USER 'user'@'%'
-> IDENTIFIED WITH 'mysql_native_password' BY 'password'
-> REQUIRE NONE
-> PASSWORD EXPIRE INTERVAL 30 DAY
-> ACCOUNT UNLOCK
-> PASSWORD HISTORY DEFAULT
-> PASSWORD REUSE INTERVAL DEFAULT
-> PASSWORD REQUIRE CURRENT DEFAULT;
2 - 2 - 1 IDENTIFIED WITH
사용자의 인증 방식과 비밀번호를 설정한다.
IDENTIFIED WITH 뒤에는 반드시 인증 방식을 명시해야 한다.
MySQL 서버의 기본 인증 방식을 사용하고자 한다면 IDENTIFIED BY 'password' 형식으로 명시해야 한다.
해당 내용은 비밀번호의 암호화 방법인 단방향 암호화에 대해서 알아야 이해가 가능하다.
모른다면 밑 링크의 내용을 참고하자.
https://20240228.tistory.com/153
MySQL 서버의 다양한 인증 방식
Native Pluggable Authentication
MySQL 5.7 버전까지 기본으로 사용하던 방식
단순히 비밀번호에 대한 해시(SHA-1 알고리즘)값을 저장해두고, 클라이언트가 보낸 값과 해시값이 일치하는지 비교하는 인증 방식
장점
삐르다.
단점
보안 문제가 있다.
공격자가 쉽게 레인보우 테이블을 활용하여 공격이 가능하다.
Caching SHA-2 Pluggable Authentcation
암호화 해시값 생성을 위해 SHA-2 알고리즘을 사용한다.
내부적으로 Salt 키를 활용하여, 수천 번의 해시 계산을 수행해서 결과를 만든다. (Key Stretching)
이처럼 해시값을 계산하는 방식은 성능이 매우 떨어진다.
이를 보완하기 위해서 해시 결괏값을 메모리에 캐시해서 사용한다.
하지만 이는 인증된 사용자가 사용하는 건 문제가 없지만 공격자의 브루트 포스 공격에 대해서는 방법이 없다.
이를 막기 위해서는 인증 시도 횟수 같은 제약이 필요해 보인다.
이 인증 방식을 사용하려면 SSL/TLS (Secure Sockets Layer / Transport Layer Security) 또는 RSA 키페어를 반드시 사용해야 하는데 이를 위해 클라이언트에서 접속할 때 SSL 옵션을 활성화해야 한다.
기본 인증 설정
MySQL5.7 버전까지는 Native Authentication이 기존 인증 방식으로 사용됐지만 MySQL 8.0 버전부터는 Caching SHA-2 Authentication이 기본 인증으로 바뀌었다.
하지만 Caching SHA-2 인증 방식은 SSL/TLS 또는 RSA 키페어가 필요하기 때문에 기존 버전과의 호환성을 위해서
Native Authentication 방식을 사용해 계정을 생성해야 할 수도 있다.
Native Authentication 방식으로 설정 변경
default_authentication_plugin은 읽기 전용 변수여서 변경이 불가능하다고 나온다.
하지만 생성과 마찬가지로 계정 각각의 인증 방식을 변경하는 방법이 있다.
또는 my.cnf 설정 파일에 추가하면 된다.
2 - 2 - 2 REQUIRE
MySQL 서버에 접속할 때 SSL/TLS 채널을 사용할지 여부를 설정
별도로 설정하지 않으면 비암호화 채널로 연결하게 된다.
Caching SHA-2 Authentcation 인증 방식은 암호화된 채널만으로 MySQL 서버에 접속할 수 있다.
2 - 2 - 3 PASSWORD EXPIRE
비밀번호의 유효 기간을 설정하는 옵션
별도로 명시하지 않으면 default_password_lifetime 시스템 변수에 저장된 기간으로 유효 기간이 설정된다.
개발자나 데이터베이스 관리자의 게정은 기간을 설정하는 것이 좋지만 응용 프로그램 접속용 계정에 유효 기간을 설정하는 것은 위험할 수 있으니 주의하자.
PASSWORD EXPIRE 절 설정 가능한 옵션들
PASSWORD EXPIRE
계정 생성과 동시에 비밀번호 만료 처리
PASSWORD EXPIRE NEVER
계정 비밀번호의 만료 기간 없음
PASSWORD EXPIRE DEFAULT
default_password_lifetime 시스템 변수에 저장된 기간으로 유효 기간이 설정
PASSWORD EXPIRE INTERVAL n DAY
비밀번호의 유효 기간을 오늘부터 n일자로 설정
2 - 2 - 4 PASSWORD HISTORY
한 번 사용했던 비밀번호를 재사용하지 못하게 설정하는 옵션
PASSWORD HISTORY 절 설정 가능한 옵션들
PASSWORD HISTORY DEFAULT
password_history 시스템 변수에 저장된 개수만큼 비밀번호의 이력을 저장
저장된 이력에 남아있는 비밀번호는 재사용할 수 없다.
PASSWORD HISTORY n
비밀번호의 이력을 최근 n개까지만 저장
저장된 이력에 남아있는 비밀번호는 재사용할 수 없다.
MySQL 서버는 password_history 테이블을 사용하여 이전에 사용했던 비밀번호를 옵션에 맞는 개수만큼 저장한다.
2 - 2 - 5 PASSWORD REUSE INTERVAL
한 번 사용했던 비밀번호의 재사용 금지 기간을 설정하는 옵션
PASSWORD REUSE INTERVAL 절 설정 가능한 옵션들
PASSWORD REUSE INTERVAL DEFAULT
password_reuse_interval 변수에 저장된 기간으로 설정
PASSWORD REUSE INTERVAL INTERVAL n DAY
n일자 이후에 비밀번호를 재사용할 수 있게 설정
2 - 2 - 6 PASSWORD REQUIRE
비밀번호가 만료되어 새로운 비밀번호로 변경될 때 현재 비밀번호를 필요로 할지 말지 결정하는 옵션
PASSWORD REQUIRE 절에 사용 가능한 옵션
PASSWORD REQUIRE CURRENT
비밀번호를 변경할 때 현재 비밀번호를 먼저 입력하도록 설정
PASSWORD REQUIRE OPTIONAL
비밀번호를 변경할 때 현재 비밀번호를 먼저 입력하지 않아도 되도록 설정
PASSWORD REQUIRE DEFAULT
password_require_current 시스템 변수의 값으로 설정
2 - 2 - 7 ACCOUNT LOCK / UNLOCK
계정 생성 시 또는 ALTER USER 명령을 통해 계정 정보를 변경할 때 계정을 사용하지 못하게 잠글지 여부를 결정
ACCOUNT LOCK: 계정을 사용하지 못하게 잠금
ACCOUNT UNLOCK: 잠긴 계정을 다시 사용 가능 상태로 잠금 해제
3. 비밀번호 관리
3 - 1 고수준 비밀번호
MySQL 서버의 비밀번호는 유효기간이나 이력 관리를 통한 재사용 금지 기능뿐만 아니라
비밀번호 글자의 조합을 강제하거나 금칙어를 설정하는 기능도 있다.
MySQL 서버에서 비밀번호의 유효성 체크 규칙을 적용하려면 validate_password 컴포넌트를 이용하면 된다.
validate_password 컴포넌트 설치 및 확인
설치 방법
INSTALL COMPONENT 'file://component_validate_password';
validate_password 컴포넌트는 MySQL 서버 에 내장돼 있다.
그래서 file:// 부분에 별도의 파일 경로를 지정하지 않아도 된다.
나는 이런 오류가 발생한다.
문제점
component_validate_password 위치 경로는 C:\Program Files\MySQL\MySQL Server 8.0\lib\plugin 였는데
찾는 경로는 C:\Program Files\MySQL\MySQL Server 8.0\bin\lib\plugin 이다.
해결방법
→ 환경변수: plugin_dir 위치를 바꿔준다.
my.ini 파일 추가
plugin_dir=C:/Program Files/MySQL/MySQL Server 8.0/lib/plugin
→ Notepad++ 사용해서 수정
exit # 나가기
net stop mysql # mysql 종료
net start mysql # mysql 시작
show variables like 'plugin_dir'; # plugin 경로 확인
설치
설치된 컴포넌트 확인
validate_password 컴포넌트가 설치되면 다음과 같이 컴포넌트에서 제공되는 시스템 변수를 확인할 수 있다.
show global variables like 'validate_password%';
비밀번호 정책 3가지
LOW
비밀번호의 길이만 검증
MEDIUM (기본값)
비밀번호의 길이 검증
숫자와 대문자, 그리고 특수문자의 배합을 검증
STRONG
MEDIUM 레벨 검증을 수행 + 금칙어가 포함됐는지 여부까지 검증
비밀번호의 길이 검증
validate_password_length 시스템 변수에 설정된 길이 이상의 비밀번호가 사용됐는지를 검증
숫자, 대소문자, 특수문자 배합 검증
validate_password_mixed_case_count, validate_password_number_count, validate_password_special_char_count 시스템 변수에 설정된 글자 수 이상을 포함하고 있는지 검증한다.
금칙어 검증
validate_password.dictionary_file 시스템 변수에 설정된 사전 파일에 명시된 단어를 포함하고 있는지를 검증한다.
validate_password.dictionary_file 활용
MySQL 서버는 1234, qwer 같은 연속된 단어를 사용해도 아무런 문제 없이 설정된다.
하지만 실제 서비스에서는 높은 수준의 보안을 요구한다.
그러니 validate_password.dictionary_file 시스템 변수에 금칙어들이 저장된 사전 파일을 등록하면 된다.
하나씩 기록하는 일은 상당히 귀찮은 일이다.
이미 만들어져 있는 파일을 내려받아 더 필요한 것을 추가해서 사용하는 것도 좋다.
하지만 policy가 STRONG이 아니면 의미가 없으니 주의하자
mysql> SET GLOBAL validate_password.policy='STRONG';
3 - 2 이중 비밀번호
데이터베이스 계정 비밀번호는 서비스가 실행 중인 상태에서 변경 불가능하다.
일반적으로 응용 프로그램 서버들이 공용으로 데이터베이스 서버를 사용하는데 이런 특징 때문에 데이터베이스의
비밀번호는 처음 설정한 상태로 몇 년 동안 사용되는 경우가 많다.
데이터베이스의 비밀번호는 보안을 위해 주기적으로 변경해야 하지만 서비스를 멈추지 않고서는 비밀번호를 변경하는 것은 불가능하다.
MySQL8.0 버전 부터는 이러한 문제점을 해결하기 위해 이중 비밀번호(Dual Password)를 도입했다.
뭔가 이중 비밀번호라고 하면 2개의 비밀번호를 모두 맞게 입력했을 때만 로그인되는 것으로 오해할 수 있지만
이 기능은 2개의 비밀번호 중 하나만 일치하면 로그인이 통과되는 것을 의미한다.
2개의 비밀번호는 프라이머리(Primary)와 세컨더리(Secondary)로 구분된다.
최근에 설정한 비밀번호가 Primary 이전 비밀번호는 Secondary 비밀번호가 된다.
적용 방법 (RETAIN CURRENT PASSWORD)
// 현재 비변 변경
ALTER USER 'root'@'localhost' IDENTIFIED BY 'old_password';
// 비밀번호를 변경하면서 기존 비밀번호를 세컨더리 비밀번호로 설정
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password' RETAIN CURRENT PASSWORD;
MySQL 서비스를 내리지 않고 응용 프로그램 데이터베이스 계정 비밀번호 바꾸기
응용 프로그램 데이터베이스 계정 이름을 'root' 라고 가정
또한 이중 비밀번호까지 적용했다고 가정
그럼 현재 root 계정은 old_password, new_password 중 아무거나 입력해도 로그인이 된다.
이 상태에서 데이터베이스에 연결하는 응용 프로그램의 설정 파일(yaml 파일) 비밀번호를 new_password로 변경하고
배포 및 재시작을 순차적으로 실행한다.
MySQL 서버에 접속하는 모든 응용 프로그램의 재시작이 완료되면 이제 세컨더리 비밀번호인 이전 비밀번호는 삭제한다.
보안을 위해서 이중 비밀번호를 적용하고 바꿨는데 여전히 이전 비밀번호로 로그인이 가능하면 현재까지의 프로세스가 의미가 없는 행동이다.
ALTER USER 'root'@'localhost' DISCARD OLD PASSWORD;
세컨더리 비밀번호를 삭제하고 접속 시도를 했더니 Access denied 되었다.
로그인이 된 비밀번호는 new_password 인 Primary 비밀번호이다.
4. 권한 (Privilege)
MySQL 5.7 버전
글로벌 권한, 객체 단위의 권한으로 구분됐다.
데이터베이스나 테이블 이외의 객체에 적용되는 권한을 글로벌 권한이라 한다.
데이터베이스나 테이블을 제어하는데 필요한 권한을 객체 권한이라 한다.
객체 권한은 GRANT 명령으로 특정 객체를 반드시 명시해야 한다.
반대로 글로벌 권한은 특정 객체를 명시하지 말아야 한다.
예외적으로 ALL(ALL PRIVILEGES)은 글로벌과 객체 권한 두 가지 용도에 사용될 수 있다.
특정 객체에 ALL 권한이 부여되면 해당 객체에 적용될 수 있는 모든 객체 권한을 부여하며
글로벌로 ALL이 사용되면 글로벌 수준에서 가능한 모든 권한을 부여하게 된다.
권한의 종류는 페이지 65의 표3.1을 참고하자
MySQL 8.0 버전
MySQL 8.0 버전부터는 5.7 버전에 동적 권한이 더 추가됐다.
그래서 MySQL 5.6 버전부터 제공되던 권한은 정적 권한이라고 한다.
정적 권한은 MySQL 서버의 소스 코드에 고정적으로 명시돼 있는 권한을 의미하며
동적 권한은 MySQL 서버가 시작되면서 동적으로 생성되는 권한을 의미한다.
MySQL 서버의 컴포넌트나 플러그인이 설치되면 그때 등록되는 권한을 동적 권한이라고 한다.
SUPER
MySQL 5.7 버전까지는 SUPER 권한이 데이터베이스 관리를 위해 꼭 필요한 권한이었지만
8.0부터는 SUPER 권한은 잘게 쪼개어져 동적 권한으로 분산됐다.
GRANT
사용자에게 권한을 부여할 때는 GRANT 명령어를 사용한다.
각 권한의 특성(범위)에 따라 ON 절에 명시되는 오브젝트(DB나 테이블)의 내용이 바뀌어야 한다.
8.0 버전부터는 존재하지 않는 사용자에 대한 GRANT 명령이 실행되면 에러가 발생한다.
글로벌 권한
GRANT SUPER ON *.* TO 'user'@'%';
글로벌 권한에 ON 절에는 항상 *.*를 사용하게 된다.
*.*: DB의 모든 오브젝트(테이블, 스토어드 프로시저, 함수)를 포함해서 MySQL 서버 전체를 의미
CREATE USER, CREATE ROLE 같은 글로벌 권한은 DB 단위나 오브젝트 단위로 부여할 수 있는 권한이 아니다.
DB 권한
GRANT EVENT ON employess.* TO 'user'@'%';
GRANT EVENT ON *.* TO 'user'@'%';
DB 권한은 특정 DB에 대해서만 권한을 부여하거나 서버에 존재하는 모든 DB에 대해 권한을 부여할 수 있다.
따라서 ON 절에 *.*, dbname.* 모두 사용할 수 있다.
여기서 DB는 내부에 존재하는 테이블뿐만 아니라 스토어드 프로그램들도 모두 포함한다.
오브젝트의 권한처럼 db.table로 오브젝트(테이블)까지 명시할 수는 없다.
테이블 권한
GRANT SELECT, INSERT, UPDATE, DELETE ON employess.departments TO 'user'@'%';
GRANT SELECT, INSERT, UPDATE, DELETE ON employess.* TO 'user'@'%';
GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'user'@'%';
테이블 특정 컬럼 권한
컬럼에 부여할 수 있는 권한은 DELETE를 제외한 INSERT, UPDATE, SELECT 3가지
각 권한 뒤에 컬럼을 명시한다.
use employess;
GRANT SELECT, INSERT, UPDATE(dept_name) ON departments TO 'user'@'%';
이 케이스는 SELECT, INSERT는 departments 테이블 전부에서 가능하지만 UPDATE는 dept_name 컬럼에 대해서만 수행 가능하다.
권한 확인
각 계정이나 권한에 부여된 권한이나 역할을 확인하기 위해서는 SHWO GRANTS 명령을 사용할 수도 있지만
표 형태로 깔끔하게 보고자 한다면 MySQL DB의 권한 관련 테이블를 참조하자.
ex) mysql.user / mysql.db / mysql.tables_priv
5. 역할 (Role)
MySQL 8.0 부터 권한을 묶어서 역할을 사용할 수 있게 됐다.
역할 정의하기
CREATE ROLE role_emp_read, role_emp_write;
CREATE ROLE은 빈 껍데기만 있는 역할을 정의한 것
각 역할에 대한 실질적인 권한을 GRANT 명령어로 부여해야 한다.
역할에 권한 부여하기
GRANT SELECT ON employess.* TO role_emp_read;
GRANT INSERT, UPDATE, DELETE ON employess.* TO role_emp_write;
역할은 그 자체로 사용될 수 없다.
즉 계정에 부여해야 한다.
계정 생성하기
CREATE USER reader@'%' IDENTIFIED BY '비밀번호';
CREATE USER writer@'%' IDENTIFIED BY '비밀번호';
현재 2개의 계정은 아무런 권한이 없기 때문에 employess DB에 아무런 쿼리도 실행할 수 없는 상태이다.
GRANT 명령으로 계정에 역할 부여하기
GRANT role_emp_read TO reader@'%';
GRANT role_emp_write TO writer@'%';
현재 계정 권한 확인하기
SHOW GRANTS;
역할 확인도 했는데 employess 테이블을 조회하려고 했더니 똑같이 권한이 없다는 에러가 나온다.
활성화된 역할 확인
SELECT current_role();
실제 역할은 부여돼 있지만 계정의 활성화된 역할을 조회하면 role_emp_read 역할이 없음을 확인할 수 있다.
역할 사용방법 (SET ROLE)
reader 계정이 role_emp_read 역할을 사용할 수 있게 하려면 SET ROLE 명령을 실행해 역할을 활성화해야 한다.
SET ROLE 'role_emp_read';
조회 성공!
역할의 자동 초기화
역할의 활성화는 계정이 로그아웃됐다가 다시 로그인하면 역할이 활성화되지 않은 상태로 초기화된다.
이는 MySQL 서버의 역할이 자동으로 활성화 되지 않게 설정돼 있기 때문이다.
사용자가 MySQL 서버에 로그인할 때 역할을 자동으로 활성화할지 여부를
active_all_roles_on_login 시스템 변수로 설정할 수 있다.
SET GLOBAL activate_all_roles_on_login=ON;
역할(Role)의 비밀
MySQL 서버의 역할은 사용자 계정과 거의 같은 모습을 한다.
MySQL 서버 내부적으로 역할과 계정은 동일한 객체로 취급된다.
mysql.user 테이블에는 역할과 계정을 구분하는 flag 컬럼도 존재하지 않는데
어떻게 구분할까?
→ 구분할 필요가 없다.
하나의 계정에 다른 계정의 권한을 병합하기만 하면 되므로 MySQL 서버는 역할과 계정을 구분할 필요가 없는 것이다.
일반적으로 CREATE USER 명령으로 계정을 생성할 때는 reader@'127.0.0.1'과 같이 계정 이름과 호스트부분을 함께 명시한다.
하지만 CREATE ROLE 명령으로 역할을 생성할 때는 호스트 부분을 별도로 명시하지 않아서 해당 부분이 차이점으로 볼 수 있겠지만 생략하면 자동으로 호스트 부분을 '%'로 추가한다.
역할의 네이밍
결론은 계정과 역할은 MySQL 서버에서 아무런 차이가 없다.
그러니 계정과 역할을 구분하기 위해 역할은 prefix에 "ROLE_"같은 네이밍 규칙을 정하자.
역할과 계정은 동일한 객체지만 MySQL 서버에서 왜 굳이 CREATE USER, CREATE ROLE 명령을 구분해서 지원할까?
이는 DB 관리의 직무를 분리할 수 있기 때문이다.
EX) 계정 생성은 금지 역할은 생성 가능
역할 조회
show grants;
select * from mysql.default_roles; // 계정별 기본 역할
select * from mysql.role_edges; // 역할에 부여된 역할 관계 그래프
'DataBase > MySQL' 카테고리의 다른 글
[MySQL] 스토어드 함수 stored function (1) | 2024.11.12 |
---|---|
[MySQL] RealMySQL 8.0 4.1 MySQL 엔진 아키텍처 (0) | 2024.11.11 |
[MySQL] 순위 함수 정리 (0) | 2024.09.15 |
[MySQL] Common Table Expression (CTE) (0) | 2024.09.12 |
[MySQL] 자주 사용하는 함수 정리 - Updatable (1) | 2024.09.09 |