Outsider's Dev Story: Database 카테고리 글 목록https://blog.outsider.ne.kr/Stay Hungry. Stay Foolish. Don't Be Satisfied.2024-03-15T10:41:37+09:00Textcube 1.10.7 : Tempo primoPostgreSQL의 JSONB 데이터 타입의 배열에서 특정 값 조회하기Outsiderhttps://blog.outsider.ne.kr/11942016-02-14T22:11:04+09:002016-02-14T22:11:00+09:00<p>작년에 <a href="https://blog.outsider.ne.kr/1061">PostgreSQL의 JSON 데이터 타입 오퍼레이터</a>라는 글에서 <a href="http://www.postgresql.org/">PostgreSQL</a> 데이터베이스의 <a href="http://www.postgresql.org/docs/9.3/static/datatype-json.html">JSON 데이터타입</a>에 대해서 간략하게 소개했다. 관계형 데이터베이스의 한계를 벗어난 기분에 JSON 데이터 타입을 마구 남용하다 보면 조만간 지옥을 맛보게 되기는 하지만 적절하게 사용하면 아주 편리한 기능 중 하나이다.<sup id="fnref:1"><a href="https://blog.outsider.ne.kr/1194#fn:1" rel="footnote">1</a></sup></p>
<p>PostgreSQL 9.4부터는 <a href="http://www.postgresql.org/docs/9.4/static/datatype-json.html">JSONB 데이터타입</a>이 추가되었다. 문서에 따르면 Insert는 JSON보다는 느리지만, 바이너리 형식으로 저장되어서 처리가 훨씬 유연하다. 내가 써봤을 때는 특이한 조건이 아니라면 JSON 대신 JSONB를 안 쓸 이유가 없다. 대신 JSON이나 JSONB 형식이나 일반적인 관계형 데이터베이스와는 형식이 다르므로 처음 쓸 때 연산자나 함수에 대해서 익히는 시간이 좀 필요하다.</p>
<p>예를 들어 <code>examples</code>이라는 테이블에서 JSONB로 정의된 <code>meta</code>라는 칼럼에 다음과 같은 데이터가 들어있다고 해보자. JSONB 데이터 타입에 JSON을 보통 다루듯이 JSON 객체를 가진 배열을 넣은 것이다.</p>
<pre class="line-numbers"><code class="language-clike">[{"id": 1, "image": "aaa.jpg"}, {"id": 2, "image": "ddd.jpg"}]
[{"id": 2, "image": "bbb.jpg"}, {"id": 5, "image": "ccc.jpg"}]
[{"id": 1, "image": "ccc.jpg"}, {"id": 3, "image": "bbb.jpg"}]
[{"id": 3, "image": "ddd.jpg"}, {"id": 6, "image": "aaa.jpg"}]
</code></pre>
<p>여기서 <code>id</code>가 <code>1</code>인 데이터를 가진 row를 조회하고 싶은 경우가 있다. 위에서 각 배열은 <code>examples</code> 테이블의 하나의 row인데 여기서 <code>meta</code> 칼럼에 배열을 하나 더 넣은 것이다. 왜 이러한 데이터베이스 디자인을 하는가는 별도의 이야기이고 이를 일반적인 데이터라고 생각하면 포함된 내용의 특정 값으로 조회하고자 하는 기능은 자연스러운 요구사항이다.</p>
<pre class="line-numbers"><code class="language-sql">SELECT * from examples where meta @> '[{"id":1}]';
</code></pre>
<p>여기서는 <code>@></code> 연산자를 사용했다. <a href="http://www.postgresql.org/docs/9.4/static/functions-json.html">문서</a>에 따르면 JSON이 해당 값을 포함하고 있는지를 검사하는 연산자이다. 그러므로 <code>meta</code> 칼럼이 <code>[{"id":1}]</code>를 포함하고 있는지를 검사하는 쿼리이다. 이렇게 조회하면 다음과 같이 <code>id</code>가 <code>1</code>인 결과만 조회된다.</p>
<pre class="line-numbers"><code class="language-clike">[{"id": 1, "image": "aaa.jpg"}, {"id": 2, "image": "ddd.jpg"}]
[{"id": 1, "image": "ccc.jpg"}, {"id": 3, "image": "bbb.jpg"}]
</code></pre>
<p>당연히 다음과 같이 <code>image</code>필드에 있는 값으로 조회할 수도 있다.</p>
<pre class="line-numbers"><code class="language-sql">SELECT * from examples where meta @> '[{"image":"bbb.jpg"}]';
</code></pre>
<pre class="line-numbers"><code class="language-clike">[{"id": 2, "image": "bbb.jpg"}, {"id": 5, "image": "ccc.jpg"}]
[{"id": 1, "image": "ccc.jpg"}, {"id": 3, "image": "bbb.jpg"}]
</code></pre>
<p><br><br></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:1">
<p><a href="http://dev.mysql.com/doc/refman/5.7/en/json.html">MySQL도 5.7부터 JSON 데이터 타입을 지원</a>한다. <a href="#fnref:1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
<p><strong><a href="https://blog.outsider.ne.kr/1194?commentInput=true#entry1194WriteComment">댓글 쓰기</a></strong></p>Alembic으로 환경별 다른 데이터베이스 사용하기Outsiderhttps://blog.outsider.ne.kr/11462015-05-23T18:06:15+09:002015-05-23T18:06:15+09:00<p>데이터베이스의 스키마를 관리할 때 <a href="https://bitbucket.org/zzzeek/alembic">Alembic</a>을 <a href="http://blog.outsider.ne.kr/1143">사용하고 있다</a>.</p>
<pre class="line-numbers"><code class="language-clike">[alembic]
script_location = alembic
sqlalchemy.url = postgresql://user:password@host/db_name
</code></pre>
<p>Alembic에서는 데이터베이스 접속 정보를 <code>alembic.ini</code>에서 다음과 관리하고 있는데 <code>[alembic]</code>이라고 된 영역이 기본 설정이고 여기서 <code>sqlalchemy.url</code>에 데이터베이스 접속 정보를 보관하고 있다. 하지만 개발을 하면 로컬 데이터베이스도 있고 테스트나 스테이지 용 데이터베이스도 있고 실제 서비스를 하는 데이터베이스가 있는데 각 데이터베이스에서 스키마를 관리해 주어야 하므로 상황별로 다른 데이터베이스 접속 정보를 사용해야 한다.</p>
<blockquote>
<p>sqlalchemy.url - A URL to connect to the database via SQLAlchemy. This key is in fact only referenced within the env.py file that is specific to the “generic” configuration; a file that can be customized by the developer. A multiple database configuration may respond to multiple keys here, or may reference other sections of the file.</p>
</blockquote>
<p><a href="http://alembic.readthedocs.org/en/latest/tutorial.html">문서</a>에 따르면 <code>sqlalchemy.url</code>는 SQLAlchemy가 사용하는 데이터베이스 접속 URL이고 <code>env.py</code>파일에서만 사용하고 있다는 것이다. 이 값이나 <code>env.py</code>파일에서 이 값을 커스터마이징할 수 있는 것 같은데 SQLAlchemy를 안 쓰다 보니 정확한 방법을 알기가 어려웠고 대부분은 Python과 함께 쓰고 있으므로 애플리케이션에서 사용하는 데이터베이스 정보를 읽어와서 Alembic에서 사용하는 것으로 보였다. 나는 Alembic만 별도로 쓰고 있었으므로 이 방법을 적용하기가 어려웠고 SQLAlchemy도 전혀 몰라서 적용하기가 어려웠다.</p>
<p>여러 가지 시도 끝에 다음과 같이 영역을 여러 가지 만들어서 필요에 따라 다른 설정을 사용하도록 할 수 있다는 걸 알게 되었다.</p>
<pre class="line-numbers"><code class="language-clike">[alembic]
script_location = alembic
sqlalchemy.url = postgresql://user:password@host/db_name
[db_test]
script_location = alembic
sqlalchemy.url = postgresql://user:password@test_host/test_db_name
[db_stage]
script_location = alembic
sqlalchemy.url = postgresql://user:password@stage_host/stage_db_name
[db_production]
script_location = alembic
sqlalchemy.url = postgresql://user:password@production_host/production_db_name
</code></pre>
<p>스키마를 적용할 때 <code>alembic upgrade head</code>을 실행하면 <code>[alembic]</code>영역의 설정을 사용하지만 <code>alembic --name db_test upgrade head</code>처럼 <code>--name</code> 옵션으로 영역의 이름을 주면 해당 영역의 값을 사용하므로 서버 환경에 따라 다른 데이터베이스 정보를 사용할 수 있다. 서버의 환경변수별로 하고 싶기도 한데 아직은 그 방법을 잘 모르겠다.</p>
<p><strong><a href="https://blog.outsider.ne.kr/1146?commentInput=true#entry1146WriteComment">댓글 쓰기</a></strong></p>데이터베이스 마이그레이션 도구 alembicOutsiderhttps://blog.outsider.ne.kr/11432015-05-11T01:17:18+09:002015-05-11T01:17:18+09:00<p>서비스를 운영하려다 보니 자연히 데이터베이스 스키마를 관리할 수 있는 마이그레이션 도구가 필요했다. 현재 백엔드에 Node.js를 사용하고 있기는 하지만 마이그레이션 도구도 Node.js일 필요는 없다고 생각했다. Node.js는 데이터베이스쪽이 좀 약하다고 생각하는 편인데 데이터베이스 라이브러리도 쓸만한 게 있고 드라이버도 꽤 안정적으로 개발되고 있지만, 역사나 인구가 적다 보니 마이그레이션 도구 같은 부분은 선택의 폭이 높지 않다. 그리고 마이그레이션 도구를 애플리케이션 내에서 자동으로 돌릴 게 아니라 별로도 실행할 것이므로 Node.js에 국한하지 않고 쓸만한 도구를 찾기 시작했다.<br />
<br></p>
<h1>Alembic</h1>
<p>주변 사람들이 어떤 마이그레이션 도구를 사용하는지 수소문해서 알게 된 도구가 <a href="https://bitbucket.org/zzzeek/alembic">Alembic</a>이다. Alembic은 <a href="http://www.sqlalchemy.org/">SQLAlchemy</a> 기반의 마이그레이션 도구로 <a href="https://www.python.org/">Python</a>을 사용하지만, 별도로 사용할 수 있으므로 프로젝트가 꼭 파이썬이거나 SQLAlchemy를 사용할 필요는 없다. <a href="http://alembic.readthedocs.org/en/latest/">문서</a>도 잘 작성되어 있고 사용법이 간단해서 금새 익힐 수 있다.</p>
<p>설치는 <code>pip install alembic</code>로 간단히 설치할 수 있다. 물론 Python이나 <a href="https://pypi.python.org/pypi/pip">pip</a>는 설치되어 있어야 한다. 만약 <code>No module named psycopg2</code>라는 오류가 발생하면 <code>sudo easy_install psycopg2</code>로 설치해야 한다.<br />
<br></p>
<h2>초기화</h2>
<p>설치하면 커맨드라인에서 <code>alembic</code> 명령어를 사용할 수 있고 다음 명령어로 초기화를 할 수 있다.</p>
<pre class="line-numbers"><code class="language-bash">$ alembic init myproject
</code></pre>
<p>alembic으로 초기화를 하면 다음과 같은 파일들이 생성된다.</p>
<pre class="line-numbers"><code class="language-clike">├── alembic.ini
└── myproject
├── README
├── env.py
├── env.pyc
├── script.py.mako
└── versions
</code></pre>
<p><code>alembic.ini</code>이 설정 파일이므로 여기서 데이터베이스 설정을 할 수 있다.</p>
<pre class="line-numbers"><code class="language-clike">[alembic]
# path to migration scripts
script_location = myproject
sqlalchemy.url = driver://user:pass@localhost/dbname
...
</code></pre>
<p>위 파일에서 <code>sqlalchemy.url</code>부분에 사용하는 데이터베이스를 지정하면 된다.<br />
<br></p>
<h2>마이그레이션 관리</h2>
<p>alembic은 리비전을 사용해서 데이터베이스 스키마를 관리한다.</p>
<pre class="line-numbers"><code class="language-bash">$ alembic revision -m "create user table"
Generating /myproject/versions/2987e9c41e5e_create_user_table.py ... done
</code></pre>
<p>위처럼 새로운 리비전을 추가하면 <code>versions</code>폴더 아래 새로운 마이그래이션 파일이 생기고 이 파일은 다음과 같이 생겼다.</p>
<pre class="line-numbers"><code class="language-python">"""create user table
Revision ID: 2987e9c41e5e
Revises:
Create Date: 2015-05-10 22:10:18.235580
"""
# revision identifiers, used by Alembic.
revision = '2987e9c41e5e'
down_revision = None
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
pass
def downgrade():
pass
</code></pre>
<p><code>upgrade()</code>는 마이그래이션 버전을 적용할 때 실행되고 <code>downgrade()</code>는 적용한 마이그래이션을 취소할 때 실행된다. 처음에는 아무 동작도 하지 않게 <code>pass</code>로 되어 있으므로 여기에 테이블 추가나 칼럼변경 같은 코드를 작성해야 한다.</p>
<pre class="line-numbers"><code class="language-python">def upgrade():
op.create_table(
'users',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String(50), nullable=False)
)
def downgrade():
op.drop_table('account')
</code></pre>
<p>alembic이 SQLAlchemy를 사용하므로 위처럼 <code>sa</code>객체를 이용해서 마이그레이션 코드를 작성할 수 있다. 애플리케이션도 SQLAlchemy를 사용하고 있다면 이 마이그레이션 코드도 자동으로 생성하는 등의 장점이 있는 것 같은데 그렇지 않다면 직접 작성해야 한다. SQLAlchemy를 사용해 봤다면 쉽게 작성할 수 있지만, 나처럼 SQLAlchemy를 사용해 본 적이 없다면 SQLAlchemy로 코드를 작성하는 것이 좀 힘 들 수 있다.</p>
<pre class="line-numbers"><code class="language-python">def upgrade():
op.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name VARCHAR(50) NOT NULL
)
""")
def downgrade():
op.execute("""
DROP TABLE users
""")
</code></pre>
<p>위처럼 <code>op.execute()</code>를 사용하면 SQL을 사용할 수 있으므로 SQLAlchemy에 익숙하지 않다면 차라리 이 방법이 더 편한다.</p>
<pre class="line-numbers"><code class="language-bash">$ alembic upgrade head
INFO [alembic.migration] Context impl PostgresqlImpl.
INFO [alembic.migration] Will assume transactional DDL.
INFO [alembic.migration] Running upgrade -> 2987e9c41e5e, create user table
</code></pre>
<p>이제 <code>alembic upgrade head</code>을 실행하면 위처럼 <code>upgrade()</code>함수가 실행되어 <code>users</code> 테이블이 생성된다. 데이터베이스에는 <code>alembic_version</code>라는 테이블이 생성되고 여기서 현재 적용된 리비전을 관리하게 된다.</p>
<pre class="line-numbers"><code class="language-bash">$ alembic revision -m "add age column"
Generating /myproject/versions/4c32125f4f5a_add_age_column.py ... done
</code></pre>
<p><code>4c32125f4f5a_add_age_column.py</code> 파일을 다음과 같이 수정해서 스키마 변경을 계속 관리할 수 있다.</p>
<pre class="line-numbers"><code class="language-python">"""add age column
Revision ID: 4c32125f4f5a
Revises: 40686f5279e2
Create Date: 2015-05-11 00:20:47.794986
"""
# revision identifiers, used by Alembic.
revision = '4c32125f4f5a'
down_revision = '40686f5279e2'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
op.execute("""
ALTER TABLE users
ADD age INTEGER
""")
def downgrade():
op.execute("""
ALTER TABLE users
DROP COLUMN age
""")
</code></pre>
<p>이 파일을 보면 아까 추가한 리비전이 지정되어 있음을 알 수 있고 여기서는 <code>users</code>테이블에 새로운 칼럼을 추가했다.</p>
<pre class="line-numbers"><code class="language-bash">$ alembic upgrade head
INFO [alembic.migration] Context impl PostgresqlImpl.
INFO [alembic.migration] Will assume transactional DDL.
INFO [alembic.migration] Running upgrade 40686f5279e2 -> 4c32125f4f5a, add age column
</code></pre>
<p>이런 식으로 스키마에 변경사항이 생길 때마다 마이그레이션 파일을 추가하고 적용할 수 있으며 개발 데이터베이스나 프로덕션 데이터베이스에서도 쉽게 적용하고 관리할 수 있다.</p>
<pre class="line-numbers"><code class="language-bash">$ alembic history
40686f5279e2 -> 4c32125f4f5a (head), add age column
<base> -> 40686f5279e2, create user table
</code></pre>
<p><code>alembic history</code> 명령어를 사용하면 스키마 히스토리도 확인할 수 있다. 러닝 커브도 높지 않고 간단히 사용할 수 있어서 현재 만족하며 사용 중이다.</p>
<p><strong><a href="https://blog.outsider.ne.kr/1143?commentInput=true#entry1143WriteComment">댓글 쓰기</a></strong></p>PostgreSQL의 JSON 데이터 타입 오퍼레이터Outsiderhttps://blog.outsider.ne.kr/10612014-06-14T22:08:43+09:002014-06-14T22:08:43+09:00<p><a href="http://www.postgresql.org/">PostgreSQL</a> 9.2부터 JSON 데이터 타입이 추가되어서 칼럼에 JSON 객체를 저장할 수 있다. 이는 아주 유용한 기능인데 RDB의 기능을 그대로 쓰면서 NoSQL처럼 Schemaless의 이점을 취할 수 있다. 관계형 디비를 사용할 때 어려운 점 중 하나가 칼럼을 추가해야 할 때인데 이미 운영 중이라면 특히 처리가 쉽지 않고 개발 중인 경우에는 계속해서 스키마가 달라질 가능성이 높으므로 처리가 어려운데 이러한 부분을 JSON으로 저장해 버릴 수 있다.</p>
<p>이 기능을 써보기 위해서 약간 테스트해본 결과 처음 JSON이 추가된 9.2와 9.3에서 사용방법이 많이 달라진 것으로 보인다. 자세히는 모르지만, 그동안 찾아본 내용을 보면 PostgrSQL은 JSON 데이터를 문자열로 저장하고 조회하는 등의 처리를 할 때 전용 JSON 함수 등을 사용해야 하는데 데이터를 저장하는 건 같지만 <a href="http://www.postgresql.org/docs/9.3/static/functions-json.html">이 관련 오퍼레이터가 9.3에서 많이 달라졌다</a>. 9.2는 안 써봤지만 9.2에선 오퍼레이터가 거의 없었던 듯하고 9.3에서 사용할 만 해진 것 같다.<br />
<br></p>
<h1>JSON 데이터 타입</h1>
<pre class="line-numbers"><code class="language-sql">CREATE TABLE posts
(
id serial NOT NULL,
meta JSON
);
</code></pre>
<p>위 <code>posts</code> 테이블에서 <code>meta</code>라는 칼럼을 <code>JSON</code>으로 지정했다. 이 테이블에는 다음 쿼리로 JSON 객체를 담을 수 있다.</p>
<pre class="line-numbers"><code class="language-sql">INSERT INTO posts (id, meta)
VALUES (
1,
'{
"author": "outsider",
"authorId": 43434,
"sns": {
"facebook": "https://facebook.com",
"twitter": "https://twitter.com"
},
"createAt": "2014-06-14",
"category": ["pg", "tech", "dev"]
}'
);
</code></pre>
<p>문자열로 값을 넣기는 하지만 JSON으로 정의했으므로 유효한 JSON이 아니면 인서트할 때 오류가 발생한다.</p>
<h1>JSON 데이터 타입 조회</h1>
<p>전체를 다시 조회할 때는 평소에 사용하던 <code>SELECT</code> 쿼리와 다를 바 없고 각 언어에서 사용하는 PostgreSQL 드라이버나 라이브러리가 JSON 데이터 타입을 지원한다면 크게 할 일은 없을 것이다. 대신 JSON 데이터의 특정 키만 조회한다거나 하면 <a href="http://www.postgresql.org/docs/9.3/static/functions-json.html">PostgreSQL에서 지원하는 JSON 오퍼레이터</a>를 사용해야 한다.</p>
<pre class="line-numbers"><code class="language-sql">SELECT meta->'author' FROM posts;
-- "outsider"
SELECT meta->'sns'->'facebook' FROM posts;
-- "https://facebook.com"
SELECT meta->'sns'->>'facebook' FROM posts;
-- https://facebook.com
SELECT meta#>'{category, 2}' FROM posts;
-- "dev"
</code></pre>
<p>JSON 데이터의 값을 조회하려면 <code>-></code>와 <code>->></code>를 위처럼 사용해야 한다. 위에처럼 JSON 객체를 탐색해서 들어가면 값을 조회할 수 있다. <code>-></code>는 JSON 배열이나 JSON 객체를 반환하고 <code>->></code>는 JSON 배열이나 객체를 텍스트로 반환한다. 그래서 <code>meta->'sns'->>'facebook'</code>처럼 사용할 수는 있지만 <code>meta->>'sns'->'facebook'</code>처럼은 사용할 수 없다. <code>#></code>는 배열 내의 값을 조회하는 오퍼레이터이다. 그래서 <code>meta#>'{category, 2}'</code>와 같이 사용하면 <code>meta</code> JSON내에서 <code>category</code>키의 배열 값에서 3번째 값을 반환한다. 그리고 여기서 meta는 칼럼 명이므로 그냥 써도 되지만 이후의 킷값은 따옴표로 묶어주지 않으면 오류가 발생한다. 처음에 보면 문법이 난해하긴 한데 동작방식을 이해하고 나면 어렵지는 않다.<br />
<br></p>
<h2>JSON 키에 대한 인덱스</h2>
<p>JSON 안에 들어있는 값을 기준으로 조회하고 싶을 수 있다. 예를 들어 위 <code>posts</code>테이블에서 <code>meta</code>의 <code>authorId</code>로 조회를 한다고 하면 인덱스를 생성하지 않으면 속도에 큰 문제가 있을 것이다. 이런 경우를 위해서 JSON내의 값으로 인덱스를 생성할 수 있다.</p>
<pre class="line-numbers"><code class="language-sql">CREATE INDEX posts_idx ON posts((meta->>'authorId'));
</code></pre>
<p>일반적인 인덱스 생성쿼리와 같고 컬럼명대신 위에서 살펴본 오퍼레이터로 JSON의 키를 지정하면 인덱스를 만들 수 있다. 여기서 주의할 점은 깊이는 여러 단계로 찾아 들어갈 수 있지만, 마지막은 <code>->></code>로 끝나야 한다. <code>-></code>를 사용하면 <strong>data type json has no default operator class for access method "btree"</strong>라는 오류가 발생한다. 그래서 <code>sns</code>안에 <code>facebook</code>에 인덱스를 걸려면 <code>meta->'sns'->>'facebook'</code>처럼 사용해야 하고 위 코드에서 괄호가 2개 들어간 것은 꼭 사용해야 하는 부분이다.</p>
<p>참고로 이제 막 사용해 본 상황이라 인덱스의 사용이나 JSON의 키를 이용한 조회의 성능 같은 부분은 아직 제대로 확인해 보지 못했다.</p>
<p><strong><a href="https://blog.outsider.ne.kr/1061?commentInput=true#entry1061WriteComment">댓글 쓰기</a></strong></p>[Book] NoSQL: 빅 데이터 세상으로 떠나는 간결한 안내서Outsiderhttps://blog.outsider.ne.kr/9872013-10-04T23:42:02+09:002013-10-04T23:42:02+09:00<div class="ttbReview">
<fieldset style="PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; MARGIN: 10px; WIDTH: 90%; PADDING-TOP: 5px"><legend><a href="http://www.aladin.co.kr/shop/wproduct.aspx?ISBN=8966260705&ttbkey=ttboutsideris1727002&COPYPaper=1" target="_blank" class="aladdin_title">NoSQL: 빅 데이터 세상으로 떠나는 간결한 안내서</a></legend>
<p>
</p><table>
<tbody>
<tr>
<td><a href="http://www.aladin.co.kr/shop/wproduct.aspx?ISBN=8966260705&ttbkey=ttboutsideris1727002&COPYPaper=1" target="_blank"><img src="http://image.aladin.co.kr/product/2413/30/cover/8966260705_1.jpg" alt="" border="0"/></a></td><td align="left" style="vertical-align:top;"><a href="http://www.aladin.co.kr/shop/wproduct.aspx?ISBN=8966260705&ttbkey=ttboutsideris1727002&COPYPaper=1" target="_blank" class="aladdin_title">NoSQL: 빅 데이터 세상으로 떠나는 간결한 안내서</a> - <img src="http://image.aladin.co.kr/img/common/star_s8.gif" border="0" alt="8점" /><br/>프라모드 사달게이 & 마틴 파울러 지음<br>윤성준 옮김<br>인사이트</td></tr></tbody></table></fieldset><br><br></div>
<p>이 책의 작년에 아마존에서 SQL쪽 베스트셀러 중 하나였던 <a href="http://www.amazon.com/NoSQL-Distilled-Emerging-Polyglot-Persistence/dp/0321826620">NoSQL Distilled: A Brief Guide to the Emerging World of Polyglot Persistence</a>의 번역서로 이름도 유명한 <a href="http://martinfowler.com/">마틴 파울러</a>가 프라모드 사달게이와 공저로 쓴 책이다.</p>
<p>이 책은 200페이지 정도로 데이터베이스 책 치고는 별로 두껍지 않은 분량을 가지고 있는데 책의 초반에 저자들이 언급했듯이 <strong>NoSQL을 사용할 지 고민할 때 판단의 근거가 될 수 있는 개념을 최대한 간결하게 제공하는데 그 목적이 있다.</strong> 그래서 NoSQL이 가진 개념들이나 고려사항들을 설명하고 있고 각 NoSQL 데이터베이스의 자세한 사용방법등은 별로 다루고 있지 않다. 물론 <strong>각 종류별 특성을 설명하면서 간략히 사용방법을 소개하기는 하지만 이는 NoSQL의 데이터베이스 종류별(키-밸류, 그래프, 도큐먼트 등)로 개념을 설명하기 위한 것일 뿐이므로 실제로 사용하려면 관련 책을 다시 보아야 할 것이다.</strong>(아마 각각의 데이터베이스에 대한 책이 이 책의 분량을 뛰어넘을 것이다.)</p>
<p>즉, <code>이 책은 NoSQL에 대해서 아직 잘 몰라서 어떤 NoSQL을 선택해야 할지 모르거나 NoSQL에 대해서 공부해 보고 싶을 때 처음 보기에 딱 좋은 책이다.</code> NoSQL은 RDBMS와 대비되는 개념으로 하나의 이름으로 묶어서 사용하고는 있지만 사실 이 안에는 전혀 다른 특성을 가진 키-밸류 스토어, 그래프, 도큐먼트 디비등이 모두 포함되어 있으므로 하나의 NoSQL을 사용해 봤다고 다른 종류의 NoSQL을 쉽게 사용할 수 있는 것이 아니다. 그래서 처음 NoSQL을 공부하려면 기존에 RDBMS를 공부했을 때와는 많이 다른 새로운 개념들로 혼란을 겪게 되는게 그런 혼란을 줄여주고 NoSQL에 대한 전체적인 그림을 이해하는데 상당히 도움이 되는 책이고 설명도 상당히 잘 되어 있고 번역품질도 좋은 편이다. 이미 NoSQL을 어느 정도 사용하고 있다면 <strong>이 책이 별로 도움이 될지는 모르겠지만 NoSQL 중 하나정도만 다뤄보고 다른 NoSQL에 대해서는 거의 모르거나 NoSQL을 처음 공부하기에는 개념잡기가 아주 좋은 책이다. 개념의 설명도 잘 되어 있고 고민해 보아야 할 부분에 대한 힌트도 많이 주는 편이다.</strong> 이 책에도 나와있듯이 NoSQL은 아직 역사가 짧기 때문에 많은 유즈케이스가 존재하지 않고 RDBMS에서의 경험만으로는 적용하기 어려운 많은 개념이 포함되어 있는데 이런 부분에 대한 자료도 많지 않고 공부하려고 해도 너무 많은 내용을 보아야 하는데 이 책이 그런 부분에 대한 길잡이를 해줄만 하다. NoSQL에 대해서는 예전에 정리했던 <a href="http://blog.outsider.ne.kr/519">NoSQL에 대해서</a>라는 글도 참고할만 하다.(어느새 저 정리를 한지가 3년이나...)<br />
<br></p>
<h1>책의 내용</h1>
<p>1부 "기본 개념"에서는 NoSQL에 공통적으로 적용될 만한 기본적인 개념들을 설명하고 있다. 기존의 RDBMS 구조에서 왜 NoSQL이 필요했고 그래서 NoSQL은 어떤 기능에 초점을 맞추고 있는지를 쉽고 명확하게 설명한다. 이 책에서는 NoSQL을 특정 기술이 아니라 하나의 동향으로 보고 있고 NoSQL이 필요한 가장 큰 이유를 <strong>클러스터가 필요한 규모의 데이터 크기와 성능 요건하에서의 데이터 접근 처리</strong>와 <strong>좀 더 편리한 데이터 조작 방식을 통한 애플리케이션 개발 생산성 향샹</strong>으로 보고 있다.</p>
<blockquote>
<p>NoSQL 데이터베이스에 대한 흔한 이야기 중 하나는 NoSQL 데이터베이스에는 스키마가 없기 때문에 애플리케이션 운영 중에도 데이터 구조를 바꾸기 쉽다는 것이다. 우리는 동의하지 않는다. 무스키마 데이터베이스 역시 암묵적 스키마를 가지고 있으며 데이터 구조를 바꿀 때 이 규칙을 수정해야 한다.</p>
</blockquote>
<p>처음에는 NoSQL의 데이터 모델이 가지는 기본적인 개념을 설명하면서 관계형 데이터베이스가 튜플 중심인데 반해 집합지향이라는 개념을 설명한다.(모든 NoSQL이 집합지향인 것은 아니다.) 집합 지향은 어플리케이션 데이터 모델을 개발할 때 데이터를 어떻게 접근할 지를 고민해 보아야 하고 관계형 데이터베이스도 복잡한 관계를 처리하는데 아주 뛰어난 것은 아니라는 것을 유념하라고 하고 있다. 그리고 스키마가 없기 때문에 균일하지 않은 데이터를 처리하기 쉽지만 데이터를 확인하려면 어플리케이션 코드를 봐야한다는 문제도 있다.</p>
<p>이어서 NoSQL의 특징인 클러스터링을 설명하고 있다. 샤딩으로 수평적 확장을 할때의 장점과 발상할 수 있는 문제 및 해결방법등을 제시하고 샤딩에 비해서 마스터-슬레이브 복제를 할 때는 또 어떤 다른 점이 있는지를 설명하고 있다.</p>
<blockquote>
<p>흔히 듣는 주장중 하나가 NoSQL 데이터베이스는 트랜잭션을 지원하지 않으므로 일관성도 지원하지 않는다는 것이다. 이런 주장은 일반적으로 틀렸다고 할 수 있는데 많은 중요사항을 얼버무려 넘겼기 때문이다. 이에 대한 첫재 해명은 트랜잭션이 없다는 것은 보통 일부 NoSL 데이터베시으 특히 집합 지향 데이터베이스에만 해당하는 것이다. 둘째, 집합 지향 데이터베이스는 원자적 업데이트를 지원하지만 한 집합에 대해서만 지원한다.</p>
</blockquote>
<p>클러스터를 이용할 때는 일관성이 항상 문제되는데 이를 어떻게 해결하고 접근해야 하는지에 대한 범용적인 개념을 설명하는데 어려운 개념이지만 이해하기가 참 좋다. 그리고 중요한 개념중 하나인 <strong>결과적 일관성(eventual consistency)</strong> 즉, 특정 시점에는 노드에 복제 일관성 불일치가 있을 수 있지만 결국에는 모든 노드가 같은 값으로 업데이트 된다는 개념을 설명하고 있다.</p>
<p>2부 "적용"에서는 NoSQL의 종류별로 좀더 구체적으로 설명하고 있다. 키-값 저장소, 문서 데이터베이스, 칼럼 패밀리 데이터 저장소, 그래프 데이터베이스를 차례차례 설명하면서 어떤 특성을 가지고 있고 어떤 장단점이 있는지를 설명해서 상황에 따라 어떤 종류를 사용해야 할지 판단해야 할 근거를 제공한다. 각 종류마다 적절한 사용처와 사용하지 말아야 할 경우를 제시하는 것도 고민할 때 꽤 유용할 꺼라 생각한다.</p>
<p>2부의 뒷부분에서는 스키마를 변경해야 하거나 레거시 시스템에 적용해야 하는 등의 실제로 일어날 수 있는 상황에 대해서 설명하고 있고 NoSQL외에 다른 데이터 처리 방법등에 대해서 소개하고 있는데 이 뒷부분은 크게 중요하지는 않아 보인다.(몇페이지 되지도 않는다.)</p>
<p><strong><a href="https://blog.outsider.ne.kr/987?commentInput=true#entry987WriteComment">댓글 쓰기</a></strong></p>