MySQL은 스웨덴 MySQL AB의 제품이었으나 썬(Sun)을 거쳐 2010년 오라클로 넘어갔다. 상용 DB 회사가 대표 오픈소스 DB를 인수하자 원 개발자 몬티 비데니우스가 이를 포크해 만든 것이 MariaDB다(딸 이름에서 따왔다). 그래서 RHEL 계열의 기본 배포 DB가 MariaDB로 교체됐다. 클라이언트 명령(mysql, mysqldump, mysqladmin)과 기본 SQL이 호환되므로 아래 내용은 양쪽에 함께 적용된다.
yum install mariadb-server mariadb # RHEL/CentOS/Rocky (서버 + 클라이언트)apt install mariadb-server # 데비안/우분투 (또는 mysql-server)systemctl start mariadb # 시작systemctl enable mariadb # 부팅 시 자동 시작 등록systemctl status mariadb # 상태 확인systemctl restart mariadb # 설정 변경 후 재시작
항목
RHEL 계열
데비안 계열
서비스(유닛)명
mariadb (MySQL 설치 시 mysqld)
mariadb 또는 mysql
메인 설정 파일
/etc/my.cnf
/etc/mysql/my.cnf
데이터 디렉터리
/var/lib/mysql
/var/lib/mysql
⚠️ 함정: 패키지명은 mariadb-server인데 서비스명은 mariadb다. systemctl start mariadb-server처럼 패키지명을 그대로 서비스명에 쓰는 보기가 오답으로 나온다. MySQL 커뮤니티 패키지를 깔면 서비스명이 mysqld가 되는 점, start만 하고 enable을 빠뜨리면 재부팅 후 DB가 올라오지 않는다는 점도 함정이다.
막 설치한 DB는 비밀번호 없는 root, 익명 사용자, 테스트 DB가 그대로 열려 있다.
이를 한 번에 정리하는 대화형 스크립트가 mysql_secure_installation이다.
단계
처리 내용
root 비밀번호
DB 관리자(root) 계정의 비밀번호 설정·변경
익명 사용자
이름 없이 접속 가능한 anonymous user 제거
원격 root 로그인
'root'@'%' 형태의 원격 root 접속 차단
test 데이터베이스
누구나 접근 가능한 기본 test DB 삭제
권한 테이블 재적재
변경 사항 즉시 반영(reload privilege tables)
여기서 말하는 DB의 root는 리눅스 시스템의 root와 완전히 별개의 계정이다. 이름만 같을 뿐 인증 정보도 권한 체계도 DB 안에 따로 존재하며, 리눅스 root 암호를 바꿔도 DB root 암호는 그대로다. "DB 관리자 계정의 비밀번호를 설정하는 명령"은 mysql_secure_installation 또는 mysqladmin -u root password '새암호'다.
mysqladmin -u root password 'NewP@ssw0rd' # 비대화형으로 root 암호 지정mysqladmin -u root -p ping # 데몬 생존 확인
mysql -u root -p # 로컬 접속(비밀번호는 프롬프트 입력)mysql -u web -p webdb # 접속과 동시에 webdb 선택mysql -h 192.168.1.50 -P 3306 -u web -p # 원격 서버 접속mysql -u root -p -e "SHOW DATABASES;" # SQL 한 줄 실행 후 종료
옵션
의미
-u
사용자명
-p
비밀번호 입력 프롬프트 (소문자)
-h
접속할 호스트(서버 IP·이름)
-P
포트 번호 (대문자, 기본 3306)
-e
셸에서 SQL 한 줄 실행 후 종료
⚠️ 함정: -p(소문자)는 비밀번호, -P(대문자)는 포트다. 대소문자를 바꿔 쓰는 보기가 있다. -p 뒤에 비밀번호를 붙일 때는 공백 없이-pMyPass로 써야 한다(띄우면 MyPass가 DB 이름으로 해석된다). 명령줄의 비밀번호는 ps와 셸 히스토리에 남으므로 실무에서는 -p만 쓰고 프롬프트에 입력한다.
접속 후 쓰는 기본 SQL은 아래와 같다. SQL 문은 세미콜론(;)으로 끝내야 실행된다.
-- 조회SHOW DATABASES; -- 데이터베이스 목록USE webdb; -- 사용할 데이터베이스 선택SHOW TABLES; -- 현재 DB의 테이블 목록DESC members; -- 테이블 구조 확인 (DESCRIBE 와 동일)-- 생성CREATE DATABASE webdb DEFAULT CHARACTER SET utf8mb4;CREATE TABLE members ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(30) NOT NULL, email VARCHAR(60), joined DATE);-- 조작INSERT INTO members (name, email, joined) VALUES ('홍길동', 'hong@ex.com', '2026-08-01');SELECT * FROM members WHERE name = '홍길동';UPDATE members SET email = 'new@ex.com' WHERE id = 1;DELETE FROM members WHERE id = 1;-- 삭제 / 종료DROP TABLE members; DROP DATABASE webdb;EXIT; -- QUIT; \q 도 동일
분류
이름
대표 명령
DDL
데이터 정의어
CREATE, ALTER, DROP, TRUNCATE
DML
데이터 조작어
SELECT, INSERT, UPDATE, DELETE
DCL
데이터 제어어
GRANT, REVOKE, COMMIT, ROLLBACK
DELETE는 DML이라 WHERE로 골라 지우고 트랜잭션으로 되돌릴 여지가 있지만, TRUNCATE는 DDL이라 테이블을 통째로 비우고 AUTO_INCREMENT도 초기화하며 일반적으로 롤백되지 않는다.
-- 계정 생성CREATE USER 'web'@'localhost' IDENTIFIED BY 'WebP@ss1'; -- 로컬 전용CREATE USER 'web'@'%' IDENTIFIED BY 'WebP@ss1'; -- 모든 호스트CREATE USER 'web'@'192.168.1.%' IDENTIFIED BY 'WebP@ss1'; -- 특정 대역만
호스트 표기
허용 범위
'user'@'localhost'
서버 자기 자신에서의 접속만
'user'@'%'
모든 호스트(원격 포함)
'user'@'192.168.1.%'
192.168.1.0/24 대역에서만
'user'@'192.168.1.50'
그 IP 하나에서만
권한은 GRANT로 주고 REVOKE로 회수한다.
GRANT ALL PRIVILEGES ON webdb.* TO 'web'@'localhost'; -- 전체 권한GRANT SELECT, INSERT, UPDATE ON webdb.* TO 'app'@'192.168.1.%'; -- 제한 부여REVOKE INSERT, UPDATE ON webdb.* FROM 'app'@'192.168.1.%'; -- 회수SHOW GRANTS FOR 'web'@'localhost'; -- 권한 확인SELECT user, host FROM mysql.user; -- 계정 목록ALTER USER 'web'@'localhost' IDENTIFIED BY 'NewP@ss2'; -- 비밀번호 변경DROP USER 'web'@'localhost'; -- 계정 삭제FLUSH PRIVILEGES; -- 권한 테이블 재적재
ON 다음의 대상 표기는 세 가지다.
*.* : 모든 DB의 모든 테이블
webdb.* : webdb의 모든 테이블
webdb.members : 그 테이블 하나만
실무 원칙은 필요한 DB에만 최소 권한을 주는 것이다.
FLUSH PRIVILEGES는 mysql.user 같은 권한 테이블을 UPDATE/INSERT로 직접 건드렸을 때 메모리의 권한 정보를 다시 읽어들이는 명령이다. GRANT·REVOKE·CREATE USER로 정상 경로를 거쳤다면 서버가 자동 반영하므로 원칙적으로 필요 없다. "권한 변경을 즉시 적용하는 명령"을 묻는 문제의 답이 될 수 있다.
'web'@'localhost'와 'web'@'%'는 이름이 같아도 서로 다른 계정이다. localhost 계정에만 권한을 주고 원격에서 붙으면 Access denied가 뜬다. 참고로 -h localhost는 유닉스 소켓(/var/lib/mysql/mysql.sock)으로, -h 127.0.0.1은 TCP 3306번으로 붙기 때문에 둘의 결과가 갈리기도 한다.
서버 동작은 my.cnf(옵션 파일)로 제어한다. 위치가 계열마다 다르다는 점이 시험 포인트다.
계열
메인 설정 파일
추가 설정 디렉터리
RHEL/CentOS
/etc/my.cnf
/etc/my.cnf.d/
데비안/우분투
/etc/mysql/my.cnf
/etc/mysql/mariadb.conf.d/ 또는 /etc/mysql/mysql.conf.d/
파일은 대괄호로 묶인 섹션 단위로 읽힌다.
서버 데몬 설정은 [mysqld], mysql 클라이언트 설정은 [client] 섹션에 쓴다.
[mysqld]port = 3306 # 서비스 포트 (기본 3306/TCP)bind-address = 0.0.0.0 # 수신 인터페이스datadir = /var/lib/mysql # 데이터 파일 저장 경로socket = /var/lib/mysql/mysql.sock # 로컬 접속용 소켓 파일log_error = /var/log/mariadb/mariadb.logmax_connections = 200 # 동시 접속 상한character-set-server = utf8mb4 # 서버 기본 문자셋
항목
의미
port
서비스 포트. 기본 3306/TCP
bind-address
접속을 받을 IP. 127.0.0.1은 로컬 전용, 0.0.0.0은 모든 인터페이스
datadir
실제 데이터베이스 파일이 놓이는 경로(/var/lib/mysql)
socket
로컬 접속에 쓰는 유닉스 소켓 파일 경로
log_error / max_connections
에러 로그 경로 / 동시 접속 허용 수
skip-networking
TCP 수신을 아예 끄고 소켓 접속만 허용
데비안 계열은 기본값이 bind-address = 127.0.0.1로 잡혀 있어 원격 접속이 막힌다. 계정을 'user'@'%'로 만들고 방화벽까지 열었는데 안 된다면 십중팔구 여기다. 반대로 0.0.0.0으로 열고 방화벽을 안 잠그면 DB가 그대로 노출된다. 또 하나, 설정을 고쳤으면 systemctl restart를 해야 반영된다.
# 실제 적용 중인 값은 서버에게 직접 물어본다mysql -u root -p -e "SHOW VARIABLES LIKE 'bind_address';"mysql -u root -p -e "SHOW VARIABLES LIKE 'datadir';"
이름 그대로 데이터베이스 내용을 SQL 문(CREATE TABLE, INSERT …)의 텍스트 파일로 뽑아내는(dump) 도구다.
mysqldump -u root -p webdb > /backup/webdb.sql # 단일 DBmysqldump -u root -p webdb members > /backup/members.sql # 특정 테이블만mysqldump -u root -p --databases webdb blogdb > /backup/two.sql # 여러 DBmysqldump -u root -p --all-databases > /backup/all.sql # 서버 전체
복구는 별도 명령이 아니다 — 덤프된 SQL 파일을 mysql 클라이언트에 입력 리다이렉션으로 흘려 넣는 것이다.
mysql -u root -p -e "CREATE DATABASE webdb;" # 대상 DB를 먼저 만들고mysql -u root -p webdb < /backup/webdb.sql # 덤프를 흘려 넣어 복구mysql -u root -p < /backup/all.sql # --all-databases 덤프는 DB명 없이
백업은 >(출력 리다이렉션), 복구는 <(입력 리다이렉션)이며 방향을 바꿔 쓰면 백업 파일이 비워진다. 더 결정적인 것 하나 — DB 이름만 준 단일 덤프에는 CREATE DATABASE 문이 들어 있지 않다. 복구 전에 대상 DB를 먼저 만들어야 하고, 만들지 않으면 Unknown database 오류가 난다. --databases나 --all-databases로 뜬 덤프에는 DB 생성 문이 포함되므로 DB명 없이 그대로 넣는다.
mysqldump는 결과물이 SQL 텍스트인 논리 백업이라 다른 서버로 옮기기 쉽다. 서비스를 세우지 않고 일관된 백업을 뜨려면 InnoDB 테이블에 --single-transaction을 쓴다. 반면 /var/lib/mysql을 tar로 묶는 물리 백업은 반드시 데몬을 정지한 뒤 해야 한다 — 동작 중 복사하면 깨진 파일이 나온다.
mysql -u root -p -e "SHOW VARIABLES LIKE 'log_error';" # 경로를 직접 확인tail -f /var/log/mariadb/mariadb.log # RHEL 계열 MariaDBtail -f /var/log/mysqld.log # RHEL 계열 MySQLtail -f /var/log/mysql/error.log # 데비안 계열journalctl -u mariadb -n 50 # 데몬이 아예 안 뜰 때
로그 종류
설정 항목
기록 내용
에러 로그
log_error
기동·정지 메시지, 치명적 오류
일반 질의 로그
general_log
모든 질의(부담이 커 상시 사용 비권장)
슬로우 질의 로그
slow_query_log
지정 시간 초과 질의(튜닝용)
바이너리 로그
log_bin
변경 이력(복제·시점 복구용)
원격 접속 실패는 원인이 정해져 있다. 아래 순서로 좁히면 대부분 잡힌다.
증상·원인
확인 방법
조치
데몬 미동작
systemctl status mariadb
systemctl start mariadb
방화벽 차단
firewall-cmd --list-all
3306/TCP 허용
bind-address가 127.0.0.1
SHOW VARIABLES LIKE 'bind_address';
0.0.0.0으로 변경 후 재시작
계정 호스트가 localhost만
SELECT user,host FROM mysql.user;
'user'@'%' 계정 생성
권한 부족
SHOW GRANTS FOR 'user'@'%';
필요한 GRANT 부여
ss -tlnp | grep 3306 # 포트가 열려 대기 중인지firewall-cmd --permanent --add-port=3306/tcp && firewall-cmd --reload
접속 실패 메시지 두 가지를 구분하면 진단이 반으로 준다. Access denied for user ...는 서버까지 도달했으나 계정·비밀번호·권한에서 거절된 것(DB 안쪽 문제)이고, Can't connect to MySQL server ...는 연결 자체가 닿지 않은 것(데몬 미동작·방화벽·bind-address 등 바깥쪽 문제)이다.