MySQL의 헷갈리는 자료형들
📜 라디오 대본
얄코: 혼밥하면서 듣는 얄코 라디오!
얄코: 오늘은 MySQL에서 사용되는 몇 몇 비슷해보이지만 다른
얄코: 자료형들에 대해 알아보겠습니다.
얄코: 저는 얄팍한 개발자 얄코
미토: 미친토끼 미토에요.
미토: 내가 DB 짜려고 테이블을 만들다 보면은 너무 화가나가지고
미토: 아니, MySQL도 그렇고 이놈의 관계형 데이터베이스들은
미토: 뭐가 그렇게 자료형이 많은거에요?
미토: 정수 하나 넣을 자리를 만들래도 뭐 TINYINT, SMALLINT,
미토: MEDIUMINT에다가 또 그냥 INT, BITINT까지
미토: 진짜 고르다 울화가 치밀어올라가지고 그냥
미토: 자바스크립트처럼 숫자면 Number, 문자열이면 String, 날짜면 Date
미토: 이렇게 좀 단순하게 맹글면은 좀 쓰기 편해요?
얄코: 물론 그렇게 하면 고민거리가 확 줄겠죠. 하지만 그렇게 만든 이유가 있어요.
얄코: 관계형 데이터베이스는 엑셀처럼 표, 테이블 구조로 된 데이터들을
얄코: 대량으로 저장하고 관리, 사용하기 위해 만들어진거에요.
얄코: 이 데이터들이 다 DB 서버의 공간을 계속 차지하는거기 때문에
얄코: 저장공간을 최대한 효율적으로 활용하는게 정말 중요한 이슈죠.
얄코: 우리가 엑셀에서 표를 만들 때 각 컬럼, 열에 들어갈 정보의 길에 따라
얄코: 해당 컬럼의 길이를 조절하잖아요.
얄코: 어떤 업소의 영업 여부가 'Y', 'N'으로 표시회는 컬럼은 짧게,
얄코: 해당 업소의 설명이 문장으로 들어가는 컬럼은 길게 만들겠죠.
얄코: 각 컬럼의 정보가 최대 어느정도 길어질 수 있는가를 감안해서
얄코: 칸들의 너비를 조절해야 화면에서 낭비되는 공간이 적을거에요.
얄코: 몇 줄 정도야 크게 상관없지만 그게 몇 천, 몇 만 행이 넘어가면
얄코: 어마어마한 너비의 화면이 허비되는거니까요.
미토: 데이터베이스도 각 컬럼에 들어갈 정보에 비해 과하지 않게
미토: 자료형을 설정해놔야 용량 낭비가 적어진다 이건거죠?
얄코: 맞아요. 옛날 사람들이 계산할 때 쓰던 주판을 떠올려볼게요.
얄코: 주판 중에서도 돌이 적게 들어간 작은 게 있고, 큰 게 있을텐데
얄코: 큰 주판이 돌이 많으니까 셀 수 있는 숫자가 더 많겠죠.
얄코: 테이블에 숫자 컬럼을 둔다는 건 각 칸마다 이 주판을 둔다는거에요.
얄코: TINYINT는 작은 주판, BITINT는 큰 주판이죠.
얄코: 이 주판의 크기만큼 각 칸은 면적, 즉 용량을 차지해요.
얄코: 큰 주판으로도 작은 숫자를 표기할 수는 있지만,
얄코: 작은 수라고 해서 주판이 차지하는 크기가 작아지지는 않겠죠.
미토: 낭비가 된다는 거네요. 끽해야 1에서 10 정도가 들어갈 컬럼에다가
미토: 백만까지 셀 수 있는 주판을 놔버리면은.
미토: 이런 데이터들이 입력될 때마다 이 공간들을 영구적으로 가져가니까
미토: 관계형 DB에서는 이런 낭비를 줄이는게 되게 중요하기땜세
미토: 사이즈 옵션 여러개를 두는거다. 그러면은 좀 납득이 되네요.
미토: 그러면은 이 정수 자료형을 쓸 때는 이 각 INT의 크기를 구글링해다가
미토: 젤루 적당한 걸로다가 지정을 하면은 되겠구만.
얄코: 네. 그리고 양수만 사용될건지 마이너스 숫자도 들어갈건지도 고려해서
얄코: 양쪽을 다 다루는 SIGNED, 또는 플러스만 나타내는 UNSINGED 중에-
얄코: 선택하는것도 용량 절약에 큰 도움이 돼요.
얄코: SIGNED는 주판의 절반은 마이어스, 나머지 절반은 플러스에 쓰는거고
얄코: UNSIGNED는 전체를 플러스에 쓰는거라 두 배를 더 표시할 수 있거든요.
미토: 음~ 그러면은 일단 정수는 오케이
미토: 근데 실수는 어떻게 골라요? 얼마 점 얼마 이런숫자들.
미토: 보니까 이거 Fixed 종류랑 Floating 종류가 있다는데 뭐에요 다 이게?
얄코: 먼저 fixed, '고정'소수점 자료형들을 살펴보자면요
얄코: Decimal이랑 Numeric이 있는데 이 둘은 이름만 다르다고 보셔도 돼요.
얄코: 이 고정소수점은 어떤 실수를 '정확히' 표현하는데 사용됩니다.
얄코: 다른 한 종류인 floaing 포인트, Float이랑 Double이 있는데
얄코: 이것들은 '부동'소수점이에요.
미토: 부동이라면 부동자세! 안 움직인다는 거 아녜요?
미토: 고정소수점이랑 똑같은 말 같은데?
얄코: 여기서의 '부동'은 떠서 움직인다, floating 한다는 뜻이에요.
얄코: 고정소수점과 달리 고정되지 않고 떠다니는거죠.
미토: 번역을 헷갈리게 해놔가지고, 동동소수점이라고 하면 되잖아요.
얄코: 자, 이 부동소수점은 실수를 '정확히' 표시하기보다는
얄코: 더 넓은 범위의 숫자를 표현하기 위해 사용돼요.
미토: 정확성과 범위 둘 중 하나로 간다? 어떻게 그렇게 돼요?
얄코: 사실 정확히 설명하자면 2진법으로 가야 하지만
얄코: 머리아픈 주제고 또 라디오니까, 그냥 10진법으로 설명할게요.
얄코: 아까 그 주판을 다시 떠올려봅시다.
얄코: 고정소수점은 담백해요. 주판에서 점의 위치를 정해서
얄코: 그 왼쪽은 정수 부분, 나머지는 소수 부분을 세는거에요.
미토: 예를 들어가지고 돌이 일곱 줄 들어간 주판이 있으면은
미토: (10진법이니까 돌이 한 줄에 0에서 9까지 센다고 치고)
미토: 이를테면 네 줄은 정수, 세 줄은 소수에 할당해가지고
미토: 1234.567 이렇게 일곱자리 숫자들을 넣을 수 있는거네요.
미토: 0에서 9999.999까지 이렇게요
미토: 한 줄만 정수에 할당해서 1.234567 이렇게 갈 수도 있구요
얄코: 맞아요. 정수부와 소수부를 합쳐서 주어진 자리까지는
얄코: 입력된 값 그대로 정확히 표시할 수 있는거죠.
미토: 그러면은 floating, 부동소수점은 뭐가 다른건데요?
얄코: 고정소수점이 주어진 자릿수 값을 정수부와 소소부로 나눈다면
얄코: 부동소수점에서는 '가수'와 '지수', 로 나누는데 벌써 머리아프죠.
얄코: 복잡한 수학개념으로 말고 간단하게 말하자면, 주어진 숫자를
얄코: X 곱하기 10의 Y승 이렇게 해석하는거에요.
얄코: 점은 항상 첫 자리 다음에 오게 합니다.
얄코: 1234.567의 경우 1.234567 곱하기 10의 3승인거죠.
얄코: 이제 아까처럼 돌 일곱 줄이 주어지면
얄코: 이것들을 이 X랑 Y에 할당하는거에요.
미토: 오호라. 그러면은 1.234567 이 일곱자리가 X
미토: 그리고 3을 나타낼 한 자리를 Y... 이거 부족하네?
미토: 입력한 숫자가 일곱자리인데 X에 일곱자리를 넣어주고
미토: Y에도 한 자리 넣을라면은 돌이 딱 일곱줄이라 모자라잖아요.
얄코: 네, 그래서 이럴 경우 X에서 한 자리를 포기하고 반올림해서
얄코: 1.23457 곱하기 10의 3승, 이렇게 6자리와 1자리로 하는거죠.
얄코: 대신 최대 1.23457 X 10의 9승까지 더 넓은 범위의 수를
얄코: 부동소수점으로 나타낼 수 있는거에요.
미토: 음~ 그러면은 Y에 한 자리수를 더 줘서 1.2346 X 10^99도 할 수 있네요.
미토: 이래가지고 정확도를 포기하는 대신에 범위를 더 크게 얻는거구만.
얄코: 맞아요. 이제 이 원리를 2진수로 바꿔서 생각하면
얄코: 컴퓨터의 부동소수점이 어떻게 동작하는지 파악할 수 있죠.
m e의 2승. 2의 e승
얄코: MySQL 뿐 아니라, 프로그래밍에서 부동소수점으로는 계산을 할 때는
얄코: 0.1 곱하기 1000이 99.999가 나오는 등 오차가 있을 수 있다는 걸
얄코: 항상 고려하고 사용해야 해요.
미토: FLOAT이랑 Double은 그럼 뭔 차이에요?
미토: FLOAT을 따블로 한게 Double인건가?
얄코: 맞아요. FLOAT은 4바이트, DOUBLE은 8바이트를 차지하죠.
미토: 그러면은 이제 숫자는 내가 어디가서 자료형 잘 골랐다는 소리 듣게
미토: 데이터타입 선택을 할 수가 있을 것 같애요.
미토: 근데 문자는 또 왜 여러가지가 있는거에요?
미토: 몇 글자 넣을 컬럼이 필요한데 CHAR이랑 VARCHAR이 있어가지고
미토: 내가 고를려고 하니까 또 화가 머리 끝까지 나는거에요!
얄코: CHAR, Character에 Variable이 붙은게 VARCHAR죠.
얄코: CHAR는 고정된 공간을 차지해요.
얄코: 어떤 컬럼을 CHAR(10)으로 설정한 다음
얄코: 3글자 문자열을 넣으면 나머지 7자는 스페이스로 채워지죠.
얄코: 이와는 달리 VARCHAR는 가변적인 공간을 차지해요.
미토: 가변적이라는거는 같은 컬럼이라도 데이터에 따라
미토: 차지하는 크기가 행마다 달라질 수가 있다는 거인 거에요?
얄코: 맞아요. CHAR처럼 무조건 정해진만큼 차지하는게 아니라
얄코: 입력되는 글자의 길이에 따라 그에 맞게 공간을 할당해요.
미토: 그러면은 VARCHAR가 짱짱맨인거잖아요?
미토: 공간을 유연하고 효율적이게 쓸 수 있는거를 두고
미토: CHAR는 왜 안 없애는거에요?
얄코: 먼저, 딱 고정된 길이의 문자열만 들어가는 컬럼에는
얄코: CHAR를 쓰는 게 더 공간을 절약해요.
얄코: VARCHAR는 말 그대로 길이가 가변적이기 때문에
얄코: 이 글자가 몇 글자인지까지 기록을 해 둬야
얄코: 컴퓨터가 이걸 어디까지 읽으면 되는지 알거든요.
얄코: 즉 글자 정보에 더해서 글자 길이 정보까지 저장한다는거죠.
미토: 그러면은 '영업', '휴무', '폐업' 이렇게
미토: 같은 길이의 문자열이 들어가는 칸에다가는
미토: CHAR을 쓰는 게 더 경제적이라는거네요.
얄코: 그렇죠. VARCHAR는 길이 정보로 한두 바이트를 더 차지해요.
얄코: 그리고 아무래도 연산시에 글자길이도 계산을 하는거다보니
얄코: 동작에 있어 CHAR보다는 미세하게 시간을 더 소모하죠.
얄코: 다루는 데이터 양이 많아지면 유의미한 속도차가 될 수 있구요.
미토: 그런거들을 고려해가지고다가 CHAR랑 VARCHAR중에
미토: 선택을 하면은 되는 것이다.
미토: 아니 근데 궁금한게, 어차피 입력값에 따라 공간이 할당되는거면은
미토: VARCHAR 뒤에 최대 길이 숫자는 왜 적어넣게 돼있는거에요?
미토: 그냥 다 맥시맘으로 해놔도 입력되는 글자수만큼만 용량 차지할텐데.
얄코: 누가 장난이나 실수로 전화번호를 100글자씩 넣는 것 등을 방지하는거죠.
미토: 옹~ 용량 조절보다는 입력되는 데이터를 통제하는게 목적인거구만.
얄코: 성능에 있어서도 불필요하게 많이 주어진 VARCHAR 길이값은
얄코: 퍼포먼스를 저하시킬 수 있다고 하니, 적당히만 주는게 좋겠죠.
미토: 그러면은 TEXT는 또 뭐에요? 이거도 글자들 저장하는 것 같은데.
얄코: TEXT는 게시물의 본문처럼 긴 문자열을 저장할 때 사용돼요.
얄코: 그냥 TEXT는 65,535바이트, 가장 큰 LONGTEXT는
얄코: 4기가 가량까지 저장할 수 있죠.
얄코: 최대 저장용량이 큰 대신 VARCHAR에 비해 제약 사항들이 있어요.
얄코: VARCHAR처럼 가변길이지만, 컬럼에 최대값을 지정하지는 못해요.
얄코: 글자 수 제한을 두지 못한다는거죠.
얄코: 그리고 VARCHAR는 문자열로 검색을 빠르게 할 수 있도록
얄코: 인덱스에 포함될 수 있는데 TEXT는 부분적으로만 가능해요.
미토: 인덱스라는거가, 데이터에 행을 추가할 때마다 지정된 컬럼에 대해서
미토: 그 값들이 다른 시트에 따로 정렬이 된 채로 행 번호랑 저장이 돼가지고
미토: 이 컬럼을 기준으로 어떤 행을 검색할 때 더 찾을 수 있게 하는거죠?
얄코: 네. TEXT에서는 이걸 앞부분에 한정적으로만 걸 수 있어요.
얄코: (다만 Full text index란 건 TEXT로도 가능합니다.)
얄코: 여튼 VARCHAR에서 가능한 default값도 TEXT에서는 줄 수 없고
얄코: SQL 데이터베이스랑 엔진마다 다르지만 MySQL에서는
얄코: 보통 TEXT의 성능이 VARCHAR보다는 떨어져요.
미토: 아니, 그러면은 VARCHAR도 '그냥 TEXT'만큼은 저장이 되는데
미토: MEDIUMTEXT랑 LONGTEXT 말고는 쓸모가 없는거 아녜요?
미토: TINYTEXT랑 TEXT는 기능도 덜하고 성능도 후달리는건데
미토: 그냥 VARCHAR에 한도 많이 줘가지고 쓰는게 안 나은가?
얄코: 웬만한 크기에 있어서는 그냥 VARCHAR을 쓰셔도 돼요.
얄코: 그런데 VARCHAR의 최대한도이자 TEXT의 크기이기도 한
얄코: 65,565바이트는 MySQL 각 행의 최대 한도에요.
얄코: 이건 엔진의 한도에 관계없이 적용된다고 공식문서에 나와있구요.
미토: 아, 그럼 VARCHAR를 맥스로 줘가지고 행 용량을 꽉 채워버리면은
미토: 다른 정보들을 못 넣는거네요. 그럼 TEXT는요?
얄코: TEXT는 VARCHAR처럼 각 행에 인라인으로 저장되는게 아니라
얄코: 다른 공간에 따로 저장되기 때문에 상관없어요.
얄코: 이 점을 감안해서, 테이블의 행별 데이터 총량이 65,565을 넘어가려 하면
얄코: TEXT를 사용하면 되는거에요.
미토: 다 존재의 이유가 있는거구만. BLOB이란거도 있던데 이거는 뭐에요?
미토: TEXT랑 똑같이 TINYBLOB부터 LONGBLOB까지 있고
미토: 크기들도 각각 똑같은데 뭐에다 쓰는거에요 이건?
얄코: TEXT가 이름처럼 글자로 된 문자열 컨텐츠를 담는거라면
얄코: BLOB은 같은 용량에, 바이너리 데이터를 담는데 사용돼요.
얄코: TEXT와는 달리 어떤 글자종류인지 캐릭터셋을 갖지 않죠.
얄코: 이미지 파일 등을 파일로 저장하지 않고 바이너리 데이터로
얄코: 데이터베이스에 저장하고자 할 때 BLOB을 사용해요.
미토: 오케이. 문자열 자료형에 대해서도 이제 좀 알 것 같애요.
미토: 그런데 그 날짜랑 시간 저장하는거 있잖아요.
미토: 거기에도 보니까 DATETIME이 있고 TIMESTAMP가 있던데
미토: 이거는 또 왜 둘이가 있는거에요? 뭐가 달라요, 얘네들은?
얄코: 가장 중요한 차이부터 말하자면, DATETIME은 말 그대로
얄코: 날짜와 시간, 이 둘의 '절대적'인 값이에요.
얄코: 그냥 몇년 몇월 며칠 몇시 몇분 몇초라고
얄코: 컬럼에 '글자로 적어넣은거'라고 보시면 돼요.
얄코: 이걸 어디서 누가 읽든 다르게 보이지 않는거구요.
얄코: 반면 TIMESTAMP는 상대적입니다. 시간대에 영향을 받아요.
얄코: 미토씨가 지금 서울에서 이 TIMESTAMP, 시간도장을 쾅 찍었어요.
얄코: 이게 서울 시간으로는 2021년 7월 15일 0시라면
얄코: 미국 뉴욕에서는 2021년 7월 14일 12시겠죠.
얄코: 때문에 이거는 이 데이터가 전파돼서 저장된 곳마다 다르게 읽혀요.
미토: 아니, 다른 값들이 저장되는건 아닐텐데 어떻게 그렇게 돼요?
얄코: DATETIME은 입력된 시간을 그냥 글자로 적어넣는거라고 했죠?
얄코: 이와 달리, TIMESTAMP가 저장하는건 숫자값이에요.
얄코: 그리니치 평균시로 1970년 1월 1일 자정부터, 입력되는 시간까지
얄코: 총 몇 초가 지났는지를 세어서 넣는거거든요.
미토: 음~ 그렇기땜세 같은 값도 DB가 돌아가는 서버에 세팅된
미토: 시간대에 따라서 다르게 읽힐 수가 있다는거죠?
얄코: 맞아요. 어떤 사이트가 세게 여러 곳에 서버를 두고 서비스를 하는 경우
얄코: 한국에서 누가 게시판에 글을 올렸을 때 글이 올라온 시간이
얄코: 프랑스에서 그쪽 서버로부터 받아와서 읽는 사람에게는
얄코: 중부 유럽 시간대에서의 일시로 읽히는 게 맞겠죠.
얄코: 이처럼 누가 뭔가를 올리거나 수정거나 할 때 날짜와 시간을 쾅 찍어서
얄코: 읽는 곳마다 해당 시간대로 보이도록 할 때는 TIMESTAMP,
얄코: 그리고 이와는 상관없이 어떤 역사 사건의 발생시를 텍스트로 남기는 등
얄코: 어디에서든 똑같이 보이도록 할 시간은 DATETIME을 쓰면 돼요.
미토: 오~
얄코: 그 외의 차이로는 DATETIME은 8바이트, TIMESTAMP는 4바이트구요
얄코: DATETIME으로는 1000년부터 9999년까지
얄코: TIMESTAMP로는 1970년부터 2038년까지 기록이 가능하다는 것
얄코: TIMESTAMP는 행 추가시 값을 안 넣으면 자동으로 현 시간이 입력된다는 것
얄코: 이정도 기억해두시면 될 거에요.
미토: 오늘 또 이렇게, 세상 살면서 화가 날 이유 몇 가지를 줄였네요.
얄코: MySQL의 자료형 이야기는 이정도로 마무리하겠습니다.
얄코: 이후 얄코라디오에서 다뤄주었으면 하는 주제들
얄코: 댓글로 남겨주시면 구상해보도록 할게요.
얄코: 이상 얄코와 미토였습니다. 즐코하세요!