Categories

Tags


개요

x-dev

  • Web Application 개발에 있어, FRONT가 중요한 역할을 하며, JSON과 같은 비정형 데이터를 저장해야 하는 요구사항이 많아지게 되었다. 이에따라, Mongo DB 와 같은 Document Store Database 가 많은 유행을 하게 되었고 지금까지 사용되어져 왔다.

  • MySQL에 Document Store기능이 내장됨으로써 더 이상 JSON type의 데이터 처리를 위해서 MongoDB와 같은 Document NoSQL Database 사용 및 운영에 대한 고민을 할 필요가 없게 되었다.

  • MySQL 은 이러한 흐름에 맞추어 Document를 저장할 수 있는 JSON Data type 을 지원 (MySQL 5.7) 하게 되었고, 이를 이용하여 MySQL 8.0에 새로 소개된 X DEV API 를 통해 MongoDB 와 같은 Document Store Database사용 및 운영에 대한 고민을 할 필요가 없어졌고, DATA 자체는

    • Undo image 및 Transaction 지원으로, 백업 / 복구가 현재까지 사용되던 방식 그대로 사용될 수 있다.
    • 현재 개발되어 사용되던 RDBMS 방식 그대로 사용될 수 있으며, JSON 을 저장하는 Document Store를 추가로 이용할 수 있다.
    • JSON 데이터를 저장하고 있는 Document Store와 일반 InnoDB Table간의 조인을 포함하여 여러가지 연산을 실행해낼 수 있다.
  • 저장된 JSON 문서에 접근하기 위한 Document-Store API방식을 X DEV API로 제공하여, SQL보다는 API방식으로 데이터를 처리하는 것을 선호하는 개발자에게 새로운 interface를 통해 개발을 용이하게 할 수 있도록 도와주었다. (개발자는 X DEV API를 통해 데이터를 관리하고, 관리자 혹은 DBA는 해당 테이블에 대해 일반적인 SQL 을 통해 관리할 수 있다. (hybrid형태))

  • X DEV API 를 이용하기 위해서는 전통적인 MySQL Client Tool 인 mysql CLI (command line interface) 를 이용하지 않고, MySQL Shell 을 이용해야 한다. 또한 MySQL 8.0 은 X DEV API를 위한 새로운 port 와 socket를 제공된다.

    mysql 8.0 [localhost] {msandbox} ((none)) > select @@global.port, @@global.mysqlx_port, @@global.socket,  @@global.mysqlx_socket\G
    *************************** 1. row ***************************
             @@global.port: 3306
      @@global.mysqlx_port: 33060
           @@global.socket: /tmp/mysql.sock
    @@global.mysqlx_socket: /tmp/mysqlx.sock
    1 row in set (0.00 sec)
    
  • 아래의 그림은 mysqlsh 을 통해 Javascript 모드로 접속한 화면이다. 전통적인 mysql client tool 과 비슷하지만, 가독성이 높은 Prompt가 적용이 되었다. mysqlsh

  • X DEV API 를 이용한 MySQL 사용은 아래와 같이 다양한 언어를 지원하며, 각각의 API를 통해 Collection 과 Document 를 저장할 수 있다. 해당 코드는 MySQL Manual 에서 발췌해왔다.

    MySQL Shell JavaScript Code

    // Create a new collection
    var myColl = db.createCollection('my_collection');
    
    // Insert a document
    myColl.add( {_id: '1', name: 'Sakila', age: 15 } ).execute();
    
    // Insert several documents at once
    myColl.add( [
    {_id: '5', name: 'Susanne', age: 24 },
    {_id: '6', name: 'User', age: 39 } ] ).execute();
    

    MySQL Shell Python Code

    # Create a new collection
    myColl = db.create_collection('my_collection')
    
    # Insert a document
    myColl.add( {'_id': '1', 'name': 'Sakila', 'age': 15 } ).execute()
    
    # Insert several documents at once
    myColl.add( [
    {'_id': '5', 'name': 'Susanne', 'age': 24 },
    {'_id': '6', 'name': 'User', 'age': 39 } ] ).execute()
    

    Node.js JavaScript Code

    // Create a new collection
    db.createCollection('myCollection').then(function (myColl) {
      return Promise.all([
        // Insert a document
        myColl
          .add({ name: 'Sakila', age: 15 })
          .execute(),
        // Insert several documents at once
        myColl
          .add([
            { name: 'Susanne', age: 24 },
            { name: 'User', age: 39 }
          ])
          .execute()
      ])
    });
    

    C# Code

    // Assumptions: test schema assigned to db, my_collection collection not exists
    
    // Create a new collection
    var myColl = db.CreateCollection("my_collection");
    
    // Insert a document
    myColl.Add(new { name = "Sakila", age = 15 }).Execute();
    
    // Insert several documents at once
    myColl.Add(new[] {
    new { name = "Susanne", age = 24 },
    new { name = "User", age = 39 } }).Execute();
    

    Python Code

    # Create a new collection
    my_coll = my_schema.create_collection('my_collection')
    
    # Insert a document
    my_coll.add({'name': 'Sakila', 'age': 15}).execute()
    
    # Insert several documents at once
    my_coll.add([
        {'name': 'Susanne', 'age': 24},
        {'name': 'User', 'age': 39}
    ]).execute()
    

    Java Code

    // Create a new collection
    Collection coll = db.createCollection("payments");
    
    // Insert a document
    coll.add("{\"name\":\"Sakila\", \"age\":15}");
    
    // Insert several documents at once
    coll.add("{\"name\":\"Susanne\", \"age\":24}",
            "{\"name\":\"User\", \"age\":39}");
    

    C++ Code

    // Create a new collection
    Collection coll = db.createCollection("payments");
    
    // Insert a document
    coll.add(R"({"name":"Sakila", "age":15})").execute();
    
    // Insert several documents at once
    std::list<DbDoc> docs = {
      DbDoc(R"({"name":"Susanne", "age":24})"),
      DbDoc(R"({"name":"User", "age":39})")
    };
    coll.add(docs).execute();
    

## 사용예제

  • Collation 생성 (With mysqlsh)
    MySQL  localhost:33060+ ssl  test  JS > var myColl = db.createCollection('min_collection');
    
  • 테이블확인 (With mysql CLI)
    mysql 8.0 [localhost] {msandbox} (test) > show create table min_collection\G
    *************************** 1. row ***************************
           Table: min_collection
    Create Table: CREATE TABLE `min_collection` (
      `doc` json DEFAULT NULL,
      `_id` varbinary(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$._id'))) STORED NOT NULL,
      PRIMARY KEY (`_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    1 row in set (0.01 sec)
    
  • Row 추가 (With mysqlsh)
    MySQL  localhost:33060+ ssl  test  JS > myColl.add({_id: '1', name: 'min', joined_year: '2018'}).execute();
    Query OK, 1 item affected (0.0603 sec)
    
    MySQL  localhost:33060+ ssl  test  JS > db.min_collection.add([{_id: '2', name: 'chan', joined_year: '2016'},{_id: '3', name: 'tim', joined_year: '2016'}]).execute();
    Query OK, 2 items affected (0.0234 sec)
    
    Records: 2  Duplicates: 0  Warnings: 0
    
  • 검색 (With mysqlsh)
    // 전체검색
    MySQL  localhost:33060+ ssl  test  JS > db.min_collection.find();
    [
        {
            "_id": "1",
            "name": "min",
            "joined_year": "2018"
        },
        {
            "_id": "2",
            "name": "chan",
            "joined_year": "2016"
        },
        {
            "_id": "3",
            "name": "tim",
            "joined_year": "2016"
        }
    ]
    3 documents in set (0.0004 sec)
    
    // _id 값이 1 을 검색
    MySQL  localhost:33060+ ssl  test  JS > db.min_collection.find('_id="1"');
    [
        {
            "_id": "1",
            "name": "min",
            "joined_year": "2018"
        }
    ]
    1 document in set (0.0005 sec)
    
    
    // 입사년이 2016년인 사람들의 이름을 뽑아 정렬한 후 첫번째값을 가져온다.
    MySQL  localhost:33060+ ssl  test  JS >  db.min_collection.find('joined_year="2016"').fields('name').sort('name').limit(1)
    [
        {
            "name": "chan"
        }
    ]
    1 document in set (0.0012 sec)
    
    // 전체 count를 가져온다.
    MySQL  localhost:33060+ ssl  test  JS > db.min_collection.count();
    
    
    
  • Index 생성 (With mysqlsh)
    db.min_collection.createIndex("testIndex", {fields:[{"field": "$.name", "type":"TEXT(100)", required:true}]});
    
  • 상태확인 (With mysql CLI)
    mysql 8.0 [localhost] {msandbox} (test) > show create table min_collection\G
    *************************** 1. row ***************************
           Table: min_collection
    Create Table: CREATE TABLE `min_collection` (
      `doc` json DEFAULT NULL,
      `_id` varbinary(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$._id'))) STORED NOT NULL,
      `$ix_t100_r_2BAA686C5604372BA51E965E6346198F5072C3D7` text GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$.name'))) VIRTUAL NOT NULL,
      PRIMARY KEY (`_id`),
      KEY `testIndex` (`$ix_t100_r_2BAA686C5604372BA51E965E6346198F5072C3D7`(100))
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    1 row in set (0.00 sec)
    
    INDEX Virtual COLUMN 통해 생성된다.
    
    mysql 8.0 [localhost] {msandbox} (test) > select * from min_collection;
    +-----------------------------------------------------+-----+-----------------------------------------------------+
    | doc                                                 | _id | $ix_t100_r_2BAA686C5604372BA51E965E6346198F5072C3D7 |
    +-----------------------------------------------------+-----+-----------------------------------------------------+
    | {"_id": "1", "name": "min", "joined_year": "2017"}  | 1   | min                                                 |
    | {"_id": "2", "name": "chan", "joined_year": "2016"} | 2   | chan                                                |
    | {"_id": "3", "name": "tim", "joined_year": "2016"}  | 3   | tim                                                 |
    +-----------------------------------------------------+-----+-----------------------------------------------------+
    3 rows in set (0.00 sec)
    
  • 삭제 (With mysqlsh)
    MySQL  localhost:33060+ ssl  test  JS > db.min_collection.remove('_id="1"').execute();
    Query OK, 1 item affected (0.1053 sec)
    
    
     MySQL  localhost:33060+ ssl  test  JS > db.min_collection.find()
    [
        {
            "_id": "2",
            "name": "chan",
            "joined_year": "2016"
        },
        {
            "_id": "3",
            "name": "tim",
            "joined_year": "2016"
        }
    ]
    2 documents in set (0.0004 sec)
    
  • 삭제 (With mysql CLI)
    mysql 8.0 [localhost] {msandbox} (test) > delete from min_collection where doc->'$.name' = 'tim';
    Query OK, 1 row affected (0.02 sec)
    
  • 상태확인 (With mysqlsh)
    MySQL  localhost:33060+ ssl  test  JS > db.min_collection.find()
    [
        {
            "_id": "2",
            "name": "chan",
            "joined_year": "2016"
        }
    ]
    1 document in set (0.0004 sec)
    
  • Truncate (With mysql CLI)
    mysql 8.0 [localhost] {msandbox} (test) > truncate table min_collection;
    Query OK, 0 rows affected (0.10 sec)
    
  • 상태확인 (With mysqlsh)
    MySQL  localhost:33060+ ssl  test  JS > db.min_collection.find()
    Empty set (0.0087 sec)
    
  • Drop (With mysql CLI)
    mysql 8.0 [localhost] {msandbox} (test) > drop table min_collection;
    Query OK, 0 rows affected (0.13 sec)
    
  • 상태확인 (With mysqlsh)
    MySQL  localhost:33060+ ssl  test  JS > db.min_collection.find();
    ERROR: 1146: Table 'test.min_collection' doesn't exist
    

## 적용범위

  • 데이터 sharding의 목적이 아닌 Document 만을 저장 하는 목적이라면 Document Store Database (예를 들면 MongoDB , CouchDB) 를 따로 운영하지 않아도 된다는 큰 장점이 있다.

    • 내부적으로 저장소는 InnoDB를 사용함으로, ACID 를 포함한 Transaction 을 모두 지원한다.

    • 백업/복구의 경우 기존의 방법을 통해 진행될 수 있다.

    • DBA 에게 익숙한 전통적인 방식의 mysql client tool 혹은 JDBC 방식으로 데이터를 조회 및 조작가능하다.

  • SQL 보다는, 제공되는 API 호츌에 익숙하며 JSON 형태의 데이터를 잘 다루는 개발자들에게 더욱 쉬운 개발 환경을 제공해줄 수 있다.