$ docker run -e MYSQL_ROOT_PASSWORD=1234 --name json_mysql mysql
Mysql 버전은 (2020년 3월 현재 최신버전)8.0.19다.
$ mysql -u root -p -h 172.17.0.2
Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 8.0.30 MySQL Community Server - GPL
Copyright (c)2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h'for help. Type '\c' to clear the current input statement.
mysql>
MySQL JSON Data type
Mysql 5.7 부터 Json 데이터 타입을 지원한다. Json 데이터 타입을 저장하기 위한 칼럼은 JSON으로 설정하면 된다. 테스트를 위한 데이터베이스를 만들었다.
나는 age_virtual 이라는 INT 데이터를 저장하는 컬럼을 만들었다. 이 컬럼에는 info컬럼의 age에 접근한다는 걸 알려주기 위해서 info->'$.age' 문을 사용했다. 이제 age_virtual 컬럼은 info->'$.age'를 가리키게 된다. 테이블 구성을 살펴보자.
DESC users;
Field
Type
Null
Key
Default
Extra
id
int(11)
NO
PRI
NULL
auto_increment
email
varchar(64)
NO
NULL
info
json
YES
NULL
age_virtual
int(11)
YES
NULL
VIRTUAL GENERATED
이제 CREATE INDEX문을 이용해서 age_virtual 컬럼을 색인해보자.
CREATEINDEX age_idx ON users(age_virtual);
EXPLAN 문을 이용해서 쿼리를 평가해보자.
EXPLAINselect*FROM users
WHERE info->"$.age"=38;
id
select_type
table
partitions
type
possible_keys
key
key_len
ref
rows
filtered
Extra
1
SIMPLE
users
NULL
ref
age_idx
age_idx
5
const
2
100.00
NULL
모든 row를 스캔하는 대신에 age_idx 인덱스를 사용해서 2개의 행에 대해서만 쿼리가 수행 된 것을 확인 할 수 있다. 데이터베이스가 운영 중이라면 ALTER를 이용해서 색인을 추가 할 수 있다. info.name을 색인해 보자.
MySQL 테스트 환경
Docker로 실행했다.
Mysql 버전은 (2020년 3월 현재 최신버전)8.0.19다.
MySQL JSON Data type
Mysql 5.7 부터 Json 데이터 타입을 지원한다. Json 데이터 타입을 저장하기 위한 칼럼은 JSON으로 설정하면 된다. 테스트를 위한 데이터베이스를 만들었다.
테스트 테이블을 만들었다.
INSERT
값을 입력(insert) 해보자.
MySQL JSON Path 표현식
MySQL은 JSON 데이터를 처리하는 쿼리를 쉽게 사용하기 위한 JSON path 표현식(expressions) 를 제공한다. 아래 쿼리를 보자.
쿼리에서 사용한 오른쪽 화살표(->)로 필드와 경로식을 구분한다. info 필드에 있는 JSON 데이터에서 key 이름이 name 요소를 가리킨다.
SELECT
SELECT와 JSON Path를 이용해서 JSON 데이터에 접근 할 수 있다.
JSON 데이터에 있는 Key 는 path 연산자(->) 로 읽을 수 있다.
결과는 아래와 같다.
조회는 할 수 있지만 색인이 걸려있지 않기 때문에 효율은 엄청나게 떨어질 것이다. 색인을 걸어야 할 건데, 이건 JSON 색인 편에서 살펴보도록 하자.
UPDATE
MySQL은 JOSN의 partial UPDATE를 지원한다. UPDATE는 JSON_SET(), JSON_REPLACE(), JSON_REMOVE() 3개의 함수를 이용해서 수행 할 수 있다.
JSON_SET(): 기존 값을 업데이트하고 존재하지 않을 경우 추가한다.
JSON_REPLACE(): 기존 값만 바꾼다.
JSON_REMOVE(): 값을 삭제한다. 이때 값은 NULL이 된다.
문법은 아래와 같다.
1인 유저의 info.age 를 46으로 업데이트 했다.
JSON 함수들
자주 사용하는 함수들만 설명한다. 전체 함수 목록은 MySQL 5.7 JSON Function Reference문서를 참고하자.
JSON_ARRAY
값을 평가해서, JSON 배열을 반환한다.
아래와 같은 데이터가 있다고 가정해보자.
JSON_ARRAY를 이용해서 JSON 배열로 리턴 할 수 있다.
JSON_EXTRACT
JSON 경로와 일치하는 데이터를 리턴한다.
JSON_OBJECT
이 함수는 SELECT 결과를 JSON 형태로 변환해서 받아보고 싶을 때 사용 할 수 있다.
아래와 같이 JSON으로 리턴할 수 있다.
아래와 같이 INSERT 문에도 사용 할 수 있다.
JSON_SET
JSON_SET은 JSON 문서에 필드를 추가하거나 업데이트하기 위해서 사용한다. 같은 필드가 있을 경우 업데이트하고, 새로운 필드는 insert 한다.
저장된 레코드의 JSON 문서의 데이터를 업데이트 해보자.
JSON_INSERT
같은 필드에 대한 값을 변경하지 않는다. 새로운 필드만 추가한다.
"a" 필드의 값은 업데이트되지 않고, "c"만 추가된걸 확인 할 수 있다.
JSON_REPLACE
같은 필드가 존재할 경우 변경한다. 존재하지 않는 필드는 무시한다.
JSON_VALID
JSON 문서 형식이 올바른지 검사한다.
JSON 색인
아래 SQL문을 보자.
잘 작동하지만 색인이 걸려있지 않기 때문에 비효율적으로 작동 할 것이다. EXPLAIN을 이용해서 쿼리를 측정해보자.
type필드는 테이블에서 행을 어떻게 찾았는지를 알려준다. ALL은 SQL문을 수행하기 위해서 전체 레코드를 전부 스캔했음을 의미한다. 실제 서비스에서 SQL문을 사용해서는 안될 것이다.
MYSQL 5.7.14 부터는 JSON 컬럼의 Key에 대해서도 색인을 만들 수 있다. 테이블을 다시 만들었다.
CREATE TABLE로 테이블을 만들 때, 아래와 같이 컬럼을 추가 할 수 있다.
여기에서 핵심은 GENERATED ALWAYS와 AS다. GENERATED ALWAYS라는 문구는 선택사항이다. 테이블 열이 generated된 컬럼임을 명시적으로 나타내려는 경우에만 필요하다. 필요한 것은 생성된 컬럼에 원하는 값을 리턴하는 AS문이다.
나는 age_virtual 이라는 INT 데이터를 저장하는 컬럼을 만들었다. 이 컬럼에는 info컬럼의 age에 접근한다는 걸 알려주기 위해서 info->'$.age' 문을 사용했다. 이제 age_virtual 컬럼은 info->'$.age'를 가리키게 된다. 테이블 구성을 살펴보자.
이제 CREATE INDEX문을 이용해서 age_virtual 컬럼을 색인해보자.
EXPLAN 문을 이용해서 쿼리를 평가해보자.
모든 row를 스캔하는 대신에 age_idx 인덱스를 사용해서 2개의 행에 대해서만 쿼리가 수행 된 것을 확인 할 수 있다. 데이터베이스가 운영 중이라면 ALTER를 이용해서 색인을 추가 할 수 있다. info.name을 색인해 보자.
인덱스를 만들어보자.
색인이 적용되는지 쿼리를 평가해보자.
JSON_STORAGE_SIZE & JSON_STORAGE_FREE
JSON_STORAGE_SIZE은 JSON 문서를 저장하는데 사용된 바이트 수를 반환한다.
보통은 sum, avg 등의 함수와 같이 사용한다.
정리
SQL을 사용 할 수 있다는 것은 엄청난 장점이다.
PostGresql도 JSON 데이터 타입을 지원한다. 유연성을 확보한 건데, 이렇게 되면 "수평적 확장"이 핵심 기술이 아닌 한 MySQL 써도 될 거 같다.
MySQL은 운영/관리의 장점도 있다.
참고
MySQL :: MYSQL 5.7 Reference Manual
MySQL for JSON: Generated Columns and Indexing
Recent Posts
Archive Posts
Tags