MySQL, PostgreSQL - Y2K38(Year 2038) 버그 여부 확인

    2038년 버그란?

    도대체 2038년에 어떤 일이 생기길래 2038년 버그라는 말이 있고, Y2K38이라는 단어까지 존재하는 것일까? 예전에 1999년에서 2000년으로 넘어갈 무렵에 전산상 표기할 수 있는 자릿수가 변경되면서 발생하는 문제를 통칭하여 Y2K 문제라고 불렀었다. 90년대 후반까지는 년도를 두자리로 부르는 것이 관례적이었는데, (마치 우리가 학번을 부르면 끝에 두 자리만 부르고, 주민등록 번호상으로도 년도는 뒤의 두 자리만 주로 쓰듯이) 1999년에서 2000년도로 넘어가면서 99->00으로 바뀌는 과정에서 00을 2000으로 인식하느냐, 1900으로 인식하느냐의 문제가 발생했었다. 그리고, 이름으로도 유추할 수 있듯이 유사한 문제가 2038년에 발생하려고 한다. 왜 그럴까?


    2038년 버그 원인/ 확인

    결국, 2038년에 기능이 정상 동작하지 않는다는 의미가 되겠다. 기본적으로 1970-01-01 00:00:00를 기준으로 소요된 시간을 Unix Epoch Time, Unis Time Stamp, POSIX Time 등으로 부른다. 기존 대부분 아키텍처가 이 값을 signed 32bit integer로 처리하고 있고, 이 값의 범위는 -2,147,483,648에서 2,147,483,647까지를 포함한다. 따라서,이 값의 최대값을 초과하게 되면 문제가 발생하게 된다.

    이와 같은 로직이 MySQL과 PostgreSQL에서는 타임값이 어떻게 변환되는지 확인하고, 임계값을 넘어가게 되면 DB에서 어떤 일이 발생하는지 확인해 보자.


    MySQL에서 확인

    mysql> select unix_timestamp('2038-01-19 12:14:07');
    +---------------------------------------+
    | unix_timestamp('2038-01-19 12:14:07') |
    +---------------------------------------+
    |                            2147483647 |
    +---------------------------------------+
    1 row in set (0.05 sec)
    
    mysql> select unix_timestamp('2038-01-19 12:14:08');
    +---------------------------------------+
    | unix_timestamp('2038-01-19 12:14:08') |
    +---------------------------------------+
    |                                     0 |
    +---------------------------------------+
    1 row in set (0.00 sec)

    MySQL에서는 unix_timestamp() 함수를 사용하여, 주어진 시각의 Unix Timestamp값을 구할 수 있다. 위에서 보는 바와 같이 2038-01-19 12:14:07까지는 해당 값을 리턴해 주지만, 1초라도 늘어나게 되면 Unix Timestamp값을 잃어버리게 된다. 한편, from_unixtime()이라는 함수를 쓰면, Unix Timestamp로부터 Date를 원복할 수 있는데, 이를 사용하여 위의 내용을 검토해 보면 다음과 같다.

    mysql> select from_unixtime(unix_timestamp('2038-01-19 12:14:07'));
    +------------------------------------------------------+
    | from_unixtime(unix_timestamp('2038-01-19 12:14:07')) |
    +------------------------------------------------------+
    | 2038-01-19 12:14:07                                  |
    +------------------------------------------------------+
    1 row in set (0.00 sec)
    
    mysql> select from_unixtime(unix_timestamp('2038-01-19 12:14:08'));
    +------------------------------------------------------+
    | from_unixtime(unix_timestamp('2038-01-19 12:14:08')) |
    +------------------------------------------------------+
    | 1970-01-01 09:00:00                                  |
    +------------------------------------------------------+
    1 row in set (0.00 sec)

    즉, 2038-01-19 12:14:07에서 1초 후는, 2038-01-19 12:14:08이 아니라 1970-01-01 09:00:00로 회귀해 버리게 된다.


    PostgreSQL에서 확인

    PostgreSQL에서도 위의 테스트를 동일하게 수행해 보자.

    dev=# select extract(epoch from '2038-01-19 12:14:07'::timestamp);
     date_part
    ------------
     2147516047
    (1 row)
    
    dev=# select extract(epoch from '2038-01-19 12:14:08'::timestamp);
     date_part
    ------------
     2147516048
    (1 row)

    위와 같이 extract 함수를 사용하고, epoch from을 지정하면 해당 타임스탬프로부터 Unix Timestamp를 추출할 수 있다. 그러나, MySQL과 달리 1초를 추가해도 MySQL과 동일한 에러가 발생하지는 않는다.

    dev=# select to_timestamp(extract(epoch from '2038-01-19 12:14:07'::timestamp));
          to_timestamp
    ------------------------
     2038-01-19 12:14:07+00
    (1 row)
    
    dev=# select to_timestamp(extract(epoch from '2038-01-19 12:14:08'::timestamp));
          to_timestamp
    ------------------------
     2038-01-19 12:14:08+00
    (1 row)

    마찬가지로, 날짜를 Unix Timestamp를 추출하고, 이를 다시 Timestamp로 복원해 봐도 정상적으로 동작하는 것을 확인할 수 있다.


    맺음말

    위의 예에서 보다시피, 모든 DBMS가 이 문제를 갖고 있는 것은 아니다. MySQL은 Timestamp 형을 쓰고 있다면, 시한부성으로 UTC의 편리성을 얻고 있는 셈이다. 따라서, 현재 MySQL에서는 2038년 이후의 데이터를 저장해야 한다면, Datetime을 써야 한다. 그리고, Datetime을 사용할 때는 Timestamp와 달리 자동으로 Timezone에 맞게 변경되지 않는다는 점을 알고 사용해야 한다.


    관련 글

    https://luran.me/377

    댓글(0)

    Designed by JB FACTORY