[SQL] SQL과 NoSQL의 차이

[SQL] SQL과 NoSQL의 차이

#목차

SQL(Structured Query Language) 이란?

SQL

SQL
  • SQL(Structured Query Language) 관계형 데이터베이스(RDBMS) 또는 SQL 데이터베이스 작업을 위한 표준 언어이다.
  • SQL을 사용하여 데이터베이스 객체를 만들고 데이터베이스 레코드를 삽입(INSERT), 업데이트(UPDATE), 삭제(DELETE) 또는 쿼리를 조회(SELECT)할 수 있다.
  • SQL은 데이터베이스 최적화 및 유지 관리 또는 데이터 분석 수행과 같은 복잡한 작업에 사용될 수 있다.
  • SQL 데이터베이스에는 MySQL, Sybase, Microsoft SQL Server, Oracle 등이 많이 사용되고 있다.
-- Query 사용예
INSERT INTO EMPLOYEES (Name, Age, PhoneNumber) 
VALUES (“EMP1”, 21, “1234567890”);

데이터베이스 구조(DB Structure)

DB Structure

DB Structure
  • SQL 데이터베이스 구조의 데이터는 열(row)과 행(column)으로 구성된 테이블로 구성되어 있다.
  • SQL 데이터베이스는 수직적 구조로 새로운 데이터가 추가될 때마다 수직적으로 쌓이게 된다.

관계형 데이터베이스(Relational database)

DB Relation

DB Structure
  • 관계형 DB(RDBMS)는 데이터베이스에 저장된 데이터의 구조, 즉 데이터 스키마를 명확하게 정의하는 것 또한 매우 중요하다.
  • 테이블의 모든 레코드(column - fields)는 정의된 열(row)을 준수해야 하며, 해당 형식(테이블/행/열)에서만 데이터 조작을 수행할 수 있다.

NoSQL(Non SQL) 이란?

NoSQL

NoSQL
  • NoSQL은 고정 스키마가 필요하지 않고 쉽게 확장되며 조인을 수행할 수 없는 비관계형 데이터베이스이다.
  • 방대한 데이터 스토리지가 필요한 분산 데이터 저장소에 적합하여 빅데이터와 실시간 웹 앱에 많이 사용된다.
  • NoSQL 데이터베이스는 관계형 스키마에 데이터를 저장하지 않기 때문에 전체 테이블의 구조를 변경하거나 나머지 행에 중복 요소를 추가하지 않고도 즉시 데이터 속성을 추가할 수 있다.
  • NoSQL 데이터베이스는 수평적 확장이 뛰어나고 파티션 허용 오차가 기반에 내장되어 있어 많은 양의 데이터에 대해 1초 미만의 응답 시간이 필요한 시나리오에서 잘 작동한다.

NoSQL의 종류

NoSQL

NoSQL
  • NoSQL 데이터베이스는 수평적 구조로 새로운 데이터가 추가될 때마다 수평적으로 쌓이게 된다.

Document Databases

[
    {
        "title" : "The Thieves",
        "year" : 2012,
        "info" : {
            "directors" : [ "Choi Dong-hoon"],
            "release_date" : "2012-07-25T00:00:00Z",
            "rating" : 8,
            "genres" : ["Action", "Drama"],
            "image_url" : "https://en.wikipedia.org/wiki/The_Thieves#/media/File:The_Thieves.jpg",
            "plot" : "Ten Thieves. Only One Diamond. They began to Move.",
            "actors" : ["Kim Yoon-seok", "Kim Hye-soo", "Lee Jung-jae", "Jun Ji-hyun", "Simon Yam", "Kim Hae-sook", "Oh Dal-su", "Kim Soo-hyun", "Derek Tsang", "Angelica Lee", "Shin Ha-kyun"]
        }
    },
    {
        "title": "The Age of Shadows",
        "year": 2016,
        "info": {
            "plot": "Enemy or Comrade",
            "rating": 43
        }
    }
]
  • Document DB는 데이터를 JSON 형식의 문서로 데이터를 저장 및 쿼리하도록 설계된 비관계형 데이터베이스이다.
  • Document DB를 사용하면 개발자들이 자신의 애플리케이션 코드에서 사용하는 것과 동일한 문서 모델 형식을 사용하여 데이터베이스에서 보다 손쉽게 데이터를 저장하고 쿼리 작업을 할 수 있다.
  • Document DB는 문서 및 문서 데이터베이스의 유연하고 반구조화된 계층적 특성을 통해 개발자는 계속해서 애플리케이션의 요구를 발전시킬 수 있다.
  • Document DB유연한 인덱싱, 강력한 임시 쿼리, 문서 모음에 대한 분석을 지원한다.
  • Document DBMongoDB, CouchDB, MarkLogic 등이 있다.
// mongo DB Query 사용예
db.movies.insert(
    {
        "title" : "The Thieves",
        "year" : 2012,
        "info" : {
            "directors" : [ "Choi Dong-hoon"],
            // ...
        },
    },
    // ...
)

Key-Value-Based Databases

Key-Value-Based DB

Key-Value-Based DB
  • Key-Value DB는 키-값 메소드를 사용하여 데이터를 저장하는 비관계형 데이터베이스 유형이다.
  • Key-Value DB는 key를 고유한 식별자로 사용하는 키-값 쌍의 집합으로 데이터를 저장한다.
  • Key-Value DB는 단순한 객체에서 복잡한 집합체에 이르기까지 무엇이든 키와 값이 될 수 있으며 파티셔닝이 가능하고 다른 유형의 데이터베이스로는 불가능한 범위까지 수평 확장을 가능하게 한다.
  • Key-Value 기반 DB는 Redis, Memcache 등이 있다.


DynamoDB의 JAVA API를 활용한 key-value 데이터 CRUD 예제

/**
 * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * This file is licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License. A copy of
 * the License is located at
 *
 * http://aws.amazon.com/apache2.0/
 *
 * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
*/

package com.amazonaws.codesamples.document;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DeleteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.UpdateItemOutcome;
import com.amazonaws.services.dynamodbv2.document.spec.DeleteItemSpec;
import com.amazonaws.services.dynamodbv2.document.spec.UpdateItemSpec;
import com.amazonaws.services.dynamodbv2.document.utils.NameMap;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.ReturnValue;

public class DocumentAPIItemCRUDExample {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    static String tableName = "ProductCatalog";

    public static void main(String[] args) throws IOException {

        createItems();

        retrieveItem();

        // Perform various updates.
        updateMultipleAttributes();
        updateAddNewAttribute();
        updateExistingAttributeConditionally();

        // Delete the item.
        deleteItem();

    }

    private static void createItems() {

        Table table = dynamoDB.getTable(tableName);
        try {

            Item item = new Item().withPrimaryKey("Id", 120).withString("Title", "Book 120 Title")
                .withString("ISBN", "120-1111111111")
                .withStringSet("Authors", new HashSet<String>(Arrays.asList("Author12", "Author22")))
                .withNumber("Price", 20).withString("Dimensions", "8.5x11.0x.75").withNumber("PageCount", 500)
                .withBoolean("InPublication", false).withString("ProductCategory", "Book");
            table.putItem(item);

            item = new Item().withPrimaryKey("Id", 121).withString("Title", "Book 121 Title")
                .withString("ISBN", "121-1111111111")
                .withStringSet("Authors", new HashSet<String>(Arrays.asList("Author21", "Author 22")))
                .withNumber("Price", 20).withString("Dimensions", "8.5x11.0x.75").withNumber("PageCount", 500)
                .withBoolean("InPublication", true).withString("ProductCategory", "Book");
            table.putItem(item);

        }
        catch (Exception e) {
            System.err.println("Create items failed.");
            System.err.println(e.getMessage());

        }
    }

    private static void retrieveItem() {
        Table table = dynamoDB.getTable(tableName);

        try {

            Item item = table.getItem("Id", 120, "Id, ISBN, Title, Authors", null);

            System.out.println("Printing item after retrieving it....");
            System.out.println(item.toJSONPretty());

        }
        catch (Exception e) {
            System.err.println("GetItem failed.");
            System.err.println(e.getMessage());
        }

    }

    private static void updateAddNewAttribute() {
        Table table = dynamoDB.getTable(tableName);

        try {

            UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", 121)
                .withUpdateExpression("set #na = :val1").withNameMap(new NameMap().with("#na", "NewAttribute"))
                .withValueMap(new ValueMap().withString(":val1", "Some value")).withReturnValues(ReturnValue.ALL_NEW);

            UpdateItemOutcome outcome = table.updateItem(updateItemSpec);

            // Check the response.
            System.out.println("Printing item after adding new attribute...");
            System.out.println(outcome.getItem().toJSONPretty());

        }
        catch (Exception e) {
            System.err.println("Failed to add new attribute in " + tableName);
            System.err.println(e.getMessage());
        }
    }

    private static void updateMultipleAttributes() {

        Table table = dynamoDB.getTable(tableName);

        try {

            UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", 120)
                .withUpdateExpression("add #a :val1 set #na=:val2")
                .withNameMap(new NameMap().with("#a", "Authors").with("#na", "NewAttribute"))
                .withValueMap(
                    new ValueMap().withStringSet(":val1", "Author YY", "Author ZZ").withString(":val2", "someValue"))
                .withReturnValues(ReturnValue.ALL_NEW);

            UpdateItemOutcome outcome = table.updateItem(updateItemSpec);

            // Check the response.
            System.out.println("Printing item after multiple attribute update...");
            System.out.println(outcome.getItem().toJSONPretty());

        }
        catch (Exception e) {
            System.err.println("Failed to update multiple attributes in " + tableName);
            System.err.println(e.getMessage());

        }
    }

    private static void updateExistingAttributeConditionally() {

        Table table = dynamoDB.getTable(tableName);

        try {

            // Specify the desired price (25.00) and also the condition (price =
            // 20.00)

            UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", 120)
                .withReturnValues(ReturnValue.ALL_NEW).withUpdateExpression("set #p = :val1")
                .withConditionExpression("#p = :val2").withNameMap(new NameMap().with("#p", "Price"))
                .withValueMap(new ValueMap().withNumber(":val1", 25).withNumber(":val2", 20));

            UpdateItemOutcome outcome = table.updateItem(updateItemSpec);

            // Check the response.
            System.out.println("Printing item after conditional update to new attribute...");
            System.out.println(outcome.getItem().toJSONPretty());

        }
        catch (Exception e) {
            System.err.println("Error updating item in " + tableName);
            System.err.println(e.getMessage());
        }
    }

    private static void deleteItem() {

        Table table = dynamoDB.getTable(tableName);

        try {

            DeleteItemSpec deleteItemSpec = new DeleteItemSpec().withPrimaryKey("Id", 120)
                .withConditionExpression("#ip = :val").withNameMap(new NameMap().with("#ip", "InPublication"))
                .withValueMap(new ValueMap().withBoolean(":val", false)).withReturnValues(ReturnValue.ALL_OLD);

            DeleteItemOutcome outcome = table.deleteItem(deleteItemSpec);

            // Check the response.
            System.out.println("Printing item that was deleted...");
            System.out.println(outcome.getItem().toJSONPretty());

        }
        catch (Exception e) {
            System.err.println("Error deleting item in " + tableName);
            System.err.println(e.getMessage());
        }
    }
}

Column-Oriented/Family Databases

1.Column-Oriented Databases

Column Oriented DB

Column-Oriented DB
  • Column-Oriented DB는 Tabled의 data를 Column 단위로 쪼개어 저장하는 DB를 의미한다.
  • Column-Oriented DB는 하나의 Column이 하나의 Disk Block안에 저장된다.
  • Column-Oriented DB는 1개의 Block만 읽고 결과를 구할 수 있기 때문에 빠른 처리가 가능하다.
  • 이처럼 Data를 분석하는 동작의 경우 Data Table에서 모든 Column이 필요한 것이 아니라 일부 Column이 필요한 경우가 대부분이다.
  • 따라서 Column-oriented DB는 OLAP(Online Analytical Processing) 처리에 유리한 반면, OTLP(Online transaction processing) 처리에 불리하다.

2.Column-Family Databases

Column Family DB

Column-Family DB
  • Column-Family DB는 Column을 나타내는 Column Key/Data/Timestamp Tuple을 Row를 나타내는 Row Key에 Mapping하여 Data Table을 표현하는 DB이다.
  • RDBMS에서 NULL값도 Disk Block 공간을 차지하지만 Column Family DB에서는 Row별로 자유로운 Column 추가/삭제가 가능하기 때문에 NULL값을 위한 Column이 별도로 필요없다.
  • 일반적으로 Column-oriented DB와 Column Family DB가 혼용되어 사용되어 Column Family DB가 Column-oriented DB라고 간주하는 경우가 많은데 같은 DB라고 할 수는 없다.
  • Column Family DB인 HBASE는 Column의 집합인 Column Family 단위로 Disk Block에 저장되기 때문에 Column-oriented DB로 분류된다.
  • 하지만 또 하나의 Column Family DB인 CassandraRow 단위로 Disk Block에 저장되기 때문에 Column-oriented DB라고 할 수 없다.
Cassandra DB의 CRUD 예제
-- create a tuple
CREATE TABLE subjects (
  k int PRIMARY KEY,
  v tuple<int, text, float>
);

-- insert values
INSERT INTO subjects  (k, v) VALUES(0, (3, 'cs', 2.1));

-- retrieve values
SELECT * FROM subjects;

Graph Databases

Graph DB

Graph DB
  • Graph DB는 관계를 저장하고 탐색하도록 특별히 구축되다.
  • Graph DB는 노드를 사용하여 데이터 엔터티를 저장하고, 엣지로는 엔터티 간의 관계를 저장하게 되는데, 엣지는 항상 시작 노드, 끝 노드, 유형과 방향을 가지며, 상-하위 관계, 동작, 소유자 등을 문서화 한다.
  • 하나의 노드가 가질 수 있는 관계의 수와 종류에는 제한이 없다.
  • Graph DB 그래프는 특정 엣지의 유형 또는 전체 그래프를 전반을 통하여 트래버스될 수 있다.
  • Graph DB에서 노드 간의 관계는 쿼리 시간에는 포함되지 않지만 데이터베이스에서 유지되기 때문에 조인 또는 관계를 트래버스하는 속도가 매우 빠르다.
  • Graph DB는 데이터 간의 관계를 만들고 이러한 관계를 신속하게 쿼리해야 할 때 소셜 네트워킹, 추천 엔진, 이상 탐지 등의 사용 사례에 유용하다.
  • Graph DBNeo4j, RedisGraph(Redis에 내장된 그래프 모듈), OrientDB 등이 있다.

Neo4j DB의 예제

MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors)
RETURN tom, m, coActors

결론!

SQL과 NoSQL 비교

ParameterSQLNoSQL
관계관계형비관계형
쿼리 언어구조화된 쿼리 언어(SQL)문서, 키-값, 그래프, 컬럼기반
사용OLAP 시스템에 사용최신 애플리케이션 개발에 대한 요구에 부응하여 개발
개요미리 정의된 스키마구조화되지 않은 데이터에 동적 데이터베이스를 사용
확장성수직 확장수평 확장
유형테이블 기반의 DB문서 기반, 키-값 쌍 또는 그래프 DB
계층적 데이터 저장적합하지 않음적합
대표적인 DBSQL, MySQL, MS-SQL, PostgresMongoDB, Redis, Neo4j, Cassandra, Hbase

2022 DB Ranking

2022 DB Ranking

2022 DB Ranking
  • SQL의 언어의 사용량이 높긴 하지만 NoSQL의 사용량도 점차 꾸준히 늘고 있다. 즉, 기획하고 설계하는 프로젝트에 대해 선택하는 언어가 다른 거 같다. 어느 한쪽이 좋은 언어 라고 얘기하기는 어려운 거 같다. 각자의 장/단점이 있는 언어이기 때문에 해당 프로젝트에 가장 알맞은 언어를 선택하여 설계하여 개발하는 것이 가장 좋은 거 같다.

Reference