흐르는 시간의 블로그...

기존 MySQL Client는 모두 default 즉 latin1으로 되어 있다.

해당 시스템들을 수정하지 않고 utf8를 함께 사용하도록 하려고 하였다.

특히 동일 DB내의 특정 테이블은 euckr로 insert 하고 해당 테이블의 내용을 다른 utf8기반의 테이블로 옮기고자 하였다.

문제는 기존 client들이 모두 latin1으로 되어 있기 때문에 utf8으로 시스템과 DB가 설정된 곳에는 정상적으로 insert가 되지 않았다.

아래의 두 링크를 참고하여 해당 문제를 해결하였다.

my.cnf의 [mysqld]에 init_connect 라인을 삽입하고 재시작하였다.

client의 character set에 따라 insert와 select가 정상적으로 되어 한글을 표시함을 확인하였다.

[mysqld]


user = mysql

character_set_server = utf8

collation_server = utf8_general_ci

init_connect=SET names binary


MySQL 5.0 으로 업그레이드 할 때 UTF-8, EUC-KR 같이 쓰는 경우 캐릭터셋 문제 해결방법, v1.3

MySQL euc-kr, latin1 문자코드 UTF-8로 컨버팅하기


작년 즈음에 개발해 두고 상용으로 거의 2년만에 돌리고자 하는 시스템이 있다.

4대의 시스템에서 도는 것을 한대에서 돌도록 통합 개발했던 것이다.


이유 없이 시스템에서 DB 처리가 안되어 일정 기간동안 데이터가 날아갔다.

오늘 복구 작업을 진행하려고 이것저것 처리했는데 로그상에 이상한 것이 보이는 것이다.

insert 작업 처리를 무려 100만개 넘게 못하고 들고 있는 것이었다.


이유가 뭔가 하고 찾다가... 결국 찾기는 찾았다.


프로그램은 다음과 같이 처리하도록 되어 있다.


1. 데이터 수신 

2. 기록테이블에 데이터 삽입

3. 최종 상태 테이블에 데이터 업데이트

4. 업데이트 후 mysql_affected_rows()를 통해 적용 row수 체크

5. row가 0인 경우 최종 상태에 업데이트 할 row가 없는 것으로 인식 ---- 문제!!!

6. 업데이트 할 row가 없으면 최종 상태에 새로운 row insert --- 문제!!!


위의 두가지 문제라는 것들은 예상하지 못했던 부분들이다.

둘 중 하나만 예상대로 되었다면 문제는 발생하지 않았을 것이다.

두가지 모두 예상 밖 혹은 문제였기에 최종적으로 문제를 발생 시켰다.


~~~~ #1 ~~~~~~~~~~

최종 상태 테이블에 ID당 하나씩만 존재했어야 한다.

하지만 해당 테이블에 key를 unique가 아닌 일반 key로 해뒀다.

이것이 가장 큰 문제이긴 했다.

어쨋든 여러개의 ID가 들어갈 수 있는 상태가 되었다.

그래서 수만개 이상의 row가 들어가는 상황이 만들어 졌다.

~~~~~~~~~~~~~~~~~


~~~~ #2 ~~~~~~~~~~

mysql_affected_rows()가 "0"을 반환한다고 where 절에 해당하는 row가 0인 것은 아니다!


https://mariadb.com/kb/en/mariadb/mysql_affected_rows/ 의 내용을 보자.

"When using UPDATE, MariaDB will not update columns where the new value is the same as the old value."


단말기가 이상하여 아주 짧은 시간에 많은 데이터를 올리다 보니 여러 데이터가 완벽히 같은 경우가 존재했다.

실질적으로는 시간만 하더라도 달랐어야 하는데 말이다.

그래서 결국 최종 데이터가 있음에도 mysql_affected_rows()는 update 쿼리 실행후 실제로 업데이트한 row가 존재하지 않았다.

~~~~~~~~~~~~~~~~~


~~~~ #3 ~~~~~~~~~~

저런 문제를 드러나게 한 것은 

우리 단말이 잘못된 설정과 문제로 인해 매우 짧은 시간에 같은 데이터를 반복적으로 보냈기 때문이다.

~~~~~~~~~~~~~~~~~

저런 문제들이 중복되어...
동일 ID에 대한 수만개 이상의 row가 최종 테이블에 존재했으며...
해당 ID의 단말은 1초에도 10개씩 update를 시도 했다.
그러다 보니 매 업데이트 때마다 수만개 이상의 row를 업데이트 했다.
거기에 마지막 업데이트는 직전의 업데이트와 동일했고 수많은 row가 꾸준히 추가되었다.
결국 계속 update 대상 row가 증가한 것이다.

...........................
문제의 해결은 unique key를 달아 놓는 것으로 끝냈다.
소스상에서 update 후 mysql_affected_rows()가 0인 경우 insert를 시도한다.
그 경우 unique key에 의해 insert가 실패한다.

소스상에서 저 부분을 가드 하려면 두가지 방법이 있다.
1. 직전의 데이터를 가지고 비교하여 같으면 업데이트 쿼리와 삽입 쿼리 모두를 작동 시키지 않는 것이다.
2. mysql_affected_rows()가 아닌 매번 select를 하여 row가 존재하면 update를 진행하는 것이다.

unique key가 존재하면 두가지 모두 꼭 필요하지는 않아 작업을 진행하지 않았다.
...

참고: https://mariadb.com/kb/en/mariadb/mysql_affected_rows/

최근 새로운 MariaDB에 데이터를 저장해야할 일이 있다.

DBA가 작성한 쿼리를 보다보니 재밋는 부분들이 보였다. 아래 같은 코드가 눈길을 끌었다.


`XXXX` BIGINT(8)


DataType 문서를 보면 BIGINT면 64bit 숫자를 나타내는 것으로 보인다.


Numeric Data Types - Document for MariaDB

위의 필드가 8자리로 한정되면 차지하는 자릿수는 8byte 일까 4byte일까?

서버팀의 다른 직원과 삼십분 가량 이것저것 뒤져보니 생각보다 황당한 답이 나왔다.


내가 이해한 바로는 일단 8byte를 사용할 것이며 뒤에 붙은 "(8)"은 의미가 없다.


관련한 내용 MySQL 5.5의 문서에서 찾을 수 있었다.


11.1.1 Numeric Type Overview

관련 부분을 발췌하면 다음과 같다.

M indicates the maximum display width for integer types. The maximum display width is 255. Display width is unrelated to the range of values a type can contain, as described inSection 11.2, “Numeric Types”. For floating-point and fixed-point types, M is the total number of digits that can be stored.


maximum display width를 나타내는 것이며 그것은 255까지 될 수 있다. 저장하는 값의 범위와 관련이 없다는 것이다.


좀 더 찾아 다음과 같은 문서를 검색하였다. MySQL 5.7의 Reference Document이다.

12.2.5 Numeric Type Attributes

MySQL supports an extension for optionally specifying the display width of integer data types in parentheses following the base keyword for the type. For example, INT(4)specifies an INT with a display width of four digits. This optional display width may be used by applications to display integer values having a width less than the width specified for the column by left-padding them with spaces. (That is, this width is present in the metadata returned with result sets. Whether it is used or not is up to the application.)


The display width does not constrain the range of values that can be stored in the column. Nor does it prevent values wider than the column display width from being displayed correctly. For example, a column specified as SMALLINT(3) has the usual SMALLINT range of -32768 to 32767, and values outside the range permitted by three digits are displayed in full using more than three digits.


실제로 범위를 넘는 숫자에 대해서 표현하는 것도 제한하지 않으며 저장하는 것도 막지 않는다는 것이다.

도대체 왜 저 기능을 넣어놨을까 궁금해지는 순간이었다.


그래서 아래와 같은 결론을 내고 실행하였다.

필요한 필드의 최대값에 맞는 숫자타입을 기본 숫자타입에서 찾아서 지정하고 maximum display width는 지정하지 않는다.


누군가에게 도움이 되길 빌며 내용을 정리해본다.

...

오랜만에 수정하는 작업이 하나 있었다.

해당 서버의 mysql의 계정 설정이 하나 빠진 것이 있어 공인IP로 접속이 되지 않았다.

관련해서 이런저런 얘기를 팀원과 하다가 하나 배운 것이 있다.


바로 protocol 옵션에 대한 것이다. 실제 사용 DB는 mariadb 5.x.x 버전이다.

mysql의 메뉴얼 페이지를 보자.

4.2.2 Connecting to the MySQL Server

클릭을 하면 바로 protocol 쪽으로 이동한다.

네개의 옵션이 있다. 정확하지는 않지만 아랫쪽으로 갈수록 빠를듯 하다.

PIPE와 MEMORY는 윈도우즈에서만 사용가능하고

SOCKET은 Unix Only이다.


내용에 보듯이 unix(linux)에서 localhost로 접속하게 되면 기본적으로 Unix socket file을 사용한다는 내용이다.

당연히 TCP 보다 빠르다.

127.0.0.1로 접속하면 TCP로 접속하지만 localhost로 접속하면 unix socket으로 접속한다.


간단하지만 db의 local machine에서 작업을 해야한다면 염두에 둬야할 내용이다.


127.0.0.1로 접속 하는 경우

MariaDB [(none)]> \s
--------------
mysql  Ver 15.1 Distrib 5.5.34-MariaDB, for Linux (x86_64) using readline 5.1

Connection id:          115593510
Current database:
Current user:           finepoint@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server:                 MariaDB
Server version:         5.5.36-MariaDB-log MariaDB Server
Protocol version:       10
Connection:             127.0.0.1 via TCP/IP
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8


localhost로 접속 하는 경우

MariaDB [(none)]> \s
--------------
mysql  Ver 15.1 Distrib 5.5.34-MariaDB, for Linux (x86_64) using readline 5.1

Connection id:          115586990
Current database:
Current user:           finepoint@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server:                 MariaDB
Server version:         5.5.36-MariaDB-log MariaDB Server
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
UNIX socket:            /var/lib/mysql/mysql.sock