<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>mariadb | bitneer.dev</title>
	<atom:link href="https://www.bitneer.dev/blog/tag/mariadb/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.bitneer.dev</link>
	<description>AI 시대에 취미로 하는 코딩</description>
	<lastBuildDate>Tue, 06 Jan 2026 20:56:57 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>

<image>
	<url>https://www.bitneer.dev/wp/wp-content/uploads/2023/11/cropped-bitneer_Logo_for_Google_200-32x32.png</url>
	<title>mariadb | bitneer.dev</title>
	<link>https://www.bitneer.dev</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>트랜잭션의 격리 수준</title>
		<link>https://www.bitneer.dev/blog/%ed%8a%b8%eb%9e%9c%ec%9e%ad%ec%85%98%ec%9d%98-%ea%b2%a9%eb%a6%ac-%ec%88%98%ec%a4%80/</link>
		
		<dc:creator><![CDATA[Choi Kyung-sik]]></dc:creator>
		<pubDate>Sun, 28 Jun 2020 03:17:03 +0000</pubDate>
				<category><![CDATA[데이터베이스 시스템]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[mariadb]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[transaction]]></category>
		<guid isPermaLink="false">https://www.bitneer.dev/?p=664</guid>

					<description><![CDATA[<div data-nosnippet>트랜잭션의 격리 수준을 이해하기 위한 배경 지식으로 트랜잭션, 고립성, 이상현상을 먼저 살펴본다. 트랜잭션의 개요 데이터베이스의 여러 연산을 묶어 놓은 것을 사용자의 관점에서는 하나의 단위로 여겨질 수 있다. 예를 들어, 다른 계좌로 돈을 이체하는 것은 사용자의 관점에서 하나의 작업이지만, 데이터베이스 시스템 내에서는 여러 연산을 결합한 것이다. 이처럼, 하나의 논리적인 작업 단위를 위한 연산들의 모음을 트랜잭션(transaction)이라 한다. 데이터의 무결성(integrity)을 보장하려면, 데이터베이스 시스템은 ...</div>
<p>The post <a rel="nofollow" href="https://www.bitneer.dev/blog/%ed%8a%b8%eb%9e%9c%ec%9e%ad%ec%85%98%ec%9d%98-%ea%b2%a9%eb%a6%ac-%ec%88%98%ec%a4%80/">트랜잭션의 격리 수준</a> appeared first on <a rel="nofollow" href="https://www.bitneer.dev">bitneer.dev</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>
  트랜잭션의 격리 수준을 이해하기 위한 배경 지식으로 트랜잭션, 고립성,
  이상현상을 먼저 살펴본다.
</p>
<h5>트랜잭션의 개요</h5>
<p>
  데이터베이스의 여러 연산을 묶어 놓은 것을 사용자의 관점에서는 하나의 단위로
  여겨질 수 있다. 예를 들어, 다른 계좌로 돈을 이체하는 것은 사용자의 관점에서
  하나의 작업이지만, 데이터베이스 시스템 내에서는 여러 연산을 결합한 것이다.
  이처럼, 하나의 논리적인 작업 단위를 위한 연산들의 모음을
  <em>트랜잭션(transaction)</em>이라 한다.
</p>
<p>
  데이터의 무결성(integrity)을 보장하려면, 데이터베이스 시스템은 트랜잭션의
  다음과 같은 특성을 관리해야 한다.
</p>
<ul>
  <li>
    <strong>원자성(atomicity):</strong> 트랜잭션의 모든 연산을 데이터베이스에
    반영하든지, 아니면 전혀 반영하지 않아야 한다.
  </li>
  <li>
    <strong>일관성(consistency):</strong> 고립 상태에 있는 트랜잭션의 실행은
    데이터베이스의 일관성을 유지해야 한다. 고립 상태란 동시에 실행하는 다른
    트랜잭션이 없다는 것을 의미한다.
  </li>
  <li>
    <strong>고립성(isolation):</strong> 여러 개의 트랜잭션을 동시에
    실행하더라도, 각 트랜잭션은 동시에 실행하고 있는 다른 트랜잭션을 인식하지
    못해야 한다.
  </li>
  <li>
    <strong>지속성(durability):</strong> 트랜잭션이 성공적으로 끝나면, 그
    트랜잭션이 데이터베이스 시스템에 수정한 내용은 시스템 장애가 발생하더라도
    영속적이어야 한다.
  </li>
</ul>
<p>위의 특성 중에 격리 수준과 관련한 고립성에 대해 좀 더 알아보자.</p>
<h5>고립성</h5>
<p>
  여러 개의 트랜잭션을 동시에 실행하면 성능이 좋아진다. 그러나 여러 트랜잭션의
  연산이 원하지 않는 방향으로 겹쳐 <em>불일치 상태(inconsistent state)</em>가
  발생할 수 있다. 동시에 실행하는 트랜잭션의 문제를 해결하는 방법은 트랜잭션을
  순차적으로 실행하는 것이다. 트랜잭션의 <em>고립성(isolation)</em>은 여러 개의
  트랜잭션을 동시에 실행하여도, 이 트랜잭션들을 순차적으로 실행한 것과 같은
  상태가 되도록 하는 특성이다.
</p>

<h5>이상현상</h5>
<p>
  <em>이상현상(Phenomena)</em>은 동시 실행하는 여러 트랜잭션이 상호 작용하면서
  나타날 수 있는 문제를 의미한다. 읽기 이상현상으로 dirty read, non-repeatable
  read, phantom read 세 가지가 있다. 쓰기 이상현상으로 lost update가 있다.
  이상현상을 예제로 보이기 위해 간단한 student 테이블을 사용할 것이다.
</p>
<table>
  <colgroup>
    <col />
    <col />
    <col />
  </colgroup>
  <tbody>
    <tr>
      <th>id</th>
      <th>name</th>
      <th>age</th>
    </tr>
    <tr>
      <td>1</td>
      <td>홍길동</td>
      <td>20</td>
    </tr>
    <tr>
      <td>2</td>
      <td>임꺽정</td>
      <td>25</td>
    </tr>
  </tbody>
</table>
<h6>Dirty Read</h6>
<p>
  dirty read는 한 트랜잭션이 진행 중에, 다른 트랜잭션이
  <strong><em>갱신</em></strong
  >은 하였으나 아직 commit하지 않은 데이터를 읽을 때 발생한다.
</p>
<table>
  <colgroup>
    <col />
    <col />
  </colgroup>
  <tbody>
    <tr>
      <th>T1</th>
      <th>T2</th>
    </tr>
    <tr>
      <td>SELECT age FROM students WHERE id = 1;</td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>UPDATE students SET age = 21 where id = 1;</td>
    </tr>
    <tr>
      <td>SELECT age FROM students WHERE id = 1; /* 21을 읽는다 */</td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>ROLLBACK;</td>
    </tr>
  </tbody>
</table>
<p>
  T2 트랜잭션에서 갱신은 하였으나 아직 커밋하지 않은 나이인 21을 T1 트랜잭션이
  읽는다. T2에서 롤백하면 결국 T1은 이상한 값을 읽어 들인 셈이다.
</p>
<h6>Non-repeatable Read</h6>
<p>
  non-repeatable read는 한 트랜잭션이 읽었던 데이터를 다시 읽을 때 데이터가
  변경되는 것으로, 다시 읽기 전에 다른 트랜잭션이 데이터를
  <strong><em>갱신</em></strong
  >하고 <strong><em>commit</em></strong
  >하기 때문에 발생한다. dirty read와 비슷해 보이지만 다른 트랜잭션에서 commit을
  한다는 차이점이 있다.
</p>
<table>
  <colgroup>
    <col />
    <col />
  </colgroup>
  <tbody>
    <tr>
      <th>T1</th>
      <th>T2</th>
    </tr>
    <tr>
      <td>SELECT age FROM students WHERE id = 1; /* 20을 읽는다 */</td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>UPDATE students SET age = 21 where id = 1; COMMIT;</td>
    </tr>
    <tr>
      <td>SELECT age FROM students WHERE id = 1; /* 21을 읽는다 */ COMMIT;</td>
      <td></td>
    </tr>
  </tbody>
</table>
<p>
  T1 트랜잭션 내에서 age가 20과 21 두 개의 값을 갖는다. 의도하지 않은 결과가
  발생할 수 있을 것이다.
</p>
<h6>Phantom Read</h6>
<p>
  phantom read는 우리말로 &#8216;유령 읽기&#8217; 정도로 바꿀 수 있겠다. 유령 읽기는 한
  트랜잭션이 진행 중에, 다른 트랜잭션이 <strong><em>추가</em></strong
  >하거나 <strong><em>삭제</em></strong
  >한 <strong><em>행(row)</em></strong
  >의 데이터를 읽어 발생한다.
</p>
<table>
  <colgroup>
    <col />
    <col />
  </colgroup>
  <tbody>
    <tr>
      <th>T1</th>
      <th>T2</th>
    </tr>
    <tr>
      <td>SELECT * FROM students WHERE age BETWEEN 10 AND 30;</td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>
        INSERT INTO students(id, name, age) VALUES(3, &#8216;장길산&#8217;, 28); COMMIT;
      </td>
    </tr>
    <tr>
      <td>SELECT * FROM students WHERE age BETWEEN 10 AND 30; COMMIT;</td>
      <td></td>
    </tr>
  </tbody>
</table>
<p>T1 트랜잭션에서 의도하지 않은 &#8216;장길산&#8217; 이름을 가진 3번째 행이 나온다.</p>
<h6>Lost Update(Dirty Write)</h6>
<p>
  Lost Update는 &#8216;갱신 손실&#8217;로 대역할 수 있겠다. 갱신 손실은 한 트랜잭션이
  데이터를 갱신한 후 다른 트랜잭션이 그 갱신한 값을 덮어쓸 때 발생한다. 다음의
  예는 한 고객이 잔고가 1000원 계좌에서 500원을 인출하고 있는데 동업자가 400원을
  동시에 출금하는 예이다.
</p>
<table>
  <colgroup>
    <col />
    <col />
    <col />
    <col />
  </colgroup>
  <tbody>
    <tr>
      <th colspan="2">Transaction 1</th>
      <th colspan="2">Transaction 2</th>
    </tr>
    <tr>
      <td>read(balance) balance = balance &#8211; 500</td>
      <td colspan="1">1000 500</td>
      <td></td>
      <td colspan="1"></td>
    </tr>
    <tr>
      <td></td>
      <td colspan="1"></td>
      <td>read(balance) balance = balance &#8211; 400</td>
      <td colspan="1">1000 400</td>
    </tr>
    <tr>
      <td>write(balance)</td>
      <td colspan="1">500</td>
      <td></td>
      <td colspan="1"></td>
    </tr>
    <tr>
      <td colspan="1"></td>
      <td colspan="1"></td>
      <td colspan="1">write(balance)</td>
      <td colspan="1">600</td>
    </tr>
  </tbody>
</table>
<p>
  잔고에 100원이 남아야 정상이지만 갱신 손실로 600원이 남았다. 위의 예에서 SQL
  문을 사용하지 않고 수식으로 표현하였다. MariaDB, PostgreSQL에서 확인한 바로는
  한 트랜잭션이 갱신을 한 상태에서 다른 트랜잭션이 동일 데이터에 갱신을 시도하면
  대기를 하여야 한다. 즉, 데이터를 갱신한 트랜잭션이 commit이나 rollback을 하기
  전에는 다른 트랜잭션은 동일 데이터를 갱신할 수 없다. 대부분의 DBMS 제품들은
  모든 격리 수준에서 갱신 시 테이블이나 행(row)에 락(lock)을 걸어 lost update가
  발생하지 않도록 하고 있다.
</p>
<h5>격리 수준(Isolation Level)</h5>
<p>
  동시에 실행하는 트랜잭션들을 순차적인 실행 상태로 만드는 것은 성능에 좋지
  않다. 성능과 일관성을 고려하여 트랜잭션에 격리 수준을 지정할 수 있다. SQL
  표준은 네 종류의 트랜잭션 격리 수준을 정의하고 있다. 다음 항목에서 아래로
  내려갈수록 일관성은 좋아지지만, 성능은 떨어진다.
</p>
<ul>
  <li>
    <strong>read uncommited:</strong> 다른 트랜잭션에서 commit하지 않은 데이터를
    읽을 수 있다. 데이터의 정확도가 중요하지 않으면서 트랜잭션의 수행 시간이 긴
    경우에 지정하여 사용할 수 있다.
  </li>
  <li>
    <strong>read commited:</strong> 다른 트랜잭션에서 commit한 데이터만을
    읽는다.
  </li>
  <li>
    <strong>repeatable read:</strong> read commited와 동일하게 다른 트랜잭션에서
    commit한 데이터만을 읽는다. 추가로 트랜잭션 진행 중에 읽었던 데이터를 다시
    읽을 때, 그 중간에 다른 트랜잭션이 그 데이터를 갱신할 수 없다.
  </li>
  <li>
    <strong>serializable:</strong> 동시 진행하는 트랜잭션들이 순차적으로 실행한
    것과 같은 결과가 나와야 한다. <em>직렬성(serializability)</em>에 더하여 연쇄
    복귀(rollback)도 없어야 한다.
  </li>
</ul>
<p>
  SQL 표준은 <strong>serializable</strong>이 디폴트이다. 이 수준은 트랜잭션 간에
  상호 작용이 전혀 없어야 하는 데 현실과는 맞지 않는다. 일반적인 데이터베이스
  시스템은 <strong>read commited</strong>나 <strong>repeatable read</strong>를
  디폴트로 사용한다. 또한, DBMS 제품마다 표준을 구현하는 방법은 다를 수 있다.
</p>
<p>아래의 표는 각 격리 수준에서 발생할 수 있는 이상현상을 나타낸 것이다.</p>
<table>
  <colgroup>
    <col />
    <col />
    <col />
    <col />
  </colgroup>
  <tbody>
    <tr>
      <th></th>
      <th>Dirty Read</th>
      <th>Non-repeatable Read</th>
      <th>Phantom Read</th>
    </tr>
    <tr>
      <th>Read Uncommitted</th>
      <td style="background-color: #ffebe6">발생할 수 있음</td>
      <td style="background-color: #ffebe6">발생할 수 있음</td>
      <td style="background-color: #ffebe6">발생할 수 있음</td>
    </tr>
    <tr>
      <th>Read Committed</th>
      <td style="background-color: #deebff">발생하지 않음</td>
      <td style="background-color: #ffebe6">발생할 수 있음</td>
      <td style="background-color: #ffebe6">발생할 수 있음</td>
    </tr>
    <tr>
      <th>Repeatable Read</th>
      <td style="background-color: #deebff">발생하지 않음</td>
      <td style="background-color: #deebff">발생하지 않음</td>
      <td style="background-color: #ffebe6">발생할 수 있음</td>
    </tr>
    <tr>
      <th>Serializable</th>
      <td style="background-color: #deebff">발생하지 않음</td>
      <td style="background-color: #deebff">발생하지 않음</td>
      <td style="background-color: #deebff">발생하지 않음</td>
    </tr>
  </tbody>
</table>
<p>
  MySQL의 InnoDB엔진은 repeatable read 격리 수준에서 phantom read가 발생하지
  않는다.
</p>
<p>
  PostgreSQL도 마찬가지로 repeatable read 격리 수준에서 phantom read가 발생하지
  않는다. 또한 트랜잭션을 read uncommitted로 설정은 할 수 있지만, dirty read는
  발생하지 않는다.
</p>
<h5>참고 자료</h5>
<ul>
  <li>
    <a href="https://www.amazon.com/gp/product/0070310866?pf_rd_r=QVPWASZQQQVJ3P915ZHY&amp;pf_rd_p=6fc81c8c-2a38-41c6-a68a-f78c79e7253f" target="_blank" rel="noopener noreferrer">Database System Concepts, 3rd</a
    >
    &#8211; Chapter 13 Transactions
  </li>
  <li>
    <a href="https://en.wikipedia.org/wiki/Isolation_(database_systems)" target="_blank" rel="noopener noreferrer">Isolation (database systems) &#8211; Wikipedia</a
    >
  </li>
  <li>
    <a href="https://postgresql.kr/docs/11/transaction-iso.html" target="_blank" rel="noopener noreferrer">PostgreSQL 11.1 문서 &#8211; 13.2. 트랜잭션 격리</a
    >
  </li>
  <li>
    <a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html" target="_blank" rel="noopener noreferrer">MySQL :: MySQL 8.0 Reference Manual :: 15.7.2.1 Transaction Isolation
      Levels</a
    >
  </li>
  <li>
    <a href="https://mariadb.com/kb/en/mariadb-transactions-and-isolation-levels-for-sql-server-users/#isolation-levels-and-locks" target="_blank" rel="noopener noreferrer">MariaDB Transactions and Isolation Levels for SQL Server Users &#8211; MariaDB
      Knowledge Base</a
    >
  </li>
</ul>
<p>The post <a rel="nofollow" href="https://www.bitneer.dev/blog/%ed%8a%b8%eb%9e%9c%ec%9e%ad%ec%85%98%ec%9d%98-%ea%b2%a9%eb%a6%ac-%ec%88%98%ec%a4%80/">트랜잭션의 격리 수준</a> appeared first on <a rel="nofollow" href="https://www.bitneer.dev">bitneer.dev</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>데비안의 MariaDB 설치와 설정</title>
		<link>https://www.bitneer.dev/blog/%eb%8d%b0%eb%b9%84%ec%95%88%ec%9d%98-mariadb-%ec%84%a4%ec%b9%98%ec%99%80-%ec%84%a4%ec%a0%95/</link>
		
		<dc:creator><![CDATA[Choi Kyung-sik]]></dc:creator>
		<pubDate>Wed, 10 Jun 2020 23:21:35 +0000</pubDate>
				<category><![CDATA[데이터베이스 시스템]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[mariadb]]></category>
		<guid isPermaLink="false">https://www.bitneer.dev/?p=627</guid>

					<description><![CDATA[<div data-nosnippet>데비안 9(Stretch)부터 MariaDB는 디폴트 MySQL 변종이다. 즉, MariaDB가 기존의 MySQL을 완전히 대체한다. 데비안 8(Jessie)의 MySQL 버전은 5.5이다. 내가 사용하는 Confluence 등이 MySQL 5.5를 지원하지 않았기 때문에 MySQL Community의 MySQL 5.6을 설치해야 했다. 최근에 Confluence, Jira, Bitbucket의 MySQL 데이터베이스를 PostgreSQL로 이전하였다. 워드프레스의 데이터베이스만이 MySQL에 남았다. 워드프레스는 현재 공식적으로 MySQL과 MariaDB만을 지원한다. 정교한 데비안 패키지 관리의 이점을 얻기 위해 MariaDB로 돌아갈 것이다. ...</div>
<p>The post <a rel="nofollow" href="https://www.bitneer.dev/blog/%eb%8d%b0%eb%b9%84%ec%95%88%ec%9d%98-mariadb-%ec%84%a4%ec%b9%98%ec%99%80-%ec%84%a4%ec%a0%95/">데비안의 MariaDB 설치와 설정</a> appeared first on <a rel="nofollow" href="https://www.bitneer.dev">bitneer.dev</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>
  데비안 9(Stretch)부터 MariaDB는 <em>디폴트 MySQL</em> 변종이다. 즉, MariaDB가
  기존의 MySQL을 완전히 대체한다. 데비안 8(Jessie)의 MySQL 버전은 5.5이다. 내가
  사용하는 Confluence 등이 MySQL 5.5를 지원하지 않았기 때문에
  <a href="https://www.mysql.com/products/community/" target="_blank" rel="noopener noreferrer">MySQL Community</a
  >의 MySQL 5.6을 설치해야 했다. 최근에 Confluence, Jira, Bitbucket의 MySQL
  데이터베이스를 PostgreSQL로 이전하였다. 워드프레스의 데이터베이스만이 MySQL에
  남았다. 워드프레스는 현재 공식적으로 MySQL과 MariaDB만을 지원한다. 정교한
  데비안 패키지 관리의 이점을 얻기 위해 MariaDB로 돌아갈 것이다. MariaDB 설치 후
  처음에 마주칠 수 있는 unix_socket 인증과 튜닝 등의 설정을 정리해 본다.
</p>
<h5>MariaDB 설치</h5>
<p>
  MySQL Community의 MySQL 패키지를 설치했다면 완전히 삭제하는 것이 필요하다.
  패키지의 의존성 문제로 삭제에 애를 먹을 수 있는데
  <strong>apt-get remove &#8211;purge &#8216;mysql-.*&#8217;</strong> 명령어를 사용하여 mysql
  관련 패키지를 모두 제거하는 편법을 사용해야 한다.
  <em>패키지를 삭제하기 전에</em> mysqldump로 데이터베이스를 백업하고 /etc/mysql
  디렉토리 안의 my.cnf 등 설정 파일을 백업하는 것이 좋을 것이다.
</p>
<p>
  데비안 9(Stretch) 또는 10(Buster)에서
  <strong>apt install mariadb-server</strong> 명령어로 MariaDB를 설치할 수 있다.
  데비안 9는 MariaDB 10.1을 데비안 10은 MariaDB 10.3을 설치할 것이다.
</p>
<pre class="language-none line-numbers" data-line="2">
<code># apt update
# apt install mariadb-server</code>
</pre>
<p>
  데비안 패키지와 관련한 문서는
  <kbd>/usr/share/doc/&lt;패키지 이름&gt;</kbd> 디렉토리에 위치한다. 패키지 설치
  후 가장 먼저 찾아볼 문서이다. 참고로 README는 <em>실제 제작자(upstream)</em>의
  문서이고, README.Debian은 <em>데비안 개발자</em>(패키지 메인테이너)가 제공하는
  문서이다. MariaDB 서버와 관련하여 볼 만한 문서는
  <kbd>/usr/share/doc/mariadb-server-10.1</kbd> 또는
  <kbd>/usr/share/doc/mariadb-server-10.3</kbd> 디렉토리 안의
  <strong>README.Debian.gz</strong> 파일이다. gzip으로 압축이 되어 있으므로
  zmore나 zcat 명령어로 보거나 윈도우 운영체제에서는 7-Zip 등으로 압축을 풀어
  편집 프로그램을 사용하여 본다.
</p>
<pre class="language-none line-numbers" data-line="1">
<code># zmore /usr/share/doc/mariadb-server-10.3/README.Debian.gz
...
# zcat /usr/share/doc/mariadb-server-10.3/README.Debian.gz &gt; ~/mariadb-server.README.Debian</code>
</pre>

<h5>systemd</h5>
<p>
  MariaDB는 버전 10.1.8부터 systemd unit 파일을 제공한다. 데비안 패키지에서
  <kbd>/lib/systemd/system/mariadb.service</kbd> 파일이 이에 해당한다.
  mariadb.service는 편리를 위해 <kbd>mysql.service, mysqld.service</kbd> 별칭을
  가진다. systemctl 사용시 .service 접미사는 생략할 수 있다. 따라서 다음과 같이
  systemctl 명령어를 사용하여 MariaDB 서비스의 시작, 종료 등을 할 수 있다.
</p>
<pre class="language-none">
<code># systemctl status mariadb
# systemctl status mysql.service
# systemclt status mysqld
# systemctl stop mysqld.service
# systemctl start mysql
# systemctl restart mariadb.service</code>
</pre>

<h5>mysql_secure_installation을 실행할 필요가 없음</h5>
<p>
  데비안의 MariaDB 패키지는 보안을 적용한 상태이기 때문에 패키지 설치 후
  mysql_secure_installation 명령어를 실행할 필요는 없다. 어떤 보안 설정들을
  적용하였는지 살펴보자.
</p>
<pre class="language-none" data-line="1,8,13,15,17,19,21">
<code># mysql_secure_installation
...
In order to log into MariaDB to secure it, we&#039;ll need the current
password for the root user.  If you&#039;ve just installed MariaDB, and
you haven&#039;t set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
...
Setting the root password ensures that nobody can log into the MariaDB
root user without the proper authorisation.

Set root password? [Y/n] n
...
Remove anonymous users? [Y/n] y
...
Disallow root login remotely? [Y/n] y
...
Remove test database and access to it? [Y/n] y
...
Reload privilege tables now? [Y/n] y
...</code>
</pre>
<p>
  위의 내용은 mysql_secure_installation 실행 과정의 일부분을 발췌한 것이다. 라인
  8에서 현재 root 계정의 암호를 묻고 있다. 처음 설치 시에는 MariaDB에 대한 root
  계정의 암호가 없으므로 <kbd>&lt;Enter&gt;</kbd> 키를 누른다. 라인 13에서
  root의 암호를 설정할 것인지를 묻는다. unix_socket을 사용하여 root 계정을
  인증하기 때문에 <kbd>n</kbd>을 입력한다. 이후 나머지 설정에서는 모두
  <kbd>y</kbd>를 입력한다. 라인 15는 anonymous 사용자를 삭제한다. 라인 17은
  root의 원격 로그인을 허용하지 않는다. 라인 19는 test 데이터베이스를 삭제한다.
  라인 21은 보안 강화를 위해 변경한 내용을 바로 적용한다.
</p>
<p>
  데비안에서 MariaDB 패키지를 설치하면 <em>이미</em> 위와 같이 설정한 상태이다.
  데비안에서 라인 13처럼 MariaDB의 root 계정에 대한 암호를 설정하지 않는 데
  다음에서 이에 대해 알아볼 것이다.
</p>
<h5>Unix Socket 인증</h5>
<p>
  데비안은 MariaDB에서 <em>unix_socket 인증 플러그인</em>을 기본적으로 사용한다.
  unix_socket 인증 플러그인이 나오기 전에 MySQL이나 MariaDB는 데이터베이스를
  보호하기 위해 자체적인 인증을 사용하였다. 그래서 시스템에 모든 권한이 있는
  root 계정이라도 MySQL 내에 암호를 별도로 가져야 했다. mysql 클라이언트 사용 시
  root 계정임에도 불필요해 보이는 <strong>-u</strong>,
  <strong>-p</strong> 옵션을 사용해야만 했다. 데이터베이스의 백업 스크립트에서
  암호를 노출해야 하는 문제도 있었다.
</p>
<p>
  unix_socket 인증 플러그인은 시스템의 인증과 MariaDB의 인증을 함께 묶어 준다.
  로컬 시스템으로 접속하여 mysql 클라이언트 실행 시 -u, -p 옵션을 사용하지 않을
  수 있게 한다. 예제를 통해 좀 더 알아보자. 규모가 큰 조직에서 미숙한 신입 DB
  관리자가 들어왔다고 가정해 보자. 시스템 전체의 권한을 주지 않으면서
  데이터베이스의 관리 권한만 주고 싶다면 다음과 같이 한다.
</p>
<pre class="language-none" data-line="1-3,7">
<code># groupadd dbadmin
# useradd dbadmin -g dbadmin -m
# passwd dbadmin
새  암호:
새  암호 재입력:
passwd: 암호를 성공적으로 업데이트했습니다
# mysql -e &quot;GRANT ALL ON *.* TO &#039;dbadmin&#039;@&#039;localhost&#039; IDENTIFIED VIA unix_socket WITH GRANT OPTION&quot;</code>
</pre>
<p>
  라인 1에서 3까지 시스템에 <kbd>dbadmin</kbd> 계정을 만들고 암호를 설정한다.
  라인 7에서 unix_socket 인증을 사용하는 <kbd>dbadmin</kbd> 계정에
  데이터베이스의 모든 권한을 주는 SQL 문을 실행한다. 다른 ssh 세션에서
  <kbd>dbadmin</kbd>으로 로그인하여 <kbd>mysql</kbd> 클라이언트를 실행해 보자.
</p>
<pre class="language-none" data-line="1">
<code>$ mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 59
Server version: 10.3.22-MariaDB-0+deb10u1 Debian 10

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type &#039;help;&#039; or &#039;\h&#039; for help. Type &#039;\c&#039; to clear the current input statement.

MariaDB [(none)]&gt;</code>
</pre>
<p>
  라인 1에서 보듯이 -u, -p 옵션을 사용하지 않았다. 로컬 시스템의 dbadmin 계정과
  암호로 로그인하면 unix_socket 인증에 의해 mysql 클라이언트를 사용할 수 있는
  것이다.
</p>
<p>
  신입 DB 관리자가 자신의 컴퓨터에서
  <a href="https://dbeaver.io/" target="_blank" rel="noopener noreferrer">DBeaver</a
  >,
  <a href="https://www.jetbrains.com/ko-kr/datagrip/features/" target="_blank" rel="noopener noreferrer">DataGrip</a
  >
  같은 그래픽 도구로 MariaDB 서버에 접속하여 데이터베이스를 관리하고 싶다고
  해보자. 그리고 집에서도 접속하고 싶어 한다. 이럴 때는 unix_socket이 아니라
  password 인증을 사용해야 한다. 다음과 같이 작업한다.
</p>
<pre class="language-none" data-line="1,6,9,12,16-18,22">
<code>MariaDB [(none)]&gt; USE mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mysql]&gt; GRANT ALL ON *.* TO &#039;dbadmin&#039;@&#039;192.168.10.%&#039; IDENTIFIED BY &#039;원하는 암호 입력&#039; WITH GRANT OPTION;
Query OK, 0 rows affected (0.000 sec)

MariaDB [mysql]&gt; GRANT ALL ON *.* TO &#039;dbadmin&#039;@&#039;%&#039; IDENTIFIED BY &#039;원하는 암호 입력&#039; WITH GRANT OPTION;
Query OK, 0 rows affected (0.000 sec)

MariaDB [mysql]&gt; SELECT user, host, password, plugin FROM user WHERE user=&#039;dbadmin&#039;;
+---------+--------------+-------------------------------------------+-------------+
| user    | host         | password                                  | plugin      |
+---------+--------------+-------------------------------------------+-------------+
| dbadmin | localhost    |                                           | unix_socket |
| dbadmin | 192.168.10.% | *CB04FE5BBC0DAAE405DB6DD0745827BCF1CF624D |             |
| dbadmin | %            | *CB04FE5BBC0DAAE405DB6DD0745827BCF1CF624D |             |
+---------+--------------+-------------------------------------------+-------------+
3 rows in set (0.000 sec)

MariaDB [mysql]&gt; DROP USER &#039;dbadmin&#039;@&#039;%&#039;;
Query OK, 0 rows affected (0.000 sec)

MariaDB [mysql]&gt;</code>
</pre>
<p>
  라인 1에서 라인 12의 SELECT 문을 사용하기 쉽게 mysql 데이터베이스로 이동한다.
  라인 6에서 암호 인증을 사용하는 dbadmin 계정에 데이터베이스의 모든 권한을
  주었다. <strong>&#8216;dbadmin&#8217;@&#8217;192.168.10.%&#8217;</strong>는 dbadmin이
  <kbd>192.168.10</kbd>의 네트워크에 있는 컴퓨터에서 접속할 수 있음을 의미한다.
  % 문자는 &#8216;모든&#8217;을 의미한다. 라인 9에서 <strong>&#8216;dbadmin&#8217;@&#8217;%&#8217;</strong>는
  네트워크 제한을 두지 않는다. dbadmin 계정은 어떤 IP에서든 접속할 수 있다.
  당연히 보안에 좋지 않다. 라인 12에서 <strong>SELECT</strong> 문을 사용하여
  dbadmin 사용자의 인증 방법과 접속할 수 있는 네트워크 대역을 확인한다. 라인
  22에서 <strong>DROP USER</strong> 문을 사용하여 &#8216;dbadmin&#8217;@&#8217;%&#8217; 사용자를
  삭제한다.
</p>
<p>
  원격으로 접속하기 위해서는 추가적인 설정이 필요하다. 이에 대해서는 아래의
  &#8216;원격 접속을 허용하기&#8217;에서 다룬다.
</p>
<h5>워드프레스의 데이터베이스 만들기</h5>
<p>
  응용 프로그램을 위한 데이터베이스 생성은 기존의 방식과 동일하게 암호 인증을
  사용한다. 워드프레스를 위한 데이터베이스와 사용자는 다음과 같이 만들 수 있다.
</p>
<pre class="language-none line-numbers" data-line="1,4">
<code>MariaDB [mysql]&gt; CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Query OK, 1 row affected (0.00 sec)

MariaDB [mysql]&gt; GRANT ALL ON wordpress.* TO &#039;wordpress&#039;@&#039;localhost&#039; IDENTIFIED BY &#039;원하는 암호 입력&#039; WITH GRANT OPTION;
Query OK, 0 rows affected (0.00 sec)

MariaDB [mysql]&gt;</code>
</pre>

<h5>MariaDB 설정 파일의 위치</h5>
<p>/etc/mysql/mariadb.cnf 파일을 보면 MariaDB 설정 파일은 다음과 같다.</p>
<ol>
  <li>/etc/mysql/mariadb.cnf</li>
  <li>/etc/mysql/conf.d/*.cnf</li>
  <li>/etc/mysql/mariadb.conf.d/*.cnf</li>
  <li>~/.my.cnf</li>
</ol>
<p>
  위의 순서로 설정 파일을 읽기 때문에 같은 옵션이 여러 번 나타난다면 가장
  마지막에 있는 설정을 적용할 것이다. MariaDB 서버와 관련한 설정은
  <kbd>/etc/mysql/mariadb.conf.d</kbd> 디렉토리 안의
  <kbd><strong>50-server.cnf</strong></kbd> 파일에서 한다.
</p>
<h5>원격 접속을 허용하기</h5>
<p>
  데비안의 MariaDB는 보안을 위해 <kbd>3306</kbd> 포트를 루프백(loop-back) 주소인
  <kbd>127.0.0.1</kbd>로 바인딩한다. 다음의 명령어로 확인할 수 있다.
</p>
<pre class="language-none line-numbers" data-line="2">
<code># netstat -tlnp | grep mysql
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      12435/mysqld</code>
</pre>
<p>
  따라서 시스템 외부에서 접근할 수 없다. 시스템 외부에서 접근할 수 있도록 하려면
  <kbd><strong>50-server.cnf</strong></kbd> 파일에서 bind-address를 다음과 같이
  변경한다.
</p>
<pre class="language-none line-numbers" data-line="1">
<code>bind-address            = 0.0.0.0</code>
</pre>
<p>변경한 설정을 반영하기 위해 MariaDB 서버를 다시 시작한다.</p>
<pre class="language-none">
<code># systemctl restart mariadb</code>
</pre>
<p>
  공유기(방화벽 역할)의 외부에서 오는 접근을 허용하려면 3306 포트에 대한 포트
  포워딩을 해야 한다. 보안에 좋지 않기 때문에 데이터베이스 서버의 포트를 여는
  경우는 거의 없을 것이다.
</p>
<h5>언어 설정</h5>
<p>
  데비안의 MariaDB에서 디폴트 <em>문자 집합(character set)</em>은
  <strong>utf8mb4</strong>이다. 디폴트 <em>데이터 정렬(collation)</em>은
  <strong>utf8mb4_general_ci</strong>이다. 데이터 정렬을
  <strong>utf8mb4_unicode_ci</strong>로 바꾸고 싶다면
  <kbd><strong>50-server.cnf</strong></kbd> 파일을 다음과 같이 편집한다.
</p>
<pre class="language-none" data-line="1,2">
<code>collation-server      = utf8mb4_unicode_ci
skip-character-set-client-handshake</code>
</pre>
<p>
  라인 1의 collation-server의 값을 utf8mb4_general_ci에서
  <strong>utf8mb4_unicode_ci</strong>로 변경한다. 라인 2의
  <strong>skip-character-set-client-handshake</strong>를 추가한다.
</p>
<p>
  MariaDB 서버를 다시 시작하고 MySQL 클라이언트를 다시 실행한 후 다음의 SQL
  문으로 변경을 확인한다.
</p>
<pre class="language-none" data-line="1">
<code>MariaDB [(none)]&gt; SHOW VARIABLES LIKE &#039;coll%&#039;;
+----------------------+--------------------+
| Variable_name        | Value              |
+----------------------+--------------------+
| collation_connection | utf8mb4_unicode_ci |
| collation_database   | utf8mb4_unicode_ci |
| collation_server     | utf8mb4_unicode_ci |
+----------------------+--------------------+
3 rows in set (0.001 sec)

MariaDB [(none)]&gt;</code>
</pre>
<p>
  MariaDB 서버의 데이터 정렬 설정은 MySQL 클라이언트 외에 다른
  <em>응용 프로그램</em>의 데이터 정렬을 강제하지는 않는다. 예를 들면
  <a href="https://www.adminer.org/" target="_blank" rel="noopener noreferrer">Adminer</a
  >는 collation_connection을 utf8mb4_general_ci로 나타낸다. 워드프레스의 일부
  플러그인은 utf8mb4_general_ci를 가지는 테이블을 만든다.
</p>
<h5>튜닝</h5>
<p>
  데이터베이스의 데이터는 &#8216;하드디스크 &gt; 메모리 &gt; CPU의 레지스터&#8217; 순으로
  옮겨간다. 오른쪽으로 갈수록 속도가 빠른 대신 저장 비용은 비싸진다.
  데이터베이스 읽기의 최적화는 디스크 입출력(I/O)을 최소화하고 메모리를 최대한
  사용하는 것이다. 단순하지만 중요한 개념이다. 데이터베이스 쓰기의 최적화는
  메모리의 휘발성으로 인해 간단하지 않다. 커밋(commit)한 데이터를 메모리에서
  디스크로 쓰기 전에 시스템 장애가 발생한다면 데이터를 잃을 것이다. 은행이나
  증권의 돈과 관련한 데이터라면 커밋 즉시 메모리에서 디스크로 써야 한다.
  안정성은 증가하지만, 성능은 준다. 게시판의 글에 대한 데이터라면 메모리를 좀 더
  활용하고 디스크에 쓰는 것을 늦출 수 있을 것이다. 데이터베이스 쓰기의 최적화는
  데이터의 종류에 따라 안정성과 성능 사이에서 고민해야 할 문제이다.
</p>
<p>
  MariaDB의 디폴트 스토리지 엔진(default_storage_engine)은 InnoDB이다. InnoDB와
  관련한 디폴트 설정값은 데스크톱 시스템에 맞게 되어 있다. 데이터베이스 전용의
  서버를 사용한다면 최적의 성능을 위해 설정값을 변경하여야 할 것이다. 전용의
  서버가 아니더라도 약간의 설정값 변경으로 성능 향상을 꾀할 수 있다. 나는
  <a href="https://mariadb.com/kb/en/configuring-mariadb-for-optimal-performance/" target="_blank" rel="noopener noreferrer">Configuring MariaDB for Optimal Performance</a
  >와
  <a href="https://mariadb.com/resources/blog/10-database-tuning-tips-for-peak-workloads/" target="_blank" rel="noopener noreferrer">10 Database Tuning Tips for Peak Workloads</a
  >
  페이지를 참고하여 3가지 정도의 설정만 추가하였다.
</p>
<pre
  class="language-none"
  data-line="1,2"
  data-label="/etc/mysql/mariadb.conf.d/50-server.cnf"
>
<code>innodb_buffer_pool_size=4096M
innodb_log_file_size=1024M
innodb_log_buffer_size=64M</code>
</pre>

<p>
  위의 설정은 모두 <kbd><strong>50-server.cnf</strong></kbd> 파일에 추가한다.
  라인 1의<strong> innodb_buffer_pool_size</strong>는 성능 최적화에 가장 중요한
  설정이다. 하나만 설정해야 한다면 이것을 해야 한다. 참고한 페이지에서는
  innodb_buffer_pool_size를 데이터베이스 전용의 서버일 때 메모리의
  <kbd>80%</kbd> 정도를 권장한다. Buffer Pool은 주로 데이터의 읽기와 관련한
  것으로 디스크보다는 메모리를 사용하기 위해 가능한 한 크게 설정하는 것을
  추천하고 있다. 데이터베이스 전용의 서버가 아니더라도 자신의 시스템 환경에 맞춰
  크게 설정하는 것이 좋겠다.
</p>

<p>
  InnoDB는 데이터를 디스크에 쓰기 전에 속도와 안정성을 목적으로 Redo Log를
  사용한다. Redo Log는 시스템 장애 발생 시 복구를 위해 변경 기록들을 저장한다.
  INSERT 문과 같은 데이터 변경이 발생한다면 그 데이터는 &#8216;Buffer Pool(메모리)
  &gt; Log Buffer(메모리) &gt; Redo Log(ib_logfile0, ib_logfile1 파일) &gt;
  테이블(디스크)&#8217;의 순으로 이동한다. Redo Log의 파일 크기인
  <strong>innodb_log_file_size</strong>는 innodb_buffer_pool_size의
  <kbd>1/4</kbd>에서 <kbd>1/2</kbd>까지를 권장한다. Log Buffer의 크기인
  <strong>innodb_log_buffer_size</strong>는 참고한 페이지에서 <kbd>64M</kbd>을
  추천하고 있다.
</p>
<p><strong>innodb_log_file_size</strong>의 변경은 다음의 절차로 한다.</p>
<ol>
  <li>MariaDB 서버를 종료한다.</li>
  <li>
    위 라인 2처럼 <kbd><strong>50-server.cnf</strong></kbd> 파일에
    <kbd>innodb_log_file_size</kbd> 설정을 추가한다.
  </li>
  <li>
    <kbd>/var/lib/mysql</kbd> 디렉토리에서 <kbd>ib_logfile0, ib_logfile1</kbd>을
    삭제하거나 다른 디렉토리로 옮긴다.
    (<strong>innodb_log_files_in_group</strong>의 디폴트 값이 <kbd>2</kbd>이기
    때문에 Redo Log 파일이 2개가 있다.)
  </li>
  <li>
    MariaDB 서버를 시작한다. 새로 설정한 크기의
    <kbd>ib_logfile0, ib_logfile1</kbd> 파일을 볼 수 있을 것이다.
  </li>
</ol>
<p>
  참고한 페이지에는 데이터 안정성과 관련한
  <strong>innodb_flush_log_at_trx_commit</strong>과
  <strong>sync_binlog</strong>, 동시 접속자 수에 대한
  <strong>max_connections</strong> 등을 다루고 있다. 나는 위 3개의 설정을
  제외하고 모두 디폴트 값을 사용할 것이어서 크게 관심을 가지지 않았지만,
  여러분에게 필요한 내용이 있을지 모르겠다.
</p>
<h5>참고 자료</h5>
<ul>
  <li>
    <a href="https://mariadb.com/kb/en/moving-from-mysql-to-mariadb-in-debian-9/" target="_blank" rel="noopener noreferrer">Moving from MySQL to MariaDB in Debian 9</a
    >
  </li>
  <li>
    <a href="https://serverfault.com/questions/111358/how-do-i-completely-remove-mysql-server-on-debian" target="_blank" rel="noopener noreferrer">How do I completely remove mysql-server on Debian?</a
    >
  </li>
  <li>
    <a href="https://mariadb.com/kb/en/systemd/" target="_blank" rel="noopener noreferrer">systemd</a
    >
  </li>
  <li>
    <a href="https://mariadb.com/kb/en/mysql_secure_installation/" target="_blank" rel="noopener noreferrer">mysql_secure_installation</a
    >
  </li>
  <li>
    <a href="https://mariadb.com/kb/en/authentication-plugin-unix-socket/" target="_blank" rel="noopener noreferrer">Authentication Plugin &#8211; Unix Socket</a
    >
  </li>
  <li>
    <a href="https://mariadb.com/kb/en/configuring-mariadb-for-optimal-performance/" target="_blank" rel="noopener noreferrer">Configuring MariaDB for Optimal Performance</a
    >
  </li>
  <li>
    <a href="https://mariadb.com/resources/blog/10-database-tuning-tips-for-peak-workloads/" target="_blank" rel="noopener noreferrer">10 Database Tuning Tips for Peak Workloads</a
    >
  </li>
  <li>
    <a href="https://mariadb.com/kb/en/innodb-system-variables/" target="_blank" rel="noopener noreferrer">InnoDB System Variables</a
    >
  </li>
  <li>
    <a href="https://mariadb.com/kb/en/server-system-variables/" target="_blank" rel="noopener noreferrer">Server System Variables</a
    >
  </li>
</ul>
<p>The post <a rel="nofollow" href="https://www.bitneer.dev/blog/%eb%8d%b0%eb%b9%84%ec%95%88%ec%9d%98-mariadb-%ec%84%a4%ec%b9%98%ec%99%80-%ec%84%a4%ec%a0%95/">데비안의 MariaDB 설치와 설정</a> appeared first on <a rel="nofollow" href="https://www.bitneer.dev">bitneer.dev</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
