본문으로 건너뛰기

Full Stack JavaScript Developer | Half-time Open Sourcerer.

View All Authors

CentOS Maria DB 설치

· 약 4분

Maria DB 를 써야하는 이유는 MySQL 이 지원이 끝났기 때문이고, 오라클이 소유하고 있어 언제 유료화가 될지 모르고, Thread Pool, 강화된 스토리지 엔진 (InnoDB -> XtraDB), 새로운 스토리지 엔진 (Aria, Cassandra)과 HandlerSocket, Virtual Column 등의 새로운 기능이 있기 때문이다.

설치를 시작해보자!

버전 확인 후 yum repo 추가

버전 및 bit 확인

## get version
$ cat /etc/*release*
## get bit
$ getconf LONG_BIT

yum repo 복사

maria로 이동해 맞는 버전을 추가한다.

image from hexo

yum repo 추가

vi /etc/yum.repos.d/MariaDB.repo

명령어로 MariaDB repository 를 생성한 뒤 복사한 내용을 붙히고 저장한다.

설치

yum install -y MariaDB-server MariaDB-client

혹여 설치가 안되면 maria 문서를 참고해서 따라해보자 (영어)

부팅 서비스 등록

$ chkconfig mysql on
## 또는
$ chkconfig --add mysql
$ chkconfig --level 345 mysql on

## 등록 확인
$ chkconfig --list mysql

maria에서는 345 레벨을 on 하라고 했는데, chkconfig mysql on 으로 실행시켜 2345 레벨을 모두 on 시켰다. 2 레벨은 not networking 이라 DB 의 원격지 접속이 안될테니 off 시켜도 무관하다.

서비스 실행

service mysql start

Starting MySQL.... [ OK ]

Maria 의 서비스명은 MySQL 로 뜬다.

Config 파일 수정 및 통합

처음 설치시 my.cnf 에서 my-server, my-client 등의 파일을 임포트해 분할 관리하게 되어있는데, 하나로 합쳐보자.

MariaDB 설치 폴더를 들어가면 My innoDB Huge 라는 config 파일이 존재한다. 이걸 그대로 사용해도 되고, 사용자 환경에 맞게 커스터마이징해서 사용해도 된다.

서버의 메모리가 8 GB 이상, innoDB 환경에서 사용가능한 config 파일을 첨부하니 이걸 써도 된다.

Thread Pool 기능을 사용하기 위해 extra_port 를 3307 로 줬다.

소스

my.cnf
[client]
port = 3306
socket = /var/lib/mysql/mysql.sock

[mysqld]
port = 3306
socket = /var/lib/mysql/mysql.sock

character-set-server = utf8mb4
collation_server = utf8mb4_general_ci

back_log = 100
max_connections = 100
max_connect_errors = 10
table_open_cache = 2048
max_allowed_packet = 16M

binlog_cache_size = 4M
binlog_format = row

max_heap_table_size = 16M

read_buffer_size = 2M
read_rnd_buffer_size = 16M
sort_buffer_size = 8M
join_buffer_size = 8M

query_cache_type = 0
default-storage-engine = InnoDB
thread_stack = 256K
tmp_table_size = 16M

secure_auth =1

skip_external_locking
skip_symbolic_links

## Replication related settings
server-id = 1
expire_logs_days = 3
log_slave_updates

## MyISAM Specific options
key_buffer_size = 32M
bulk_insert_buffer_size = 64M
myisam_sort_buffer_size = 8M
myisam_max_sort_file_size = 16M
myisam_repair_threads = 1
myisam_recover = FORCE,BACKUP

## INNODB Specific options
innodb_additional_mem_pool_size = 16M
innodb_buffer_pool_size = 2G
innodb_data_file_path = ibdata1:10M:autoextend
#innodb_data_home_dir = <directory>
innodb_thread_concurrency = 16
innodb_flush_log_at_trx_commit = 0

innodb_log_buffer_size = 32M
innodb_log_file_size = 1024M
innodb_log_files_in_group = 2
#innodb_log_group_home_dir

innodb_max_dirty_pages_pct = 75

[mysqldump]
quick
max_allowed_packet = 16M
default-character-set = utf8

[mysql]
no-auto-rehash

[myisamchk]
key_buffer_size = 512M
sort_buffer_size = 512M
read_buffer = 8M
write_buffer = 8M

[mysqlhotcopy]
interactive-timeout

[mysqld_safe]
open-files-limit = 8192

[mariadb]
## thread pool
thread_handling=pool-of-threads
thread_pool_idle_timeout = 3600
thread_pool_stall_limit = 100
extra_port = 3307
extra_max_connections=10

MySQL Dump 명령어

· 약 1분

MySQL Dump 명령어 사용법

스크립트 생성

vi dump.sh

dump.sh 파일을 만들고 아래 내용을 추가해 준다.

mysqldump -u 유저명 -p비밀번호 데이터베이스명> 경로`date+%y%m%d%H`.sql

스크립트를 원하는 시간대로 cron 에 등록한다.

예제

ID : test, PW: test, DB : test 의 dump 를 생성하고 싶을 때

mysqldump -u test-ptest test> /home/test/dump/`date+%y%m%d%H`.sql

Database 유저 생성 및 권한 설정

· 약 1분

유저 생성

CREATE USER '유저명'@'%' IDENTIFIED BY '비밀번호'; -- 모든 아이피 접근
CREATE USER '유저명'@'localhost' IDENTIFIED BY '비밀번호'; -- 로컬호스트 접근
CREATE USER '유저명'@'127.0.0.1' IDENTIFIED BY '비밀번호'; -- 로컬호스트 접근

데이터베이스에 모든 권한 부여

-- 해당 디비명의 유저의 모든권한 설정
GRANT ALL PRIVILEGES ON 디비명.* TO '유저명'@'%' WITH GRANT OPTION;

권한 적용

FLUSH PRIVILEGES;

권한 확인

SHOW GRANTS FOR '유저명'@'%';

여담

권한 부여는 쉘상에서 접근해야지 오류가 없다.

부분 권한 또는 부분 테이블로 권한을 제한하고 싶다면, MySQL Grant Syntax를 참조한다.

MySQL에서 MariaDB 이관시 서브쿼리의 정렬이 바뀔 때

· 약 2분

@Rownum 변수를 사용해 페이징 쿼리를 구현했는데, MySQL 에서는 정상적으로 정렬이 되다가 Maria 로 이관 후에 정렬이 반대로 나오는 경우가 있다.

오류

쿼리

SELECT SQL_CALC_FOUND_ROWS @RNUM:=@RNUM+1 AS ROWNUM, R.* FROM (
SELECT @RNUM:=0, Q.* FROM (

SELECT * FROM TABLE1
WHERE ID = '1'
ORDER BY REG DESC
) Q
) R
LIMIT 0,10

설명

Rownum 변수를 사용해 TABLE1 에서 최신 날짜 순으로 데이터를 가지고 온다.

해결

LIMIT 2^64-1를 서브쿼리 내에 추가해준다.

LIMIT 의 최댓값은 unsigned 64bit-1 이므로 해당 값을 날려준다.

쿼리

SELECT SQL_CALC_FOUND_ROWS @RNUM:=@RNUM+1 AS ROWNUM, R.* FROM (
SELECT @RNUM:=0, Q.* FROM (

SELECT * FROM TABLE1
WHERE ID = '1'
ORDER BY REG DESC

LIMIT 18446744073709551615
) Q
) R
LIMIT 0,10

원인

Optimizer 가 임시테이블을 만든 후 filesort 하기 때문에 발생한다.

MySQL 버전 및 설정 확인

· 약 1분

Database Migration 또는 Module 설치시 MySQL 버전을 자주 물어본다.

쿼리

SELECT version();

SHOW variables LIKE 'datadir';

설명

첫번째 쿼리 실행시 버전이 나오고, 두번째 쿼리 실행시 설치 경로가 나온다.

MySQL Lock 해제

· 약 1분

잘못된 업데이트 쿼리를 돌리다가 테이블이 잠길 경우 Lock을 해제해줘야 한다.

쿼리

show processlist

kill ${processid}

설명

show processlist 쿼리를 날리면 현재 database를 사용 중인 프로세스의 목록이 나온다. 목록에서 해당 쿼리를 찾은 뒤 process id를 가지고 kill 쿼리를 실행하면 된다.

HTML 태그 모두 제거

· 약 1분

스마트 에디터 같은 에디터를 쓸 대 내용을 텍스트로만 받아야할 경우가 있다.

예제

var data = data.replace(/[<][^>]*[>]/g, "");

설명

< > 안의 태그들을 모두 빈값으로 치환한다.

JSON.parse unexpected type Error

· 약 2분

database 에 이미 json 으로 저장된 데이터를 가져와 JSON.parse 메소드로 JSON 데이터를 파싱하는 중에 값이 string 인데도 오류가 나는 경우가 있다.

로그를 확인해보면

{"data":"[{"name":"gracefullight"},{"name","daniel"}]"}

[] 배열 기호 앞뒤로 따움표가 들어가 있다. 이중 object 가 있다면 선언문 앞뒤로 따움표가 들어가 파싱 오류가 난다.

원인

jQuery.ajax 의 dataType 을 json 으로 설정해 데이터를 받는데, 이미 json 형태의 string 이 넘어와 두번 치환이 된 것 같다.

다시 json 을 인식할 수 있게 치환해보자

소스

const jsonReplace = function (j) {
if (j) {
j = j
.replace(/"\[/g, "[")
.replace(/\]"/g, "]")
.replace(/"\{/g, "{")
.replace(/\}"/g, "}");
}
return j;
};

설명

해당 함수로 오류가 나는 json string 을 치환 후에 다시 JSON.parse 를 실행하면 된다.

여담

정규식을 활용해 replace 중첩을 피하게 짤 수 있을 것 같은데...

hasOwnProperty vs prototype.hasOwnProperty

· 약 2분

hasOwnProperty

for-in 구문을 쓸 때 상속된 프로퍼티나 this 구문을 방지하기 위하여 보통 hasOwnProperty 로 체크한다.

for (prop in obj) {
// eslint-disable-next-line no-prototype-builtins
if (obj.hasOwnProperty(prop)) {
// code...
}
}

문제점

위와 같은 방식은 아래와 같이 hasOwnProperty 가 재정의 된 객체에서 오류를 발생할 수 있다.

소스

const obj = {
hasOwnProperty: function () {
return false;
},
data1: 1,
};

for (prop in obj) {
// obj.hasOwnProperty(prop) 의 결과는 항상 false
// eslint-disable-next-line no-prototype-builtins
if (obj.hasOwnProperty(prop)) {
// 이 구문은 실행되지 않는다.
}
}

prototype.hasOwnProperty

따라서 prototype 을 사용해 코딩해야한다.

소스

for (prop in obj) {
// 첫번째 방법
if ({}.hasOwnProperty.call(obj, prop)) {
// 체크
}

// 두번째 방법
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
// 체크
}
}

설명

첫번째 방법은 익명 Object 를 생성한 후 obj 와 prop 을 밀어넣는 방식이고 두번째 방법은 기본 Object 의 hasOwnProperty 를 가져와 obj 와 prop 을 밀어넣는 방식이다.

두번째 방법이 새(익명) Object 를 생성하지 않기에 이 방법을 사용하면 된다.

더 자세하게 알고 싶다면 mdn을 참조한다.