2015. 3. 27. 12:23 Summary of/Real MySQL
16.1 임의(랜덤) 정렬
16.1.1 지금까지의 구현
16.1.2 인덱스를 이용한 임의 정렬
mysql> SET @random_base = floor((rand() * 10000000));
mysql> SELECT * FROM ( (SELECT * FROM tb_number WHERE rand_val >= @random_base ORDER BY rand_val ASC LIMIT 30) UNION ALL (SELECT * FROM tb_number WHERE rand_val < @random_base ORDER BY rand_val DESC LIMIT 30) )tb_rand ORDER BY RAND() LIMIT 30; |
16.2 페이징 쿼리
6.2.1 지금까지의 방법
CREATE TABLE tb_article( board_id INT NOT NULL, article_id INT NOT NULL AUTO_INCREMENT, article_title VARCHAR(100) NOT NULL, … PRIMARY KEY(article_id), INDEX ix_boardid(board_id, article_id) ); |
SELECT * FROM tb_article WHERE board_id=1 ORDER BY article_id DESC LIMIT n, m; |
16.2.2 불필요한 접근을 제거하기 위한 페이징
SELECT * FROM tb_article WHERE board_id=1 AND article_id < 165 ORDER BY article_id DESC LIMIT 0, 20; |
16.3 MySQL 에서 시퀀스 구현
16.3.1 시퀀스용 테이블 준비
MySQL에서 시퀀스 구현하기
CREATE TABLE mysql_sequences( seq_name VARCHAR(10) NOT NULL, seq_currval BIGINT UNSIGNED NOT NULL, PRIMARY KEY(seq_name) )ENGINE = MyISAM |
16.3.2 시퀀스를 위한 스토어드 함수
INSERT INTO mysql_sequences SET seq_name=’시퀀스이름’, seq_currval=(@v_current_value:=1) ON DUPLICATE KEY UPDATE seq_currval=(@v_current_value:=seq_currval+1); |
SELECT @v_current_value AS nextval; |
위의 내용을 조금더 개선하면 …
DELIMITER ;;
CREATE FUNCTION nextval() RETURNS BIGINT UNSIGNED MODIFIES SQL DATA SQL SECURITY INVOKER BEGIN INSERT INTO mysql_sequences SET seq_name=’시퀀스이름’, seq_currval=(@v_current_value:=1) ON DUPLICATE KEY UPDATE seq_currval=(@v_current_value:=seq_currval+1);
RETURN @v_current_value; END |
SELECT nextval(); |
on duplicate key로 값을 조회하는것도 좋지만 위의 예제처럼 스토어드 함수로 캡슐화하면 실수를 조금 더 줄일 수 있어
16.3.3 여러 시퀀스 처리하기
DELIMITER ;;
CREATE FUNCTION nextval(p_seq_name CHAR(10) CHARSET latin1) RETURNS BIGINT UNSIGNED MODIFIES SQL DATA SQL SECURITY INVOKER BEGIN INSERT INTO mysql_sequences SET seq_name=IFNULL(p_seq_name, ‘DEFAULT’), seq_currval=(@v_current_value:=1) ON DUPLICATE KEY UPDATE seq_currval=(@v_current_value:=seq_currval+1);
RETURN @v_current_value; END |
SELECT nextval(‘ARTICLE’); |
16.3.4 시퀀스 사용 시 주의사항
SELECT NEXTVAL(); /*여기서 가져온 값이 16이라고 가정 */ INSERT INTO tb_article(article_id, …) VALUES( 16, …); |
16.4 큰 문자열 칼럼의 인덱스(해시)
16.5 테이블 파티션
16.6 SNS 의 타임라인 구현
16.6.1 예제 시나리오
|
16.6.2 인덱스 테이블 사용
16.6.3 Try & Fail 쿼리
SELECT * FROM tb_article WHERE 작성자 IN (‘김세형’,’임현석’) AND 작성일시 > ? /* 검색 범위 시작 일시 */ AND 작성일시 <= ? /* 검색 범위 종료 일시 */ ORDER BY 작성일시 DESC LIMIT 20; |
16.7 MySQL 표준 설정
16.7.1 MySQL 표준 설정의 필요성
16.7.2 표준설정의 예시(p.948 참조)
16.8 복제를 사용하지 않는 MySQL의 설정
16.9 MySQL 복제 구축
16.9.1 MySQL 복제의 형태
16.9.2 확장(스케일 아웃)
16.9.3 가용성
16.9.4 복제가 구축된 MySQL 에서의 작업
SET sql_log_bin = OFF; ALTER TABLE employees ADD INDEX ix_lastname(lastname); SET sql_log_bin = ON; |
16.10 SQL 작성 표준
16.10.1 조인 조건은 항상 ON 절에 기재
SELECT * FROM employees e LEFT JOIN dept_manager dm WHERE e.first_name=’Smith’ AND dm.emp_no = e.emp_no |
16.10.2 테이블 별칭(Alias) 사용 및 칼럼 명에 테이블 별칭 포함
SELECT first_name, last_name FROM employees e INNER JOIN dept_manager dm ON dm.emp_no = e.emp_no WHERE first_name=’Smith’; |
16.10.4 FULL GROUP BY 사용
16.10.5 DELETE, UPDATE 쿼리에서 ORDER BY.. LIMIT.. 사용 자제
16.10.6 문자열 리터럴 표기는 홑따옴표만 사용
16.10.7 서브쿼리는 조인으로 변경
16.10.8 UNION [ALL] 은 사용 자제
16.10.9 스토어드 함수는 가능하면 DETERMINISTIC 으로 정의
16.10.10 스토어드 프로그램에서는 예외 처리 코드를 작성
16.10.11 UPDATE, DELETE 쿼리와 적용 건수(Affected row counts) 체크
16.10.12 숫자 값은 반드시 숫자 타입의 칼럼으로 정의
16.13 스키마 검토
SELECT table_schema, table_name, column_name, column_type, character_set_name, collation_name FROM information_schema.columns WHERE table_schema = 'employees' AND column_name LIKE '%dept_no%'; |
|
[Real MySQL] 15장 데이터 타입 (0) | 2015.03.27 |
---|---|
[Real MySQL] 14장 데이터 모델링 (0) | 2015.03.27 |
[Real MySQL] 12장 쿼리 종류별 잠금 (0) | 2015.03.27 |
[Real MySQL] 7장 쿼리 작성 및 최적화 (4) | 2015.03.27 |
[Real MySQL] 6장 실행 계획 (0) | 2015.03.27 |
2015. 3. 27. 12:21 Summary of/Real MySQL
15.1 문자열
15.1.1 저장 공간
15.1.2 비교 방식
15.1.3 문자 집합(캐릭터 셋)
15.1.4 콜레이션
15.1.5 문자열 이스케이프 처리
15.2 숫자
15.2.1 정수
15.2.2 부동 소수점
15.2.3 DECIMAL
15.2.4 정수 타입의 칼럼을 생성할 때의 주의사항
15.2.5 자동 증가 옵션 사용
15.3 날짜와 시간
15.3.1 TIMESTAMP 타입의 옵션
15.3.2 타임존 등록 및 사용
15.4 ENUM과 SET
15.4.1 ENUM
15.4.2 SET
15.5 TEXT, BLOB
15.6 공간(Spatial) 데이터 타입
15.6.1 POINT 타입
15.6.2 LINESTRING 타입
15.6.3 POLYGON 타입
15.6.4 GEOMETRY 타입
[Real MySQL] 16장 베스트 프렉티스 (0) | 2015.03.27 |
---|---|
[Real MySQL] 14장 데이터 모델링 (0) | 2015.03.27 |
[Real MySQL] 12장 쿼리 종류별 잠금 (0) | 2015.03.27 |
[Real MySQL] 7장 쿼리 작성 및 최적화 (4) | 2015.03.27 |
[Real MySQL] 6장 실행 계획 (0) | 2015.03.27 |
2015. 3. 27. 12:20 Summary of/Real MySQL
14.1 논리 모델링
14.1.1 모델링 용어
14.1.2 용어집(p.825)
14.1.3 엔터티
( p. 828 참고)
14.1.4 어트리뷰트 (속성)
14.1.5 식별자 (Primary Key)
14.1.6 관계(릴레이션)
14.1.7 엔터티의 통합
14.1.8 관계의 통합
14.1.9 모델 정규화
14.2 물리 모델링
14.2.1 프라이머리 키 선택
14.2.2 데이터 타입 선정
14.2.3 반정규화
SELECT * FROM 게시물, 회원 WHERE 회원.회원번호 = 게시물.작성회원번호 AND 게시물.게시판번호 = 1 ORDER BY 회원.작성회원명 LIMIT 10; |
INSERT INTO access_log(..., referrer_url, referrer_url_hash, …) VALUES(..., ‘http://…...’, MD5(‘http://…...’), …); |
SELECT * FROM access_log WHERE referrer_url_hash=MD5(‘http://…...’); |
SELECT * FROM access_log WHERE referrer_url_hash=MD5(‘http://…...’) AND referrer_url=’http://…...’; |
[Real MySQL] 16장 베스트 프렉티스 (0) | 2015.03.27 |
---|---|
[Real MySQL] 15장 데이터 타입 (0) | 2015.03.27 |
[Real MySQL] 12장 쿼리 종류별 잠금 (0) | 2015.03.27 |
[Real MySQL] 7장 쿼리 작성 및 최적화 (4) | 2015.03.27 |
[Real MySQL] 6장 실행 계획 (0) | 2015.03.27 |
2015. 3. 27. 12:18 Summary of/Real MySQL
12장 쿼리 종류별 잠금
12.1 InnoDB의 기본 잠금 방식
12.1.1 SELECT
SELECT * FROM employees WHERE emp_no=10001 LOCK IN SHARE MODE; SELECT * FROM employees WHERE emp_no=10001 FOR UPDATE; |
12.1.2 INSERT, UPDATE, DELETE
12.2 문장별로 사용하는 잠금
12.2.1 SELECT 쿼리의 잠금
12.2.2 INSERT 쿼리의 잠금
CREATE TABLE tb_test123 (
fdpk INT NOT NULL,
PRIMARY KEY(fdpk)
);
INSERT INTO tb_test123 VALUES (1), (6),(8),(9);
BEGIN; // 트랜잭션 시작
INSERT INTO tb_test123 VALUES(5);
BEGIN;
INSERT INTO tb_test123 VALUES (3);
BEGIN;
INSERT INTO tb_test123 VALUES (4);
주의 : 공유잠금을 가지고 있는 상태에서 다시 배타적 잠금을 걸게 되면 데드락 발생 할 수 도
[참고] 배타적 잠금 : 해당 트랜잭션에서 레코드나 간격을 변형하기 위해 획득하는 잠금
공유 잠금 : 레코드나 간격을 읽을때 다른 트랜잭션이 변경하지 못하게 하는 용도의 잠금
12.2.3 UPDATE 쿼리의 잠금
12.2.4 DELETE 쿼리의 잠금
12.2.5 DDL 문장의 잠금
12.2.6 InnoDB에서 여러 쿼리 패턴 간의 잠금 대기
CREATE TABLE empl (
emp_no INT NOT NULL,
last_name VARCHAR(16) DEFAULT NULL,
PRIMARY KEY (emp_no)
) ENGINE= INNODB;
INSERT INTO empl VALUES (120, 'Hello'), (130,'Hi'), (140, 'Bye');
12.3 InnoDB 에서 데드락 만들기
12.3.1 패턴 1 (상호 거래 관련)
12.3.2 패턴 2 (유니크 인덱스 관련)
12.3.3 패턴 3 (외래키 관련)
12.3.4 패턴 4 (서로 다른 인덱스를 통한 잠금)
[Real MySQL] 15장 데이터 타입 (0) | 2015.03.27 |
---|---|
[Real MySQL] 14장 데이터 모델링 (0) | 2015.03.27 |
[Real MySQL] 7장 쿼리 작성 및 최적화 (4) | 2015.03.27 |
[Real MySQL] 6장 실행 계획 (0) | 2015.03.27 |
[Real MySQL] 5장 인덱스 (0) | 2015.03.27 |
2015. 3. 27. 12:16 Summary of/Real MySQL
7.2 매뉴얼의 SQL 문법 표기를 읽는 방법
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORTY] [IGNORE] [INTO] tbl_name SET col_name = {expr | DEFAULT}, … [ ON DUPLICATE KEY UPDATE col_name = expr [, col_name = expr] …] |
7.3 MySQL 연산자와 내장 함수
7.3.1 리터럴 표기법
7.3.2 MySQL 연산자
7.3.3 MySQL 내장 함수
SELECT emp_no, first_name, CASE gender WHEN ‘M’ THEN ‘Man’ WHEN ‘F’ THEN ‘Woman’ ELSE ‘Unknown’ END AS gender FROM employees LIMIT 10; |
INSERT INTO tab_statistice(member_id, visit_count) SELECT member_id, COUNT(*) AS cnt FROM tab_accesslog GROUP BY member_id ON DUPLICATE KEY UPDATE visit_count = visit_count + VALUES(visit_count); |
7.3.4 SQL 주석
7.4 SELECT
7.4.1 SELECT 각 절의 처리 순서
7.4.2 WHERE 절과 GROUP BY 절, 그리고 ORDER BY 절의 인덱스 사용
7.4.3 WHERE 절의 비교 조건 사용 시 주의사항
SELECT * FROM titles WHERE to_date IS NULL; SELECT * FROM titles WHERE ISNULL(to_date); SELECT * FROM titles WHERE ISNULL(to_date) = 1; //인덱스 사용 X SELECT * FROM titles WHERE ISNULL(to_date) = true; //인덱스 사용 X |
SELECT * FROM employees WHERE emp_no=10001; SELECT * FROM employees WHERE first_name =’Smith’; SELECT * FROM employees WHERE emp_no=’10001’; SELECT * FROM employees WHERE first_name=10001; // 인덱스 사용 X |
7.4.4 DISTINCT
SELECT COUNT(DISTINCT s.salary) FROM employees e, salaries s WHERE e.emp_no = s.emp_no AND e.emp_no BETWEEN 100001 AND 100100; |
7.4.5 LIMIT n
SELECT * FROM employees WHERE emp_no BETWEEN 10001 AND 10010 ORDER BY first_name LIMIT 0, 5; |
7.4.6 JOIN
SELECT * FROM employees e, dept_emp de WHERE e.emp_no = de.emp_no; |
SELECT * FROM employees e LEFT JOIN dept_manager mgr ON mgr.`emp_no` = e.emp_no WHERE mgr.`dept_no`='d001'; |
SELECT * FROM employees e LEFT JOIN dept_manager mgr ON mgr.`emp_no` = e.emp_no AND mgr.`dept_no`='d001'; |
SELECT COUNT(*) FROM dept_emp de LEFT JOIN emplyees e ON e.emp_no = de.emp_no WHERE de.dept_no = ‘d001’; |
SELECT COUNT(*) FROM dept_emp de WHERE de.dept_no = ‘d001’; |
SELECT t1.id FROM tab_test1 t1 LEFT JOIN tab_test2 t2 ON t1.id = t2.id WHERE t2.id IS NULL; |
SELECT e.yearmonth, e.event_name, n.news_title FROM tab_event e LEFT JOIN tab_news n ON n.yearmonth = e.yearmonth UNION SELECT n.yearmonth, e.event_name, n.news_title FROM tab_news n LEFT JOIN tab_event e ON e.yearmonth = n.yearmonth; |
SELECT e.yearmonth, e.event_name, n.news_title FROM tab_event e LEFT JOIN tab_news n ON n.yearmonth = e.yearmonth UNION ALL SELECT n.yearmonth, e.event_name, n.news_title FROM tab_news n LEFT JOIN tab_event e ON e.yearmonth = n.yearmonth WHERE e.yearmonth IS NULL; |
SELECT * FROM dept_emp de, employees e WHERE de.dept_no = ‘d001’ AND e.emp_no = de.emp_no LIMIT 100, 10; |
dept_emp가 드라이빙 테이블이 되고, dept_no가 d001인 레코드를 한 건씩 읽으면서 employees 테이블과 조인하면서 LIMIT 100, 10조건이 만족될 때까지 조인이 수행 -> 총 110건 읽고, 110번 조인 |
SELECT * FROM (SELECT * FROM dept_emp WHERE dept_no = ‘d001’ LIMIT 100, 10) de, employees e WHERE e.emp_no = de.emp_no; |
dept_emp로 부터 가져온 10건의 레코드와만 10번 조인 |
7.4.7 GROUP BY
SELECT first_name, last_name, COUNT(*) FROM employees GROUP BY first_name ORDER BY first_name; |
EXPLAIN SELECT from_date FROM salaries GROUP BY from_date; |
|
EXPLAIN SELECT from_date FROM salaries GROUP BY from_date ORDER BY NULL; |
|
SELECT dept_no, COUNT(*) FROM dept_emp GROUP BY dept_no WITH ROLLUP; |
|
SELECT de.dept_no, SUM(CASE WHEN e.hire_date BETWEEN '1980-01-01' AND '1989-12-31' THEN 1 ELSE 0 END) AS cnt_1980, SUM(CASE WHEN e.hire_date BETWEEN '1990-01-01' AND '1999-12-31' THEN 1 ELSE 0 END) AS cnt_1990, SUM(CASE WHEN e.hire_date BETWEEN '2000-01-01' AND '2009-12-31' THEN 1 ELSE 0 END) AS cnt_2000, COUNT(*) AS cnt_total FROM dept_emp de, employees e WHERE e.emp_no = de.emp_no GROUP BY de.dept_no WITH ROLLUP; |
7.4.8 ORDER BY
7.4.9 서브 쿼리
SELECT * FROM dept_emp de WHERE de.emp_no = (SELECT e.emp_no FROM employees e WHERE e.first_name='Georgi' AND e.last_name='Facello' LIMIT 1); |
7.4.10 집합 연산
7.4.11 LOCK IN SHARE MODE와 FOR UPDATE
SELECT * FROM employees WHERE emp_no=10001 LOCK IN SHARE MODE; SELECT * FROM employees WHERE emp_no=10001 FOR UPDATE;
SELECT * FROM employees WHERE emp_no=10001 /*! LOCK IN SHARE MODE */; SELECT * FROM employees WHERE emp_no=10001 /*! FOR UPDATE */; |
7.4.12 SELECT INTO OUTFILE
SELECT emp_no, first_name, last_name INTO OUTFILE ‘c:\\temp\\result.csv’ FIELDS TERMINATED BY ‘,’ OPTIONALLY ENCLOSED BY ‘“‘ ESCAPED BY ‘“’ LINES TERMINATED BY ‘\r\n’ FROM employees WHERE emp_no BETWEEN 10001 AND 10100; |
7.5 INSERT
7.5.1 INSERT와 AUTO_INCREMENT (p.495)
CREATE TABLE tb_innodb (
fd1 CHAR,
fd2 INT AUTO_INCREMENT,
PRIMARY KEY(fd1, fd2),
UNIQUE KEY(fd2)
)ENGINE = INNODB;
CREATE TABLE tb_myisam (
fd1 CHAR,
fd2 INT AUTO_INCREMENT,
PRIMARY KEY(fd1, fd2)
)ENGINE = MYISAM;
INSERT INTO tb_innodb VALUES ('A', NULL), ('A', NULL), ('B', NULL),('B', NULL);
INSERT INTO tb_myisam VALUES ('A', NULL), ('A', NULL), ('B', NULL),('B', NULL);
CREATE TABLE tb_autoincrement(
seq_no INT NOT NULL AUTO_INCREMENT,
NAME VARCHAR(30),
PRIMARY KEY(seq_no)
);
INSERT INTO tb_autoincrement VALUES(NULL, 'hyeonseok lim');
SELECT LAST_INSERT_ID();
7.5.2 INSERT IGNORE (p.501)
7.5.3 REPLACE (p.502)
CREATE TABLE tb_replace (
fd1 INT NOT NULL,
fd2 INT NOT NULL,
PRIMARY KEY(fd1),
UNIQUE INDEX ux_fd2 (fd2)
);
INSERT INTO tb_replace(fd1, fd2) VALUES (1,1), (2,2), (3,3), (4,4), (5,5);
mysql> select * from tb_replace;
+-----+-----+
| fd1 | fd2 |
+-----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
+-----+-----+
replace into tb_replace(fd1, fd2) values (5,3); -- (5,3 ) 존재 하지 않는 값
mysql> select * from tb_replace;
+-----+-----+
| fd1 | fd2 |
+-----+-----+
| 1 | 1 |
| 2 | 2 |
| 5 | 3 |
| 4 | 4 |
+-----+-----+
(3,3) (5,5) 삭제 되고 (5,3) 투입
7.5.4 INSERT INTO ... ON DUPLICATE KEY UPDATE … (IU) (p.504)
CREATE TABLE tb_insertondup (
fd1 INT NOT NULL,
fd2 INT NOT NULL,
PRIMARY KEY(fd1)
);
INSERT INTO tb_insertondup VALUES (1,1), (2,2);
mysql> select * from tb_insertondup;
+-----+-----+
| fd1 | fd2 |
+-----+-----+
| 1 | 1 |
| 2 | 2 |
+-----+-----+
INSERT INTO tb_insertondup (fd1, fd2) VALUES (1, 100) ON DUPLICATE KEY UPDATE fd2= fd2 +100;
mysql> select * from tb_insertondup;
+-----+-----+
| fd1 | fd2 |
+-----+-----+
| 1 | 101 |
| 2 | 2 |
+-----+-----+
7.5.5 INSERT … SELECT … (p.506)
INSERT INTO temp_employees SELECT * FROM employees LIMIT 10;
7.5.6 LOAD DATA(LOCAL) INFILE …
7.6 UPDATE
7.6.1 UPDATE … ORDER BY … LIMIT n (p. 516)
UPDATE salaries
SET salary=salary * 1.1
ORDER BY salary DESC LIMIT 10;
사용자 변수 이용하면…
SET @ranking := 0;
UPDATE salaries
SET ranking = (@ranking := @ranking + 1)
ORDER BY salary DESC;
이런식으로 아주 유용하게 만들수 잇음 -> 자세한 얘기는 챕터 9에서...to be continued...
7.6.2 JOIN UPDATE (p.517)
CREATE TABLE tb_test1 (emp_no INT, first_name VARCHAR(14), PRIMARY KEY(emp_no));
INSERT INTO tb_test1
VALUES (10001, NULL), (10002, NULL), (10003, NULL), (10004, NULL);
UPDATE tb_test1 t1, employees e
SET t1.first_name= e.first_name
WHERE e.emp_no = t1.emp_no;
// 쿼리를 우선조인 하기 때문에 테이블의 조인 순서에 따라 update 문장의 성능이 달라짐
// MySQL 에서는 update 에 대해서는 실행 계획을 확인 할 수 없음
// 이럴때는 select 쿼리로 변경해 실행 계획을 확인 해봐야돼
SELECT * FROM tb_test1 t1, employees e
WHERE e.emp_no = t1.emp_no;
UPDATE departments d, dept_emp de
SET d.emp_count = COUNT(*)
WHERE de.dept_no = d.dept_no
GROUP BY de.dept_no; // 이러지마 에러
UPDATE departments d,
(SELECT de.dept_no, COUNT(*) AS emp_count FROM dept_emp de GROUP BY de.dept_no) dc
SET d.emp_count = dc.emp_count
WHERE dc.dept_no = d.dept_no;
7.7 DELETE
7.7.1 DELETE … ORDER BY … LIMIT n (p.520)
DELETE FROM employees ORDER BY first_name LIMIT 10;
7.7.2 JOIN DELETE (p. 520)
DELETE e FROM employees e, dept_emp de, departments d
WHERE e.emp_no = de.emp_no AND de.dept_no = d.dept_no
AND d.dept_no = 'd001';
DELETE e, de, d
FROM employees e, dept_emp de, departments d
WHERE e.emp_no = de.emp_no AND de.dept_no = d.dept_no
AND d.dept_no = 'd001';
7.8 스키마 조작 (DDL)
7.8.1 데이터베이스 (p. 522)
CREATE DATABASE IF NOT EXISTS employees;
SHOW DATABASES;
USE employees;
ALTER DATABASES employees CHARACTER SET=euckr COLLATE= euckr_korean_ci;
DROP DATABSE IF EXISTS employees;
7.8.2 테이블
SHOW CREATE TABLE employees ; // 칼럭의 목록과 인덱스, 외래키 정보를 동시에 보여줌
CREATE TABLE `employees` (
`emp_no` INT(11) NOT NULL,
`birth_date` DATE NOT NULL,
`first_name` VARCHAR(14) NOT NULL,
`last_name` VARCHAR(16) NOT NULL,
`gender` ENUM('M','F') NOT NULL,
`hire_date` DATE NOT NULL,
PRIMARY KEY (`emp_no`),
KEY `idx_first_name` (`first_name`)
) ENGINE=INNODB DEFAULT CHARSET=latin1
DESC employees; // 인덱스, 외래키 테이블 자체의 속성을 보여주지는 않음
ALTER TABLE employees CHARACTER SET ‘euckr’; // 기존 문자열을 변경하는게 아니고 앞으로 추가 될 문자열 변경
CREATE TABLE temp_emp_stat(hire_year INT NOT NULL, emp_count INT);
RENAME TABLE emp_stat TO backup_emp_stat, temp_emp_stat TO emp_stat;
// 하나의 rename table 명령으로 두 테이블의 이름 변경 작업을 처리
MySQL 은 네임 락이라는 잠금 이용해 rename table 명령에 있는 테이블에 잠금 걸고
작업 진행. 테이블을 바꾸는 동안 잠시 테이블이 없어질수 있지만 select 쿼리시
테이블이 없다는 에러를 받는게 아니고 rename table 이 완료될때 까지 기다림
RENAME TABLE db1.employees TO db2.employees;
SHOW TABLES status LIKE ‘employees’\G;
\G : 레코드의 칼럼을 라인당 하나씩 표현하게 하는 옵션
CREATE TABLE temp_employees LIKE employees;
CREATE TABLE temp_empolyees (
birth_date DATE not null…
...
) ENGINE = INNODB
AS
SELECT emp_no, birth_date, first name.. from employees LIMIT 10;
CREATE TABLE AS SELECT 명령은 SELECT 되는 칼럼과 저장할 칼럼의 이름이 일치해야 함
7.8.3 칼럼 변경
7.8.4 인덱스 변경
7.8.5 프로세스 조회
7.8.6 프로세스 강제 종료
7.8.7 시스템 변수 조회 및 변경
7.8.8 경고나 에러 조회
7.8.9 권한 조회 (p.546)
7.9 SQL 힌트
7.9.1 힌트의 사용법
SELECT * FROM employees USE INDEX (PRIMARY) WHERE emp_no = 10001;
SELECT * FROM employees /*! USE INDEX (PRIMARY) */ WHERE emp_no = 10001;
이런식으로 /* 뒤에 바로 ! 표시해서 힌트가 기술 될거란걸 SQL 서버에 알려줌
주석이 SQL 의 일부로 해석됨
7.9.2 STRAIGHT_JOIN
SELECT *
FROM employees e, dept_emp de, departments d
WHERE e.emp_no = de.emp_no AND d.dept_no = de.dept_no;
이 쿼리의 경우 어느테이블이 드라이빙이 되고 드리븐이 될지 알수 없음
SELECT /*! STRAIGHT_JOIN */ e.frist_name, e.last_name, d.dept_name
FROM employees e, dept_emp de, departments d
WHERE e.emp_no = de.emp_no AND d.dept_no = de.dept_no;
employees -> dept_emp -> departments 순으로 조인이 수행됨
힌트가 있으면 옵티마이저는 힌트를 맹신하고 그 힌트에 맞게 쿼리를 실행 함
7.9.3 USE INDEX / FORCE INDEX / IGNORE INDEX
7.9.4 SQL_CACHE / SQL_NO_CACHE
7.9.5 SQL_CALC_FOUND_ROWS
SELECT SQL_CALC_FOUND_ROWS * FROM employees LIMIT 5;
// 처음엔 5개만
SELECT FOUND_ROWS() ;
// 두번째엔 limit 와 관계없이 처음 select 쿼리 만족했던 모든 레코드 건수를 화면에 출력
7.9.6 기타 힌트
7.10 쿼리 성능 테스트
7.10.2 쿼리의 성능 테스트
7.10.3 쿼리 프로파일링
SELECT * FROM employees WHERE emp_no=10001;
SHOW PROFILES;
[Real MySQL] 14장 데이터 모델링 (0) | 2015.03.27 |
---|---|
[Real MySQL] 12장 쿼리 종류별 잠금 (0) | 2015.03.27 |
[Real MySQL] 6장 실행 계획 (0) | 2015.03.27 |
[Real MySQL] 5장 인덱스 (0) | 2015.03.27 |
[Real MySQL] 4장 트랜잭션과 잠금 (0) | 2015.03.27 |
2015. 3. 27. 12:13 Summary of/Real MySQL
6장 실행 계획
6.1 개요
6.1.1 쿼리 실행 절차 (p.263)
6.1.2 옵티마이저의 종류 (p.265)
6.1.3 통계 정보 (p.265)
6.2 실행 계획 분석
6.2.1 id 칼럼
EXPLAIN
SELECT e.emp_no, e.first_name, s.from_date, s.salary
FROM employees e, salaries s
WHERE e.emp_no = s.emp_no
LIMIT 10;
EXPLAIN
SELECT
((SELECT COUNT(*) FROM employees) + (SELECT COUNT(*) FROM departments)) AS total_count;
// id check
6.2.2 select_type 칼럼
EXPLAIN
SELECT * FROM (
(SELECT emp_no FROM employees e1 LIMIT 10)
UNION ALL
(SELECT emp_no FROM employees e2 LIMIT 10)
UNION ALL
(SELECT emp_no FROM employees e3 LIMIT 10)
)tb;
EXPLAIN
SELECT
e.first_name,
(SELECT CONCAT('Salary change count : ', COUNT(*)) AS message
FROM salaries s WHERE s.emp_no-e.emp_no
UNION
SELECT CONCAT('Department change count : ', COUNT(*)) AS message
FROM dept_emp de WHERE de.emp_no = .e.emp_no
)AS message
FROM employees e
WHERE e.emp_no=10001;
EXPLAIN
SELECT emp_no FROM salaries WHERE salary > 100000
UNION ALL
SELECT emp_no FROM dept_emp WHERE from_date > '2001-01-01';
EXPLAIN
SELECT
e.first_name,
(SELECT COUNT(*) FROM dept_emp de, dept_manager dm WHERE dm.dept_no = de.dept_no) AS cnt
FROM employees e
WHERE e.emp_no=10001;
EXPLAIN
SELECT e.first_name,
(SELECT COUNT(*)
FROM dept_emp de, dept_manager dm
WHERE dm.dept_no=de.dept_no AND de.emp_no=e.emp_no)AS cnt
FROM employees e
WHERE e.emp_no=10001;
EXPLAIN
SELECT *
FROM
(SELECT de.emp_no FROM dept_emp de) tb,
employees e
WHERE e.emp_no=tb.emp_no;
EXPLAIN
SELECT *
FROM emplo0yees e
WHERE e.emp_no = (
SELECT @status FROM dept_emp de WHERE de.dept_no="d005");
사용자 변수 (@status) : 다른 커넥션에서는 공유 못하고 해당 커넥션에서만 사용 가능
6.2.3 table 칼럼 (p.278 그림 참고)
6.2.4 type 칼럼
EXPLAIN SELECT * FROM employees WHERE emp_no=10005;
EXPLAIN SELECT * FROM dept_emp WHERE dept_no='d005';
//type check
EXPLAIN
SELECT * FROM dept_emp de, employees e
WHERE e.emp_no=de.emp_no AND de.dept_no='d005';
EXPLAIN
SELECT * FROM dept_emp WHERE dept_no='d005';
EXPLAIN
SELECT *
FROM employee_name
WHERE emp_no=10001
AND emp_no BETWEEN 10001 AND 10005
AND MATCH(first_name, list_name) AGAINST ('Facello' IN BOOLEAN MODE);
EXPLAIN SELECT * FROM titles WHERE to_date='1985-03-01' OR to_date IS NULL;
EXPLAIN SELECT * FROM departments WHERE dept_no IN(SELECT dept_no FROM dept_emp WHERE emp_no=10001);
EXPLAIN
SELECT * FROM departments WHERE dept_no IN(
SELECT dept_no FROM dept_emp WHERE dept_no BETWEEN 'd001' AND 'd003');
EXPLAIN
SELECT dept_no FROM dept_emp WHERE dept_no BETWEEN 'd001' AND 'd003';
EXPLAIN
SELECT * FROM employees
WHERE emp_no BETWEEN 10001 AND 11000
OR first_name='smith';
EXPLAIN
SELECT * FROM departments ORDER BY dept_name DESC LIMIT 10;
6.2.5 possible_keys
6.2.6 key
6.2.7 key_len
EXPLAIN
SELECT * FROM dept_emp WHERE dept_no='d005';
6.2.8 ref
EXPLAIN
SELECT *
FROM employees e, dept_emp de
WHERE e.emp_no=de.emp_no;
6.2.9 rows
6.2.10 Extra
EXPLAIN
SELECT DISTINCT d.dept_no
FROM departments d, dept_emp de WHERE de.dept_no=d.dept_no;
EXPLAIN
SELECT d.dept_no, NULL IN(SELECT id.dept_name FROM departments id)
FROM departments d;
EXPLAIN
SELECT e.emp_no, COUNT(*) AS cnt
FROM employees e
WHERE e.emp_no=10001
GROUP BY e.`emp_no`HAVING e.emp_no IS NULL;
EXPLAIN
SELECT *
FROM dept_emp de,
(SELECT emp_no FROM employees WHERE emp_no=1) tb1
WHERE tb1.emp_no=de.emp_no AND de.dept_no='d005';
EXPLAIN SELECT 1;
EXPLAIN
SELECT *
FROM dept_emp de
LEFT JOIN departments d ON de.dept_no=d.dept_no
WHERE d.dept_no IS NULL;
EXPLAIN
SELECT *
FROM employees e1, employees e2
WHERE e2.emp_no >= e1.emp_no;
EXPLAIN
SELECT table_name
FROM information_schema.`TABLES`WHERE table_schema = 'employees' AND table_name = 'employees';
EXPLAIN SELECT MAX(emp_no), MIN(emp_no) FROM employees;
EXPLAIN SELECT * FROM employees ORDER BY last_name DESC;
EXPLAIN
SELECT *
FROM dept_emp de, employees e
WHERE de.from_date > '2005-01-01' AND e.emp_no < 10904;
EXPLAIN
SELECT * FROM employees GROUP BY gender ORDER BY MIN(emp_no);
EXPLAIN
SELECT * FROM employees WHERE emp_no BETWEEN 10001 AND 10100 AND gender='F';
6.2.11 EXPLAIN EXTENDED(Filtered 칼럼)
EXPLAIN EXTENDED
SELECT * FROM employees
WHERE emp_no BETWEEN 10001 AND 10100 AND gender='F';
6.2.12 EXPLAIN EXTENDED(추가 옵티마이저 정보)
6.2.13 EXPLAIN PARTITIONS(Partitions 칼럼)
6.3 MySQL의 주요 처리 방식
6.3.1 풀 테이블 스캔 (p.330)
6.3.2 ORDER BY 처리(using filesort) (p.331)
SELECT * FROM employees e, salaries s
WHERE s.emp_no=e.emp_no
AND e.emp_no BETWEEN 100002 AND 100020;
SELECT * FROM employees e, salaries s
WHERE s.emp_no=e.emp_no AND e.emp_no BETWEEN 100002 AND 100010
ORDER BY e.last_name;
SELECT * FROM employees e, salaries s
WHERE s.emp_no=e.emp_no AND e.emp_no BETWEEN 100002 AND 100010
ORDER BY s.salary;
6.3.3 GROUP BY 처리 (p.347)
6.3.4 DISTINCT 처리 (p.351)
EXPLAIN
SELECT COUNT(DISTINCT s.salary)
FROM employees e, salaries s
WHERE e.emp_no=s.emp_no
AND e.emp_no BETWEEN 100001 AND 100100;
6.3.5 임시 테이블(Using temporary) (p.354)
SELECT * FROM employees GROUP BY last_name ORDER BY first_name;
group by 와 order by 칼럼이 다르고 last_name 칼럼에 인덱스가 없어 임시 테이블과 정렬작업까지 수행해야하는 형태
6.3.6 테이블 조인 (p.358)
FOR(record1 IN TABLE1){ FOR(record2 IN TABLE2){ IF(record1.join_column == record2.join_column){ join_record_found(record1.*, record2.*); }ELSE{ join_record_notfound(); } } } |
FOR(record1 IN TABLE1){ // OUTER FOR(record2 IN TABLE2){ // INNER IF(record1.join_column == record2.join_column){ join_record_found(record1.*, record2.*); }ELSE{ join_record_found(record1.*, NULL); } } } |
6.4 실행 계획 분석 시 주의사항
6.4.1 Select_type 칼럼의 주의 대상 (p.373)
6.4.2 Type 칼럼의 주의 대상 (p.374)
6.4.3 Key 칼럼의 주의 대상 (p.374)
6.4.4 Rows 칼럼의 주의 대상 (p.374)
6.4.5 Extra 칼럼의 주의 대상 (p.374)
[Real MySQL] 12장 쿼리 종류별 잠금 (0) | 2015.03.27 |
---|---|
[Real MySQL] 7장 쿼리 작성 및 최적화 (4) | 2015.03.27 |
[Real MySQL] 5장 인덱스 (0) | 2015.03.27 |
[Real MySQL] 4장 트랜잭션과 잠금 (0) | 2015.03.27 |
[Real MySQL] 3장 아키텍처 (0) | 2015.03.27 |
2015. 3. 27. 12:10 Summary of/Real MySQL
5.1 디스크 읽기 방식
5.1.1 저장 매체 (p.201)
5.1.2 디스크 드라이브와 솔리드 스테이트 드라이브 (p.202)
5.1.3 랜덤 I/O 와 순차 I/O (p.204)
5.2 인덱스란? (p.206)
5.3 B(Balanced) - Tree 인덱스
5.3.1 구조 및 특성 (p.209)
5.3.2 B-Tree 인덱스 키 추가 및 삭제 (p.211)
인덱스 키 추가
인덱스 키 삭제
인덱스 키 변경
인덱스 키 검색
5.3.3 B-Tree 인덱스 사용에 영향을 미치는 요소 (p.214)
인덱스 키 값의 크기
B-Tree 깊이
선택도
읽어야 하는 레코드의 건수
5.3.4 B-Tree 인덱스를 통한 데이터 읽기 (p. 219)
MySQL 이 인덱스를 이용하는 대표적인 방법 3가지
5.3.5 다중 칼럼(Multi-column) 인덱스 (p.223)
5.3.6 B-Tree 인덱스의 정렬 및 스캔 방향 (p.225)
인덱스의 정렬
인덱스 스캔 방향
5.3.7 B-Tree 인덱스의 가용성과 효율성 (p.228)
비교조건의 종류와 효율성
인덱스의 가용성
가용성과 효율성 판단 : 사용못하는 경우 (그냥 읽어보자)
5.4 해시(Hash) 인덱스
5.4.1 구조 및 특성(p.233)
5.4.2 해시 인덱스의 가용성 및 효율성 (p.235)
5.5 R-Tree 인덱스
5.5.1 구조 및 특성 (p.237)
5.5.2 R-Tree 인덱스의 용도 (p.240)
5.6 Fractal-Tree 인덱스
5.6.1 Fractal-Tree 의 특성 (p.241)
5.6.2 Fractal-Tree 의 가용성과 효율성 (p.243)
5.7 전문검색(Full Text Search) 인덱스
5.7.1 인덱스 알고리즘 (p.244)
5.7.2 구분자와 N-그램의 차이 (p.246)
5.7.3 전문 검색 인덱스의 가용성 (p.249)
5.8 비트맵 인덱스와 함수 기반 인덱스 (p.250)
5.9 클러스터링 인덱스
5.9.1 클러스터링 인덱스 (p.250)
5.9.2 보조 인덱스(Secondary Index)에 미치는 영향 (p.253)
5.9.3 클러스터 인덱스의 장점과 단점 (p.254)
5.9.4 클러스터 테이블 사용시 주의사항 (p.255)
5.10 유니크 인덱스
5.10.1 유니크 인덱스와 일반 보조 인덱스의 비교 (p.256)
5.10.2 유니크 인덱스 사용시 주의 사항 (p.257)
5.11 외래키
5.11.1 자식 테이블의 변경이 대기하는 경우 (p.259)
5.11.2 부모 테이블의 변경 작업이 대기하는 경우 (p.260)
5.12 기타 주의 사항 (p.260)
[Real MySQL] 12장 쿼리 종류별 잠금 (0) | 2015.03.27 |
---|---|
[Real MySQL] 7장 쿼리 작성 및 최적화 (4) | 2015.03.27 |
[Real MySQL] 6장 실행 계획 (0) | 2015.03.27 |
[Real MySQL] 4장 트랜잭션과 잠금 (0) | 2015.03.27 |
[Real MySQL] 3장 아키텍처 (0) | 2015.03.27 |
2015. 3. 27. 12:07 Summary of/Real MySQL
4.1 트랜잭션
4.1.1 MySQL에서의 트랜잭션
- 트랜잭션 : 하나 이상의 쿼리로 이루어진 논리적인 작업 셋 자체가 100% 적용되거나 또는 아무것도 적용되지 않아야 함을 보장해주는 것
* MyISAM/MEMORY/MERGE : 트랜잭션 지원X
* InnoDB : 트랜잭션 지원
INSERT INTO tab_a …; IF(_is_insert1_succeed){ INSERT INTO tab_b …; IF(_is_insert2_succeed){ // 처리 완료 }ELSE{ DELETE FROM tab_a WHERE …; IF(_is_delete_succeed){ // 처리 실패 및 tab_a, tab_b 모두 원상 복구 완료 }ELSE{ // 해결 불가능한 심각한 상황 발생 // tab_b에 INSERT는 안되고, 하지만 tab_a에는 // INSERT되어 버렸는데, 삭제는 안되고… } } } |
트랜잭션을 지원하지 않을 때 |
try{ START TRANSACTION; INSERT INTO tab_a …; INSERT INTO tab_b …; COMMIT; }catch(exception){ ROLLBACK; } |
트랜잭션을 지원할 때 |
4.1.2 주의사항
- 트랜잭션의 범위를 최소화하라
- 각 단위 프로그램이 커넥션을 소유하는 시간이 길어질수록 사용 가능한 여유 커넥션의 개수가 감소
- 각 단위 프로그램에서 커넥션을 가져가기 위해 기다려야 하는 상황 발생 가능성
- 메일 전송이나 FTP 파일 전송 작업 또는 네트워크를 통해 원격 서버와 통신하는 등과 같은 작업은 트랜잭션 내에서 제거하라
- 프로그램이 실행되는 동안 서버와 통신할 수 없는 상황이 발생한다면 웹 서버뿐 아니라 DBMS 서버까지 위험해지는 상황 발생 가능성
- 논리적 작업 셋에 따라 트랜잭션으로 분리하라
4.2 MySQL 엔진의 잠금
4.2.1 글로벌 락
- FLUSH TABLES WITH READ LOCK 명령으로만 획득 가능
- MySQL에서 제공하는 잠금 가운데 가장 범위가 큼( MySQL 서버 전체 )
- SELECT를 제외한 대부분의 DDL, DML 문장을 실행하는 경우 글로벌 락이 해제될 때까지 해당 문장이 대기 상태
- 덤프 파일로 일관된 백업을 받아야 할 때는 글로벌 락을 사용
4.2.2 테이블 락
- 개별 테이블 단위로 설정되는 잠금
- 명시적/ 묵시적으로 특정 테이블의 락을 획득 가능
- LOCK TABLES table_name [READ | WRITE] 명령으로 획득, UNLOCK TABLES 명령으로 반납
- MyISAM 이나 MEMORY 테이블은 묵시적으로 테이블 락
- InnoDB 테이블은 스키마를 변경하는 쿼리(DDL)의 경우에만 영향
4.2.3 유저 락
- GET_LOCK() 함수를 이용해 임의로 잠금 설정 가능
- 사용자가 지정한 문자열에 대해 획득하고 반납
- 여러 클라이언트가 상호 동기화를 처리해야 할 때 데이터베이스의 유저 락을 이용하면 쉽게 해결
- 많은 레코드를 한 번에 변경하는 트랜잭션의 경우에 유용하게 사용 - 배치 프로그램
4.2.4 네임 락
- 데이터베이스 객체의 이름을 변경하는 경우 획득
- 명시적으로 획득하거나 해제 불가능
mysql> RENAME TABLE rank TO rank_backup, rank_new TO rank; |
기존 rank를 백업하고 새로운 rank_new를 서비스용으로 대체하고자 하는 경우 |
mysql> RENAME TABLE rank TO rank_backup; mysql> RENAME TABLE rank_new TO rank; |
이런 식으로 하면 'Table not found 'rank'' 오류 발생 가능 |
4.3 MyISAM과 MEMORY 스토리지 엔진의 잠금
4.3.1 잠금 획득
- 읽기 잠금 : 테이블에 쓰기 잠금이 걸려 있지 않으면 읽기 잠금을 획득하고 읽기 작업을 시작
- 쓰기 잠금 : 테이블에 아무런 잠금이 걸려 있지 않아야만 쓰기 잠금 획득, 아닐 경우 다른 잠금이 해제될 때까지 대기
4.3.2 잠금 튜닝
- SHOW STATUS LIKE 'Tables%' 명령으로 테이블 락에 대한 작업 상황 확인 가능
- Tables_locks_immediate : 잠금이 풀리기를 기다리지 않고 잠금 획득 횟수
- Tables_locks_waited : 기다려야 했던 횟수
- 잠금 대기 쿼리 비율 = waited / (immediate + waited) * 100;
- 이 수치가 높다면 테이블을 분리 혹은 InnoDB 스토리지 엔진으로 변환 방법 고려
4.3.3 테이블 수준의 잠금 확인 및 해제
- SHOW OPEN TABLES 명령으로 서버의 모든 테이블 잠금 여부 확인 가능
- FROM DB명 LIKE '패턴' 을 추가하면 DB에 생성된 테이블 중 패턴이 일치하는 테이블만 확인
- 어떤 클라이언트의 커넥션이 잠금을 기다리고 있는 지 확인 하려면 SHOW PROCESSLIST 명령
- 클라이언트를 종료시키는 방법은 KILL QUERY 클라이언트_ID 명령
4.4 InnoDB 스토리지 엔진의 잠금
4.4.1 InnoDB의 잠금 방식
- 비관적 잠금 : 현재 트랜잭션에서 변경하고자 하는 레코드에 대해 잠금을 획득하고 변경 작업을 처리 - InnoDB는 비관적 잠금
- 낙관적 잠금 : 우선 변경 작업을 수행하고 마지막에 충돌이 있었는지 확인해 ROLLBACK 처리
4.4.2 InnoDB의 잠금 종류
- 레코드 락
- 레코드 자체만을 잠그는 것
- InnoDB의 레코드 락은 인덱스의 레코드를 잠근다
- 갭 락
- 레코드 그 자체가 아니라 레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 것
- 레코드 사이에 새로운 레코드가 생성되는 것을 제어
- 네스트 키 락
- 레코드 락과 갭 락을 합쳐 놓은 형태의 잠금
- 바이너리 로그에 기록되는 쿼리가 슬레이브에서 실행될 때 마스터에서 만들어 낸 결과와 동일한 결과를 만들어내도록 보장하는 것이 주 목적
- 데드락 발생 가능성 -> 바이너리 로그 포맷을 ROW 형태로 바꿔서 넥스트 키락 줄일 것
- 자동 증가 락
- AUTO_INCREMENT 컬럼에 중복되지 않고 저장된 순서대로 증가한 일련번호 값을 저장하기 위해 사용
- INSERT와 REPLACE 같은 새로운 레코드를 저장하는 쿼리에만 필요
- AUTO_INCREMENT 값을 가져오는 순간만 락이 걸렸다가 즉시 해제
- innodb_autoinc_lock_mode의 값으로 작동 방식 변경 가능
4.4.3 인덱스와 잠금
- InnoDB의 잠금은 인덱스를 잠그는 방식 -> 변경해야 할 레코드를 찾기 위해 검색한 인덱스의 레코드를 모두 잠가야 한다.
- 만약에 인덱스가 하나도 없다면 테이블을 풀 스캔하면서 모든 레코드를 잠그게 된다.
4.4.4 트랜잭션 격리 수준과 잠금
- 불필요한 레코드의 잠금 현상은 넥스트 키 락 때문에 발생
- 넥스트 키 락을 필요하게 만드는 주 원인은 바이너리 로그
- 레코드 기반의 바이너리 로그를 사용하거나 바이너리 로그를 사용하지 않는 경우 넥스트 키 락의 사용을 대폭 줄일 수 있다.
- 다음과 같은 설정의 조합으로 대부분의 넥스트 키 락을 제거 가능
버전 |
설정의 조합 |
MySQL 5.0 |
innodb_locks_unsafe_for_binlog=1 트랜잭션 격리 수준을 READ-COMMITTED로 설정 |
MySQL 5.1 이상 |
바이너리 로그를 비활성화 트랜잭션 격리 수준을 READ-COMMITTED로 설정 |
레코드 기반의 바이너리 로그 사용 innodb_locks_unsafe_for_binlog=1 트랜잭션 격리 수준을 READ-COMMITTED로 설정 |
4.5 MySQL의 격리 수준
|
DIRTY READ |
NON-REPEATABLE READ |
PHANTOM READ |
READ UNCOMMITTED |
발생 |
발생 |
발생 |
READ COMMITTED |
발생하지 않음 |
발생 |
발생 |
REPEATABLE READ |
발생하지 않음 |
발생하지 않음 |
발생 |
SERIALIZABLE |
발생하지 않음 |
발생하지 않음 |
발생하지 않음 |
4.5.1 READ UNCOMMITTED
각 트랜잭션에서의 변경 내용이 COMMIT이나 ROLLBACK 여부에 상관 없이 다른 트랜잭션에서 보여짐
<Dirty read>
4.5.2 READ COMMITTED
어떤 트랜잭션에서 데이터를 변경했어도 COMMIT이 완료된 데이터만 다른 트랜잭션에서 조회 가능
<Non-repeatable read>
4.5.3 REPEATABLE READ
InnoDB 스토리지 엔진에서 기본적으로 사용되는 격리 수준
트랜잭션이 ROLLBACK될 가능성에 대비해 변경되기 전 레코드를 언두 공간에 백업해두고 실제 레코드 값을 변경
<Phantom read>
4.5.4 SERIALIZABLE
한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서의 접근을 잠금
4.5.5 REPEATABLE READ 격리 수준과 READ COMMITTED 격리 수준의 성능 비교
의도적인 경우가 아니라면 REPEATABLE READ와 READ COMMITTED의 성능 차이는 거의 없다. (하나의 트랜잭션을 열어 그 트랜잭션에서 모든 테이블의 데이터를 SELECT 후, 그대로 계속 놔두면 InnoDB의 언두 영역이 계속 커져서 시스템 테이블 스페이스의 I/O가 유발되는 경우)
[Real MySQL] 12장 쿼리 종류별 잠금 (0) | 2015.03.27 |
---|---|
[Real MySQL] 7장 쿼리 작성 및 최적화 (4) | 2015.03.27 |
[Real MySQL] 6장 실행 계획 (0) | 2015.03.27 |
[Real MySQL] 5장 인덱스 (0) | 2015.03.27 |
[Real MySQL] 3장 아키텍처 (0) | 2015.03.27 |
2015. 3. 27. 12:01 Summary of/Real MySQL
3.1.1 MySQL의 전체 구조
MySQL
3.1.2 MySQL의 스레딩 구조 (p.103)
MySQL 서버는 프로세스기반이 아닌 스레드 기반으로 작동
INNODB
MyISAM
3.1.3 메모리 할당 및 사용 구조 (p.105)
글로벌 메모리 영역
로컬 메모리 영역 (세션 메모리 영역)
3.1.4 플러그인 스토리지 엔진 모델 (p.106)
MySQL 5.1 부터 전문 검색 엔진을 위한 파서(인덱싱할 키워드를 분리해내는 작업)도 플러그인 형대로 개발해서 사용 가능
MySQL 에서 쿼리가 실행되는 과정의 대부분의 작업이 MySQL 엔진에서 처리되고, 마지막 데이터 기/쓰기 작업만 스토리지 엔진에 의해 처리됨
핸들러(핸들러 객체) - 어떤 기능을 처리하기 위해 사용하는 운전대와 같은 역할을 하는 객체
하나의 쿼리작업은 여러 하위작업으로 나뉘는데, 각 하위 작업이 MySQL 엔진에서 처리되는지 스토리지 엔진영역에서 처리되는지 구분할 줄 알아야 함
show engines; - 지원되는 스토리지엔진
show plugins; - 플러그인 확인
3.1.5 쿼리 실행 구조 (p.110)
Parser : 사용자 요청으로 들어온 쿼리를 토큰으로 분리해 트리 형태의 구조로 만들어내는 작업
전처리기 : 파서 과정에서 만들어진 파서 트리를 기반으로 쿼리에 구조적 문제가 있는지 확인
옵티마이저(두뇌) : 사용자의 요청으로 들어온 쿼리를 저렴한 비용으로 가장빨리 처리할지 결정
실행엔진(손,발) :
핸들러(스토리지 엔진) : MySQL 서버의 가장 밑단에서 MySQL 실행 엔진의 요청에 따라 데이터를 디스크로 저장하고 디스크로부터 읽어오는 역할을 담당해
3.1.6 복제(Replication) (p.111)
복제 : 2대 이상의 MySQL 서버가 동일한 데이터를 담도록 실시간으로 동기화하는 기술
MySQL 에서는 쓰기와 읽기의 역할로 구분해, 전자를 마스터라하고 후자를 슬레이브라 하는데 일반적으로 MySQL 서버의 복제에서는 마스터는 반드시 1개이며 슬레이브는 1개 이상으로 구상 될 수 있어
마스터(Master)
슬레이브(Slave)
주의
참고
DML(Data Manipulation Language) - 스키마 객체의 데이터를 입력, 수정 ex) INSERT, UPDATE, DELETE, SELECT, LOCK TABLE, EXPLAIN PLAN, CALL...
DDL(Data Definition Language) - DB 의 스키마 객체를 생성, 변경, 제거,권한부여, 박탈...ex) CREATE. ALTER. DROP, TRUNCATE, GRANT, REVOKE, COMMENT...
DCL(Data Control Language) - 트랜잭션의 성격을 제어 ex) SET TRANSACTION, COMMIT, ROLLBACK, SAVEPOINT...
3.1.7 쿼리 캐시 (p.114)
타 DBMS 에는 없는 MySQL 의 독특한 기능중 하나
쿼리캐시의 결과를 내려보내기전 확인해야할 사항
쿼리캐시를 사용하지 않으려면?
3.2 InnoDB 스토리지 엔진 아키텍쳐
3.2.1 InnoDB 스토리지 엔진의 특성 (p.120)
3.2.2 InnoDB 버퍼 풀 (p.121)
MyISAM : 키 캐시가 인덱스의 캐시만을 주로 처리
InnoDB 버퍼 풀은 아직 디스크에 기록되지 않은 변경된 데이터 가지고 있어-called Dirty Page
3.2.3 언두(Undo) 로그 (p.122)
두가지 용도로 사용
3.2.4 인서트 버퍼(Insert Buffer) (p.122)
InnoDB 는 변경해야할 인덱스 페이지가 버퍼 풀에 있으면 바로 업데이트를 수행하지만, 그렇지 않고 디스크로부터 읽어와서 업데이트 해야 한다면 이를 즉시 실행하지 않고 임시 공간에 저장해 두고 바로 사용자에게 결과를 반환하는 형태로 성능을 향상시키게 되는데, 이때 사용하는 임시 메모리 공간을 인서트 버퍼(Insert Buffer) 라고 함
사용자에게 결과를 전달하기 전에 중복여부를 체크해야하는 유니크 인덱스는 인서트 버퍼로 사용 못함 (WHY?)
3.2.5 리두(Redo) 로그 및 로그 버퍼 (p.123)
디스크의 부하를 줄이기 위해 대부분의 DBMS 에는 변경된 데이터를 버퍼링해 두기 위해 InnoDB 버퍼 풀과 같은 장치가 포함되어 있어. 하지만 이 장치만으로는 ACID 를 보장 할 수 없는데 이를 위해 변경된 내용을 순차적으로 디스크에 기록하는 로그파일을 가지고 있어 이걸 리두(Redo) 로그라고 부름(보통 DBMS 에서 로그라 하면 리두 로그를 칭하는 경우가 많아)
리두 로그 버퍼링에 사용되는 공간 = 로그 버퍼
3.2.6 MVCC(Multi Version Concurrency Control) (p.124)
MVCC 의 목적 : 잠금을 사용하지 않는 일관된 읽기를 제공하는데 있어
Insert 문을 이용하여 member 테이블을 만든 후 update 실행하면 커밋 실행 여부와 상관없이 InnoDB 버퍼풀은 새로운 업데이트 값으로 변경 됨 그리고 디스크의 데이터 파일에는 체크포인트나 InnoDB 의 Write 스레드에 의해 새로운 값으로 업데이트 되었을수도 있고 아닐 수 도 있어
ex) create table member (
m_id int not null,
m_name varchar(20) not null,
m_area varchar(100) not null,
primary key(m_id),
index ix_area(m_area)
);
insert into member(m_id, m_name, m_area) values(12, 'hong kil dong', 'seoul');
update member set m_area='Kyungki' where m_id=12;
Q) select * from member where m_id = 12; 를 하면 어떤 데이터가 조회 될까요?
A. MySQL 초기화 파라미터에 설정된 격리 수준(Isolation) 에 따라 다름
a. 만약 격리수준이 READ_UNCOMMITED 인경우
i. 데이터가 커밋됐든 아니든, 변경된 상태의 데이터를 읽어서 반환
b. READ_COMMITED 나 그 이상의 격리수준(PEPEATABLE_READ, SERIALIZABLE) 인 경우
. 변경되기 이전의 내용을 보관하고 있는 언두 영역의 데이터를 반환
위의 과정을 DBMS 는 MVCC 라고 함
3.2.7 잠금 없는 일관된 읽기(Non-locking consistent read) (p.127)
3.2.8 InnoDB 와 MyISAM 스토리지 엔진 비교 (p.127)
MySQL 5.5 부터 InnoDB 스토리지 엔진이 기본 스토리지 엔진으로 채택
한줄요약 : InnoDB가 더 좋아
3.2.9 InnoDB 와 MEMORY(HEAP) 스토리지 엔진 비교 (p.131)
MEMORY 스토리지 엔진
메모리가 충분하다면 테이블 수준의 잠금을 사용하는 MEMORY 스토리지 엔진보다 레코드 수준의 잠금을 사용하는 InnoDB 스토리지 엔진이 훨씬 빠른 트랜잭션 처리를 보장해줌
3.3 MyISAM 스토리지 엔진 아키텍쳐
3.3.1 키 캐시 (p.133)
InnoDB의 버퍼 풀과 비슷한 역할을 하는 것이 MyISAM의 키 캐시(Key cache, 키 버퍼)
키 캐시는 인덱스만 대상으로 작동하며 또한 인덱스의 디스크 쓰기 작업에 대해서만 부분적으로 버퍼링 역할을 함
키 캐시 공간을 설정하는 파라미터 : key_buffer_size
3.3.2 운영체제의 캐시 및 버퍼 (p.135)
MyISAM 테이블의 데이터는 디스크로부터의 I/O 를 해결해 줄만한 어떠한 캐시나 버퍼링 기능이 MyISAM 스토리지 엔진에는 없어
3.4 MEMORY 스토리지 엔진 아키텍쳐
3.4.1 주의사항 (p.136)
3.4.2 MEMORY 스토리지 엔진의 용도 (p.136)
사용자가 명시적으로 MEMORY 테이블을 정의해서 사용할 때도 있지만, 사실 MEMORY 테이블은 MySQL 엔진이 쿼리를 처리하는 과정에서 임시로 생성되는 임시 테이블(Temporary table) 용도로 자주 사용됨
3.5 NDB(Network Database)클러스터 스토리지 엔진
3.5.1 NDB 클러스터의 특성 (p. 137)
3.5.2 NDB 클러스터의 아키텍처 (p. 140)
NDB 클러스터 노드의 종류
데이터 노드 간의 파티션 관리
3.5.3 클러스터 간의 복제 구성
3.5.4 NDB 클러스터의 성능
3.5.5 NDB 클러스터의 네트워크 영향
3.5.6 NDB 클러스터의 용도
3.6 TOKUDB 스토리지 엔진
3.6.1 프랙탈 트리 인덱스 지원
3.6.2 대용량 데이터와 빠른 INSERT 처리
3.6.2 트랜잭션 및 잠금 처리
3.6.4 그 이외의 특징
3.6.5 TokuDB의 주 용도
3.7 전문 검색 엔진
3.7.1 트리톤 전문 검색 엔진
3.7.2 mGroonga 전문 검색 엔진
3.7.3 스핑크스 전문 검색 엔진
3.8 MySQL 로그 파일
3.8.1 에러 로그 파일
3.8.2 제너럴 쿼리 로그 파일
3.8.3 슬로우 쿼리 로그
3.8.4 바이너리 로그와 릴레이 로그
[Real MySQL] 12장 쿼리 종류별 잠금 (0) | 2015.03.27 |
---|---|
[Real MySQL] 7장 쿼리 작성 및 최적화 (4) | 2015.03.27 |
[Real MySQL] 6장 실행 계획 (0) | 2015.03.27 |
[Real MySQL] 5장 인덱스 (0) | 2015.03.27 |
[Real MySQL] 4장 트랜잭션과 잠금 (0) | 2015.03.27 |