MHA 특징

  • 한번의 failover 수행만 가능 ( mha daemon 은 한번의 failover 만 수행 가능 / failover 후 재구성 필요 )
  • candidate master 중 1번째로 지정된 slave 가 master 로 전환되며 기존 master 에 붙어있던 slave 는 new slave 로 전환
  • MHA로 failover가 되면, 모든 slave Node에 down되기 직전의 master Node 데이터 변경 내용을 모두 적용 함.
  • 모든 Node에 최신 데이터 변경 내용을 적용 후 시작 되기 때문에, master - slave 간 데이터 동기화 지연이 클수록 복구 시간이 오래 걸림.
  • vip 를 이동하는 형태로 failover 되므로 서버에서 새로운 마스터 장비의 ip 변경이 불필요함 ( old master 장비에 셋팅되어 있던 VIP를 new master 장비에 셋팅하는 방식 .. Application은 서버 정상 일때나 failover가 된 이후 모두 VIP로 DB에 접속함 )
  • MHA를 통해 DB가 스위칭 될때 일시적으로 기존 연결이 끊어지므로, application 서버는 connection 재구성 예외 처리 로직이 필요

MHA 구성 조건

  • MySQL 서버 최소 2대 이상 필요
  • MySQL Master - Slave Replication 구성 필요
  • Manager Server 1대 필요(Slave 장비에서 구성 가능)
  • VIP 1개
  • test 환경 master : 10.10.10.10 slave : 10.10.10.20 vip : 10.10.10.30

MySQL 설치

아래 rpm 설치 후 진행

yum install -y automake \
binutils bison \
gcc gcc-c++ \
gettext libtool \
make openssl \
openssl openssl-devel \
zlib zlib-devel \
cmake \
gcc-toolset-10-gcc gcc-toolset-10-gcc-c++ gcc-toolset-10-binutils gcc-toolset-10-annobin \
libaio libaio-devel \
perl ncurses-devel \
git redhat-rpm-config rpm-build rpm-sign \
cyrus-sasl cyrus-sasl-devel \
curl curl-devel \
libtirpc-devel \
libudev-devel

MHA 설치

rpm 다운로드 링크

manager : https://github.com/yoshinorim/mha4mysql-manager
node : https://github.com/yoshinorim/mha4mysql-node

mha 구성을 위해 필요한 rpm 설치

dnf config-manager --set-enabled powertools
yum install perl-Module-Install

yum install perl
yum install perl*

yum install perl-DBD-MySQL
yum install perl-Config-Tiny
yum install perl-Log-Dispatch
yum install perl-Parallel-ForkManager
yum install perl-Class-Load
yum install perl-Data-OptList
yum install perl-Date-Manip
yum install perl-Email-Date-Format
yum install perl-Log-Dispatch-FileRotate
yum install perl-Log-Log4perl
yum install perl-Mail-Sender
yum install perl-Mail-Sendmail
yum install perl-MailTools
yum install perl-MIME-Lite
yum install perl-MIME-Types
yum install perl-Params-Util
yum install perl-Params-Validate
yum install perl-Sub-Install
yum install perl-TimeDate
yum install perl-XML-DOM
yum install perl-XML-RegExp

install module 오류
해결 : yum install perl-Module-Install

Can't locate inc/Module/Install.pm in @INC (you may need to install the inc::Module::Install module) (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5) at Makefile.PL line 1.
BEGIN failed--compilation aborted at Makefile.PL line 1.

perl-Log-Dispatch 오류

powertools 관련 rpm 설치

해결 : dnf config-manager --set-enabled powertools

perl-PAR-Packer-Tk conflicting 오류
yum install perl* 실행 시 아래 에러는 무시 가능

Error:
 Problem 1: conflicting requests
  - nothing provides perl(Tk::ColoredButton) needed by perl-PAR-Packer-Tk-1.052-2.el8.noarch
  - nothing provides perl(Tk::EntryCheck) needed by perl-PAR-Packer-Tk-1.052-2.el8.noarch
  - nothing provides perl(Tk::Getopt) needed by perl-PAR-Packer-Tk-1.052-2.el8.noarch
  - nothing provides perl(Tk::Pod) needed by perl-PAR-Packer-Tk-1.052-2.el8.noarch
 Problem 2: conflicting requests
  - nothing provides perl-HTML-Strip needed by perl-sword-1.8.1-18.el8.x86_64
(try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages)

mha node 설치

모든 장비에 설치

tar -zxf mha4mysql-node-0.57.tar.gz
cd mha4mysql-node-0.57
perl Makefile.PL && make && make install

mha4mysql-node-0.57]# perl Makefile.PL && make && make install
*** Module::AutoInstall version 1.06
*** Checking for Perl dependencies...
[Core Features]
- DBI ...loaded. (1.627)
- DBD::mysql ...loaded. (4.023)
*** Module::AutoInstall configuration finished.
Checking if your kit is complete...
Looks good
Writing Makefile for mha4mysql::node
cp lib/MHA/BinlogManager.pm blib/lib/MHA/BinlogManager.pm
cp lib/MHA/BinlogPosFindManager.pm blib/lib/MHA/BinlogPosFindManager.pm
cp lib/MHA/BinlogPosFinderXid.pm blib/lib/MHA/BinlogPosFinderXid.pm
cp lib/MHA/BinlogHeaderParser.pm blib/lib/MHA/BinlogHeaderParser.pm
cp lib/MHA/BinlogPosFinder.pm blib/lib/MHA/BinlogPosFinder.pm
cp lib/MHA/NodeUtil.pm blib/lib/MHA/NodeUtil.pm
cp lib/MHA/BinlogPosFinderElp.pm blib/lib/MHA/BinlogPosFinderElp.pm
cp lib/MHA/SlaveUtil.pm blib/lib/MHA/SlaveUtil.pm
cp lib/MHA/NodeConst.pm blib/lib/MHA/NodeConst.pm
cp bin/filter_mysqlbinlog blib/script/filter_mysqlbinlog
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/filter_mysqlbinlog
cp bin/apply_diff_relay_logs blib/script/apply_diff_relay_logs
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/apply_diff_relay_logs
cp bin/purge_relay_logs blib/script/purge_relay_logs
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/purge_relay_logs
cp bin/save_binary_logs blib/script/save_binary_logs
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/save_binary_logs
Manifying blib/man1/filter_mysqlbinlog.1
Manifying blib/man1/apply_diff_relay_logs.1
Manifying blib/man1/purge_relay_logs.1
Manifying blib/man1/save_binary_logs.1
Installing /usr/local/share/perl5/MHA/BinlogManager.pm
Installing /usr/local/share/perl5/MHA/BinlogPosFindManager.pm
Installing /usr/local/share/perl5/MHA/BinlogPosFinderXid.pm
Installing /usr/local/share/perl5/MHA/BinlogHeaderParser.pm
Installing /usr/local/share/perl5/MHA/BinlogPosFinder.pm
Installing /usr/local/share/perl5/MHA/NodeUtil.pm
Installing /usr/local/share/perl5/MHA/BinlogPosFinderElp.pm
Installing /usr/local/share/perl5/MHA/SlaveUtil.pm
Installing /usr/local/share/perl5/MHA/NodeConst.pm
Installing /usr/local/share/man/man1/filter_mysqlbinlog.1
Installing /usr/local/share/man/man1/apply_diff_relay_logs.1
Installing /usr/local/share/man/man1/purge_relay_logs.1
Installing /usr/local/share/man/man1/save_binary_logs.1
Installing /usr/local/bin/filter_mysqlbinlog
Installing /usr/local/bin/apply_diff_relay_logs
Installing /usr/local/bin/purge_relay_logs
Installing /usr/local/bin/save_binary_logs
Appending installation info to /usr/lib64/perl5/perllocal.pod

mha manager 설치

manager 장비에서 설치

cd /home/dba_admin/mha-5.7/
tar -zxf mha4mysql-manager-0.57.tar.gz
cd mha4mysql-manager-0.57
perl Makefile.PL && make && make install

mha4mysql-manager-0.57]# perl Makefile.PL && make && make install
*** Module::AutoInstall version 1.06
*** Checking for Perl dependencies...
[Core Features]
- DBI ...loaded. (1.627)
- DBD::mysql ...loaded. (4.023)
- Time::HiRes ...loaded. (1.9725)
- Config::Tiny ...loaded. (2.14)
- Log::Dispatch ...loaded. (2.41)
- Parallel::ForkManager ...loaded. (1.18)
- MHA::NodeConst ...loaded. (0.57)
*** Module::AutoInstall configuration finished.
Checking if your kit is complete...
Looks good
Writing Makefile for mha4mysql::manager
cp lib/MHA/ManagerUtil.pm blib/lib/MHA/ManagerUtil.pm
cp lib/MHA/Config.pm blib/lib/MHA/Config.pm
cp lib/MHA/HealthCheck.pm blib/lib/MHA/HealthCheck.pm
cp lib/MHA/ServerManager.pm blib/lib/MHA/ServerManager.pm
cp lib/MHA/ManagerConst.pm blib/lib/MHA/ManagerConst.pm
cp lib/MHA/ManagerAdmin.pm blib/lib/MHA/ManagerAdmin.pm
cp lib/MHA/FileStatus.pm blib/lib/MHA/FileStatus.pm
cp lib/MHA/MasterFailover.pm blib/lib/MHA/MasterFailover.pm
cp lib/MHA/ManagerAdminWrapper.pm blib/lib/MHA/ManagerAdminWrapper.pm
cp lib/MHA/MasterMonitor.pm blib/lib/MHA/MasterMonitor.pm
cp lib/MHA/MasterRotate.pm blib/lib/MHA/MasterRotate.pm
cp lib/MHA/SSHCheck.pm blib/lib/MHA/SSHCheck.pm
cp lib/MHA/Server.pm blib/lib/MHA/Server.pm
cp lib/MHA/DBHelper.pm blib/lib/MHA/DBHelper.pm
cp bin/masterha_stop blib/script/masterha_stop
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/masterha_stop
cp bin/masterha_conf_host blib/script/masterha_conf_host
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/masterha_conf_host
cp bin/masterha_check_repl blib/script/masterha_check_repl
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/masterha_check_repl
cp bin/masterha_check_status blib/script/masterha_check_status
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/masterha_check_status
cp bin/masterha_master_monitor blib/script/masterha_master_monitor
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/masterha_master_monitor
cp bin/masterha_check_ssh blib/script/masterha_check_ssh
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/masterha_check_ssh
cp bin/masterha_master_switch blib/script/masterha_master_switch
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/masterha_master_switch
cp bin/masterha_secondary_check blib/script/masterha_secondary_check
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/masterha_secondary_check
cp bin/masterha_manager blib/script/masterha_manager
/usr/bin/perl "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/masterha_manager
Manifying blib/man1/masterha_stop.1
Manifying blib/man1/masterha_conf_host.1
Manifying blib/man1/masterha_check_repl.1
Manifying blib/man1/masterha_check_status.1
Manifying blib/man1/masterha_master_monitor.1
Manifying blib/man1/masterha_check_ssh.1
Manifying blib/man1/masterha_master_switch.1
Manifying blib/man1/masterha_secondary_check.1
Manifying blib/man1/masterha_manager.1
Installing /usr/local/share/perl5/MHA/ManagerUtil.pm
Installing /usr/local/share/perl5/MHA/Config.pm
Installing /usr/local/share/perl5/MHA/HealthCheck.pm
Installing /usr/local/share/perl5/MHA/ServerManager.pm
Installing /usr/local/share/perl5/MHA/ManagerConst.pm
Installing /usr/local/share/perl5/MHA/ManagerAdmin.pm
Installing /usr/local/share/perl5/MHA/FileStatus.pm
Installing /usr/local/share/perl5/MHA/MasterFailover.pm
Installing /usr/local/share/perl5/MHA/ManagerAdminWrapper.pm
Installing /usr/local/share/perl5/MHA/MasterMonitor.pm
Installing /usr/local/share/perl5/MHA/MasterRotate.pm
Installing /usr/local/share/perl5/MHA/SSHCheck.pm
Installing /usr/local/share/perl5/MHA/Server.pm
Installing /usr/local/share/perl5/MHA/DBHelper.pm
Installing /usr/local/share/man/man1/masterha_stop.1
Installing /usr/local/share/man/man1/masterha_conf_host.1
Installing /usr/local/share/man/man1/masterha_check_repl.1
Installing /usr/local/share/man/man1/masterha_check_status.1
Installing /usr/local/share/man/man1/masterha_master_monitor.1
Installing /usr/local/share/man/man1/masterha_check_ssh.1
Installing /usr/local/share/man/man1/masterha_master_switch.1
Installing /usr/local/share/man/man1/masterha_secondary_check.1
Installing /usr/local/share/man/man1/masterha_manager.1
Installing /usr/local/bin/masterha_stop
Installing /usr/local/bin/masterha_conf_host
Installing /usr/local/bin/masterha_check_repl
Installing /usr/local/bin/masterha_check_status
Installing /usr/local/bin/masterha_master_monitor
Installing /usr/local/bin/masterha_check_ssh
Installing /usr/local/bin/masterha_master_switch
Installing /usr/local/bin/masterha_secondary_check
Installing /usr/local/bin/masterha_manager
Appending installation info to /usr/lib64/perl5/perllocal.pod

MySQL 설정

MySQL 계정 생성

master 및 slave 모두 생성

GRANT ALL PRIVILEGES ON *.* TO 'mhauser'@'%' IDENTIFIED BY 'mhauser비밀번호';
GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'%' IDENTIFIED BY 'repluser비밀번호';

MySQL 설정 변경

relay-log=/log/mysqld_relay_bin
relay_log_purge=0

Mysql binary File Copy

cp /usr/local/mysql/bin/* /usr/bin/ ( ln -s /usr/local/mysql/bin/* /usr/bin/ )

MySQL Backup 및 Restore

backup
mysqldump ... --master-data=2 --user=root -p --port=3306 TestDB > TestDB.sql
restore
mysql -h 10.10.10.20 -u root -p -P 3306 < testDB.sql

MySQL Replication 구성

slave에서 실행

change master to
master_host='10.10.10.10',
master_port=3306,
master_user='repluser',
master_password='repluser비밀번호',
master_log_file='mysql-bin.000XXX', – master 백업 파일에서 참고
master_log_pos= XXX; – master 백업 파일에서 참고

start slave;

OS 설정

모든 장비에서 실행

유저 생성

adduser mhauser

passwd mhauser
usermod -G mysql mhauser

유저 기간 만료 확인 및 수정

chage -l mhauser | grep -i "^password expires"

위의 값이 never가 아닐 경우 아래 명령어로 영구화 필요

chage -E -1 -M 99999 "mhauser"

mha directory 생성

mhatest의 경우는 서비스명이나 서비스코드 형태를 사용

mkdir -p /mha/{log,conf,remote,scripts,work}
mkdir /mha/work/mhatest
mkdir /mha/remote/mhatest
chown -R mhauser.mhauser /mha
chmod -R 750 /mha

mhauser 권한 부여

~]# vi /etc/ssh/sshd_config

아래 부분 주석해제 후 저장

RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys


-- visudo 명령어로 암호없이 ssh계정 사용하기위해 아래 부분 변경 
(ssh 통신 시 암호없이 통신을하기 위한 방법이며, 보안을 위해 VIP 알리아스를 만든 후 
ohnew 계정은 VIP 알리아스만 실행하고자 변경)

~]# visudo

23라인 부근의 아래 커멘트 부근에 아래 명령어 추가 
## Command Aliases
## These are groups of related commands...

Cmnd_Alias VIP = /sbin/ifconfig, /sbin/arping


57라인 부근에 이부분 주석 처리
# Defaults requiretty


105 라인 부근에 아래 부분 추가
## Allow root to run any commands anywhere
mhauser ALL=(ALL) NOPASSWD: VIP

mha user ssh 설정

su - mhauser ( 반드시 mhauser 계정으로 실행 )
ssh-keygen -t rsa ( 암호없이 생성을 위해 enter 연타 )

# source 서버에서 target 서버로 전달
ssh-copy-id mhauser@10.10.10.10
ssh-copy-id mhauser@10.10.10.20

================================================================================================================
 ~]$ ssh-copy-id mhauser@10.10.10.10
The authenticity of host '10.10.10.10 (10.10.10.10)' can't be established.
ECDSA key fingerprint is 38:58:d9:71:8a:ed:89:af:1d:c8:d1:69:e0:e1:b8:dd.
Are you sure you want to continue connecting (yes/no)? yes
/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
mhauser@10.10.10.10's password:
/etc/profile.d/logging.sh: line 7: ifconfig: command not found
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'mhauser@10.10.10.10'"
and check to make sure that only the key(s) you wanted were added.
================================================================================================================

# 접속 테스트 ( 모든 장비 Node, Manager 간 비밀번호 없이 접속 가능 )
ssh mhauser@10.10.10.10
ssh mhauser@10.10.10.20

VIP 설정

master node 장비에서만 실행

-- IP 확인
~]# ifconfig
bond0: flags=5187<UP,BROADCAST,RUNNING,MASTER,MULTICAST> mtu 1500
inet 10.10.10.10 netmask 255.255.255.0 broadcast 10.10.10.255
.....

-- VIP 설정
~]# ifconfig bond0:0 10.10.10.30 up netmask 255.255.255.128 up

-- VIP 적용
~]# arping -c3 -D -I bond0 -s 10.10.10.30 -U 10.10.10.1
ARPING 10.10.10.1 from 10.10.10.10 bond0
Unicast reply from 10.162.131.10 [00:00:5E:00:01:15] 0.770ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)
[root@Nmsprofdb01 ~]#

-- VIP 확인
[root@Nmsprofdb01 ~]# ifconfig
bond0: flags=5187<UP,BROADCAST,RUNNING,MASTER,MULTICAST> mtu 1500
inet 10.10.10.10 netmask 255.255.255.0 broadcast 10.10.10.255
.....
bond0:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.10.30 netmask 255.0.0.0 broadcast 10.255.255.255

master_ip_failover 파일 생성

manager 장비 /mha/script/master_ip_failover 파일 생성 후 추가

#!/usr/bin/env perl
# use strict;
# use warnings FATAL => 'all';
use Getopt::Long;
my (
$command, $ssh_user, $orig_master_host, $orig_master_ip, $orig_master_ssh_port,
$orig_master_port, $new_master_host, $new_master_ip, $new_master_port, $new_master_user,
$new_master_password, $new_master_ssh_port
);
my $vip = '10.10.10.30'; # Virtual IP
my $key = "0";
my $devicenm = "bond0";
my $ssh_start_vip = "sudo /sbin/ifconfig $devicenm:$key $vip netmask 255.255.255.128 up";
my $ssh_stop_vip = "sudo /sbin/ifconfig $devicenm:$key down";
my $ssh_mac_refresh = "sudo /sbin/arping -c3 -D -I $devicenm -s 10.10.10.30 10.10.10.1";
GetOptions(
'command=s' => \$command,
'ssh_user=s' => \$ssh_user,
'orig_master_host=s' => \$orig_master_host,
'orig_master_ip=s' => \$orig_master_ip,
'orig_master_port=i' => \$orig_master_port,
'orig_master_ssh_port=i' => \$orig_master_ssh_port,
'new_master_host=s' => \$new_master_host,
'new_master_ip=s' => \$new_master_ip,
'new_master_port=i' => \$new_master_port,
'new_master_user=s' => \$new_master_user,
'new_master_password=s' => \$new_master_password,
'new_master_ssh_port=i' => \$new_master_ssh_port,
);
exit &main();
sub main {
print "\n\nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\n\n";
if ( $command eq "stop" || $command eq "stopssh" ) {
# $orig_master_host, $orig_master_ip, $orig_master_port are passed.
# If you manage master ip address at global catalog database,
# invalidate orig_master_ip here.
my $exit_code = 1;
eval {
print "Disabling the VIP on old master: $orig_master_host \n";
&stop_vip();
$exit_code = 0;
};
if ($@) {
warn "Got Error: $@\n";
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "start" ) {

# all arguments are passed.
# If you manage master ip address at global catalog database,
# activate new_master_ip here.
# You can also grant write access (create user, set read_only=0, etc) here.
my $exit_code = 10;
eval {
print "Enabling the VIP - $vip on the new master - $new_master_host \n";
&start_vip();
$exit_code = 0;
};
if ($@) {
warn $@;
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "status" ) {
print "Checking the Status of the script.. OK \n";
`ssh -p $orig_master_ssh_port $ssh_user\@$orig_master_host \" $ssh_start_vip \"`;
exit 0;
}
else {
&usage();
exit 1;
}
}
# A simple system call that enable the VIP on the new master
sub start_vip() {
`ssh -p $new_master_ssh_port $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
`ssh -p $new_master_ssh_port $ssh_user\@$new_master_host \" $ssh_mac_refresh\"`;
}
# A simple system call that disable the VIP on the old_master
sub stop_vip() {
`ssh -p $orig_master_ssh_port $ssh_user\@$orig_master_host \" $ssh_stop_vip \"`;
}
sub usage {
print
"Usage: master_ip_failover --command=start|stop|stopssh|status --orig_master_host=host --orig_master_ip=ip --orig_master_port=port --new_master_host=host --new_master_ip=ip --new_master_port=port\n";
}

scripts/master_ip_failover 실행 권한 추가

chmod +x /mha/scripts/master_ip_failover

conf 파일 추가

/mha/conf/mhatest.conf 파일 생성 후 작성

[server default]
# mysql user and password
user=mhauser
password=mhauserDB비밀번호
ssh_user=mhauser
repl_user=repluser
repl_password=repluserDB비밀번호

# manager log file
manager_log=/mha/log/mhatest.log

# working directory on the manager
manager_workdir=/mha/work/mhatest/

# working directory on MySQL servers
remote_workdir=/mha/remote/mhatest/

master_binlog_dir=/log/

master_ip_failover_script=/mha/scripts/master_ip_failover --orig_master_ssh_port=22 --new_master_ssh_port=22

[server1]
hostname=10.10.10.10
port=59306
candidate_master=1

[server2]
hostname=10.10.10.20
port=59306
candidate_master=1

#no_master=1
#ignore_fail=1

relay log 삭제 스크립트 추가

relay log purge 스크립트 등록
vi /mha/scripts/relay_log_purge.sh
chmod 755 /mha/scripts/relay_log_purge.sh

#!/bin/sh

source ~/.bash_profile

USER=mhauser
PORT=$1
PASSWORD='mhauser패스워드'


# Slave Check
v_mysql=`which mysql`
SLAVE_STATUS=`$v_mysql -u root -p XXXX -P 3306 -e 'show slave status\G;' | grep Running | grep Yes | wc -l`

if [ $SLAVE_STATUS -lt 2 ]
then
echo 'The master does not execute purge...' >> /mha/log/purge_relay.log
exit;
fi

/usr/local/bin/purge_relay_logs --user=$USER --port=$PORT --password=$PASSWORD --disable_relay_log_purge >> /mha/log/purge_relay.log 2>&1

crontab 등록

crontab -e

######################################################################
#
# Relay Log Purge Daily
#
######################################################################
0 5 * * * /mha/scripts/relay_log_purge.sh 3306

perl 실행 권한 부여

chmod 755 /usr/bin/perl 

MHA 설정 Test

mhauser OS 계정으로 실행

masterha_check_ssh

ll /usr/bin/perl
su - mhauser
masterha_check_ssh --conf=/mha/conf/mhatest.conf


~]$ masterha_check_ssh --conf /mha/conf/mhatest.conf
Thu Jun 1 18:52:28 2017 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Thu Jun 1 18:52:28 2017 - [info] Reading application default configurations from /mha/conf/mhatest.conf..
Thu Jun 1 18:52:28 2017 - [info] Reading server configurations from /mha/conf/mhatest.conf..
Thu Jun 1 18:52:28 2017 - [info] Starting SSH connection tests..
Thu Jun 1 18:52:28 2017 - [debug]
Thu Jun 1 18:52:28 2017 - [debug] Connecting via SSH from mhauser@10.10.10.20(10.10.10.20:22) to mhauser@10.10.10.10(10.10.10.10:22)..
Thu Jun 1 18:52:28 2017 - [debug] ok.
Thu Jun 1 18:52:29 2017 - [debug]
Thu Jun 1 18:52:28 2017 - [debug] Connecting via SSH from mhauser@10.10.10.20(10.10.10.20:22) to mhauser@10.10.10.10(10.10.10.10:22)..
Thu Jun 1 18:52:29 2017 - [debug] ok.
Thu Jun 1 18:52:29 2017 - [info] All SSH connection tests passed successfully.
===============================================================================================================

masterha_check_repl

ll /usr/bin/perl
su - mhauser
masterha_check_repl --conf=/mha/conf/mhatest.conf


~]$ masterha_check_repl --conf=/mha/conf/mhatest.conf
Mon Jun 5 14:15:35 2017 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Mon Jun 5 14:15:35 2017 - [debug] MHA::MasterMonitor version 0.56.
Mon Jun 5 14:15:35 2017 - [debug] Connecting to servers..
Mon Jun 5 14:15:36 2017 - [debug] Connected to: 10.10.10.10(10.10.10.10:59306), user=mhauser
Mon Jun 5 14:15:36 2017 - [debug] Connected to: 10.10.10.20(10.10.10.20:59306), user=mhauser
Mon Jun 5 14:15:36 2017 - [debug] Comparing MySQL versions..
Mon Jun 5 14:15:36 2017 - [debug] Comparing MySQL versions done.
Mon Jun 5 14:15:36 2017 - [debug] Connecting to servers done.
.....
Checking the Status of the script.. OK
Bad port 'mhauser@10.10.10.20'
Mon Jun 5 14:15:38 2017 - [debug] OK.
Mon Jun 5 14:15:38 2017 - [warning] shutdown_script is not defined.
Mon Jun 5 14:15:38 2017 - [debug] Disconnected from 10.10.10.10(10.10.10.10:59306)
Mon Jun 5 14:15:38 2017 - [debug] Disconnected from 10.10.10.20(10.10.10.20:59306)
Mon Jun 5 14:15:38 2017 - [debug] Got exit code 0 (Not master dead).
MySQL Replication Health is OK.

Mysql 버전 확인 부분 오류 수정

~]$ masterha_check_repl --conf=/mha/conf/mhatest.conf

## 에러 내용
Fri Nov  4 11:30:59 2022 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Fri Nov  4 11:30:59 2022 - [info] Reading application default configuration from /mha/conf/mhatest.conf..
Fri Nov  4 11:30:59 2022 - [info] Reading server configuration from /mha/conf/mhatest.conf..
Fri Nov  4 11:30:59 2022 - [info] MHA::MasterMonitor version 0.57.
Fri Nov  4 11:31:00 2022 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln427] Error happened on checking configurations. Redundant argument in sprintf at /usr/local/share/perl5/MHA/NodeUtil.pm line 190.
Fri Nov  4 11:31:00 2022 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln525] Error happened on monitoring servers.
Fri Nov  4 11:31:00 2022 - [info] Got exit code 1 (Not master dead).

## 수정 방법
/usr/local/share/perl5/MHA/NodeUtil.pm 파일의 190번 라인을 아래 형태로 수정

~]# vi  /usr/local/share/perl5/MHA/NodeUtil.pm

182 sub parse_mysql_version($) {
183   my $str = shift;
184   my $result = sprintf( '%03d%03d%03d', $str =~ m/(\d+)/g );
185   return $result;
186 }
187
188 sub parse_mysql_major_version($) {
189   my $str = shift;
190   my $result = sprintf( '%03d%03d%03d', $str =~ m/(\d+)/g );
191   return $result;
192 }

mha 실행

su - mhauser

nohup masterha_manager --conf=/mha/conf/mhatest.conf > /mha/log/mhatest.log 2>&1 &

Query 형태

Example

t1 테이블과 t2 테이블의 조건에 해당하는 행 모두 삭제 ( t1, t2 테이블 동시 삭제 )

  t1 id값이 1인 행 삭제

  21 id값이 1인 행 삭제

Delete 실행

결과

참고

https://www.mysqltutorial.org/mysql-delete-join/

---  MEMORY  ----

 * innodb buffer pool size를 크게 가져갈수록 디스크 IO를 줄일수 있음
 * 일반적으로 OS의 70&~80% 사용

 

---  CPU  ----

 * 하이퍼 스레딩 활성화
 * 일반적으로 CPU보다는 memory 및 Disk IO를 올리는것이 성능 향상에 효과적
 * MySQL 버전에 따른 코어수 제한 
   - MySQL 5.1 : ~ 4코어 
   - MySQL 5.5 : ~ 16코어 
   - MySQL 5.6 : ~ 36 쓰레드(Core)
   - MySQL 5.7 : ~ 64 쓰레드(32 Core-HT)
   - MySQL 8.0 : ~ 100 쓰레드(48 Core-HT)

 

---  Disk  ----

 * SSD, NVMe 사용시 innodb_page_size=4K, innodb_flush_neighbors=0 로 사용을 추천
 * innodb datadir, tmp 파일 및 undo 로그 모두 Ramdom IO가 발생함으로 SSD로 할당
 * 로그(빈로그 등등)의 경우 Sequnential IO가 발생함으로 디스크로 할당
 * innodb_io_capacity
   - innodb가 사용 가능한 I/O 대역폭
   - 너무 큰 값을 사용한다고 해서 많은 이점이 있지는 않음( 20000 이상은 권장되지 않음 )
   - 쓰기 작업이 많은 시스템은 높은 값이 유리 ( Ex LogDB )
   - 쓰기 작업이 적은 시스템은 낮은 값이 유리
 * innodb_io_capacity_max
   - innodb가 사용 가능한 I/O 최대 대역폭 
   - 일반적으로 innodb_io_capacity 값의 두배로 설정
 

---  OS  ---- 

 * MySQL은 Linux 에 최적화 되어 있다. 
 * MySQL 이외에 OS에서 설정을 해줘야되는 부분
   - ulimit -n, 파일 수 제한 (connections, open tables, ...)
   - ulimit -u, 스레드 수 제한 (connections, InnoDB background threads, event scheduler, ....)
 * NUMA 기반 서버의 경우 innodb_numa_interleave를 1로 설정 
   - mysql과 os의 numa 설정이 다른경우, 노드간 메모리 사용 불균형으로 swap이 발생하여 성능 이슈가 발생할 가능성이 있음.
 * InnoDB를 사용하는 경우 OS의 파일시스템 캐시를 사용하지 않는다.( 더블 버퍼링을 막아 메모리를 효율적으로 사용 )
   - set inndbo_flush_method=O_DIRECT
   - innodb pool이 훨씬 고도화된 형태로 동작

---  ETC   ---

 * innodb_log_file_size(redo log 사이즈)
   - 크게 가져 갈수록 체크포인트 동작이 덜 필요하여 성능에 좋으나, 너무 크게 설정하는 경우 DB 복구 시간이 느려짐.
   - 운영서버 최소 512MB 이상 권장.   
 * innodb_log_files_in_group(리두 로그 파일 갯수)
   - Default는 2이지만 일반적으로 3으로 운영
 * skip-name-resolve
   - client에서 server로 접속시 dns lookup 과정을 생략
   - dns 서버가 느리거나 장애 발생시 DB 접속문제가 발생하는 것을 해결할 수 있음.
 * innodb_write_io_threads / innodb_read_io_threads
   - SHOW ENGINE INNODB STATUS 에서 지연된 쓰기/읽기 요청이 64개 이상인 경우 값을 올려주는것이 좋음.
 * innodb_log_file_size
   - 로그 파일 크기
   - innodb_buffer_pool_size의 25% 정도로 유지
   - 이 값이 클수록 버퍼 풀에서 체크포인트 발생 빈도가 낮음.
   - 이 값이 클수록 크래시 발생시 복구가 그만큼 느려짐.

 

---  ACID 유지  ---   

 * innodb_flush_log_at_trx_commit = 1 
   - commit마다 리두 로그에 기록하고 디스크에 flush
   - ACID가 보장이 되지만, 디스크 I/O가 높음
   - 성능 위주 DB로 설정하고 싶은 경우 0
 * sync_binlog = 1
   - 트랜잭션이 커밋되기 전에 이진 로그를 디스크에 동기화
   - 정전 및 운영 체제 충돌에도 ACID가 보장되지만, 디스크 I/O가 높음
   - 성능 위주 DB로 설정하고 싶은 경우 0
 * innodb_doublewrite = 1
   - innoDB의 페이지를 쓰기 전에 버퍼 풀에서 플러시된 페이지를 중복해서 저장
   - MySQL Crash 복구 중에 이중으로 쓰여진 버퍼에서 페이지 복사본을 찾아 복구 진행
   - 데이터 무결성을 유지할 수 있으나, 이중으로 데이터를 기록하여 I/O가 높음. (이중으로 기록 하지만 2배의 I/O가 발생하지는 않음)
   - 성능 위주 DB로 설정하고 싶은 경우 0   

 

---  Buffer per Client Connections  ---

 * connection(세션)당 할당되는 버퍼들 
   - read_buffer_size : Sequnential scan (full table scan)을 사용할 때 사용하는 버퍼 
   - read_rnd_buffer_size : 정렬 작업후, 정렬된 순서대로 데이터를 다시 읽어 들일 때 사용하는 버퍼 
   - join_buffer_size : 인덱스를 사용하지 않는 조인에 사용되는 버퍼, Session level에서 join 단위로 생성
   - sort_buffer_size : 인덱스를 사용하지 않는 정렬에 사용하는 버퍼 
   - binlog_cache_size (if binary logginigs is enabled)
 * connection이 많을 경우 이 버퍼들이 메모리를 점유하기 때문에 크게 세팅하면 안된다.
   - 만약 크게 필요한 경우가 있다면 필요한 세션에서만 크게 잡아서 사용한다. 

 

참고

https://iamwhat.tistory.com/entry/MySQL-%ED%8F%AC%ED%8D%BC%EB%A8%BC%EC%8A%A4-%ED%8A%9C%EB%8B%9D

   

FOUND_ROWS 적용 전

페이징 처리를 위해 limit 사용 시 전체 게시물의 수를 알기 위해 쿼리를 한번 더 해야 한다.

 

1. 해당 조건을 만족하는 게시물의 전체 수 조회
2. 그 페이지에서 보여줄 내용 조회

FOUND_ROWS 적용 후

SQL_CALC_FOUND_ROWS & FOUND_ROWS

 

SELECT 최상단에 SQL_CALC_FOUND_ROWS 라는 옵션만 주고 FOUND_ROWS() 를 사용하면 된다.

쿼리는 마찬가지로 두번이지만 두 번째 쿼리는 훨씬 빠르다.

mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_enchovy WHERE id < 100 LIMIT 10;
mysql> SELECT FOUND_ROWS();

첫번째 쿼리 검색 결과 : 10개

두번째 쿼리 카운트 값 : WHERE조건에 해당되는 Rows 카운트 ( 1~100사이에 누락된 번호가 없다면 99을 리턴 )

 

주의사항

1. SQL_CALC_FOUND_ROWS 키워드는 UNION의 첫 번째 SELECT에 나타나야 한다.
2. FOUND_ROWS()의 값은 UNION ALL을 사용하는 경우에만 정확합니다. ALL이 없는 UNION을 사용하면 중복 제거가 발생하고 FOUND_ROWS()의 값은 근사치만 된다.
3. UNION에 LIMIT가 없는 경우 SQL_CALC_FUND_ROWS는 무시되고 UNION을 처리하기 위해 생성된 임시 테이블의 행 수를 반환한다.

4. 복제시) FOUND_ROWS() 은 statement-based 형태로는 안정적으로 복제되지 않습니다, 이경우 row-based 형태로 자동 변경되어 복제가 됩니다.

 

※ MySQL 4.7.16 이전 버전에는 버그가 있어, 원하는 결과를 리턴하지 않음

* 5.7.16 이전 버전의 Query중 SybQuery가 있다면, SubQuery 내의 결과 Row 수를 FOUND_ROWS()로 Retrun
* 5.7.16 버그 픽스 이후는 전체쿼리문을 하나의 구문으로 보고 결과 ROW수를 FOUND_ROWS()로 return
  FOUND_ROWS() 함수의 기능은 이전 SELECT 된 쿼리의 ROWS를 RETURN 하는 함수(즉, 5.7버전의 기능이 정확)

    

FOUND_ROWS() 함수는 MySQL 8.0.17에서 더 이상 사용되지 않으며 향후 버전의 MySQL에서 제거될 것으로 예상된다..

참고

https://string.tistory.com/93

MySQL 성능 최적화 작업 중 하나로 DISK I/O를 줄이는 방법중 하나

파일 포멧을 Barracuda로 설정

Barracuda 이전의 파일 포멧은 Antelope에는 Row 포멧이 'Compact'만 제공되었으나,

Barracuda 파일 포멧에서는 'Compact'이외에 'Dynamic','Compressed'이 추가됨.(기본은 'Compact')

 

Row 포맷에 따라 가변 길이 컬럼(VARCHAR, TEXT, BLOB)의 처리와 압축 기능 사용이 결정된다.
InnoDB에서는 VARCHAR, TEXT, BLOB을 완전히 동일한 타입으로 인식하고 처리하는 점 참고하자.

Compact  가변 길이 컬럼의 767 byte 까지만 B-Tree에 저장하고, 나머지는 Off-Page에 저장
Dynamic  Page 사이즈와, Row 사이즈를 고려하여, 길이가 긴 컬럼들은 전체를 Off-Page에 저장하되, B-Tree에는 20 byte의 포인터만 저장
Compressed  Dynamic과 거의 유사하지만 KEY_BLOCK_SIZE 옵션을 통해 16KB의 데이터 Page를 압축 가능
 (KEY_BLOCK_SIZE = 1, 2, 4, 8, 16 기본값 : 8)
 KEY_BLOCK_SIZE = 16인 경우 기본적으로 데이터 Page를 압축하지 않지만, VARCHAR, TEXT, BLOB 컬럼만 선택적으로 압축

기존 Compact Row 포맷에서 VARCHAR, TEXT, BLOB 컬럼을 많이 포함하고 있는 테이블의 경우, 데이터 Page가 VARCHAR, TEXT, BLOB 데이터들로 채워져서, Page 내 의미있는 데이터의 밀도가 낮아지고 Range 스캔시 읽어야 할 Page가 많아져 성능 저하를 일으키는 문제가 있었다.
하지만 Barracuda 파일 포맷에서는 이런 테이블들을 선택적으로 Dynamic 또는 Compressed로 변경해서 I/O를 최적화 할 수 있는 장점이 있다.

 

my.cnf
innodb_file_per_table
innodb_file_format=barracuda
동적으로 MySQL 서버에 설정
SET GLOBAL innodb_file_format=barracuda;
SET GLOBAL innodb_file_per_table=ON;
테이블 생성시 압축 옵션 추가
CREATE TABLE tblTest_compressed
(
  id int(11) NOT NULL AUTO_INCREMENT,
  aa int(11) DEFAULT NULL,
  bb varchar(100)  DEFAULT NULL,
  PRIMARY KEY (id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;
 

참고

http://jason-heo.github.io/mysql/2014/03/05/innodb-barracuda.html

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=seuis398&logNo=70094098699

MySQL Blocking Query 

Version 5.7

select blocking.trx_mysql_thread_id                       as blocking_processlist_id
      ,max(proc.USER)                                          as blocking_user
      ,max(proc.HOST)                                          as blocking_host
      ,max(proc.DB)                                            as blocking_db
      ,max(ifnull(blocking.trx_query, 'Null'))                 as blocking_query
      ,max(TIMESTAMPDIFF(second, blocking.trx_started, now())) as blocking_started_sec
      ,max(ifnull(blocking.trx_started, 'Null'))               as blocking_started_time
      ,max(blocking.trx_state)                                 as blocking_state
      ,'Null'                                             as blocking_command
      ,max(concat(blocking.trx_id, ' (', ifnull(waiting.blocking_lock_id, 'Null'), ')')) as trx_id
      ,max(ifnull(lock_info.lock_mode, 'Null'))                                          as lock_mode
      ,max(ifnull(lock_info.lock_type, 'lock')) as lock_type
      ,if(max(waiting_info.trx_started is not null)
      ,concat('\nlock waiting into : ', count(*)
      ,substring(group_concat('\n\t'
                 ,'waiting ', now() - waiting_info.trx_started, '(sec) : '
                 ,'query -> ', waiting_info.trx_query
                  order by waiting_info.trx_started), 1, 1000))
      ,'\nlock waiting into : Null') as trx_info
from information_schema.INNODB_TRX blocking
left join information_schema.INNODB_LOCK_WAITS waiting on waiting.blocking_trx_id = blocking.trx_id
left join information_schema.INNODB_TRX waiting_info on waiting_info.trx_id = waiting.requesting_trx_id
left join information_schema.INNODB_LOCKS lock_info on lock_info.lock_trx_id = blocking.trx_id
inner join information_schema.processlist proc on proc.id = blocking.trx_mysql_thread_id
where blocking.trx_state = 'RUNNING'
and blocking.trx_requested_lock_id is null
and blocking.trx_wait_started is null
group by blocking.trx_mysql_thread_id ;

MySQL Status Check Query

 

Version 5.7 & 8.0

select concat('LongQuery', '::', count(*)) as current_value
from information_schema.processlist proc
where proc.command not in ('sleep', 'daemon', 'binlog dump', 'killed')
and proc.id <> connection_id()
and proc.time > 10
union all
select concat('LongTransaction', '::', count(*)) as current_value
from information_schema.innodb_trx trx
where TIMEDIFF(now(), trx.trx_started) > '00:01:00'
union all
select concat('MetaLock', '::', count(*)) as current_value
from performance_schema.metadata_locks ml
where ml.lock_status = 'PENDING'
union all
select concat('LockWait', '::', count(*)) as current_value
from information_schema.INNODB_TRX trx
where trx.trx_state = 'LOCK WAIT'
and trx.trx_wait_started <= date_sub(now(), interval 3 second) ;

MySQL Long Query

Version 5.7 & 8.0

select proc.time, proc.command, proc.id
     , proc.user, proc.host, proc.db, proc.info
from information_schema.processlist proc
where proc.command not in ('sleep', 'daemon', 'binlog dump', 'killed')
and proc.id <> connection_id()
and proc.time > 10
order by proc.time desc ;

MySQL Long Transaction Query

Version 5.7 & 8.0

select trx_mysql_thread_id as 'id'
     , proc.user
     , proc.host
     , proc.command
     , trx.trx_state
     , concat(trx_started, '') as 'start_time'
     , concat(TIMEDIFF(now(), trx_started), '') as 'diff_time'
     , ifnull(trx_query, '.......') as 'query_info'
     , ( select ifnull(esc.SQL_TEXT, '.......')
         from performance_schema.threads thread
       , performance_schema.events_statements_current esc
         where thread.processlist_id = trx.trx_mysql_thread_id
         and esc.thread_id = thread.thread_id) as 'query_info2'
from information_schema.innodb_trx trx
    ,information_schema.processlist proc
where proc.id = trx.trx_mysql_thread_id
and trx.trx_query is null
and TIMEDIFF(now(), trx.trx_started) > '00:01:00'
order by trx_started ;

MySQL Meta Lock Query

Version 5.7 & 8.0

select th.thread_id as 'blocking_thread_id'
     , th.processlist_id as 'blocking_id'
     , th.processlist_user as 'blocking_user'
     , th.processlist_host as 'blocking_host'
     , th.processlist_db as 'blocking_db'
     , th.processlist_command as 'blocking_command'
     , th.processlist_time as 'blocking_time'
     , ifnull(th.processlist_state, '---') as 'blocking_state'
     , ifnull(th.processlist_info, ifnull((select esc.sql_text
                                           from performance_schema.events_statements_current esc
                                           where esc.thread_id = th.thread_id), '---')) as 'blocking_query'
     , (select group_concat(concat('(id : ', waiting.processlist_id, ', waiting : ', waiting.processlist_time, ' sec) -> ', processlist_info)
        order by waiting.processlist_time desc)
        from performance_schema.threads waiting
        where waiting.processlist_state = 'Waiting for table metadata lock') as 'waiting_query'
from (select granted.owner_thread_id
            ,count(*) as cnt
      from performance_schema.metadata_locks pending
          ,performance_schema.metadata_locks granted
      where pending.lock_status   = 'PENDING'
      and granted.object_type   = pending.object_type
      and granted.object_schema = pending.object_schema
      and granted.object_name   = pending.object_name
      and granted.owner_thread_id != pending.owner_thread_id
      and granted.lock_status   = 'GRANTED'
      group by granted.owner_thread_id
      order by 2 desc
      limit 1) as metalock_parent
     ,performance_schema.threads th
where th.thread_id = metalock_parent.owner_thread_id
and th.processlist_time >= 3 ;

MySQL Lock Wait Query

Version 8.0

select TIMESTAMPDIFF(second, trx_waiting.trx_started, now()) as 'waiting_started_sec'
     , concat(trx_waiting.trx_started, '') as 'waiting_started_time'
     , trx_waiting.trx_state as 'waiting_state'
     , waiting_thread.processlist_command as 'waiting_command'
     , waiting_thread.processlist_id as 'waiting_processlist_id'
     , waiting_thread.processlist_user as 'waiting_user'
     , waiting_thread.processlist_host as 'waiting_host'
     , waiting_thread.processlist_db as 'waiting_db'
     , waiting_thread.processlist_info as 'waiting_query'
     , TIMESTAMPDIFF(second, blocking_thread_time.trx_started, now()) as 'blocking_started_sec'
     , concat(blocking_thread_time.trx_started, '') as 'blocking_started_time'
     , blocking_thread_time.trx_state as 'blocking_state'
     , blocking_thread.processlist_command as 'blocking_command'
     , blocking_thread.processlist_id as 'blocking_processlist_id'
     , blocking_thread.processlist_user as 'blocking_user'
     , blocking_thread.processlist_host as 'blocking_host'
     , blocking_thread.processlist_db as 'blocking_db'
     , ifnull(blocking_thread.processlist_info
           , (select esc.SQL_TEXT
              from performance_schema.events_statements_current esc
              where esc.thread_id = blocking_thread.thread_id)) as 'blocking_query'
from information_schema.innodb_trx trx_waiting
    ,performance_schema.data_lock_waits dlw
    ,performance_schema.threads waiting_thread
    ,performance_schema.threads blocking_thread
    ,information_schema.innodb_trx blocking_thread_time
    -- performance_schema.events_statements_current
where trx_waiting.trx_state = 'LOCK WAIT'
and trx_waiting.trx_wait_started <= date_sub(now(), interval 3 second)
and cast(dlw.requesting_engine_transaction_id as char charset utf8mb4) = trx_waiting.trx_id
and waiting_thread.thread_id = cast(dlw.requesting_thread_id as char charset utf8mb4)
and blocking_thread.thread_id = cast(dlw.blocking_thread_id as char charset utf8mb4)
and blocking_thread_time.trx_id = cast(dlw.BLOCKING_ENGINE_TRANSACTION_ID as char charset utf8mb4) ;

테이블 생성시 auto_increment 설정

CREATE TABLE people 
(
    number INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255)
);

 

AUTO_INCREMENT 확인

다음에 삽입될 Auto_increment(number 컬럼)의 값 확인

SHOW TABLE STATUS WHERE name = '테이블 이름';
SHOW TABLE STATUS LIKE '테이블 이름';

mysql> show table status where name = 'people' \G;
*************************** 1. row ***************************
           Name: _people
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 6
 Avg_row_length: 2730
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: 82
    Create_time: 2021-12-31 15:02:42
    Update_time: 2021-12-31 15:10:03
     Check_time: NULL
      Collation: utf8mb4_general_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)

AUTO_INCREMENT  초기값 변경

AUTO_INCREMENT를 초기화(변경)할 때는 변경할 AUTO_INCREMENT의 값이 해당 컬럼에서 가장 큰 값보다 커야 합니다

ALTER TABLE 테이블 이름 AUTO_INCREMENT=변경할 숫자;

mysql> alter table _people auto_increment=5
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show table status where name = 'people' \G;
*************************** 1. row ***************************
           Name: _people
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 6
 Avg_row_length: 2730
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: 5
    Create_time: 2021-12-31 15:57:51
    Update_time: 2021-12-31 15:10:03
     Check_time: NULL
      Collation: utf8mb4_general_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)

AUTO_INCREMENT  1부터 순서대로 정렬

레코드 삭제와 업데이트 등과 같은 이유로 AUTO_INCREMENT로 설정된 컬럼의 숫자가 난잡하게 될 수 있습니다. 이때 보기 좋게 1부터 순서대로 정렬할 수 있습니다.

정렬 전 데이터
SET @count=0;
UPDATE 테이블 이름 SET 컬럼 이름=@count:=@count+1;

mysql> SET @count=0;
mysql> UPDATE people SET number=@count:=@count+1;
정렬 후 데이터
# 데이터 정렬 후 AUTO_INCREMENT 값을 변경
ALTER TABLE 테이블 이름 AUTO_INCREMENT=변경할 숫자;

mysql> alter table people AUTO_INCREMENT=4;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

예제

AUTO_INCREMENT5변경

 

 

ex) ALTER TABLE people AUTO_INCREMENT=5;

// people 테이블의 AUTO_INCREMENT 값을 5로 변경

* 변경된 것을 확인(SHOW TABLE STATUS LIKE 'people';)

1) 데이터 삽입

 
 

ex) INSERT INTO people VALUES (NULL, "name_D");

Auto_increment5변경했기 때문에 number에 5삽입됩니다.

삽입을 완료하면 Auto_increment6이 됩니다.

다시 AUTO_INCREMENT4로 변경하려고 하더라도 오류는 발생하지 않지만 number 컬럼의 가장 큰 값5이기 때문에 Auto_increment의 값은 4로 변경되지 않고 6으로 되어있습니다.

 
 

* 현재 5보다 큰 숫자로만 변경 가능

2) 데이터 삭제

 
 

 

ex) DELETE FROM people WHERE number=5;

number가 5인 데이터를 삭제하면 number 컬럼에서 가장 큰 값3이기 때문에 Auto_increment4로 변경 가능합니다.

* 현재 3보다 큰 숫자로 변경 가능

​* 참고

Auto_increment1로 초기화하려면 테이블의 데이터가 없어야 합니다.

 

참고

https://m.blog.naver.com/dldudcks1779/222006115309

 처음과 끝은 각각 ^과 $로 표현할 수 있어요. 예를 들어서, '^a'라면, a로 시작하는 패턴입니다. 그리고 'E$'이라면, E로 끝나는 패턴입니다. 그러면 '^eat$'은 무엇을 의미할까요? "eat"를 찾으라는 거겠네요. 그리고, .은 임의의 문자를 의미하는데요. '^...$'은, 길이가 3인 문자열이 패턴에 매칭된다는 이야기입니다.

 

 

 이 쿼리를 수행해 봅시다. 그러면 문자열이 시작하고 임의의 문자가 3개 나오고 끝납니다. 그 말은, "Tom"이나 "Bob"도 matching이 된다는 소리일 거에요.

 

 

 

 결과는, _name 필드의 문자열 길이가 3인 것들만 출력됩니다.

 

 


 그런데, 우리는 _name이 모음으로 시작하는 것들을 구한다고 해 봅시다. LIKE를 쓰면 대충 10개의 조건절을 and로 연결하거나, 아니면 left 함수로 1개의 문자열을 가져온 것이, IN에 속해있는지. 등 조금 복잡하게 쿼리를 작성해야 할 거에요.

 

 [...] 패턴은 ...에 속하는 문자 집합이 패턴에 걸린다는 의미인데요. 예를 들자면 이런 경우를 봅시다.

 

 

 이 쿼리는 무엇을 의미할까요? 일단 문자열이 시작했다는 ^가 들어와 있고, []가 있네요. 안에 것을 보니까 aAeE가 있는데, 첫 문자의 가능 집합이 a, A, e, E라는 것입니다. 즉, _name 필드가 이 4개의 문자로 시작하는 레코드들을 모두 출력하라는 의미입낟.

 

 

 쿼리의 결과는 다음과 같습니다. 그러면, a, A, e, E라는 문자가, 첫 번째에도 나오고 두 번째에도 나와야 하는 경우는 어떻게 작성해야 할까요? 이 때, {n}이라는 키워드를 씁니다. 해당 패턴이 n회 반복된다.

 

 

 이 문장의 의미는, string이 시작하고 나서, a나 A나 e나 E가 2회 반복되어야 한다는 것입니다. 즉, aa~나 aA~나 ae~나 aE나, ... , EE가 해당 패턴에 걸릴 거에요. 그러면 저 셋 중에는, eat가 있는 필드만 걸리겠네요.

 

 

 하핫. 만약에 1회 이상 2회 미만 그러한 패턴이 나타나는 패턴이 참이 되게 하고 싶다면 어떻게 하면 될까요? {n,m}을 쓰시면 됩니다. 이것은 패턴이 n회 이상, m회 이하 등장한다는 의미입니다. Arie, Asia, eat 셋 중에, 다음 패턴을 만족하는 것이 몇 개나 있을까요?

 

 

 일단, a나 A나 e나 E가 나타나는 패턴이 최소 1회, 최대 1회 반복이 되고, 임의의 문자가 하나 나오면 A?나 A?나 e?나 E?가 저것과 매치가 될 거에요. 그러면, 저 셋 중에서 만족하는 것이 없어요. 그런데, 아래와 같은 경우에는 이야기가 달라집니다.

 

 

 보시면 패턴이 1회 이상 2회 이하 반복되고, 임의의 문자 하나가 들어오고 끝나면 만족을 하는데요.

 

 

 eat는 만족합니다. e가 aAeE 패턴에 만족하고, a 또한 aAeE 조건에 만족합니다. 2회 반복되었고, 다음에 t는 임의의 문자 중 하나입니다. 개행을 제외한. 따라서 eat가 있는 레코드만 출력이 됩니다. [^...]은 무엇을 의미할까요? ...에 속하지 않는 문자 집합을 의미합니다.

 

 

 위 쿼리를 실행해 봅시다. 그러면 문자열이 시작하고, 첫 번째 문자가 a나 A가 아니여야 합니다. _name 필드가 그러한 것만 고르는 query입니다. 한 번 실행을 해 봅시다.

 

 

 Asia랑 Anie는 빠졌음을 알 수 있어요.

 

 


 *과 +이 남았네요. *은 패턴이 0번 이상, +은 1번 이상 나타난다는 것을 의미합니다. 예를 들어서, 다음과 같은 것을 생각해 봅시다.

 

 

 이것은 문자열이 시작했을 때, A,a,E,e가 최소 0회 이상 나오는 패턴과 매칭이 됩니다.

 

 

 실행 결과는 아래와 같이 나옵니다. 시작 문자가 어느 것이던 상관 없다는 겁니다. 그런데 +가 붙으면 이야기가 달라지는데요. 이것은 최소 1회 이상 문제의 패턴이 반복되어야 한다는 겁니다.

 

 

 쿼리를 이렇게 작성해 봅시다.

 

 

 그러면 A나 a나 E나 e로 시작하는 것들만 나올 거에요. 마지막으로, |가 있는데요. 이것은 or를 의미합니다.

 

 

 다음 쿼리는 A로 시작하거나, B로 시작하는 패턴을 찾으라는 겁니다.

 

 

 결과는 3개의 row가 리턴됩니다.

 

 


 그러면 _name이 모음으로 시작하면서, 모음으로 끝나는 것들은 어떻게 구하면 좋을까요? 일단, 저는 다음과 같은 쿼리를 수행해 보겠습니다. 당연하게도, 길이가 1짜리면서 모음인 데이터를 추가한 겁니다.

 

 

 다음에, 쿼리를 생각해 봅시다. 일단 문자열이 시작해야 하고, [aeiouAEIOU] 라는 pattern이 들어와야 할 겁니다. 다음에 임의의 문자가 0개 이상 오고, [aeiouAEIOU] 패턴이 온 다음에 끝나야 겠지요.

 

 

 그러면, 이런 식으로 작성할 수 있을 겁니다. 그런데, 이 경우 길이가 1인 데이터는 매칭이 되지 않을 겁니다.

 

 

 결과를 확인해 보면, "A"라는 것은 matched가 되지 않았음을 알 수 있어요. 그러면 어떻게 작성하면 좋을까요?

 

 

 이렇게 작성하면 어떻게 수행이 될까요? 일단, 첫 패턴이 모음인지 검사합니다. 그 다음에 괄호 안으로 들어갈 건데요. 판단 state를 그려보면 아래와 같습니다.

 

 

 보시면 모음이 나온 후에 바로 Terminate가 되거나, 아니면 임의의 문자가 0회 이상 나온 후에, 모음으로 끝나는 패턴만 regexp에 걸리게끔 하고 있어요. 조금 어려운 예제였습니다. 그러면, 모음으로 시작하거나, 모음으로 끝나는 건 어떻게 작성하면 좋을까요?

 

 

 Conditions를 괄호로 묶고 |를 붙이면 됩니다. 어떤 조건인가요? ^[모음들]이거나, [모음들]$이겠지요. 이 둘을 괄호로 묶어준 다음에 |로 연결해 주시면 됩니다. 정규 표현식을 연습하는 사이트는 여러 곳이 있습니다. 거기에서 연습해 보셔도 좋을 듯 싶습니다.

 

참고

https://codingdog.tistory.com/entry/mysql-regexp-%EB%B3%B5%EC%9E%A1%ED%95%9C-%ED%8C%A8%ED%84%B4-%EB%A7%A4%EC%B9%AD%EC%9D%84-%ED%95%B4-%EB%B4%85%EC%8B%9C%EB%8B%A4

 

mysql regexp : 복잡한 패턴 매칭을 해 봅시다.

 저번에 LIKE 연산자를 배웠습니다. 이번에는 조금 더 복잡한 패턴 매칭에 대해서 다뤄보겠습니다. [관련글] LIKE 연산자 알아봅시다.  눈치 채신 분도 있으실지 모르겠습니다. 자바에서 파싱할

codingdog.tistory.com

mysql 8.0 기준

Table 12.11 Date and Time Functions

NameDescription
ADDDATE() Add time values (intervals) to a date value
ADDTIME() Add time
CONVERT_TZ() Convert from one time zone to another
CURDATE() Return the current date
CURRENT_DATE(), CURRENT_DATE Synonyms for CURDATE()
CURRENT_TIME(), CURRENT_TIME Synonyms for CURTIME()
CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP Synonyms for NOW()
CURTIME() Return the current time
DATE() Extract the date part of a date or datetime expression
DATE_ADD() Add time values (intervals) to a date value
DATE_FORMAT() Format date as specified
DATE_SUB() Subtract a time value (interval) from a date
DATEDIFF() Subtract two dates
DAY() Synonym for DAYOFMONTH()
DAYNAME() Return the name of the weekday
DAYOFMONTH() Return the day of the month (0-31)
DAYOFWEEK() Return the weekday index of the argument
DAYOFYEAR() Return the day of the year (1-366)
EXTRACT() Extract part of a date
FROM_DAYS() Convert a day number to a date
FROM_UNIXTIME() Format Unix timestamp as a date
GET_FORMAT() Return a date format string
HOUR() Extract the hour
LAST_DAY Return the last day of the month for the argument
LOCALTIME(), LOCALTIME Synonym for NOW()
LOCALTIMESTAMP, LOCALTIMESTAMP() Synonym for NOW()
MAKEDATE() Create a date from the year and day of year
MAKETIME() Create time from hour, minute, second
MICROSECOND() Return the microseconds from argument
MINUTE() Return the minute from the argument
MONTH() Return the month from the date passed
MONTHNAME() Return the name of the month
NOW() Return the current date and time
PERIOD_ADD() Add a period to a year-month
PERIOD_DIFF() Return the number of months between periods
QUARTER() Return the quarter from a date argument
SEC_TO_TIME() Converts seconds to 'hh:mm:ss' format
SECOND() Return the second (0-59)
STR_TO_DATE() Convert a string to a date
SUBDATE() Synonym for DATE_SUB() when invoked with three arguments
SUBTIME() Subtract times
SYSDATE() Return the time at which the function executes
TIME() Extract the time portion of the expression passed
TIME_FORMAT() Format as time
TIME_TO_SEC() Return the argument converted to seconds
TIMEDIFF() Subtract time
TIMESTAMP() With a single argument, this function returns the date or datetime expression; with two arguments, the sum of the arguments
TIMESTAMPADD() Add an interval to a datetime expression
TIMESTAMPDIFF() Subtract an interval from a datetime expression
TO_DAYS() Return the date argument converted to days
TO_SECONDS() Return the date or datetime argument converted to seconds since Year 0
UNIX_TIMESTAMP() Return a Unix timestamp
UTC_DATE() Return the current UTC date
UTC_TIME() Return the current UTC time
UTC_TIMESTAMP() Return the current UTC date and time
WEEK() Return the week number
WEEKDAY() Return the weekday index
WEEKOFYEAR() Return the calendar week of the date (1-53)
YEAR() Return the year
YEARWEEK() Return the year and week

참고

https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_adddate

 

MySQL :: MySQL 8.0 Reference Manual :: 12.7 Date and Time Functions

12.7 Date and Time Functions This section describes the functions that can be used to manipulate temporal values. See Section 11.2, “Date and Time Data Types”, for a description of the range of values each date and time type has and the valid formats

dev.mysql.com

문제

MySQL의 like 조건에서 \가 포함된 경우 원하는 형태로 조회가 되지 않음

해결

조건

title 컬럼의 \m가 포함된 형태로 검색 ( 검색 내용에 \ 가 포함 )

1. \를 두개 더 붙인다

where title like '%\\\m%'


2. escape를 사용
   기본적으로 escape가 \로 설정되어 있음. 이걸 다른 문자로 변경(예제는 | 를 사용)

where title like '%|m%' escape '|'

참고

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=leokevin&logNo=220645049628


to Top