[삽질기] MySQL 밀리세컨드 저장 및 Go ORM에서 처리

중국내 1위 쇼핑몰인 TMall API를 이용하여 매출 데이터를 정산하는 서비스를 만들고 있습니다. 이 API를 호출하기 위해 시간 범위를 입력하게 되는데 이때 이전 작업 시간 이후 부터 처리하기 위해 이전 처리 시간을 DB에 저장하고 이를 이용하여 다음번 호출하는 형태로 만들고 있습니다. 이 서비스 개발중에 밀리세컨드 처리와 관련하여 삽질한 내용을 공유합니다.

go_mysql

MySQL의 datetime  타입

이 작업의 상태 정보를 저장하기 위해 MySQL의 “datetime” 타입을 사용하였는데 기본 사이즈로 컬럼을 지정하면 밀리세컨드를 저장하지 않는다고 되어 있습니다.

A DATETIME or TIMESTAMP value can include a trailing fractional seconds part in up to microseconds (6 digits) precision. In particular, any fractional part in a value inserted into a DATETIME or TIMESTAMP column is stored rather than discarded. With the fractional part included, the format for these values is ‘YYYY-MM-DD HH:MM:SS[.fraction]’, the range for DATETIME values is ‘1000-01-01 00:00:00.000000’ to ‘9999-12-31 23:59:59.999999’, and the range for TIMESTAMP values is ‘1970-01-01 00:00:01.000000’ to ‘2038-01-19 03:14:07.999999’. The fractional part should always be separated from the rest of the time by a decimal point; no other fractional seconds delimiter is recognized.

(https://dev.mysql.com/doc/refman/5.7/en/datetime.html)

밀리세컨드까지 저장하려면 datetime(6) 를 사용해야 합니다.

Golang의 ORM에서 밀리세컨드 지원

MySQL로 삽질하는 중에 더 미궁속으로 빠지게 만든 놈이 Golang의 ORM 인데 주로 두가지 종류의 ORM 을 사용하고 있습니다. 하나는 xorm이고 하나는 beego orm  입니다. 이들 ORM은 기본적으로 밀리세컨드 단위로 포맷 변환을 지원하지 않습니다. 엄밀하게 말하면 지원하지 않는다라기 보다는 MySQL 과 같이 사용할 때 지원되지 않는다라고 볼 수 있습니다. xorm의 formatTime() 함수는 다음과 같이 구현되어 있습니다.

위 코드에서 보면 Type이 “TIMESTAMPZ” 인 경우 nano seconds까지 지원하고 있습니다. 하지만 MySQL에는 TIMESTAMPZ 타입이 없습니다(제가 구글링 해본 결론인데 혹시 있다면 알려주세요.)

또 다른 ORM인 beego의 ORM에는 다음과 같이 상수 정의가 되어 있습니다.

따라서 MySQL + Go의 ORM 조합인 경우 밀리세컨드 표현에 있어 주의해야 할 것 같습니다.

SQLServer의 NVARCAHR 와 Golang

또 하나 삽질은 SQLServer와의 Go ORM의 조합입니다. SQLServer의 데이터 타입에는 varchar 이외에 nvarchar가 있습니다. UTF8 문자를 저장하기 위해 사용하는 타입이라고 합니다.

문제는 이 nvarchar 타입에 index가 잡혀 있고, go의 sqlserver 드라이버를 이용하여 where 조건에 해당 컬럼을 지정하는 경우 문제가 발생할 수 있습니다. go sqlserver 드라이버는 string 타입을 nvarchar로 변경하는 코드를 다음과 같이 자동으로 추가합니다.

이렇게 되면 index가 잡혀 있다 하더라도 index를 충분히 사용하지 못하는 문제가 있습니다. SQLServer와 GO 사용 시 주의해서 사용해야 할 것 같습니다.

Update:

  • 2017/06/28
    • 이 건과 관련된 삽질이 마무리가 된 것 같았는데 다시 몇시간 고생했습니다. 원인은 스테이징 장비는 MySQL이 아니라 MariaDB 였습니다. MariaDB를 사용하면서 MySQL JDBC 드라이버를 사용하면 여전히 밀리세컨드를 저장하지 못하는 문제가 있습니다.
    • MariaDB의 JDBC 드라이버를 사용해도 이슈가 있기는 한데 해결되었다고 합니다. 낮은 버전 사용할 때는 주의해야 할 것 같습니다.