<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>MOVED TO MEDIUM</title>
    <description>moved to medium
</description>
    <link>http://garimoo.github.io/</link>
    <atom:link href="http://garimoo.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Thu, 09 Jan 2020 01:33:09 +0000</pubDate>
    <lastBuildDate>Thu, 09 Jan 2020 01:33:09 +0000</lastBuildDate>
    <generator>Jekyll v3.8.5</generator>
    
      <item>
        <title>[Redis Documentation #3] 레디스의 HA, Sentinel</title>
        <description>&lt;h1 id=&quot;고가용성---센티널&quot;&gt;고가용성 - 센티널&lt;/h1&gt;
&lt;blockquote&gt;
  &lt;p&gt;원문: https://redis.io/topics/sentinel&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;레디스 센티널은 레디스의 가용성을 보장해준다. 센티널을 사용하면 특정 장애에는 사람의 개입 없이도 버티는 레디스의 서버구성을 만들 수 있다. 센티널은 모니터링, 통지(notification), 서버구성의 안내자 역할과 같은 기능을 제공한다.&lt;/p&gt;

&lt;p&gt;다음은 센티널의 기능이다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;모니터링&lt;/strong&gt;: 센티널은 마스터 및 슬레이브 인스턴스가 예상대로 작동하는지 지속적으로 확인한다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;통지(notification)&lt;/strong&gt;: 센티널은 API를 통해 시스템 관리자, 다른 컴퓨터 프로그램에 모니터링되는 레디스 인스턴스 중 하나에 문제가 있음을 알릴 수 있다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;자동 페일오버&lt;/strong&gt;: 마스터가 예상대로 작동하지 않는 경우, 센티널은 슬레이브를 마스터로 승격하는 페일오버 프로세스를 시작할 수 있으며, 다른 추가 슬레이브는 새 마스터를 사용하도록 재구성되어 레디스 서버를 사용하는 애플리케이션은 연결 시 사용할 새 주소에 대해 안내받는다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;구성 제공자(Configuration provider)&lt;/strong&gt;: 클라이언트는 주어진 서비스를 담당하는 현재 레디스 마스터의 주소를 요청하기 위해 센티널에 연결한다. 장애 조치가 발생하면 센티널은 새 주소를 알려준다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;센티널의-분산-개념&quot;&gt;센티널의 분산 개념&lt;/h2&gt;
&lt;p&gt;레디스 센티널은 분산 시스템이다.&lt;/p&gt;

&lt;p&gt;센티널 자체는 여러 센티널 프로세스가 함께 협력하여 실행되도록 설계되었다. 이런 구성의 이점은 다음과 같다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;장애 감지(Failure detection)는 마스터 상태가 비정상적이라는 것에 대해 여러 센티널이 동의하면 수행된다. 이는 잘못된 감지의 가능성을 낮춘다.&lt;/li&gt;
  &lt;li&gt;센티널은 모든 센티널 프로세스가 작동하지 않더라도 작동하여, 시스템이 장애에 대해 견고해 질 수 있게 만든다. 그 자체가 SPOF(Single Point Of Failure) 인 시스템은 필요가 없다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;센티널, 레디스 인스턴스(마스터와 슬레이브), 연결된 클라이언트는 어떤 관점에서 봤을 때 또한 더 큰 분산 시스템이다. 이 문서에서는 센티널의 기본 속성을 이해하기 위해 필요한 기본 정보부터, 센티널의 정확한 작동 방식을 이해하기 위해 더 복잡한 정보까지 점진적으로 설명한다.&lt;/p&gt;

&lt;h1 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h1&gt;
&lt;h2 id=&quot;obtaining-sentinel&quot;&gt;Obtaining Sentinel&lt;/h2&gt;
&lt;p&gt;지금 버전의 센티널은 센티널2라고 부른다.&lt;/p&gt;

&lt;p&gt;이 버전은 더 강력하고 단순한 알고리즘을 사용해서 초기의 센티널을 다시 구현한 것이다. 레디스 2.8 버전 이후 안정적인 레디스 센티널을 출시했고, 불안정한 브랜치에서 새로운 개발이 발생하고, 뉴 피처들이 안정적이라고 여겨지는 최신의 브랜치에 다시 포팅되기도 한다.&lt;/p&gt;

&lt;p&gt;레디스 2.6과 함께 제공된 레디스 센티널 버전1은 더이상 사용되지 않으므로 사용해서는 안된다.&lt;/p&gt;

&lt;h2 id=&quot;runing-sentinel&quot;&gt;Runing Sentinel&lt;/h2&gt;
&lt;p&gt;다음 명령줄을 사용하여 Sentinel을 실행할 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;redis-sentinel /path/to/sentinel.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;아니면 다음과 같이 레디스 서버를 센티널 모드에서 사용할 수도 있다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;redis-server /path/to/sentinel.conf --sentinel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;두가지는 방법 모두 똑같이 적용된다.&lt;/p&gt;

&lt;p&gt;그러나 센티널을 실행할 때에는 반드시 &lt;strong&gt;구성 파일(configuration file)을 사용&lt;/strong&gt; 해야 하는데, 이 파일은 재시작 시 다시 로드되는 현재 상태를 저장하기 위해 시스템에서 사용하기 때문이다. 센티널은 구성 파일이 없거나 해당 경로에 쓸 수 없는 경우 시작되지 못한다.&lt;/p&gt;

&lt;p&gt;센티널은 기본적으로 TCP 포트 26379 에 대한 연결을 수신하기 위해 실행되므로, 센티널이 작동하려면 서버의 포트 26379가 열려 있어야 다른 센티널 인스턴스로부터 연결을 받을 수 있다. 그렇지 않으면 센티널은 다른 서버와 연결되지 못해, 무엇을 해야 하는지에 대해 동의할 수 없으므로 페일오버도 수행할 수 없다.&lt;/p&gt;

&lt;h2 id=&quot;센티널에-대해-알아야-할-기본-사항&quot;&gt;센티널에 대해 알아야 할 기본 사항&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;튼튼한 구성을 위해서는 센티널 인스턴스가 세개 이상 필요하다.&lt;/li&gt;
  &lt;li&gt;세 개의 센티널 인스턴스는 서로 물리적으로 영향받지 않는 컴퓨터나 가상 머신에 설치되어야 한다. 서로 다른 물리적 서버 또는 가상 시스템이란 서로 다른 가용성 영역(availability zone)에서 실행되는 경우를 의미한다.&lt;/li&gt;
  &lt;li&gt;센티널 + 레디스 구조의 분산 시스템은 레디스가 비동기 복제를 사용하기 때문에 장애가 발생하는 동안 썼던 내용들이 유지됨을 보장할 수 없다. 그러나 특정 순간으로 쓰기 손실을 제한시키는 방법들이 존재한다.&lt;/li&gt;
  &lt;li&gt;클라이언트에서 센티널을 지원할 수 있어야 한다. 유명한 클라이언트 라이브러리들은 센티널을 지원하지만 모든 클라이언트에서 지원해주지는 않는다.&lt;/li&gt;
  &lt;li&gt;개발 환경(운영 환경에서 한다면 더 좋지만)에서 테스트를 해보지 않는다면 안전한 HA 구성이라고 말할 수 없다.&lt;/li&gt;
  &lt;li&gt;센티널, 도커 또는 다른 형태의 NAT 이나 포트 매핑을 주의해서 사용해야 한다. 도커는 포트 재매핑을 통해 다른 센티널 프로세스의 자동 발견과, 마스터의 슬레이브 목록을 깨트릴 수 있다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;센티널-구성하기&quot;&gt;센티널 구성하기&lt;/h2&gt;
&lt;p&gt;레디스 소스에는 센티널을 구성하는 데 사용할 수 있는 파일인 sentinel.conf 파일이 존재하고, 센티널을 사용하기 위한 최소의 구성 파라미터는 아래와 같다.&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mymaster&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6379&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;milliseconds&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mymaster&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;failover&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mymaster&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parallel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syncs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mymaster&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resque&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;192&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;168&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6380&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;milliseconds&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resque&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;failover&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resque&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parallel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syncs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resque&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;모니터링을 할 마스터를 지정하고, 각 분리된 마스터(슬레이브가 있을 수 있음)에 다른 이름을 부여해야 한다. 슬레이브는 자동으로 발견되기 때문에 굳이 명시하지 않아도 된다. 센티널은 (재시작시 정보를 유지하기 위해) 슬레이브에 대한 추가 정보를 conf 파일에 자동으로 업데이트한다. 또한 페일오버시 슬레이브가 마스터로 승격될 때마다, 그리고 새로운 센티널이 발견될 때마다 구성파일이 다시 작성된다.&lt;/p&gt;

&lt;p&gt;위의 예제 구성은 기본적으로 각각 마스터와, 존재할 수 도 있는 슬레이브로 구성된 두 세트의 레디스 인스턴스를 모니터링한다. 한 세트의 이름이 mymaster 이고, 다른 한 가지가 resque이다.&lt;/p&gt;

&lt;p&gt;sentinel monitor 구문의 인자에 대한 설명은 아래와 같다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sentinel monitor &amp;lt;master-group-name&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt; &amp;lt;quorum&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 예제의 첫번째 줄은 레디스에게 &lt;em&gt;mymaster&lt;/em&gt; 라고 부를 마스터 (127.0.0.1) 와 포트 6379번을 쿼럼이 2인 상태로 모니터링 하라는 것을 의미한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;쿼럼(quorum)&lt;/strong&gt; 은 마스터에 도달할 수 없다는 것에 동의해야 하는 센티널의 수로, 이 수를 만족 하는 경우 페일오버를 시작한다.&lt;/li&gt;
  &lt;li&gt;쿼럼은 오직 실패를 탐지하는 데에만 사용된다. 페일오버를 실제로 수행하려면 센티널 중 하나가 페일오버의 리더로 선출되고, 계속 진행할 수 있는 권한이 있어야 한다. 이는 센티널의 과반수 득표 프로세스에 의해 발생한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;예를 들어 5개의 센티널 프로세스가 있고, 마스터에 대해 쿼럼이 2로 설정된 경우&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;두 개의 센티널이 마스터에 연결할 수 없다는 것에 동시에 동의하는 경우, 둘 중 하나가 페일오버를 시작하려고 할 것이다.&lt;/li&gt;
  &lt;li&gt;적어도 세개의 센티널이 살아있는 경우여야지만 페일오버가 승인되어 실제 실행될 것이다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;다른-센티널-옵션&quot;&gt;다른 센티널 옵션&lt;/h2&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sentinel &amp;lt;option_name&amp;gt; &amp;lt;master_name&amp;gt; &amp;lt;option_value&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;down-after-milliseconds&lt;/code&gt; 는 센티널이 다운되었다고 생각한 순간부터 인스턴스에 도달할 수 없는 시간 (핑에 응답하지 않거나 오류로 응답)을 의미한다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parallel-syncs&lt;/code&gt; 는 장애조치를 마친 후 새 마스터를 사용하도록 동시에 재구성될 수 있는 슬레이브의 수를 의미한다. 숫자가 작을수록 페일오버 프로세스가 완료되는 데에 더 많은 시간이 걸린다. 복제를 수행하는 프로세스가 슬레이브에 의해 차단되지는 않지만, 마스터로부터 대용량 데이터를 로드하기 위해 순단이 발생한다. 슬레이브가 이전 데이터를 받아오도록 구성된 경우, 이 순단때문에 모든 마스터와 슬레이브가 동시에 다시 동기화 되는것을 원하지 않을 수 있다. 이 옵션을 1로 설정하면 한 번에 하나의 슬레이브만 중단되도록 설정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;센티널-구성-예제&quot;&gt;센티널 구성 예제&lt;/h2&gt;

&lt;p&gt;이제 센티널에 대한 기본 정보를 파악했으니, 센티널 프로세스를 어디에 배치해야 하는지, 센티널 프로세스가 얼마나 필요한지 등을 궁금해 할 수 있다. 이 섹션은 몇 가지 예시 구성을 보여준다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+-------------+               +-------------+
| Sentinel S1 |---------------| Sentinel S2 |
+-------------+               +-------------+

+-------------+                +-------------+
| Sentinel S1 |------ // ------| Sentinel S2 |
+-------------+                +-------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;이런 식으로 네트워크의 연결과 단절을 표현한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;마스터들은 M1, M2, M3… Mn으로 표시&lt;/li&gt;
  &lt;li&gt;슬레이브(리플리카)는 R1, R2, R3… Rn으로 표시&lt;/li&gt;
  &lt;li&gt;센티널은 S1, S2, S3,,, Sn&lt;/li&gt;
  &lt;li&gt;클라이언트는 C1, C2,,, Cn&lt;/li&gt;
  &lt;li&gt;Sentinel 동작으로 인해 인스턴스가 역할을 변경하면 대괄호 안에 넣으므로 [M1]은 센티널의 개입으로 인해 마스터로 변한 인스턴스를 의미한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;센티널은 페일오버를 시작하기 위해 항상 다수와 통신해야 하기 때문에 단 두개의 센티널만 사용하는 설정은 없다는 것에 유의해라.&lt;/p&gt;

&lt;h3 id=&quot;예제1-두개의-센티널만-사용하는-경우-절대-사용-금지&quot;&gt;예제1: 두개의 센티널만 사용하는 경우. 절대 사용 금지.&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

Configuration: quorum = 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;이 구성에서 M1이 실패하는 경우, 두 센티널이 failure의 합의에 도달하게 되고, (쿼럼이 1이기 때문에) 과반수를 넘었기 때문에 페일오버를 승인해 R1이 마스터로 승격된다.&lt;/li&gt;
  &lt;li&gt;M1이 실행중인 박스가 작동을 멈추면 S1도 작동을 멈춘다. 다른 박스 S2에서 실행되는 센티널은 혼자서 페일오버를 승인할 수 없으므로 시스템을 사용할 수 없게 된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;다른 페일오버를 명령하기 위해 과반수 이상의 동의가 필요하고, 그 후에 마지막 구성에 대해 모든 센티널에게 전파한다는 것에 유의해야 한다. 그리고, 한 쪽에서 아무런 동의 없이 페일오버를 수행하는 것은 매우 위험하다는 것은 다음 예제를 통해 알 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+----+           +------+
| M1 |----//-----| [M1] |
| S1 |           | S2   |
+----+           +------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;위의 구성은 대칭적인 두 개의 마스터를 갖게 된 구성이다. (S2는 권한 없이 페일오버 될 수 있다고 가정) 클라이언트는 두 마스터에 계속해서 쓸 수 있고, 이는 &lt;em&gt;split brain condition&lt;/em&gt; 을 초래하게 된다.&lt;/p&gt;

&lt;h3 id=&quot;예제2-세개의-박스를-이용한-기본-구성&quot;&gt;예제2: 세개의 박스를 이용한 기본 구성&lt;/h3&gt;

&lt;p&gt;아래는 기본적인 센티널 구성이다. 이 구성에 안정성을 위해 다른 요소들을 쉽게 추가할 수 있다. 한 개의 박스에서 레디스와 센티널 프로세스를 동시에 실행시킨다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

Configuration: quorum = 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;마스터 M1이 실패하면 S2와 S3은 실패에 대해 동의하고, 페일오버를 승인하여 클라이언트가 계속 진행할 수 있게 한다.&lt;/p&gt;

&lt;p&gt;모든 센티널 설정에서 레디스는 비동기식으로 복제되므로, 도중에 write 한 내용을 잃을 위험이 항상 존재한다. 그러나 위의 설정에서 다음 그림과 같이 이전의 마스터에 연결된 클라이언트로 인해 높은 위험이 존재한다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;         +----+
         | M1 |
         | S1 | &amp;lt;- C1 (writes will be lost)
         +----+
            |
            /
            /
+------+    |    +----+
| [M2] |----+----| R3 |
| S2   |         | S3 |
+------+         +----+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;위의 경우, 네트워크 파티션은 이전의 마스터 M1와 통신을 하지 못하기 때문에 슬레이브 R2가 마스터로 승격한다. 그러나 기존 마스터와 동일한 파티션에 있는 C1같은 클라이언트는 계속해서 이전 마스터에게 데이터를 쓴다. 파티션이 복구될 때, 마스터는 데이터셋을 버리고, 새로운 마스터의 슬레이브로 재구성될 것이기 때문에 C1이 작성했던 이 데이터는 손실될 것이다.&lt;/p&gt;

&lt;p&gt;이 문제는 마스터가 더이상 지정된 수의 슬레이브에게 쓰기를 전송할 수 없는 것을 감지했을 경우 쓰기 전송을 중지할 수 있는 다음과 같은 레디스 복제 기능을 사용해서 이 문제를 완화시킬 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;min-slaves-to-write 1
min-slaves-max-lag 10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;마스터가 이런 구성으로 이루어져 있을 때, 레디스 인스턴스가 적어도 하나의 슬레이브에 쓸 수 없는 경우 쓰기를 중지한다. 복제는 비동기로 이루어지기 때문에 슬레이브는 연결이 끊어졌거나, 지정된 시간 이상 ack를 보내지 않음을 의미한다.&lt;/p&gt;

&lt;p&gt;이 구성을 사용하면 위의 예제에서 이전 레디스 마스터 M1을 10초 후에 사용할 수 없게 된다. 파티션이 복구되면 C1은 유효한 값으로 새 마스터에 연결을 진행한다.&lt;/p&gt;

&lt;p&gt;하지만 두개의 슬레이브가 다운될 경우 마스터는 살아있음에도 불구하고, 마스터에 쓰기를 진행할 수 없다. 이는 트레이드 오프이므로 감당해야 한다.&lt;/p&gt;

&lt;h3 id=&quot;예제-3-클라이언트-내부의-센티널-구성&quot;&gt;예제 3: 클라이언트 내부의 센티널 구성&lt;/h3&gt;

&lt;p&gt;때때로 마스터에 연결된 슬레이브가 하나만 필요할 경우도 존재한다. 예제2는 이럴 때에는 사용할 수 없으므로 센티널이 클라이언트가 있는 곳에 배치하여 다음처럼 사용할 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;            +----+         +----+
            | M1 |----+----| R1 |
            |    |    |    |    |
            +----+    |    +----+
                      |
         +------------+------------+
         |            |            |
         |            |            |
      +----+        +----+      +----+
      | C1 |        | C2 |      | C3 |
      | S1 |        | S2 |      | S3 |
      +----+        +----+      +----+

      Configuration: quorum = 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위의 구성에서, 센티널은 클라이언트 수와 같다. C1, C2, C3은 레디스에 연결된 단일 클라이언트를 의미하는게 아니라 앱서버, 레디스 어플리케이션과 같은 것을 의미한다.&lt;/p&gt;

&lt;p&gt;M1과 S1이 실행중인 박스에 장애가 발생하면 문제없이 페일오버를 할 수 있지만, 다른 네트워크 파티션이 발생할 경우에는 다른 동작을 유발한다는 것을 알 수 있다. 예를 들어, 레디스 클라이언트와 슬레이브간에 네트워크 단절이 일어난 경우 센티널을 이용할 수 없다.&lt;/p&gt;

&lt;p&gt;C3과 M1이 분리되는 경우(위의 예제에서는 어렵겠지만, 다른 레이아웃이나 소프트웨어 계층에서 장애가 발생하면 가능) 예제2에서와 비슷한 이슈를 가질 수 있다. 마스터는 슬레이브와 연결이 끊어졌을 때 쿼리를 받아들이는 걸 멈출 수 없고, split brain 문제가 일어날 수 있다.&lt;/p&gt;

&lt;p&gt;따라서 이것은 유효한 설정이지만, 예제2에서의 구성과 같은 HA 시스템이 관리하기 더 간단하다는 장점이 있고, 쓰기를 위한 시간이 존재한다.&lt;/p&gt;

&lt;h3 id=&quot;예제-4-클라이언트쪽이-3개-이하인-센티널-클라이언트&quot;&gt;예제 4: 클라이언트쪽이 3개 이하인 센티널 클라이언트&lt;/h3&gt;

&lt;p&gt;예제 3의 경우에서, 클라이언트가 3개 이하일 경우 다음과 같이 혼합된 설정을 사용해야 한다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;            +----+         +----+
            | M1 |----+----| R1 |
            | S1 |    |    | S2 |
            +----+    |    +----+
                      |
               +------+-----+
               |            |  
               |            |
            +----+        +----+
            | C1 |        | C2 |
            | S3 |        | S4 |
            +----+        +----+

      Configuration: quorum = 3

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;예제 3의 설정과 비슷하지만, 여기서는 사용할 수 있는 네개의 서버에서 네 개의 센티넬을 실행한다. 마스터 M1이 사용가능하지 않으면, 다른 세 개의 센티널이 페일오버를 수행한다.&lt;/p&gt;

&lt;p&gt;이론적으로 C2.S4가 실행되는 박스를 제거하고, 쿼럼을 2로 설정하는 편이 보편적이다. 그러나 어플리케이션 계층을 포함하지 않고 레디스쪽에서만 HA를 원할 일은 거의 없다.&lt;/p&gt;

&lt;h2 id=&quot;센티널-도커-nat-에서-가능한-이슈&quot;&gt;센티널, 도커, NAT 에서 가능한 이슈&lt;/h2&gt;

&lt;p&gt;도커는 port mapping 을 사용한다. 도커 컨테이너 내에서 돌아가는 프로그램은 실제 프로그램이 사용하는 것과 다른 포트로 노출될 수 있다. 이는 동일한 포트를 사용해서 동시에 동일한 서버에서 여러 개의 컨테이너를 실행할 때에 유용하다.&lt;/p&gt;

&lt;p&gt;도커뿐 아니라 다른 NAT 시스템에 의해 포트나, IP 주소가 재매핑되는 경우가 발생할 수 있다.&lt;/p&gt;

&lt;p&gt;센티널에서 IP와 포트를 재매핑하여 사용할 경우 다음과 같은 문제가 발생할 수 있다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;센티널은 각 센티널이 연결을 위해 수신하는 포트와 IP 주소를 알려주는 &lt;em&gt;hello&lt;/em&gt; 메시지를 기반으로 auto-discovery 하기 때문에, 센티널 자동 동작은 더이상 작동하지 않을 수 있다. 그러나 센티널은 주소나 포트가 재매핑되었다는 것을 알 수 없기 때문에, 다른 센티널이 접속할 때 허용하지 않게 된다.&lt;/li&gt;
  &lt;li&gt;레디스 마스터에서 INFO 명령어를 통해 나열되는 슬레이브의 정보가 잘못될 수 있다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;센티널은 마스터의 INFO 정보를 사용해서 슬레이브를 자동으로 탐지하므로, 탐지된 슬레이브에 도달할 수 없을 것이며, 마스터가 죽었을 때 좋은 페일오버가 가능한 슬레이브가 존재함에도 불구하고, 절대 페일오버 할 수 없을 것이다. 도커가 포트를 1:1로 매핑하지 않는 이상.&lt;/p&gt;

&lt;p&gt;첫번째 문제를 해결하기 위해, 아래 명령어를 사용해서 매핑하는 작업이 필요하다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sentinel announce-ip &amp;lt;ip&amp;gt;
sentinel announce-port &amp;lt;port&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;a-quick-tutorial&quot;&gt;A quick tutorial&lt;/h1&gt;

&lt;p&gt;이 문서의 다음 섹션에서는 센티널 API, 구성과 semantic에 대한 모든 세부 사항을 점진적으로 다룬다. 그러나 이 섹션은 최대한 빨리 시스템을 사용하고자 하는 사람들을 위해 세개의 센티널 인스턴스를 구성하고, 상호작용하는 방법을 보여준다.&lt;/p&gt;

&lt;p&gt;여기서는 인스턴스가 포트 5000, 5001, 5002에서 실행된다고 가정한다. 포트 6379에서는 레디스 마스터가 실행되고 있고, 6380에서는 슬레이브가 실행되고 있다고 가정한다. IP는 루프백 IP인 127.0.0.1을 사용했다.&lt;/p&gt;

&lt;p&gt;세 가지 센티널의 설정 파일은 다음과 같다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;port 5000
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;나머지 두 개의 설정 파일은 동일하지만 포트 번호로 5001과 5002를 사용한다.&lt;/p&gt;

&lt;p&gt;위의 설정 파일에서 다음과 같은 몇가지 사항을 유의해야 한다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;이 마스터 셋은 mymaster라 부른다. 이는 마스터와 슬레이브를 식별하는 정보이다. 마스터셋의 이름이 다르면, 센티널은 한번에 여러 마스터와 슬레이브들을 감시할 수 있다.&lt;/li&gt;
  &lt;li&gt;쿼럼은 2로 설정되어있다. (sentinel monitor 의 마지막 인자값)&lt;/li&gt;
  &lt;li&gt;down-after-milliseconds 는 5000밀리초, 즉 5초이므로 마스터가 이 시간 내에 ping에 대해 어떠한 응답도 하지 않는다면 실패로 감지한다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;세개의 센티널을 시작하면 다음과 같은 로그가 남는다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+monitor master mymaster 127.0.0.1 6379 quorum 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이는 센티널 이벤트이고, 나중에 지정한 이름으로 SUBSCRIBE를 사용할 경우 Pub/Sub을 통해 이러한 이벤트를 수신할 수 있다.&lt;/p&gt;

&lt;p&gt;센티널은 장애 감지와 페일오버 중에 다양한 이벤트를 생성하고 기록한다.&lt;/p&gt;

&lt;h2 id=&quot;센티널에게-마스터-상태에-대해-질문하기&quot;&gt;센티널에게 마스터 상태에 대해 질문하기&lt;/h2&gt;

&lt;p&gt;센티널을 사용하는 가장 중요한 이유는 마스터가 잘 동작하고 있는지 모니터링하기 위함일 것이다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ redis-cli -p 5000
127.0.0.1:5000&amp;gt; sentinel master mymaster
 1) &quot;name&quot;
 2) &quot;mymaster&quot;
 3) &quot;ip&quot;
 4) &quot;127.0.0.1&quot;
 5) &quot;port&quot;
 6) &quot;6379&quot;
 7) &quot;runid&quot;
 8) &quot;953ae6a589449c13ddefaee3538d356d287f509b&quot;
 9) &quot;flags&quot;
10) &quot;master&quot;
11) &quot;link-pending-commands&quot;
12) &quot;0&quot;
13) &quot;link-refcount&quot;
14) &quot;1&quot;
15) &quot;last-ping-sent&quot;
16) &quot;0&quot;
17) &quot;last-ok-ping-reply&quot;
18) &quot;735&quot;
19) &quot;last-ping-reply&quot;
20) &quot;735&quot;
21) &quot;down-after-milliseconds&quot;
22) &quot;5000&quot;
23) &quot;info-refresh&quot;
24) &quot;126&quot;
25) &quot;role-reported&quot;
26) &quot;master&quot;
27) &quot;role-reported-time&quot;
28) &quot;532439&quot;
29) &quot;config-epoch&quot;
30) &quot;1&quot;
31) &quot;num-slaves&quot;
32) &quot;1&quot;
33) &quot;num-other-sentinels&quot;
34) &quot;2&quot;
35) &quot;quorum&quot;
36) &quot;2&quot;
37) &quot;failover-timeout&quot;
38) &quot;60000&quot;
39) &quot;parallel-syncs&quot;
40) &quot;1&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;위와 같이, 센티널에서 마스터에 관한 다양한 정보를 확인할 수 있다. 그 중에서도 특별히 확인해야 할 몇 가지 정보들이 존재한다.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;num-other-sentinels 의 값이 2이다. 해당 센티널은 이미 이 마스터를 위해 두 개의 센티넬을 더 감지했다는 사실을 인지하고 있다는 의미이다.&lt;/li&gt;
  &lt;li&gt;flag 에 해당하는 값은 master이다. 마스터가 다운되었을 경우 이 값이 s_down 혹은 o_down 으로 바뀔 것이다.&lt;/li&gt;
  &lt;li&gt;num-slaves의 값은 1로, 센티널이 마스터에 붙은 슬레이브가 존재한다는 것을 인지하고 있다는 의미이다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;이 인스턴스에 대해 더 자세히 알아보려면 다음의 명령어를 통해 확인할 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SENTINEL slaves mymaster
SENTINEL sentinels mymster
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;첫번째는 마스터에 연결된 슬레이브에 대한 정보, 두번째는 마스터에 연결된 다른 센티널에 대한 정보를 제공한다.&lt;/p&gt;

&lt;h2 id=&quot;현재-마스터의-주소-얻기&quot;&gt;현재 마스터의 주소 얻기&lt;/h2&gt;
&lt;p&gt;앞서 명시한 대로, 센티넬은 마스터셋에 연결하고자 하는 클라이언트의 구성 제공자 (configuration provider)와 같은 역할을 한다. 페일오버 혹은 재구성으로 인해 현재 마스터에 대해 알 수 없기 때문에 다음과 같은 API를 사용해서 마스터의 주소를 얻어올 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;127.0.0.1:5000&amp;gt; SENTINEL get-master-addr-by-name mymaster
1) &quot;127.0.0.1&quot;
2) &quot;6379&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;페일오버-테스트&quot;&gt;페일오버 테스트&lt;/h2&gt;
&lt;p&gt;위의 구성에서 마스터를 죽이고 구성이 변경되는지 확인하는 것으로 간단히 페일오버 테스트를 진행할 수 있다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;redis-cli -p 6379 DEBUG sleep 30
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;해당 명령어는 30초동안 마스터에 접근하지 못하게 한다. 이는 어떤 이유로 인해 마스터가 잠시 행에 걸려있는 상황을 시뮬레이션 한 것이다.&lt;/p&gt;

&lt;p&gt;센티널 로그를 확인해 보면, 다음과 같은 많은 작업이 일어났음을 확인할 수 있다.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;각각의 센티널은 마스터가 +sdown 이벤트와 함께 다운된 것을 감지한다.&lt;/li&gt;
  &lt;li&gt;이 이벤트는 나중에 +odown으로 에스컬레이션되며, 이는 다수의 센티널이 마스터에 도달할 수 없다는 사실에 동의했음을 의미한다.&lt;/li&gt;
  &lt;li&gt;센티널은 첫번째 페일오버를 시도할 센티널에 투표한다.&lt;/li&gt;
  &lt;li&gt;페일오버 발생.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;다음 명령어를 통해 mymaster의 master가 누구인지 다시 질문한다면, 아까와는 다른 다음과 같은 응답을 받을 수 있게 된다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;127.0.0.1:5000&amp;gt; SENTINEL get-master-addr-by-name mymaster
1) &quot;127.0.0.1&quot;
2) &quot;6380&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;지금까지 이해했다면, 센티널 구성을 위해 점프하거나, 더 자세한 구성을 위해 아래 내용을 읽으면 된다.&lt;/p&gt;

&lt;h1 id=&quot;sentinel-api&quot;&gt;Sentinel API&lt;/h1&gt;

&lt;p&gt;센티널은 API를 제공해서 상태를 검사하고, 모니터링되는 마스터와 슬레이브의 상태를 확인하고, 특정 알림을 수신하기 위해 구독하며, 런타임 중 센티널 구성을 변경한다.&lt;/p&gt;

&lt;p&gt;기본적으로 센티널은 TCP 포트 26379를 사용해서 실해오딘다. 센티널은 레디스 프로토콜을 사용해서 명령을 수신하므로, redis-cli 나 다른 레디스 클라이언트를 사용해서 센티널과 통신할 수 있다.&lt;/p&gt;

&lt;p&gt;센티널에 직접 쿼리해서 모니터링되는 레디스 인스턴스의 상태를 센티널의 관점에서 확인하고, 다른 센티널이 알고 있는 것을 확인하는 등의 작업을 수행할 수 있다. 또는 Pub/Sub를 사용해서 센티널로부터 push style 알람을 수신할 수 있으며, 페일오버와 같은 일부 이벤트가 발생할 때마다 error condition을 입력하는 인스턴스 등을 사용할 수 있다.&lt;/p&gt;

&lt;h2 id=&quot;sentinel-commands&quot;&gt;Sentinel commands&lt;/h2&gt;
&lt;h3 id=&quot;ping&quot;&gt;PING&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;PONG을 리턴&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-masters&quot;&gt;SENTINEL masters&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;모니터링되는 마스터들과 상태를 나열&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-master-master-name&quot;&gt;SENTINEL master &amp;lt;master name&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;특정 마스터의 상태를 나열&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-slaves-master-name&quot;&gt;SENTINEL slaves &amp;lt;master name&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;특정 마스터에 연결된 슬레이브들과, 그 상태를 나열&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-sentinels-master-name&quot;&gt;SENTINEL sentinels &amp;lt;master name&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;해당 마스터에 연결된 센티넬들과, 그 상태를 나열&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-get-master-addr-by-name-master-name&quot;&gt;SENTINEL get-master-addr-by-name &amp;lt;master name&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;ip와 port 정보를 리턴.&lt;/li&gt;
  &lt;li&gt;페일오버가 진행중이거나, 진행 완료된 경우 승격된 슬레이브의 IP와 port를 리턴.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-reset-pattern&quot;&gt;SENTINEL reset &amp;lt;pattern&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;일치하는 이름을 가진 모든 마스터를 재설정.&lt;/li&gt;
  &lt;li&gt;패턴은 글로벌 스타일의 패턴이다.&lt;/li&gt;
  &lt;li&gt;재설정 프로세스는 마스터의 이전 상태를 삭제하고 (진행중인 페일오버 상태 포함) 마스터가 인지하고 있던 모든 슬레이브와 센티널의 연결을 제거한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-ckquorum-master-name&quot;&gt;SENTINEL ckquorum &amp;lt;master name&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;현재 센티널 구성이 마스터를 페일오버하는데 필요한 쿼럼과, 페일오버를 승인하는데에 필요한 과반수에 도달하는지 확인.&lt;/li&gt;
  &lt;li&gt;센티널 구성이 정상인지 확인하기 위해 모니터링 시스템에서 사용해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-flushconfig&quot;&gt;SENTINEL flushconfig&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;센티널이 현재 상태를 포함해서 디스크에 config 파일을 다시 쓰도록 강제 설정.&lt;/li&gt;
  &lt;li&gt;일반적으로 센티널은 상태가 변경될 때마다 config을 다시 기록.&lt;/li&gt;
  &lt;li&gt;때때로 작업 오류, 디스크 오류 등으로 해당 config 파일이 손상될 수 있기 때문에, 이럴 경우 강제하는 방법이 유용하다.&lt;/li&gt;
  &lt;li&gt;이전 config 파일이 완전히 누락된 경우에도 작동됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;reconfiguring-sentinel-at-runtime&quot;&gt;Reconfiguring Sentinel at Runtime&lt;/h2&gt;
&lt;p&gt;레디스 2.8.4버전 이상부터는 센티널은 주어진 마스터의 구성을 추가, 제거, 변경할 수 있는 API를 제공한다. 여러 개의 센티널이 있는 경우, 레디스 센티널이 제대로 작동하려면 모든 인스턴스에서 변경 사항을 적용해야  한다는 것에 유의해야 한다. 이는 단일 센티널의 구성을 변경한다고 해서 네트워크의 다른 센티널에 변경사항이 자동으로 전파되는 것은 아님을 의미한다.&lt;/p&gt;

&lt;p&gt;다음은 센티널 인스턴스의 config을 업데이트하기 위해 사용되는 SENTINEL의 하위 명령이다.&lt;/p&gt;

&lt;h3 id=&quot;sentinel-monitor-name-ip-port-quorum&quot;&gt;SENTINEL MONITOR &amp;lt;name&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt; &amp;lt;quorum&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;지정된 이름, ip, 포트, 쿼럼을 가진 새 마스터의 모니터링 시작&lt;/li&gt;
  &lt;li&gt;IP에 hostname을 사용할 수는 없다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-remove-name&quot;&gt;SENTINEL REMOVE &amp;lt;name&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;지정된 마스터를 제거하기 위해 사용.&lt;/li&gt;
  &lt;li&gt;마스터는 더이상 모니터링되지 않고, 센티널의 내부 상태에서 완전히 제거되므로, SENTINEL masters 등의 명령어에 더이상 나열되지 않음.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-set-name-option-value&quot;&gt;SENTINEL SET &amp;lt;name&amp;gt; &amp;lt;option&amp;gt; &amp;lt;value&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;레디스 서버의 CONFIG SET 커맨드와 비슷.&lt;/li&gt;
  &lt;li&gt;특정 마스터의 파라미터를 변경하기 위해 사용됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;다음은 object-cache 라는 이름의 마스터의 down-after-milliseconds 구성을 변경하기 위해 사용한 SENTINEL SET 명령어의 예시이다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SENTINEL SET objects-cache-master down-after-milliseconds 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;SENTINEL REMOVE 이후에 SENTINEL MONITOR 명령어로 재구성할 필요 없이, SENTINEL SET 명령어를 통해 아래처럼 쿼럼 구성만 변경할 수도 있다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SENTINEL SET objects-cache-master quorum 5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;SENTINEL MASTER 명령어가 모든 구성 파라미터를 필드/쌍 배열로 제공하므로  SENTINEL GET 명령어는 존재하지 않는다.&lt;/p&gt;

&lt;h2 id=&quot;adding-or-removing-sentinels&quot;&gt;Adding or removing Sentinels&lt;/h2&gt;

&lt;p&gt;센티넬의 자동 검색 매커니즘 때문에 구성에 새로운 센티널을 추가하는 것은 간단하다. 현재의 마스터를 모니터링하도록 구성된 새 센티널을 시작하면 된다. 10초 이내에 센티널은 다른 센티널 목록과 마스터에 부착된 슬레이브 집합을 알게 될 것이다.&lt;/p&gt;

&lt;p&gt;한 번에 여러 센티널을 추가해야 하는 경우, 센티널이 인식되기를 기다리면서 차례로 하나씩 추가하는 것이 좋다. This is useful in order to still guarantee that majority can be achieved only in one side of a partition, in the chance failures should happen in the process of adding new Sentinels.&lt;/p&gt;

&lt;p&gt;네트워크 단절이 발생하지 않았을 경우, 30초 정도의 지연으로 모든 센티널을 추가할 수 있다. 추가하는 프로세스가 끝나면 SENTINEL MASTER mastername 명령을 사용해서 모든 센티널이 마스터를 모니터링하는 총 센티널 수에 동의하는지 확인할 수 있다.&lt;/p&gt;

&lt;p&gt;센티널을 제거하는 방법은 좀 더 복잡하다. 센티널은 이미 등록된 센티널에 오랫동안 연결되지 않더라도 지우지 않는다. 왜냐하면 페일오버와 새로운 구성을 만드는데 필요한 대부분의 것들을 동적으로 변경하고 싶지 않아서이다. 따라서 센티널을 제거하려면 다음과 같은 스텝을 따라야 한다. (네트워크 단절이 없는 경우)&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;제거할 센티널의 프로세스 중지&lt;/li&gt;
  &lt;li&gt;SENTINEL RESET * 명령을 다른 모든 센티널 인스턴스로 전송 (인스턴스간에 최소 30초 이상 대기하면서 기다려야 함)&lt;/li&gt;
  &lt;li&gt;모든 센티널의 SENTINEL MASTER mastername 출력을 검사하여 모든 센티널이 현재 활성화된 센티널의 수와 맞는지 확인&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;removing-the-old-master-or-unreachable-slaves&quot;&gt;Removing the old master or unreachable slaves&lt;/h2&gt;

&lt;p&gt;센티널은 오랫동안 연결이 되지 않을 때에도, 마스터의 슬레이브에 대해 확인하지 않는다. 이는 센티널이 네트워크 파티션 또는 장애 이벤트 후에 반환되는 슬레이브를 올바르게 재구성할 수 있어야 하기 때문에 유용하다.(이해안됨)&lt;/p&gt;

&lt;p&gt;또한 페일오버 이후에, 실패한 마스터는 자동적으로 새로운 마스터의 슬레이브로 추가된다. 이렇게 되면 다시 사용할 수 있게 되는 즉시 새로운 마스터를 복제하도록 재구성될 것이다.&lt;/p&gt;

&lt;p&gt;그러나 때때로 센티널이 감시하는 슬레이브의 목록에서 영원히 해당 슬레이브 (옛 마스터일 수 있음)를 제거하길 원할 수 있다.&lt;/p&gt;

&lt;p&gt;이 작업을 수행하려면 SENTINEL RESET mastername 명령을 모든 센티널에게 보내야 한다. SENTINEL RESET mastername 명령은 10초 이내에 슬레이브 목록을 새로 고치고, 현재 마스터의 INFO 출력에서 올바르게 복제된 것으로 나열된 슬레이브만 추가한다.&lt;/p&gt;

&lt;h2 id=&quot;pubsub-messages&quot;&gt;Pub/Sub Messages&lt;/h2&gt;

&lt;p&gt;클라이언트는 채널에 SUBSCRIBE 또는 PSUBSCRIBE 를 적용해서 특정 이벤트에 대한 알림을 받기 위해 Redis와 호환되는 Pub/Sub 서버 (PUBLISH를 사용할 수는 없음) 인 센티널을 사용할 수 있다.&lt;/p&gt;

&lt;p&gt;채널의 이름은 이벤트 이름과 동일하다. 예를 들어, +sdown이라는 채널은 SDOWN을 입력하는 인스턴스와 관련된 모든 알림을 수신한다. (SDOWN은 쿼리 중인 센티널의 관점에서 인스턴스에 더 이상 연결할 수 없음을 의미한다.)&lt;/p&gt;

&lt;p&gt;모든 메시지를 받으려면 PSUBSCRIBE * 을 사용하면 된다.&lt;/p&gt;

&lt;p&gt;다음은 이 API를 사용해서 수신할 수 있는 채널의 메시지 및 형식 목록이다. 첫 번째 단어는 채널/이벤트 이름이고, 나머지는 데이터의 형식이다.&lt;/p&gt;

&lt;p&gt;NOTE: 인스턴스의 세부 정보가 지정된 경우, 대상 인스턴스를 식별하기 위해 다음과 같은 인수가 제공됨을 의미.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;instance-type&amp;gt; &amp;lt;name&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt; @ &amp;lt;master-name&amp;gt; &amp;lt;master-ip&amp;gt; &amp;lt;master-port&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;마스터를 식별하는 부분(@ 인수부터 끝까지)은 선택 사항이며, 인스턴스 자체가 마스터가 아닌 경우에만 지정된다.&lt;/p&gt;

&lt;p&gt;### +reset-master &amp;lt;instance details&amp;gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;마스터가 리셋됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;slave-instance-details&quot;&gt;+slave &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;새로운 슬레이브가 감지되어 추가됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;failover-state-reconf-slaves-instance-details&quot;&gt;+failover-state-reconf-slaves &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;페일오버 상태가 reconf-slaves 상태로 변경됨&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;failover-detected-instance-details&quot;&gt;+failover-detected &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;페일오버가 다른 센티넬에 의해 시작되었거나, 다른 엔티티가 발견됨(연결된 슬레이브가 마스터가 되었다던지)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;slave-reconf-sent-instance-details&quot;&gt;+slave-reconf-sent &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;리더 센티널이 새로운 슬레이브를 해당 인스턴스에 구성하기 위해 SLAVE OF 명령어를 보냄.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;slave-reconf-inprog-instance-details&quot;&gt;+slave-reconf-inprog &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;재구성된 슬레이브가 새로운 마스터 ip:port 쌍의 슬레이브로 발견되었지만, 싱크 프로세스가 완료되지는 않음.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-dup-sentinel-instance-details&quot;&gt;-dup-sentinel &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;하나 이상의 센티널이 복제에 의해 제거됐음으로 확인. (센티널 인스턴스가 다시 시작될 때 발생)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-instance-details&quot;&gt;+sentinel &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;같은 마스터를 보고 있는 새로운 센티널이 발견되어 연결됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sdown-instance-details&quot;&gt;+sdown &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;특정 인스턴스가 주관적인(Subjectively) down 상태가 됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-sdown-instance-details&quot;&gt;-sdown &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;특정 인스턴스가 주관적인(Subjectively) down 상태에서 벗어남.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;odown-instance-details&quot;&gt;+odown &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;특정 인스턴스가 객관적인 (Objectively) down 상태가 됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-odown-instance-details&quot;&gt;-odown &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;특정 인스턴스가 객관적인 (Objectively) down 상태에서 벗어남.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;new-epoch-instance-details&quot;&gt;+new-epoch &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;현재 상태가 업데이트됨&lt;/li&gt;
  &lt;li&gt;The current epoch was updated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;try-failover-instance-details&quot;&gt;+try-failover &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;새로운 페일오버의 진행을 위해 다수결에 의해 선출되기를 기다리는 중.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;elected-leader-instance-details&quot;&gt;+elected-leader &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;선거에서 이겼으며, 페일오버 실행 가능함.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;failover-state-select-slave-instance-details&quot;&gt;+failover-state-select-slave &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;새로운 페일오버의 상태는 select-slave. 마스터로 승격시키기 위해 적합한 슬레이브를 찾고 있는 상태.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;no-good-slave--instance-details&quot;&gt;no-good-slave  &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;승격시킬만한 슬레이브가 존재하지 않음. 현재 상태에서 몇분동안 더 시도해 보고, 아마도 이 상태는 바뀌고 state machine은 이 경우 페일오버를 중단함.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;이 상태에서 성공적으로 바껴서 페일오버가 중단된다는 건지, 없으면 기다리다가 아예 상태가 변경된다는 건지 확인이 필요함.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;selected-slave--instance-details&quot;&gt;selected-slave  &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;승격시킬만한 좋은 슬레이브를 발견함&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;failover-state-send-slaveof-noone---instance-details&quot;&gt;failover-state-send-slaveof-noone   &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;승격된 슬레이브를 마스터로 재구성하여 전환되길 기다리는 중&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;failover-end-for-timeout-instance-details&quot;&gt;failover-end-for-timeout &amp;lt;instance details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;페일오버가 타임아웃으로 인해 중단되었지만, 슬레이브는 결국 새로운 마스터로 구성될 것임&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;failover-end-instance_details&quot;&gt;failover-end &amp;lt;instance_details&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;페일오버가 성공적으로 완료됨. 모든 슬레이브가 마스터의 복제본으로 구성 완료됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;switch-master-master-name-oldip-oldport-newip-newport&quot;&gt;switch-master &amp;lt;master name&amp;gt; &amp;lt;oldip&amp;gt; &amp;lt;oldport&amp;gt; &amp;lt;newip&amp;gt; &amp;lt;newport&amp;gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;마스터의 새 IP와 주소는 구성이 변경된 후 지정된 새 주소를 의미. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;대부분의 외부 사용자가 제일 관심을 가질 정보.&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;tilt&quot;&gt;+tilt&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Tilt 모드 시작&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-tilt&quot;&gt;-tilt&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Tilt 모드 해제&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;-busy-상태-처리&quot;&gt;-BUSY 상태 처리&lt;/h2&gt;

&lt;p&gt;루아 스크립트가 지정된 루아 스크립트 시간제한 시간보다 오래 걸릴 경우, 레디스 인스턴스는 -BUSY 에러를 리턴한다. 페일오버를 트리거하기 전에 레디스 센티널이 SCRIPT KILL 커맨드를 날렸을 때 이런 일이 발생한다. 스크립트가 읽기전용일 때에만 성공한다.&lt;/p&gt;

&lt;p&gt;인스턴스가 이 시도 이후에도 계속 에러 상태로 남아있다면, 결국 페일오버될 것이다.&lt;/p&gt;

&lt;h2 id=&quot;슬레이브-우선순위&quot;&gt;슬레이브 우선순위&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;레디스 인스턴스는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slave-priority&lt;/code&gt; 라는 구성 파라미터를 가지고 있고,  슬레이브 인스턴스에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INFO&lt;/code&gt; 명령어를 통해 확인 가능하다. 센티널은 이 정보를 페일오버시 마스터로 승격시킬 슬레이브를 정할 때 사용한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;만약 슬레이브의 우선순위가 0으로 설정되었다면, 해당 슬레이브는 절대 마스터로 승격되지 않음.&lt;/li&gt;
  &lt;li&gt;낮은 우선순위의 슬레이브일 수록 마스터로 승격시킬 때 센티널이 선호함.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;예를 들어 마스터와 동일한 데이터 센터를 가지고 있는 슬레이브 S1이 존재하고, 다른 데이터 센터를 가지고 있는 슬레이브 S2가 있다고 하자. S1의 우선순위가 10이고, S2가 100이라면 마스터가 죽었을 때 S1, S2 모두 마스터로 승격이 가능하지만 S1이 더 선호된다.&lt;/p&gt;

&lt;h2 id=&quot;센티널과-레디스-인증&quot;&gt;센티널과 레디스 인증&lt;/h2&gt;
&lt;p&gt;마스터가 보안을 위해 클라이언트의 암호를 필요로 하도록 구성되면, 슬레이브에서도 마스터와 인증하고, 비동기 복제 프로토콜에 사용되는 마스터-슬레이브 연결을 만들기 위해 이 암호를 사용해야 한다.&lt;/p&gt;

&lt;p&gt;이는 다음 구문을 통해 사용된다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;마스터의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirepass&lt;/code&gt; 를 사용해서 인증 비밀번호를 설정하고, 인스턴스가 인증되지 않은 클라이언트에 대한 요청을 처리하지 않도록 한다.&lt;/li&gt;
  &lt;li&gt;슬레이브의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;masterauth&lt;/code&gt; 를 사용해서 마스터에서 인증을 받아서 데이터를 올바르게 복제할 수 있도록 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;센티널을 사용할 때, (싱글 마스터 구조가 아니라면) 페일오버가 끝나면 슬레이브는 마스터의 역할을 하고, 마스터는 슬레이브처럼 구성될 수 있다. 따라서 위의 구문은 슬레이브와 마스터 두 개의 인스턴스 모두에 설정해 주어야 한다.&lt;/p&gt;

&lt;p&gt;또한, 슬레이브도 동일한 데이터를 복제받아 가지고 있는데. 마스터에서만 데이터를 보호하길 원하지 않을 것이기 때문에 이는 일반적으로 정상적인 설정이다.&lt;/p&gt;

&lt;p&gt;그러나 인증 없이 액세스할 수 있는 슬레이브가 필요한 드문 경우에는, 해당 슬레이브의 우선순위를 0으로 설정하여 마스터로 승격되지 않도록 하고, 이 슬레이브에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;masterauth&lt;/code&gt; 구문만 사용하면 된다. 이 구성에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirepass&lt;/code&gt; 지시문을 사용하지 않기 때문에, 데이터를 인증되지 않은 클라이언트가 읽을 수 있도록 한다.&lt;/p&gt;

&lt;p&gt;센티널 구성에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirepass&lt;/code&gt;를 사용해서 구성할 때, 감시하는 서버가 레디스 인스턴스에 연결되도록 하려면, 다음과 같은 구문을 이용해야 한다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sentinel auth-pass &amp;lt;master-group-name&amp;gt; &amp;lt;pass&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;인증을-사용한-센티널-인스턴스의-구성&quot;&gt;인증을 사용한 센티널 인스턴스의 구성&lt;/h3&gt;

&lt;p&gt;AUTH 명령어를 통해 클라이언트의 인증을 요구하도록 센티널 인스턴스 자체를 구성할 수도 있다. 하지만 이 기능은 레디스 5.0.1부터 사용 가능하다.&lt;/p&gt;

&lt;p&gt;이를 가능하게 하려면 모든 센티널 인스턴스에 다음과 같은 구문을 추가하면 된다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;requirepass &quot;your_password_here&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이렇게 구성할 경우, 센티널은 다음과 같은 두 가지 일을 하게 된다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;클라이언트가 센티널에 명령어를 전송하기 위해 패스워드가 필요하다. 이는 레디스에서 이런 설정 지시어가 작동하는 방식이므로 분명한 일이다.&lt;/li&gt;
  &lt;li&gt;Moreover the same password configured to access the local Sentinel, will be used by this Sentinel instance in order to authenticate to all the other Sentinel instances it connects to.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;즉, 모든 센티널 인스턴스에서 동일한 requirepass 패스워드를 구성해야 한다. 모든 센티널은 모든 다른 센티널과 통신할 수 있으므로, 모든 센티널에 액세스하기 위해 각 센티널에 암호를 구성할 필요가 없으며, 그렇지 않는다면 매우 매우 실용적이지 않았을 것이다.&lt;/p&gt;

&lt;p&gt;이 구성을 사용하기 전에 클라이언트 라이브러리가 AUTH 명령을 센티널 인스턴스로 보낼 수 있는지 확인하여야 한다.&lt;/p&gt;

&lt;h2 id=&quot;센티널-클라이언트의-구현&quot;&gt;센티널 클라이언트의 구현&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;센티널은 새 마스터 인스턴스 (VIP 또는 비슷한 시스템) 에 대한 모든 요청을 리디렉션 하는 스크립트를 실행하도록 시스템을 구성하지 않는 한 명시적으로 클라이언트의 지원이 필요하다. 클라이언트 라이브러리의 구현 주체는 센티널 클라이언트 가이드 를 참고하면 된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;more-advanced-concepts&quot;&gt;More advanced concepts&lt;/h1&gt;

&lt;p&gt;이 장에서는 어떻게 센티널이 작동하는지에 대한 디테일에 대해 알아볼 것이다.&lt;/p&gt;

&lt;h2 id=&quot;sdown-odown-실패-상태&quot;&gt;SDOWN, ODOWN 실패 상태&lt;/h2&gt;

&lt;p&gt;레디스 센티널은 &lt;em&gt;서버 다운&lt;/em&gt; 에 대해 두 가지의 다른 개념을 가지고 있다. 하나는 &lt;em&gt;주관적 다운(Subjectively Down)&lt;/em&gt; 이라 부르는 SDOWN이고, 이 다운 상태는 하나의 센티널 인스턴스가 가지고 있는 상태이다. 다른 하나는 &lt;em&gt;객관적 다운(Objectively Down)&lt;/em&gt; 이라 불리는 ODOWN이고, 이는 충분한 센티널이 (적어도 모니터링하는 마스터에 대해 설정된 쿼럼 파라미터 이상을 만족하는 수) SDOWN 조건을 가지고 있을 때, 그리고 다른 센티널들에게 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SENTINEL is-master-down-by-addr&lt;/code&gt; 커맨드를 받았을 때 발생한다.&lt;/p&gt;

&lt;p&gt;센티널의 관점에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is-master-down-after-milliseconds&lt;/code&gt; 파라미터에 지정된 구성 시간동안 PING에 대해 유효한 응답을 받지 못하면 SDOWN 조건에 도달하게 된다.&lt;/p&gt;

&lt;p&gt;PING에 대한 유효한 반환값은 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;PING 명령어가 +PONG 명령어를 반환함.&lt;/li&gt;
  &lt;li&gt;PING 명령어가 -LOADING 명령어를 반환함.&lt;/li&gt;
  &lt;li&gt;PING 명령어가 -MASTERDOWN 명령어를 반환함.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;다른 반환값(혹은 반환값이 없는 경우 포함)은 모두 유효하지 않다. 하지만 INFO 출력에서 자신을 슬레이브라고 표현하는 마스터는 다운된 것으로 고려한다.&lt;/p&gt;

&lt;p&gt;SDOWN은 전체 구성된 간격 동안 유효한 반환값을 받지 못할 경우를 의미하므로, 인터벌이 30초인 인스턴스가 ping 반환값을 29초마다 답변한다고 하면 이 인스턴스는 작동중인 것이라고 간주한다.&lt;/p&gt;

&lt;p&gt;SDOWN은 페일오버에 대한 충분한 트리거는 아니다. 이는 단지 하나의 센티널이 레디스 인스턴스가 유효하지 않다고 판단한 것을 의미한다. 페일오버를 위해서는 ODOWN 상태에 도달해야 한다.&lt;/p&gt;

&lt;p&gt;SDOWN에서 ODOWN이 되기 위해서는 동의에 대한 어려운 게 아닌, 가십 형태의 알고리즘을 사용한다. 주어진 기간 안에 마스터가 작동을 하지 않는다는 리포트를 여러 센티널에서 파악했다는 리포트를 받으면 SDOWN에서 ODOWN으로 승격된다. 이 응답이 후에 사라진다면, 플래그가 초기화된다.&lt;/p&gt;

&lt;p&gt;페일오버를 시작하기 위해 과반수를 이용한 엄격한 인증작업이 필요하지만, 페일오버는 ODOWN 상태에 도달하지 않고서는 시작될 수 없다. ODOWN 상태는 마스터에만 적용된다. 슬레이브와 같은 마스터가 아닌 인스턴스들이 작동하지 않을 때에 센티널은 SDOWN 형태로 인식하지만, ODOWN 상태로는 절대 도달하지 않는다.&lt;/p&gt;

&lt;p&gt;SDOWN 은 하지만 의미적인 암시를 포함한다. 예를 들어 SDOWN 상태의 슬레이브는 센티널이 페일오버를 진행할 때 마스터로 승격되도록 선택되지 않는다.&lt;/p&gt;

&lt;h2 id=&quot;센티널과-슬레이브의-자동-발견&quot;&gt;센티널과 슬레이브의 자동 발견&lt;/h2&gt;
&lt;p&gt;센티널은 서로의 가용성을 상호 점검하고, 메시지를 교환하기 위해 다른 센티널과 연결되어 있다. 하지만 센티널은 레디스 인스턴스의 Pub/Sub 기능을 사용하여 동일한 마스터 및 슬레이브를 모니터링하는 다른 센티널을 검색하므로, 실행중인 모든 센티널 인스턴스에서 다른 센티널 주소 목록을 구성할 필요가 없다.&lt;/p&gt;

&lt;p&gt;이 기능은 hello 라는 메시지를 __ sentinel __ :hello 라는 채널에 전송하면서 실행된다.&lt;/p&gt;

&lt;p&gt;마찬가지로 센티널은 마스터에 연결된 슬레이브의 목록을 구성할 필요가 없다. 센티널이 자동으로 레디스에 쿼리하여 알아내기 때문이다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;모든 센티널은 모니터링된 모든 마스터와 슬레이브의 Pub/Sub  채널 __ sentinel __ :hello 에 매 2초마다 ip, port, runid를 포함한 메시지를 발송한다.&lt;/li&gt;
  &lt;li&gt;모든 센티널은 Pub/Sub 채널 __ sentinel __ : hello 를 구독하면서 마스터에 연결된 알지 못한 센티널에 대해 찾는다. 새로운 센티널을 발견하면, 이 마스터에 대한 센티널 목록에 해당 인스턴스를 추가한다.&lt;/li&gt;
  &lt;li&gt;Hello 메시지는 현재 마스터에 대한 정확한 전체 구성 정보를 포함한다. 이 정보를 수신한 센티널이 가지고 있는 마스터에 대한 데이터가 오래되었다면 이를 새로운 구성으로 바로 업데이트한다.&lt;/li&gt;
  &lt;li&gt;새로운 센티널을 리스트에 추가하기 전에 항상 이 센티널이 이미 존재하는 인스턴스인지 같은 runid나 주소(ip와 port 쌍)를 확인한다. 이 경우에 같은 정보를 가지고 있던 센티널은 제거되고, 새로운 것이 등록된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;인스턴스에-대한-센티널-재구성-페일오버-프로시저-제외&quot;&gt;인스턴스에 대한 센티널 재구성 (페일오버 프로시저 제외)&lt;/h2&gt;
&lt;p&gt;장애 조치가 진행중이지 않을 때에도 센티널은 항상 모니터링되는 인스턴스에 대해 현재의 구성으로 업데이트하려고 시도한다. 구체적으로는 다음과 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;슬레이브 (현재 구성에 의한) 가 마스터가 되어야 한다고 주장할 때, 현재 마스터로 복제 할 슬레이브로 구성됨.&lt;/li&gt;
  &lt;li&gt;잘못된 마스터에 연결된 슬레이브는 새로운 마스터의 데이터를 복제해 가도록 재구성됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;센티널이 슬레이브를 재구성하려면, 새로운 구성을 브로드캐스트 하는 데에 사용되는 기간보다 긴 시간 동안 잘못된 구성을 발견하고 있어야 한다.&lt;/p&gt;

&lt;p&gt;이렇지 않으면 센티널이 오래된 구성으로 인해 (에를 들어 방금 분리된 파티션 문제로 인해 다시 연결되었기 때문에) 업데이트를 받기 전에 슬레이브 구성을 변경하려고 시도할 수 있기 때문이다.&lt;/p&gt;

&lt;p&gt;Also note how the semantics of always trying to impose the current configuration makes the failover more resistant to partitions:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;페일오버된 마스터는 그것들이 사용 가능해 졌을 때 슬레이브로 재구성됨.&lt;/li&gt;
  &lt;li&gt;파티션중에 분할된 슬레이브는 도달할 수 있게 되면 재구성한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;여기서 기억해야 할 중요한 교훈은 다음과 같다. 센티널은 각 프로세스가 항상 마지막 논리적 구성을 모니터링되는 인스턴스 세트에 적용하려고 하는 시스템이라는 것이다.&lt;/p&gt;

&lt;h2 id=&quot;슬레이브-선출과-우선순위&quot;&gt;슬레이브 선출과 우선순위&lt;/h2&gt;

&lt;p&gt;마스터가 ODOWN 상태이고, 센티널이 과반수의 센티널으로부터 페일오버에 대한 권한을 얻어서 페일오버를 수행하기 위해 준비가 되었을 때, 센티널 인스턴스는 적당한 슬레이브를 선출해야 한다.&lt;/p&gt;

&lt;p&gt;슬레이브 선출 프로세스는 슬레이브를 다음과 같은 정보들로 평가한다.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;마스터로부터 분리된 시간&lt;/li&gt;
  &lt;li&gt;슬레이브 우선순위&lt;/li&gt;
  &lt;li&gt;복제 offset 진행시간&lt;/li&gt;
  &lt;li&gt;run ID&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;마스터 타임아웃이 발생한 시간 (down-after-milliseconds) 의 10배의 시간에, 페일오버를 수행중인 센티널의 관점에서 마스터를 사용할 수 없는 시간을 더한 것보다 마스터로부터 연결이 해제된 시간이 큰 슬레이브는 페일오버에 적합하지 않은 것으로 간주되어 마스터로 선택받지 못하고 건너 뛰게 된다.&lt;/p&gt;

&lt;p&gt;좀 더 엄격한 조건에서, INFO 출력으로 마스터로부터 연결을 할 수 없는 슬레이브를 다음 수식으로 구할 수 있다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;슬레이브는 위의 조건을 만족한 인스턴트만 고려하며, 기준에 따라 다음과 같은 순서로 정렬된다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;슬레이브는 레디스 인스턴스의 redis.conf 파일에 구성된 슬레이브 우선순위에 따라 정렬된다. 우선순위가 낮을수록 선호된다.&lt;/li&gt;
  &lt;li&gt;우선순위가 같다면, 슬레이브가 처리한 replication offset을 확인하여, 마스터로부터 더 많은 데이터를 받은 슬레이브가 선택된다.&lt;/li&gt;
  &lt;li&gt;다수의 슬레이브의 우선순위가 같고, 마스터에서 동일한 데이터를 받아왔다면, 사전순으로 runID가 작은 슬레이브를 선택한다. 작은 run ID를 갖는다는게 슬레이브에게 실질적인 이점은 없지만, 임의의 슬레이브를 선택하는 과정 없이 하나의 슬레이브를 바로 결정할 수 있다는 점에서 유용하다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;물리적으로 선호되는 서버가 있다면 마스터, 슬레이브 모두 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slave-priority&lt;/code&gt; 변수를 구성해야 한다. 아니라면, 모든 인스턴스가 기본 실행ID 으로 실행될 수 있다 (?)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slave-priority&lt;/code&gt; 값이 0인 레디스 인스턴스는 절대 센티널에 의해 새로운 마스터로 선택되지 않는다. 이 차이만 빼면, 페일오버 이후 재구성된 마스터에 슬레이브로 붙는다는 사실은 차이가 없다.&lt;/p&gt;

&lt;h1 id=&quot;algorithm-and-internals&quot;&gt;Algorithm and internals&lt;/h1&gt;

&lt;p&gt;이 장에서는 센티널의 동작에 대해 자세히 알아본다. 사용자가 모든 세부 사항을 알고 있어야 하는건 아니지만, 센티널에 대한 깊은 이해는 센티널을 보다 효과적으로 배치하고 운영하는 데에 도움이 될 수 있을 것이다.&lt;/p&gt;

&lt;h2 id=&quot;quorum&quot;&gt;Quorum&lt;/h2&gt;

&lt;p&gt;이전 섹션에서 센티널에서 모니터링되어지는 모든 마스터는 쿼럼과 관련되어 있다는 것을 확인했다. 쿼럼은 마스터에 닿을 수 없거나, 장애상황에 처해 페일오버가 일어나야 한다고 동의하는 센티널 프로세스의 수를 명시적으로 설정한다.&lt;/p&gt;

&lt;p&gt;하지만, 페일오버가 시작된 이후, 실제로 수행되기 위해서는, 센티널의 과반수 이상이 페일오버 권한을 가지고 있어야 한다. 소수의 센티널이 있는 파티션에서는 절대 슬레이브를 마스터로 승격시키지 않는다.&lt;/p&gt;

&lt;p&gt;조금 더 명확하게 설명하자면 다음과 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;쿼럼(Quroum): 마스터가 &lt;strong&gt;ODOWN&lt;/strong&gt;으로 표시될 수 있도록 장애상태를 파악해야 하는 센티널 프로세스의 수.&lt;/li&gt;
  &lt;li&gt;페일오버는 &lt;strong&gt;ODOWN&lt;/strong&gt; 상태에 의해 트리거된다.&lt;/li&gt;
  &lt;li&gt;페일오버가 트리거되면, 페일오버를 시도하는 센티널은 과반수 이상의 센티널에게 권한을 요청해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;차이는 미묘해보일 수 있지만, 실제로 이해하고 사용하기에는 사실 간단하다. 예를 들어 당신이 5개의 센티널 인스턴스를 가지고 있고, 쿼럼이 2일 때, 두개의 센티널이 마스터에 도달가능하지 않다고 인식하자마자 페일오버는 트리거될 것이다. 하지만, 두개의 센티널 중 권한을 가진 하나의 센티널에서만 페일오버가 가능하다.&lt;/p&gt;

&lt;p&gt;쿼럼이 5로 구성된 경우, 모든 센티널에서 마스터가 장애상황이라는 것을 판단하고, 모든 센티널에게 권한을 받아야 한다.&lt;/p&gt;

&lt;p&gt;이것은 쿼럼이 두 가지 방법으로 센티널을 조정하는 데에 사용될 수 있음을 의미한다.&lt;/p&gt;

&lt;p&gt;1, 쿼럼이 센티널의 과반수보다 작게 설정된 경우, 기본적으로 센티널이 마스터의 장애에 더 민감해지도록 하여, 소수의 센티널이 마스터와 더이상 연결될 수 없도록 즉시 페일오버를 수행한다.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;쿼럼이 센티널의 과반수보다 큰 값으로 설정되면, 마스터가 다운되는 것에 동의하는 연결된 센티널의 수가 많을 때에만 장애 조치를 수행할 수 있다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;configuration-epochs&quot;&gt;Configuration epochs&lt;/h2&gt;
&lt;p&gt;센티널은 다음과 같은 몇 가지 중요한 이유로 인해 페일오버를 시작하기 위해 다수로부터 인증을 받아야 한다.&lt;/p&gt;

&lt;p&gt;센티널이 인증되면, 페일오버된 마스터에 대한 고유한 구성 epoch를 얻는다. 이는 페일오버가 완료된 후, 새 구성의 버전을 지정하는데 사용되는 번호이다. 과반수 이상이 특정 버전이 특정 센티널에 할당되었다는 것을 동의했기 때문에, 다른 센티널이 이 번호를 사용할 수 없다. 이는 페일오버로 인한 모든 구성이 유니크한 버전이라는 것을 의미한다. 이게 얼마나 중요한지 알아보자.&lt;/p&gt;

&lt;p&gt;센티널은 다음과 같은 규칙이 있다. 센티널이 특정 마스터의 페일 오버를 위해 다른 센티널을 투표하면, 해당 마스터를 페일오버하기 위해 잠시 기다린다. 이 시간을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;failover-timeout&lt;/code&gt; 이라 하고, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sentinel.conf&lt;/code&gt; 에서 구성할 수 있다.이는 동일한 마스터를 동시에 페일오버하지 않는다는 것을 의미하며, 먼저 권한이 있는지 묻는 첫 번째 시도 이후, 잠시 시간이 지난 후 두 번째 시도가 일어남을 의미한다.&lt;/p&gt;

&lt;p&gt;레디스 센티널은 만약 마스터가 죽은 경우, 과반수 이상의 센티널이 연결 가능하고, 결국 한개의 센티널이 페일오버를 시도하여 liveness한 속성이 있다는 것을 보장한다.&lt;/p&gt;

&lt;p&gt;또한, 센티널은 모든 센티널이 다른 구성 epoch를 사용하여 동일한 마스터를 페일오버할 수 있는 safety 속성을 보장한다.&lt;/p&gt;

&lt;h2 id=&quot;configuration-propagation&quot;&gt;Configuration propagation&lt;/h2&gt;

&lt;p&gt;센티널이 마스터를 성공적으로 페일오버시킬 수 있게 되면, 새로운 센티널은 주어진 마스터에 대한 정보를 업데이트 할 수 있도록 새로운 구성을 브로드캐스팅하기 시작한다.&lt;/p&gt;

&lt;p&gt;페일오버가 성공한 것으로 간주되려면, 센티널이 선택된 슬레이브에게 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SLAVE OF NO ONE&lt;/code&gt; 명령어를 보내야 하고, 해당 슬레이브가 마스터로 전환된 것을 나중에 INFO 명령어로 확인할 수 있어야 한다.&lt;/p&gt;

&lt;p&gt;이 시점에서, 슬레이브의 재구성이 진행중이더라도, 페일오버는 성공적으로 수행된 것으로 고려되고, 모든 센티널은 새로운 구성을 보고받기 시작한다.&lt;/p&gt;

&lt;p&gt;새로운 구성이 전파되는 방식은 모든 센티널의 페일오버가 다른 버전 넘버(구성 epoch)로 인증되어야 하는 이유이다.&lt;/p&gt;

&lt;p&gt;모든 센티널은 마스터 및 모든 센티널에서 레디스 Pub/Sub 메시지를 사용하여 마스터 버전의 구성을 지속적으로 브로드캐스트한다. 동시에 모든 센티널은 다른 센티널이 어떤 구성으로 이루어져 있는지 보내는 메시지를 기다린다.&lt;/p&gt;

&lt;p&gt;구성은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_sentinel__:hello&lt;/code&gt; 라는 Pub/Sub 채널에서 브로드캐스트된다.&lt;/p&gt;

&lt;p&gt;모든 구성이 다른 버전번호를 가지고 있기 때문에, 큰 버전은 항상 작은 버전보다 우선된다.&lt;/p&gt;

&lt;p&gt;예를 들어 마스터 mymaster의 구성은 마스터가 192.168.1.50:6379라고 생각하는 모든 센티널 에 의해 시작된다.이 구성을 버전 1이라 하자. 잠시 뒤, 센티널은 페일오버로 인해 버전 2로 구성된다. 페일오버가 성공적으로 완료되면 새로운 구성을 브로드캐스팅 하기 시작할 것이다. 버전 2의 새로운 마스터를 192.168.1.50:9000 이라 하자. 모든 다른 인스턴스는 이 구성을 보고, 새로운 구성의 버전정보가 더 높기 때문에 본인의 구성을 업데이트 할 것이다.&lt;/p&gt;

&lt;p&gt;이것은 센티널이 두번째 liveness라는 속성을 보장한다는 것을 의미한다. 통신할 수 있는 센티널 세트는 모두 더 높은 버전 번호와 동일한 구성으로 수렴된다.&lt;/p&gt;

&lt;p&gt;예를 들어, 마스터 mymaster의 구성은 마스터가 192.168.1.50:6379라고 생각하는 모든 Sentinels로 시작합니다. 이 구성에는 버전 1이 있습니다. 얼마 후 Sentinel이 버전 2로 페일 오버 할 수있는 권한이 부여됩니다. 페일 오버가 성공하면 버전 2를 사용하여 192.168.1.50:9000이라고 새 구성을 브로드 캐스트하기 시작합니다. 다른 모든 인스턴스 새 구성에는 더 큰 버전이 있으므로이 구성이 표시되고 그에 따라 구성이 업데이트됩니다.&lt;/p&gt;

&lt;p&gt;기본적으로 네트워크가 파티션된 경우, 많은 파티션은 높은 로컬 구성으로 수렴된다. 파티션이 없는 특별한 경우에는 각각이 싱글 파티션이고, 모든 센티널이 해당 구성에 동의해야 한다.&lt;/p&gt;

&lt;h2 id=&quot;consistency-under-partitions&quot;&gt;Consistency under partitions&lt;/h2&gt;
&lt;p&gt;레디스 센티널 구성은 결국 일관적으로, 모든 파티션을 사용 가능한 상위 구성으로 수렴시키는 역할을 한다. 하지만 센티널을 사용하는 real-world 시스템에서는 세 가지의 다른 플레이어가 존재한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;레디스 인스턴스&lt;/li&gt;
  &lt;li&gt;센티널 인스턴스&lt;/li&gt;
  &lt;li&gt;클라이언트&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;시스템의 동작을 정의하려면 위의 셋을 모두 고려해야 한다.&lt;/p&gt;

&lt;p&gt;다음은 각각 레디스 인스턴스와 센티널 인스턴스를 실행하는 세개의 노드가 있는 간단한 네트워크 구성이다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;            +-------------+
            | Sentinel 1  |----- Client A
            | Redis 1 (M) |
            +-------------+
                    |
                    |
+-------------+     |          +------------+
| Sentinel 2  |-----+-- // ----| Sentinel 3 |----- Client B
| Redis 2 (S) |                | Redis 3 (M)|
+-------------+                +------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 시스템에서 원래 상태는 레디스 3이 마스터이고, 1과 2는 슬레이브였다. 이전 마스터와 슬레이브를 분리하는 파티션이 발생하여 센티널 1과 2는 레디스1을 새로운 마스터로 승격시키는 페일오버를 시작했다.&lt;/p&gt;

&lt;p&gt;센티널은 센티널 1과 2가 이제 마스터에 대한 새로운 구성을 갖도록 한다. 하지만 센티널 3은 다른 파티션에 존재하기 때문에 오래된 구성을 유지한다.&lt;/p&gt;

&lt;p&gt;네트워크 파티션이 회복되었을 때 센티널3의 구성이 업데이트된다는 것을 알고 있다. 하지만 이전 마스터에 붙어 있는 클라이언트가 있는 경우 파티션동안 어떤 일이 발생할까?&lt;/p&gt;

&lt;p&gt;클라이언트는 계속 레디스 3에 쓸 수 있다. 파티션이 다시 결합되면 레디스 3은 레디스 1의 슬레이브로 바뀌고, 파티션 중에 작성된 모든 데이터는 손실된다.&lt;/p&gt;

&lt;p&gt;구성에 따라 다음과 같은 시나리오를 원할 수도, 원하지 않을 수도 있다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;레디스를 캐시로 사용하는 경우, 데이터가 손실되더라도 클라이언트 B가 여전히 이전 마스터에 쓸 수 있는 것이 편리할 수 있다.&lt;/li&gt;
  &lt;li&gt;레디스를 저장소로 사용하는 경우,이는 좋지 않으므로 이 문제를 방지하기 위한 시스템을 구성해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;레디스는 비동기식으로 복제되므로, 시나리오에서는 데이터 손실을 완전히 방지할 방법이 없지만, 다음 레디스의 구성 옵션을 사용하여 레디스 3과 레디스 1의 차이를 좁힐 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;min-slaves-to-write 1
min-slaves-max-log 10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위의 구성을 통해, 레디스 인스턴스는 마스터로 작동할 때, 적어도 하나의 슬레이브에 쓸 수 없는 경우 쓰기 허용을 중지한다. 복제가 비동기적으로 쓸 수 없다는 것은 실제로 슬레이브의 연결이 끊어졌거나, 지정된 지연 시간동안 비동기 승인을 보내지 않았음을 의미한다.&lt;/p&gt;

&lt;p&gt;이 구성을 사용하면 위의 예에서 레디스는 10초 후에 사용할 수 없게 된다. 파티션이 회복되면 센티널3이 새로운 구성을 받아들이고, 센티널 B는 유효한 구성을 받아와서 계속 진행할 수 있다.&lt;/p&gt;

&lt;p&gt;일반적으로 레디스+센티널 조합은 마지막 페일오버의 구성으로 머지되는, 결론적으로 일관성있는 시스템이다. 그리고, 이전 마스터의 데이터는 버려지고, 새로운 마스터의 데이터를 복제해 온다. 따라서 인정된 쓰기를 잃을 수 있는 가능성이 항상 존재한다. 이는 레디스의 비동기식 복제와 시스템의 “가상” 머지 기능의 폐기 특성 때문이다. 이는 센티널에 국한된 문제가 아니며, 일관성을 중요시 여기는 복제 상태 시스템에서 페일오버가 발생할 때 같은 문제가 밠애할 수 있다. 승인 된 쓰기가 손실되는 것을 피할 수 있는 방법은 다음 두가지 뿐이다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;동기 복제 (혹은 적절한 일치 알고리즘을 사용하는 상태 시스템)을 사용&lt;/li&gt;
  &lt;li&gt;동일한 객체의 다른 버전을 머지할 수 있는 시스템을 사용&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;레디스는 현재 위의 시스템 중 하나를 사용할 수 없으며, 이는 개발 목표에 존재하지 않음. 그러나 Netflix &lt;a href=&quot;https://github.com/Netflix/dynomite&quot;&gt;Dynomite&lt;/a&gt;와 같은 레디스 솔루션에 2번을 구현하는 프록시가 존재한다.&lt;/p&gt;

&lt;h2 id=&quot;sentinel-persistent-state&quot;&gt;Sentinel persistent state&lt;/h2&gt;

&lt;p&gt;센티널의 상태는 구성 파일에 유지된다. 예를 들어 마스터에 대해 새로운 구성이 수신되거나, 리더 센티널에 의해 작성될 때마다, 구성은 구성 epoch와 함께 디스크에 저장된다. 이는 센티널 프로세스를 중지하고 다시 시작하는 것이 안전하다는 것을 의미함.&lt;/p&gt;

&lt;h2 id=&quot;tilt-mode&quot;&gt;TILT mode&lt;/h2&gt;

&lt;p&gt;레디스 센티널은 서버 자체의 시간에 크게 의존한다. 예를 들어 인스턴스가 사용 가능한지 이해하기 위해 PING 명령에 대한 최근의 성공적인 응답 시간을 기억하고, 현재 시간과 비교하여 얼마나 오래됐는지 이해한다.&lt;/p&gt;

&lt;p&gt;하지만 서버 시간이 예기치 않은 방식으로 변경되거나, 서버 사용량이 많거나 어떤 이유로 프로세스가 차단된 경우, 센티널이 예기치 않은 방식으로 동작할 수 있다.&lt;/p&gt;

&lt;p&gt;TILT 모드는 시스템의 안정성을 저하시킬 수 있는 이상한 것이 감지될 때, 센티널이 들어갈 수 있는 “보호” 모드이다. 센티널 타이머 인터럽트는 1초에 보통 10회 호출되므로, 타이머 인터럽트에 대한 두 호출 사이에 100 밀리초 이상이 소요될 것으로 예상한다.&lt;/p&gt;

&lt;p&gt;센티널이 하는 일은 타이머 인터럽트가 호출된 이전 시간을 등록하고, 현재 호출과 비교하는 것이다. 시차가 음수이거나 예기치 않게 큰 경우(2초 이상), TILT 모드는 시작되고, 혹은 이미 TILT 모드인 경우 TILT 모드의 종료가 연기된다.&lt;/p&gt;

&lt;p&gt;TILT 모드일 때, 센티널은 모든 것을 계속 모니터랑하지만, 다음과 같이 동작한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;모든 동작을 멈춘다.&lt;/li&gt;
  &lt;li&gt;실패를 감지하는 기능이 신뢰되지 않으므로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SENTINEL is-master-down-by-addr&lt;/code&gt; 요청에 부정적으로 응답한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;모든 것이 30초동안 정상인 것으로 보이면 TILT 모드가 종료된다.&lt;/p&gt;

&lt;p&gt;어떤 식으로든 TILT 모드는 많은 커널이 제공하는 시계 API를 사용해서 대체될 수 있지만, 현재의 시스템이 스케줄러에 의해 오랫동안 일시 중단되거나, 실행되지 않는 경우의 문제를 피할 수 있는 프로세스이기 때문에 이게 좋은 해결책인지 확실하지 않다.&lt;/p&gt;
</description>
        <pubDate>Fri, 06 Sep 2019 18:57:00 +0000</pubDate>
        <link>http://garimoo.github.io/database/2019/09/06/redis_ha.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/database/2019/09/06/redis_ha.html</guid>
        
        
        <category>Database</category>
        
      </item>
    
      <item>
        <title>[Redis Documentation #4] 센티널 관련 파라미터 </title>
        <description>&lt;h1 id=&quot;센티널--관련-파라미터&quot;&gt;센티널  관련 파라미터&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;센티널은 기본 설정으로 bind와 protected-mode 파라미터가 비활성화되어 있음.
이렇게 하면 local 이외에 외부에서 센티널에 접속할 수 없음.
그러므로 bind에 ip를 지정하고 protected-mode를 yes로 설정하고 사용해야 함.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;daemonize&quot;&gt;DAEMONIZE&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;센티널은 기본적으로 데몬으로 실행되지 않음.&lt;/li&gt;
  &lt;li&gt;데몬으로 실행하려면 YES(Recommend)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;pidfile&quot;&gt;PIDFILE&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;데몬으로 실행될 때 process id를 pid 파일에 기록함.&lt;/li&gt;
  &lt;li&gt;pid 파일의 경로를 지정&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var/run/redis-sentinel.pid&lt;/code&gt; 가 기본 경로&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;logfile&quot;&gt;LOGFILE&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;로그파일을 지정&lt;/li&gt;
  &lt;li&gt;데몬으로 실행할 경우 로그파일을 지정하지 않으면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/null&lt;/code&gt;로 날아감.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;announce-ip--announce-port&quot;&gt;ANNOUNCE-IP / ANNOUNCE-PORT&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;NAT으로 인해 센티널은 로컬이 아닌 주소를 통해 외부에서 접근할 수 있음.&lt;/li&gt;
  &lt;li&gt;해당 IP가 제공되면 일반적으로 로컬 주소를 감지하는 방법이 아니라, HELLO 메시지를 통해 지정된 IP 주소의 인스턴스를 감지함.&lt;/li&gt;
  &lt;li&gt;마찬가지로 announce 가 제공된 경우, 이 정보가 유효하고 0이 아니라면, 센티널은 지정된 TCP 포트를 이용함.&lt;/li&gt;
  &lt;li&gt;ANNOUNCE-IP만 지정된 경우, 센티널은 port 옵션으로 지정된 대로 지정된 IP및 서버 포트를 사용하고, ANNOUNCE-PORT만 지정된 경우, 자동으로 감지된 로컬 IP와 지정된 포트를 사용함.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;dir&quot;&gt;DIR&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;작업 디렉토리 지정&lt;/li&gt;
  &lt;li&gt;conf, log 파일이 위치&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-monitor&quot;&gt;SENTINEL MONITOR&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sentinel monitor &amp;lt;master-name&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;redis-port&amp;gt; &amp;lt;quorum&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;최소 &amp;lt;quorum&amp;gt; 개의 센티널이 동의하는 경우에만 센티널이 이 마스터를 모니터링하고, ODOWN (Objectively Down) 상태로 간주&lt;/li&gt;
  &lt;li&gt;ODOWN의 쿼럼이 무엇이든 센티널은 페일오버를 시작하기 위해 과반수 이상의 센티널에 의해 선출되어야 하므로, 소수의 페일오버는 수행되지 않음.&lt;/li&gt;
  &lt;li&gt;슬레이브는 자동으로 검색되므로, 어떠한 방식으로도 슬레이브를 지정할 필요가 없음.&lt;/li&gt;
  &lt;li&gt;복제본이 추가되면 센티널은 구성 파일을 다시 작성함. 복제본이 마스터로 승격되는 경우에도 구성파일은 다시 작성됨.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NOTE: 마스터 이름에는 특수 문자나 공백이 없어야 함.
유효한 셋은 `A-z 0-9` 와 문자 `.-_`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-auth-path&quot;&gt;SENTINEL AUTH-PATH&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SENTINEL auth-path &amp;lt;master-name&amp;gt; &amp;lt;password&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;마스터와 슬레이브를 인증하는 데에 사용할 비밀번호를 설정&lt;/li&gt;
  &lt;li&gt;모니터링할 레디스 인스턴스에 비밀번호가 설정된 경우 유용함.&lt;/li&gt;
  &lt;li&gt;마스터 비밀번호는 슬레이브에도 사용되므로 슬레이브를 사용하여 인스턴스를 모니터링 하려면 마스터 및 슬레이브 인스턴스에서 다른 비밀번호를 설정할 수 없음.&lt;/li&gt;
  &lt;li&gt;인증이 필요한 레디스 인스턴스와, 필요하지 않은 인스턴스를 혼합하여 사용하는 경우 AUTH 명령이 꺼진 레디스 인스턴스에 영향을 미치지는 않음.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-down-after-milliseconds&quot;&gt;SENTINEL DOWN-AFTER-MILLISECONDS&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SENTINEL down-after-milliseconds &amp;lt;master-name&amp;gt; &amp;lt;milliseconds&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;SDOWN 상태로 인식하기 위해 마스터(또는 연결된 슬레이브나 센티널)에 도달할 수 없는 시간
    &lt;ul&gt;
      &lt;li&gt;unreachable: 지정된 기간동안 PING에 적절한 응답을 하지 않음&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;default는 30초&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-deny-scripts_reconfig&quot;&gt;SENTINEL DENY-SCRIPTS_RECONFIG&lt;/h3&gt;

&lt;h3 id=&quot;sentinel-monitor-master&quot;&gt;SENTINEL MONITOR MASTER&lt;/h3&gt;

&lt;h3 id=&quot;sentinel-parallel-syncs&quot;&gt;SENTINEL PARALLEL-SYNCS&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;페일오버 도중에 새 마스터를 바라보도록 재구성 할 수 있는 슬레이브의 수.&lt;/li&gt;
  &lt;li&gt;동기화를 수행하는 동안 쿼리가 다른 슬레이브에 도달할 수 없도록 피하기 위해서는 작은 수로 설정해야 함.&lt;/li&gt;
  &lt;li&gt;1일 경우 한번에 한 슬레이브씩 처리함.&lt;/li&gt;
  &lt;li&gt;만약 이 기능이 없다면 여러 슬레이브가 동시에 새 마스터에 데이터 전체 동기(Full resync)를 요청함.
    &lt;h3 id=&quot;sentinel-failover-timeout&quot;&gt;SENTINEL FAILOVER-TIMEOUT&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;페일오버가 설정한 시간이 지나도 완료되지 않으면 취소하는 시간&lt;/li&gt;
  &lt;li&gt;기본은 3분&lt;/li&gt;
  &lt;li&gt;지정된 센티널이 동일한 마스터에 대해 이전의 페일오버를 이미 시도한 뒤, 페일오버를 다시 시작하는 데에 필요한 시간은 페일오버 타임아웃 시간의 &lt;strong&gt;두 배&lt;/strong&gt; 가 필요함.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;센티널의 현재 구성에 따라 잘못된 마스터에 대해 복제본을 연결하여 복제하는데 필요한 시간은, 그래서 올바른 마스터에 연결되어 복제를 진행해야 하는 시간은 페일오버 타임아웃 시간과 같다. (센티널에서 잘못된 구성임을 감지한 시간부터)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;페일오버가 이미 진행중이지만, 어떠한 구성적으로 변화가 없었을 때 페일오버를 취소하는 데에 필요한 시간 (SLAVE OF NO ONE을 승격된 마스터에서 승인하지 않았을 경우)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;진행중인 페일오버가 모든 슬레이브가 새로운 마스터의 복제본으로 재구성될 때까지 대기하는 최대 시간을 의미. 그러나 이 시간이 지나도 복제본은 센티널에 의해 재구성되지만, 지정된 병렬 동기화 진행으로는 재구성되지 않음.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-config-epoch&quot;&gt;SENTINEL CONFIG-EPOCH&lt;/h3&gt;

&lt;h3 id=&quot;sentinel-notification-script&quot;&gt;SENTINEL NOTIFICATION-SCRIPT&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SENTINEL notification script &amp;lt;master-name&amp;gt; &amp;lt;script-path&amp;gt;&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;센티널 notification-script나 sentinel-reconfig-script는 관리자에게 알리거나, 페일오버 이후 클라이언트를 다시 구성하기 위해 호출되는 스크립트를 구성하는데에 사용됨. 스크립트는 오류 처리를 위해 다음 규칙에 의해 실행됨.&lt;/li&gt;
  &lt;li&gt;스크립트가 1로 종료되면 나중에 재시도됨. (최대 10회까지 재시도)&lt;/li&gt;
  &lt;li&gt;스크립트가 2 또는 더 높은 값으로 종료되면 스크립트는 재시도되지 않음.&lt;/li&gt;
  &lt;li&gt;스크립트의 최대 실행 시간은 60초, 이 타임아웃에 도달하면 스크립트는 SIGKILL로 종료되고, 실행이 다시 시도됨.&lt;/li&gt;
  &lt;li&gt;예를 들어 WARNING 레벨에서 생성된 센티널 이벤트 (예: -sdonw, odown 등) 에 대해 지정된 알림 스크립트를 호출할 수 있다.&lt;/li&gt;
  &lt;li&gt;이 스크립트는 이메일, SMS 또는 기타 메시징 시스템을 통해 시스템 관리자에게 모니터링되는 레디스 시스템에 문제가 있음을 알릴 수 있음.&lt;/li&gt;
  &lt;li&gt;스크립트는 단지 두개의 인수만으로 호출됨. 첫번째는 이벤트 유형, 두번째는 이벤트 설명&lt;/li&gt;
  &lt;li&gt;이 옵션이 제공되면 센티널을 시작하려면 스크립트가 존재해야 하며, 실행 가능해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-client-reconfig-script&quot;&gt;SENTINEL CLIENT-RECONFIG-SCRIPT&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sentinel client-reconfig-script &amp;lt;master-name&amp;gt; &amp;lt;script-path&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;페일오버로 인해 마스터가 변경된 경우 어플리케이션별 작업을 수행하여 구성이 변경되었으며, 마스터가 다른 주소에 있음을 클라이언트에 알리기 위해 스크립트를 호출할 수 있음.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;다음 인수가 스크립트에 전달됨&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;master-name&amp;gt; &amp;lt;role&amp;gt; &amp;lt;state&amp;gt; &amp;lt;from-ip&amp;gt; &amp;lt;from-port&amp;gt; &amp;lt;to-ip&amp;gt; &amp;lt;to-port&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;&amp;lt;state&amp;gt; 는 항상 failover&lt;/li&gt;
  &lt;li&gt;&amp;lt;role&amp;gt;은 leader 혹은 observer 임.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;from-ip, from-port, to-ip, to-port 인수는 마스터의 이전 주소와 선택한 복제본 (현재 마스터)의 새 주소를 전달하는 데 사용됨.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;기본적으로 SENTINEL SET은 런타임중에 알림 스크립트나 클라이언트 재구성 스크립트를 변경할 수 없음.&lt;/li&gt;
  &lt;li&gt;클라이언트가 스크립트를 설정하고 장애조치를 트리거하여 프로그램을 실행시킬 수 있는 간단한 보안 문제를 피하기 위함.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-leader-epoch&quot;&gt;SENTINEL LEADER-EPOCH&lt;/h3&gt;

&lt;h3 id=&quot;sentinel-rename-command&quot;&gt;SENTINEL RENAME-COMMAND&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;센티널이 모니터하는 레디스 서버가 명령을 rename했을 경우 여기에 바뀐 명령을 설정함.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sentinel-known-replica&quot;&gt;SENTINEL KNOWN-REPLICA&lt;/h3&gt;

&lt;h3 id=&quot;sentinel-known-sentinel&quot;&gt;SENTINEL KNOWN-SENTINEL&lt;/h3&gt;

&lt;h1 id=&quot;참고&quot;&gt;참고&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;https://redis.io/commands&lt;/li&gt;
  &lt;li&gt;sentinel.conf 파일 (5.0.3 버전)&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 06 Sep 2019 11:03:00 +0000</pubDate>
        <link>http://garimoo.github.io/database/2019/09/06/sentinel_parameter.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/database/2019/09/06/sentinel_parameter.html</guid>
        
        
        <category>Database</category>
        
      </item>
    
      <item>
        <title>[Redis] 레디스 파이프라인을 이용해 더미데이터 생성</title>
        <description>&lt;h1 id=&quot;레디스에-파이프라인을-이용해-더미데이터-생성하기&quot;&gt;레디스에 파이프라인을 이용해 더미데이터 생성하기&lt;/h1&gt;
&lt;h2 id=&quot;레디스-연결-제대로-되는지-확인&quot;&gt;레디스 연결 제대로 되는지 확인&lt;/h2&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ (printf &quot;PING\r\nPING\r\nPING\r\n&quot;; sleep 1)|nc &amp;lt;server IP&amp;gt; &amp;lt;port&amp;gt;
+PONG
+PONG
+PONG
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;더미데이터-생성&quot;&gt;더미데이터 생성&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;다음 파일 &lt;a href=&quot;/assets/20190529/redis_dummy.txt&quot;&gt;다운로드&lt;/a&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$  echo -e &quot;$(cat redis_dummy.txt)&quot; | /home1/cubrid1/redis-5.0.3/src/redis-cli -h &amp;lt;server IP&amp;gt; -p &amp;lt;port&amp;gt; --pipe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;파일은 레디스 프로토콜을 통해 작성되어있음.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; DBSIZE
(integer) 364
127.0.0.1:6379&amp;gt; DBSIZE
(integer) 364
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;DBSIZE 명령어를 통해 데이터가 잘 들어갔는지 확인할 수 있음.
    &lt;h2 id=&quot;출처&quot;&gt;출처&lt;/h2&gt;
  &lt;/li&gt;
  &lt;li&gt;http://intro2libsys.info/introduction-to-redis/pipelining-and-mass-insertions&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 29 May 2019 11:57:00 +0000</pubDate>
        <link>http://garimoo.github.io/database/2019/05/29/redis_pipelining-and-mass-insertions.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/database/2019/05/29/redis_pipelining-and-mass-insertions.html</guid>
        
        
        <category>Database</category>
        
      </item>
    
      <item>
        <title>[Redis Documentation #2] 리플리케이션 관련 파라미터 </title>
        <description>&lt;h1 id=&quot;리플리케이션-관련-파라미터&quot;&gt;리플리케이션 관련 파라미터&lt;/h1&gt;
&lt;h3 id=&quot;slaveof&quot;&gt;SLAVEOF&lt;/h3&gt;
&lt;blockquote&gt;
  &lt;p&gt;레디스 5부터 더이상 슬레이브라는 단어를 사용하지 않는다. 대신 REPLICAOF 명령어의 사용을 권장한다. SLAVEOF 명령어는 오직 역호환성을 위해 계속 작동할 것이다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;replicaof&quot;&gt;REPLICAOF&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;replica의 복제 설정을 즉시 변경&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REPLICAOF hostname port&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;해당 서버를 지정된 서버의 복제본으로 만듬.&lt;/li&gt;
      &lt;li&gt;해당 서버가 이미 다른 마스터의 복제본인 경우, 이전 서버에 대한 복제를 중지하고, 데이터셋을 삭제한 뒤 새 서버에 대한 동기화를 시작함.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REPLICAOF NO ONE&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;해당 서버가 이미 replica로 작동 중인 경우 복제가 해제되어 해당 서버가 마스터로 변환됨.&lt;/li&gt;
      &lt;li&gt;복제된 내용을 삭제하지는 않음. 따라서 다시 복제본으로 작동할 수 있도록 재구성할 수 있음.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;replica-serve-stale-data&quot;&gt;REPLICA-SERVE-STALE-DATA&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;슬레이브가 마스터와의 연결이 끊겼거나, 복제가 진행중인 상황에서 해당 파라미터에 의해 두 가지 방법으로 작동됨.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;YES&lt;/strong&gt; : 오래된 데이터거나, 데이터 셋이 비어있는 경우(첫번째 동기화 진행중) 에도 클라이언트 요청에 응답함.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;NO&lt;/strong&gt;: 아래 명령어 이외에는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SYNC with master in progress&lt;/code&gt; 오류로 응답.
    &lt;ul&gt;
      &lt;li&gt;INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST, HOST, LATENCY&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;replica-read-only&quot;&gt;REPLICA-READ-ONLY&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;기본 설정은 read-only &lt;strong&gt;YES&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;쓰기를 허용하도록 슬레이브를 구성할 수 있다. 복제본 인스턴스에 대해 쓰기를 허용하는 것은 사용 후에 삭제할 데이터를 저장하는 데 유용할 수 있지만 (마스터와 재동기화 후 삭제되기 때문에), 잘못된 구성의 적용으로 클라이언트가 write를 했을 때 문제를 발생할 가능성이 존재함.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NOTE: 읽기 전용 슬레이브는 인터넷에서 신뢰할 수 없는 클라이언트에 노출되도록 설계되지 않았다. 단지 인스턴스의 misuse에 대한 protection layer일 뿐이다. 여전히 읽기 전용 슬레이브는 기본적으로 CONFIG, DEBUG 등과 같은 모든 관리 명령을 내보낼 수 있다. 제한적으로 어드민 또는 위험한 명령어에 대해 `rename-command`를 사용해서 읽기 전용 슬레이브의 보안을 개선할 수 있다.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;repl-diskness_sync&quot;&gt;REPL-DISKNESS_SYNC&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;WARNING: 이 기능은 현재 EXPERIMENTAL한 기능임.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;부분 동기화를 진행할 수 없거나, 새로운 연결을 시도할 경우 full synchronization을 수행해야 함. RDB 파일은 마스터에서 복제본으로 전송됨.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;DISK-Backed&lt;/strong&gt;: 마스터는 디스크에 RDB 파일을 쓰는 새로운 프로세스 생성. 나중에 파일은 부모 프로세스에 의해 슬레이브로 증분 전달됨.
    &lt;ul&gt;
      &lt;li&gt;RDB 파일이 생성되는 동안, 자식 프로세스에 의해 더 많은 복제본들이 RDB 파일에 추가되어 함께 전송될 수 있음.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Diskless&lt;/strong&gt;: 마스터는 디스크를 사용하지 않고 RDB 파일을 복제 소켓에 직접 쓰는 새로운 프로세스 생성
    &lt;ul&gt;
      &lt;li&gt;전송이 시작되면 새로운 복제본이 대기열에 오르고, 해당 복제가 끝난 다음에 새로운 복제본이 전달됨.&lt;/li&gt;
      &lt;li&gt;마스터는 여러 복제본이 도착하고 전송을 병렬화 하게 진행하기 희망하기 때문에 몇 초(configurable amount of time)을 대기함.&lt;/li&gt;
      &lt;li&gt;느린 디스크와 빠른 네트워크인 경우, diskless 복제가 더 효과적&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;repl-diskless-sync-delay&quot;&gt;REPL-DISKLESS-SYNC-DELAY&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;소켓을 통해 복제본으로 RDB를 전송하는 자식 프로세스의 대기 시간을 구성할 수 있음.&lt;/li&gt;
  &lt;li&gt;전송이 시작되면, 다음 RDB 전송을 위해 대기할 새로운 복제본을 제공할 수 없으므로, 서버는 한번에 더 많은 복제본이 도착하도록 지연시간을 갖고 기다림.&lt;/li&gt;
  &lt;li&gt;지연은 초 단위로 지정되며, 기본값은 5초.&lt;/li&gt;
  &lt;li&gt;0초로 설정하여 대기시간 없이 바로 전송하게 설정 가능.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;repl-ping-replica-period&quot;&gt;REPL-PING-REPLICA-PERIOD&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;슬레이브에서 마스터로 ping을 전송하는 간격&lt;/li&gt;
  &lt;li&gt;기본값은 10초&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;repl-timeout&quot;&gt;REPL-TIMEOUT&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;마스터와 슬레이브간에 연결이 끊겼다고 인식하는 시간.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;마스터-관점&quot;&gt;마스터 관점&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;1초마다 replconf 명령을 슬레이브로 보내고, ack가 timeout 시간동안 오지 않으면 슬레이브에 대한 연결을 해제하고 정보를 지움
    &lt;ul&gt;
      &lt;li&gt;평상시에는 마지막 항목 lag이 0이거나 1&lt;/li&gt;
      &lt;li&gt;복제 서버로부터 ack가 오지 않으면 그 시간만큼 lag 숫자가 증가&lt;/li&gt;
      &lt;li&gt;lag이 timeout을 초과하면 마스터 서버는 복제서버 정보 삭제&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;슬레이브-관점&quot;&gt;슬레이브 관점&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;슬레이브는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;repl-ping-replica-period&lt;/code&gt; 간격으로 ping을 보내는데, timeout 시간동안 응답이 없으면 마스터와의 연결이 다운된 것으로 인식&lt;/li&gt;
  &lt;li&gt;info replication으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master_last_io_seconds_ago&lt;/code&gt; 확인
    &lt;ul&gt;
      &lt;li&gt;마스터로부터 응답을 받지 못한 시간&lt;/li&gt;
      &lt;li&gt;연결이 다운되었을 때는 -1&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;repl-disable-tcp-nodelay&quot;&gt;REPL-DISABLE-TCP-NODELAY&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190519/1.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;YES : NODELAY FALSE&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;데이터를 모아서 큰 패킷으로 전송&lt;/li&gt;
      &lt;li&gt;최대 40ms의 지연 발생 가능&lt;/li&gt;
      &lt;li&gt;지연시간이 짧고 트래픽이 많을 경우에 사용 권장&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;NO&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;기본 설정&lt;/li&gt;
      &lt;li&gt;yes일 경우보다 대역폭을 더 많이 사용&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;repl-backlog-size&quot;&gt;REPL-BACKLOG-SIZE&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;부분적 재동기화(partial resynchronization)에 사용되는 버퍼 사이즈 단위를 지정&lt;/li&gt;
  &lt;li&gt;기본값은 1mb
    &lt;ul&gt;
      &lt;li&gt;kb, mb, gb 단위 사용 가능&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;0으로 설정시 아래와 같은 에러 발생
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  *** FATAL CONFIG FILE ERROR ***
  Reading the configuration file, at line 392
  &amp;gt;&amp;gt;&amp;gt; 'repl-backlog-size 0'
  repl-backlog-size must be 1 or greater.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;해당 서버에 슬레이브가 연결되는 순간 해당 파라미터만큼 backlog-buffer 할당됨&lt;/li&gt;
  &lt;li&gt;백로그 크기가 클 수록 재동기화를 수행하는 시간이 길어짐
```
Resynchronization 방식&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;슬레이브와 연결이 끊어졌을 때 입력되는 데이터를 backlog-buffer에 저장했다가, 다시 연결되면 전체 데이터를 다시 보낼 필요 없이 해당 버퍼의 데이터를 슬레이브에 보내서 동기화한다. 이렇게 동작하는 것을 부분 동기화 (partial resynchronization)이라 한다. 입력되는 데이터가 이 버퍼 사이즈를 초과하면 전체 동기화(full synchronization)을 시도한다.
```&lt;/p&gt;

&lt;h3 id=&quot;repl-backlog-ttl&quot;&gt;REPL-BACKLOG-TTL&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;마스터에서 한동안 슬레이브가 연결되지 않을 때 백로그 버퍼를 해제하기 위해 경과해야 하는 시간&lt;/li&gt;
  &lt;li&gt;연결이 해제된 슬레이브가 부분적으로 재동기화 할 가능성이 있기 때문에 항상 백로그를 누적해야 하기 때문에 해당 시간이 초과하기 전까지는 백로그를 항상 유지함.&lt;/li&gt;
  &lt;li&gt;값이 0이면 백로그를 절대 해제하지 않음을 의미&lt;/li&gt;
  &lt;li&gt;기본 설정은 3600초&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;replica-priority&quot;&gt;REPLICA-PRIORITY&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;레디스 센티넬에서 마스터가 올바르게 작동하지 않을 때 마스터로 승격할 슬레이브을 선택하기 위해 사용.&lt;/li&gt;
  &lt;li&gt;기본값은 100, 숫자가 작을수록 우선순위가 높음&lt;/li&gt;
  &lt;li&gt;0은 마스터가 되지 않도록 하는 값
    &lt;ul&gt;
      &lt;li&gt;다른 슬레이브가 있을 경우에만 해당됨&lt;/li&gt;
      &lt;li&gt;다운된 마스터에 오직 하나의 슬레이브만 있다면 이 값이 0이어도 마스터로 승격됨&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;min-replicas-to-write&quot;&gt;MIN-REPLICAS-TO-WRITE&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;복제가 성공적으로 수행되어야 하는 최소한의 슬레이브 수&lt;/li&gt;
  &lt;li&gt;슬레이브가 설정한 수 보다 적으면 마스터는 쓰기 명령을 수행하지 못하고 에러 리턴&lt;/li&gt;
  &lt;li&gt;기본 값은 0 (기능 비활성화)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;min-replicas-max-lag&quot;&gt;MIN-REPLICAS-MAX-LAG&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;복제가 성공적으로 수행되어야 하는 시간을 설정&lt;/li&gt;
  &lt;li&gt;마스터가 슬레이브에에 1초마다 보내는 replconf의 ack로 확인.&lt;/li&gt;
  &lt;li&gt;설정한 시간동안 슬레이브로부터 ack가 없으면 해당 값이 줄어듬.&lt;/li&gt;
  &lt;li&gt;기본 값은 10&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;replica-announce-ip--replica-announce-port&quot;&gt;REPLICA-ANNOUNCE-IP / REPLICA-ANNOUNCE-PORT&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;해당 옵션을 사용해서 마스터는 연결된 슬레이브의 주소와 포트를 다른 방식으로 나열할 수 있음.&lt;/li&gt;
  &lt;li&gt;포트 포워딩 또는 NAT을 사용할 경우 슬레이브는 실제로 서로 다른 IP나 port를 통해 연결되는 경우 필요.&lt;/li&gt;
  &lt;li&gt;예를 들어 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;info replication&lt;/code&gt; 명령어 또는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;role&lt;/code&gt; 명령어로 해당 마스터에 연결된 슬레이브의 정보를 지정한 값으로 노출시킬 수 있음.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;참고&quot;&gt;참고&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;https://redis.io/commands&lt;/li&gt;
  &lt;li&gt;redis.conf 파일 (5.0.3 버전)&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 19 May 2019 21:05:00 +0000</pubDate>
        <link>http://garimoo.github.io/database/2019/05/19/redis_replication_parameter.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/database/2019/05/19/redis_replication_parameter.html</guid>
        
        
        <category>Database</category>
        
      </item>
    
      <item>
        <title>[Redis Documentation #1] 리플리케이션 </title>
        <description>&lt;h1 id=&quot;복제란&quot;&gt;복제란&lt;/h1&gt;
&lt;blockquote&gt;
  &lt;p&gt;원문: https://redis.io/topics/replication&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;레디스 리플리케이션 (레디스 클러스터나 레디스 센티넬처럼 추가 레이어로 제공되는 HA기능은 제외) 은 &lt;em&gt;Leader Follower&lt;/em&gt; (마스터-슬레이브) 복제를 사용하고 구성하는 것이 매우 간단하다. 복제 인스턴스는 마스터 인스턴스의 정확한 복사본이 될 수 있다. 복제본은 링크가 끊어질 때마다 매번 자동으로 마스터에 다시 연결되며, 마스터에게 무슨 일이 일어나든 상관 없이 정확한 복사본이 되려 한다. 이 시스템은 세 가지의 주요 메커니즘을 사용하여 작동한다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;마스터와 복제본이 잘 연결되어 있는 경우에, 마스터 측에서 발생하는 데이터 입력, 키의 만료와 제거 등 마스터 데이터셋의 변경작업들을 복제하기 위해 명령어 스트림을 전달하여 복제본을 업데이트시킨다.&lt;/li&gt;
  &lt;li&gt;마스터와 복제본 사이의 링크가 끊어졌을 때, 네트워크 이슈나 타임아웃이 마스터나 복제본 측에서 감지되기 때문에, 복제본은 다시 연결하여 부분적인 재동기화를 진행하려고 시도한다. 이는 연결이 끊겼을 때 놓쳤던 명령어 스트림의 일부만을 얻으려고 시도하는 것을 의미한다. (partial resync)&lt;/li&gt;
  &lt;li&gt;부분적인 재동기화가 불가능 할 때, 복제본은 완전한 재동기화를 시도한다. 마스터가 모든 데이터의 스냅샷을 생성해서, 복제본에 전송한 다음, 데이터셋이 변경될 때 명령어 스트림을 계속 전송해야 하는 복잡한 프로세스로 진행된다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;레디스는 기본적으로 비동기식 복제를 사용하는데, 이는 짧은 지연시간과 높은 성능을 가진다. 마스터는 복제본에서 명령어가 처리되는 것을 기다리지 않는다. 하지만 필요한 경우, 복제본에서 어떤 명령어가 수행됐는지 알 수 있다. 이를 통해 선택적으로 동기식 복제를 수행할 수 있다.&lt;/p&gt;

&lt;p&gt;특정 데이터의 동기식 복제는 &lt;a href=&quot;https://redis.io/commands/wait&quot;&gt;WAIT&lt;/a&gt; 명령어를 사용해서 클라이언트가 요청할 수 있다. 하지만 &lt;a href=&quot;https://redis.io/commands/wait&quot;&gt;WAIT&lt;/a&gt;은 다른 레디스 인스턴스에 지정된 수의 복사본이 있는지 확인할 수 있을 뿐, 레디스 인스턴스를 강한 일관성을 가진 복제 시스템으로 변환하지는 않는다. 레디스 쓰기는 여전히 페일오버 중에 손실될 수 있다. 하지만 &lt;a href=&quot;https://redis.io/commands/wait&quot;&gt;WAIT&lt;/a&gt;을 사용하면 실패 이벤트 이후에 쓰기모드를 잃을 가능성이 크게 저하되어 특정 장애 모드를 트리거하기 어렵다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; WAIT &amp;lt;numreplicas&amp;gt; &amp;lt;timeout&amp;gt;
이 명령은 모든 이전 쓰기 명령이 최소한 지정된 복제본 수만큼 성공적으로 전송되고 확인 응답 될 때까지 현재 클라이언트를 차단한다. 지정한 시간 초과에 도달하면 지정한 수의 복제본에 아직 도달하지 않은 경우에도 명령이 리턴된다.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;레디스 복제의 특징은 다음과 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;레디스는 비동기식 복제를 사용하며, 비동기식 슬레이브-마스터 는 처리되는 데이터의 양을 인식한다.&lt;/li&gt;
  &lt;li&gt;마스터는 다수의 복제본을 가질 수 있다.&lt;/li&gt;
  &lt;li&gt;복제본은 다른 복제본에서의 연결을 가질 수 있다. 같은 마스터를 가지고 있는 구조 이외에도, 계단식 구조로 다른 복제본과 수직 연결될 수 있다. 레디스 4.0 이후 모든 하위 복제본(sub-slave)은 마스터로부터 정확히 동일한 복제 스트림을 수신하게 된다.&lt;/li&gt;
  &lt;li&gt;레디스 복제는 마스터 측에서 차단되지 않는다. 이는 하나 이상의 복제본이 동기화 혹은 부분 비동기화를 수행할 때 마스터는 쿼리를 계속해서 처리할 수 있음을 의미한다.&lt;/li&gt;
  &lt;li&gt;복제는 복제본에서도 대부분 차단되지 않는다. 복제본에서 초기 동기화를 수행하는 동안은 이전 버전의 데이터셋을 사용해서 쿼리를 수행할 수 있다. 그렇지 않으면 복제 스트림이 중단된 경우 클라이언트에 오류를 반환하도록 레디스 복제본을 구성할 수 있다.(redis.conf의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REPLICA-SERVE-STALE-DATA&lt;/code&gt; 파라미터 설정) 하지만 최초 동기화 이후에는 &lt;strong&gt;이전 데이터셋을 삭제하는 작업&lt;/strong&gt; 이 선행된 후 새 데이터셋을 로드한다. 이 짧은 기간 동안은 들어오는 연결이 차단된다 (매우 큰 데이터셋의 경우 몇 초까지 걸릴 수 있음). 레디스 4.0부터는 이전 데이터셋의 삭제가 다른 스레드에서 발생하도록 레디스를 구성할 수 있지만, 초기 데이터셋을 로드하는 건 여전히 메인 스레드에서 발생해서 복제본을 차단할 수 있다.&lt;/li&gt;
  &lt;li&gt;복제는 read-only 쿼리 (예를 들어, 느린 O(N) 작업은 복제본에서 수행되도록 할 수 있음) 에 대한 다중 복제본을 가지거나, 단순히 데이터 안전 및 HA를 개선하기 위해 모두 사용할 수 있다.&lt;/li&gt;
  &lt;li&gt;마스터가 디스크에 전체 데이터셋을 쓰도록 하는 비용을 피하기 위해 복제를 사용할 수 있다. 일반적으로는 마스터의 redis.conf를 구성해서 디스크에 지속되지 않도록 한 다음, 때때로 디스크에 저장하도록 구성된 복제본을 연결하거나 AOF를 사용하도록 설정하는 것이 포함된다. 하지만 다시 시작하는 마스터는 빈 데이터셋으로 시작하기 때문에 이 설정은 주의해서 사용되어야 한다. 복제본이 동기화를 시도할 때, 복제본도 비워지게 된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;마스터-자동-시작과-persistence-기능-사용&quot;&gt;마스터 자동 시작과 Persistence 기능 사용&lt;/h2&gt;

&lt;p&gt;레디스 복제가 사용되는 설정에서는 마스터와 복제본에서 persistence 기능을 켜는 것이 좋다. 예를 들어 디스크 속도가 느려 레이턴시가 발생해서 이 작업이 불가능 한 경우, &lt;strong&gt;재부팅 후 자동으로 다시 시작되지 않도록&lt;/strong&gt; 인스턴스를 구성해야 한다.&lt;/p&gt;

&lt;p&gt;persistence 기능이 꺼진 마스터가 자동 재시작되도록 구성된 상태가 위험한 이유를 이해하기 위해서, 마스터와 모든 복제본에서 데이터가 지워지는 다음의 장애 상황을 확인해보자.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;노드 A가 마스터이고 지속성이 꺼진 상태에서, 노드 B와 C가 A에서 복제되는 환경이다.&lt;/li&gt;
  &lt;li&gt;노드 A에서 크래시가 발생하지만,  프로세스를 자동 재시작하는 시스템이 존재하여 리부팅 된다. 하지만 지속성이 꺼져 있어, 노드는 빈 데이터 셋으로 재시작된다.&lt;/li&gt;
  &lt;li&gt;노드 B와 C는 비어 있는 노드 A를 복제하므로, 데이터의 사본을 파괴할 것이다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;레디스 센티넬을 HA를 위해 사용할 경우, 프로세스의 자동 재시작을 사용하는것과 마찬가지로 마스터에서 persistence 기능을 끄는 것도 위험하다. 마스터는 센티넬이 장애를 감지하지 못할 정도로 빠르게 재시작하여, 위에서 설명한 장애 상황이 발생할 수 있다.&lt;/p&gt;

&lt;p&gt;모든 상황에서 데이터의 안정성이 중요하기 때문에, persistence 기능이 꺼진 마스터에서 복제를 사용할 때마다 인스턴스의 자동 재시작을 비활성해야 한다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Persistence (지속성)

레디스는 메모리 기반 DB 이기 때문에 전원이 꺼지면 데이타가 모두 날라가게 된다. 이에 파일에 메모리상의 데이타를 저장해두고 redis 서버 실행시 다시 그 파일에서 데이타를 읽어와 메모리상에 올리는 기능을 제공하는데 이를 Persistence (지속성) 라고 한다. 레디스는 RDB 와 AOF 의 두가지 지속성을 제공하고, 두 가지를 함께 사용 가능하다.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;레디스의-복제-작동-방식&quot;&gt;레디스의 복제 작동 방식&lt;/h2&gt;

&lt;p&gt;모든 레디스 마스터는 복제 ID (replication ID)를 가지고 있다. 이는 데이터셋의 특정 스토리를 나타내는 pseudo 랜덤 스트링이다. 각각의 마스터는 데이터셋을 수정하는 새로운 변경사항으로 복제본 상태를 업데이트하기 위해, 생성되는 복제 스트림의 모든 바이트에 대해 증가하는 오프셋을 갖는다. 실제로 연결된 복제본이 없는 경우에도 복제 오프셋이 증가한다.&lt;/p&gt;

&lt;p&gt;| Replication ID, offset |
| — |&lt;/p&gt;

&lt;p&gt;이 정보로 마스터 데이터셋의 정확한 버전을 식별할 수 있다.&lt;/p&gt;

&lt;p&gt;복제본이 마스터에 연결되면 그것들은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSYNC &lt;/code&gt; 명령어를 사용해서 그들의 예전 복제 ID와 진행됐던 오프셋을 전달한다. 이렇게 하면 마스터는 필요한 증분 파트만 보낼 수 있다. 그러나 마스터 버퍼에 충분한 백로그(backlog)가 존재하지 않거나, 복제본이 더이상 알 수 없는 기록(복제 ID)를 참조하는 경우엔 전체 재동기화가 발생한다. 이 경우, 복제본은 처음부터 데이터 셋의 전체 사본을 받게 된다.&lt;/p&gt;

&lt;p&gt;마스터는 RDB 파일을 생성하기 위해 백그라운드에 저장 프로세스를 실행한다. 동시에 클라이언트로부터 받은 모든 새로운 쓰기 명령을 버퍼링한다. 백그라운드 저장이 완료되면 마스터는 데이터베이스 파일을 복제본으로 전송하여 디스크에 저장한 다음 메모리에 로드한다. 그 다음 마스터는 버퍼링 된 모든 명령을 복제본에게 보낸다. 이것은 명령어 스트림으로 동작하며, 레디스 프로토콜 자체의 형식과 비슷하다.&lt;/p&gt;

&lt;p&gt;텔넷을 통해 직접 시도해 볼 수 있다. 서버가 일부 작업을 수행하는 동안 레디스 포트에 연결하고 &lt;a href=&quot;https://redis.io/commands/sync&quot;&gt;SYNC&lt;/a&gt; 명령을 실행해 보자. 대량 전송이 나타나고, 마스터가 수신한 모든 명령어는 텔넷 세션에서 재발행(re-issued) 되는 것을 볼 수 있다. 실제로 &lt;a href=&quot;https://redis.io/commands/sync&quot;&gt;SYNC&lt;/a&gt;  는 더이상 새로운 레디스 인스턴스에서 사용되지 않는 오래된 프로토콜이지만, 여전히 역호환성을 위해 존재한다. 이는 부분 재동기화를 허용하지 않기 때문에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSYNC&lt;/code&gt;가 대신 사용된다.&lt;/p&gt;

&lt;p&gt;이미 언급했듯이, 복제본은 마스터-슬레이브 링크가 어떤 이유에서든 다운되면 자동으로 다시 연결할 수 있다. 마스터가 여러 개의 동시 복제본 동기화 요청을 수신하는 경우 모든 요청을 처리하기 위해 단일 백그라운드 저장을 수행한다.&lt;/p&gt;

&lt;h2 id=&quot;복제-id&quot;&gt;복제 ID&lt;/h2&gt;

&lt;p&gt;앞서 두 인스턴스가 동일한 복제 ID와 오프셋을 가지고 있다면, 그것은 정확히 동일한 데이터를 가지고 있다고 했다. 그러나 복제ID란 무엇이며, 인스턴스에 사실 두 개의 복제 ID (메인 ID와 보조(secondary) ID)가 존재하는 이유에 대해 이해하는 것이 유용하다.&lt;/p&gt;

&lt;p&gt;복제ID는 기본적으로 데이터셋의 &lt;em&gt;히스토리&lt;/em&gt; 를 기록한다. 인스턴스가 마스터로 처음부터 재시작되거나, 복제본이 마스터로 승격될 때마다 이 인스턴스에 대한 새 복제 ID가 생성된다. 마스터에 연결된 복제본은 핸드셰이크 과정 이후 복제 ID를 상속받는다. 동일한 ID를 가진 두 가지 인스턴스는 동일한 데이터를 가지고 있지만, 잠재적으로 다른 시간에 존재한다는 사실에 의해 관련되어 있다. 이는 가장 최근에 업데이트된 데이터셋을 보유하고 있는 히스토리 (복제 ID)에 대해 이해할 수 있는 논리적 시간으로 작동하는 오프셋이다.&lt;/p&gt;

&lt;p&gt;예를 들어, 두 인스턴스 A와 B가 동일한 복제 ID를 가지고 있지만, 하나는 오프셋 1000이고, 다른 하나는 오프셋 1023인 경우, 첫 번째 인스턴스에는 데이터셋에 적용된 특정 커맨드가 없다는 것을 의미한다. 또한 A가 단지 몇 개의 커맨드를 적용함으로써 B의 상태에 정확하게 도달할 수 있는 것을 의미한다.&lt;/p&gt;

&lt;p&gt;레디스 인스턴스가 두 개의 복제 ID를 갖는 이유는 마스터로 승격되는 복제본 때문이다. 페일오버 이후 마스터로 승격된 복제본은 복제 ID가 이전 마스터의 것이기 때문에 이전 복제 ID를 계속 기억해야 한다. 이와 같이 다른 복제본들이 새로운 마스터와 동기화 할 때, 그들은 이전의 마스터 복제 ID를 사용해서 부분적인 재동기화를 수행하려고 할 것이다. 왜냐하면, 복제본이 마스터로 승격될 때 당시의 오프셋을 기억하면서 보조 ID를 이전의 주 복제 ID로 설정하기 때문이다. 페일오버 이후 new history가 시작되기 때문에 주 복제 ID는 새로운 랜덤 값을 갖게 된다. 연결 중인 새 복제본을 처리할 때 마스터는 ID와 오프셋을 현재 ID와 보조 ID(안전을 위해 지정된 오프셋까지) 와 일치시킨다. &lt;strong&gt;간단히 말해서, 페일오버 이후, 새로운 마스터에 연결된 복제본은 완전한 동기화를 수행할 필요가 없다는 것을 의미한다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;페일오버 후 마스터로 승격된 복제본이 복제 ID를 변경해야 하는 이유는 일부 네트워크의 파티션으로 인해 이전 버전의 마스터가 여전히 마스터로 작업하고 있을 수 있기 때문이다.동일한 복제 ID를 유지하면 두 인스턴스의 동일한 오프셋이 동일한 데이터 셋을 갖는다는 사실을 위반할 수 있다.&lt;/p&gt;

&lt;h2 id=&quot;디스크를-사용하지-않는-동기화-diskless-replication&quot;&gt;디스크를 사용하지 않는 동기화 (Diskless Replication)&lt;/h2&gt;

&lt;p&gt;일반적으로 완전 재동기화(full resynchronization)는 디스크에 RDB 파일을 만든 다음, 복제본에게 데이터를 공급하기 위해 디스크에서 동일한 RDB를 다시 로드해야 한다.&lt;/p&gt;

&lt;p&gt;느린 디스크의 경우 마스터에게는 이 프로세스가 매우 답답한 작업이 될 수 있다. 레디스 버전 2.8.18은 디스크 없는 복제를 지원하는 첫 번째 버전이다. 이 설정에서 하위 프로세스는 디스크를 중간 저장소로 사용하지 않고 RDB를 직접 복제본으로 전송한다.&lt;/p&gt;

&lt;h2 id=&quot;복제-구성&quot;&gt;복제 구성&lt;/h2&gt;

&lt;p&gt;기본적인 레디스 복제를 구성하는 것은 간단하다. 아래 내용을 복제본의 redis.conf 파일에 추가하면 된다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slaveof 192.168.1.1 6379
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;물론 192.168.1.1 6379를 마스터 IP주소 (또는 호스트 이름)와 포트로 교체해야 한다. 또는 &lt;a href=&quot;https://redis.io/commands/slaveof&quot;&gt;SLAVEOF&lt;/a&gt; 명령을 호출하여 마스터 호스트가 복제본과 동기화를 시작할 수 있다.&lt;/p&gt;

&lt;p&gt;마스터가 부분 재동기화를 수행하기 위해 사용하는 복제 백로그를 조정하기 위한 파라미터도 몇 가지 존재한다. diskless 복제는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;repl-diskless-sync&lt;/code&gt; 매개 변수를 사용하여 실행할 수 있다. 첫 번재 복제본 이후에 더 많은 복제본이 도착할 때까지 대기하기 위해 전송을 시작하는 지연은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;repl-diskless-sync-delay&lt;/code&gt; 매개 변수에 의해 제어된다. 자세한 내용은 Redis 배포와 함께 제공된 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis.conf&lt;/code&gt; 의 내용을 참고해라.&lt;/p&gt;

&lt;h2 id=&quot;읽기-전용-복제본&quot;&gt;읽기 전용 복제본&lt;/h2&gt;

&lt;p&gt;레디스 2.6 버전 이후 복제본은 기본적으로 읽기 전용 모드를 사용한다. 이 동작은 &lt;strong&gt;redis.conf&lt;/strong&gt; 파일의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slave-read-only&lt;/code&gt; 옵션에 의해 제어되며, &lt;a href=&quot;https://redis.io/commands/config-set&quot;&gt;CONFIG SET&lt;/a&gt; 커맨드를 사용하여 런타임 도중에 활성화 및 비활성화를 제어할 수 있다.&lt;/p&gt;

&lt;p&gt;읽기 전용 복제본은 모든 쓰기 명령을 거부하므로 실수로 복제본에 쓰는 것이 불가능하다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DEBUG&lt;/code&gt; 또는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONFIG&lt;/code&gt; 와 같은 어드민 명령어들이 여전히 사용 가능하기 때문에, 이 기능이 해당 노드를 신뢰할 수 없는 클라이언트가 존재하는 네트워크에 노출하기 위한 것이라고는 할 수 없다. 하지만 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read-command&lt;/code&gt; 명령을 사용하여 &lt;strong&gt;redis.conf&lt;/strong&gt; 에서 명령을 사용하지 않도록 설정하면 읽기 전용 인스턴스의 보안을 개선할 수 있다.&lt;/p&gt;

&lt;p&gt;읽기 전용 기능을 회수하고, 쓰기가 가능한 복제본 인스턴스를 갖는 것이 왜 허용되는지 궁금할 수 있을 것이다. 쓰기 가능한 복제본에서는 로컬 키에 대한 접근이 필요한 오래 걸리는 연산(sorted set 등)을 수행하는 테스트 작업이 가능하다. 복제본과 마스터가 다시 동기화되거나 복제본이 다시 시작되면 이런 테스트 내용이 삭제되기 때문에 편리하다.&lt;/p&gt;

&lt;p&gt;그러나 버전 4.0 이전의 &lt;strong&gt;쓰기 가능 복제본은 만료 시간이 설정된 키(EXPIRE 명령어 등)를 만료시킬 수 없었다는 점에 유의해야 한다.&lt;/strong&gt; &lt;a href=&quot;https://redis.io/commands/expire&quot;&gt;EXPIRE&lt;/a&gt; 또는 최대 TTL 시간을 포함한 명령어를 사용했을 때 그 키는 누출되고, 읽기 명령으로 액세스하는 동안은 키가 보이지 않을 수 있지만, 전체 키 개수에 해당 키가 표시되고 키에 할당된 메모리는 계속 사용되었다. 따라서 일반적으로 쓰기 가능한 복제본과 TTL 키를 혼합하면 문제가 발생할 수 있었다.&lt;/p&gt;

&lt;p&gt;레디스 4.0 이상 버전은 이 문제를 해결했고, 이제 쓰기 가능한 복제본은 마스터처럼 TTL 키를 제거할 수 있다.&lt;/p&gt;

&lt;p&gt;또한 레디스 4.0부터 복제본에 쓰는 내용은 오직 로컬에서만 유지되며, 해당 인스턴스에 연결된 서브 복제본으로 전파되지 않는다. 서브 복제본은 항상 최상위 마스터가 중간 복제본으로 보낸 것과 동일한 복제 스트림을 전달받는다.&lt;/p&gt;

&lt;p&gt;아래와 같이 복제 노드가 세팅되어 있다 하자.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;A ---&amp;gt; B ---&amp;gt; C
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;만약 B가 쓰기 가능일 때, C는 B가 작성한 것을 볼 수 없다. 대신에 마스터 노드인 A와 동일한 데이터셋을 갖는다.&lt;/p&gt;

&lt;h2 id=&quot;마스터의-인증이-필요한-복제본&quot;&gt;마스터의 인증이 필요한 복제본&lt;/h2&gt;

&lt;p&gt;만약에 마스터가 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirepass&lt;/code&gt; 파라미터를 이용해서 인증 패스워드를 가지고 있다면, 모든 동기화 작업에서 해당 암호를 사용하도록 복제본을 구성하는 것은 간단하다. 실행중인 인스턴스에서 수행하려면 redis-cli에서 다음을 입력해라.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;config set masterauth &amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;영구하게 지속하고 싶을 경우에는, config 파일에 아래 설정을 추가해라.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;masterauth &amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;n개-이상의-복제본이-연결된-경우에서만-쓰기-허용&quot;&gt;N개 이상의 복제본이 연결된 경우에서만 쓰기 허용&lt;/h2&gt;

&lt;p&gt;레디스 2.8버전부터는 최소한 N개의 복제본이 현재 마스터에 연결되어 있는 경우에만 쓰기를 허용하도록 레디스 마스터를 구성할 수 있다. 그러나 레디스는 비동기 복제를 사용하기 때문에 복제본이 실제로 명령어 스트림을 받았는지 확인할 수 없기 때문에 항상 데이터 손실의 가능성이 있다.&lt;/p&gt;

&lt;p&gt;다음은 이 기능의 작동 방식이다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;레디스 복제본은 매 초마다 ping을 소행하여 복제 스트림의 처리량을 확인한다.&lt;/li&gt;
  &lt;li&gt;레디스 마스터는 모든 복제본으로부터 ping을 받은 마지막 시간을 기억한다.&lt;/li&gt;
  &lt;li&gt;사용자는 최대 지연시간을 초과하지 않는 복제본의 최소 수를 구성할 수 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;지연이 M초 미만인 N개의 복제본이 있어야 쓰기가 허용된다.&lt;/p&gt;

&lt;p&gt;이것이 데이터 안전 매커니즘을 위한 최선의 노력이라고 생각할 수 있겠지만, 여기서 데이터의 비손실은 보장되지 않지만, 적어도 데이터 손실의 시간대는 주어진 시간으로 제한된다. In general bound data loss is better than unbound one.&lt;/p&gt;

&lt;p&gt;조건이 충족되지 않으면 마스터는 대신 오류로 응답하고, 쓰기는 허용되지 않는다. 이 기능에는 두 가지 관련 파라미터가 존재한다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;min-slaves-to-write &amp;lt;number of slaves&amp;gt;
min-slaves-max-lag &amp;lt;number of seconds&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;레디스-복제에서-키를-만료하는-방법&quot;&gt;레디스 복제에서 키를 만료하는 방법&lt;/h2&gt;

&lt;p&gt;레디스의 키는 제한된 시간 이후에 만료될 수 있다. 이러한 기능은 시간을 계산하는 인스턴스의 능력에 따라 달라지지만, 레디스 복제본은 키가 루아 스크립트를 사용하여 변경되는 경우에도 만료된 키를 올바르게 복제한다.&lt;/p&gt;

&lt;p&gt;이러한 기능을 구현하려면 레디스는 마스터와 복제본에서 클럭을 동기화 하는 기능에 의존할 수 없다. 이것은 해결할 수 없는 문제이며, 경쟁 조건 (race condition) 과 데이터 셋의 분리를 초래하므로 레디스는 만료된 키의 복제가 작동하도록 하기 위해 세 가지 주요 기법을 사용한다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;복제본에서는 키가 만료되지 않고, 대신 마스터가 키를 만료시키기를 기다린다. 마스터가 키를 만료시킬 때 (또는 LRU 에 의해 제거될 때) 모든 복제본에게 &lt;a href=&quot;https://redis.io/commands/del&quot;&gt;DEL&lt;/a&gt; 명령어를 합성(synthesize) 한다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;그러나 때때로 마스터가 제시간에 &lt;a href=&quot;https://redis.io/commands/del&quot;&gt;DEL&lt;/a&gt; 명령을 제공할 수 없는 경우에, 복제본에는 이미 논리적으로 만료된 키가 메모리 내에 있을 수 있다.  In order to deal with that the slave uses its logical clock in order to report that a key does not exist only for read operations that don’t violate the consistency of the data set (as new commands from the master will arrive). 이런 식으로 복제본은 논리적으로 만료된 키를 보고하는 것을 피할 수 있다. 실용적인 면에서, 복제본을 이용해서 규모를 조정하는 HTML 파편 캐시는 원하는 시간보다 이미 오래된 아이템을 반환하는 것을 피할 것이다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;루아 스크립트 실행 중 키 만료는 수행되지 않는다. 루아 스크립트가 실행될 때, 개념적으로 마스터의 시간이 동결되어 스크립트가 실행되는 동안 주어진 키가 존재하거나 존재하지 않게 된다. 스크립트 중간에 키가 만료되는 것을 방지하고, 데이터 셋에 동일한 효과가 보장되는 방식으로 동일한 스크립트를 복제본에 전송하기 위해 필요하다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;일단 복제본이 마스터로 승격되면 독립적으로 키를 만료시키기 시작할 것이며, 이전의 마스터로부터 어떠한 도움도 요구하지 않을 것이다.&lt;/p&gt;

&lt;h2 id=&quot;도커-및-nat에서의-복제-구성&quot;&gt;도커 및 NAT에서의 복제 구성&lt;/h2&gt;

&lt;p&gt;도커, 혹은 포트 포워딩이나 NAT(Network Address Translation)을 사용하는 다른 유형의 컨테이너를 사용하는 경우, 특히 마스터 &lt;a href=&quot;https://redis.io/commands/info&quot;&gt;INFO&lt;/a&gt; 또는 &lt;a href=&quot;https://redis.io/commands/role&quot;&gt;ROLE&lt;/a&gt; 명령 출력이 검색되는 레디스 센티넬 또는 다른 시스템을 사용하는 경우, 약간의 주의가 필요하다.&lt;/p&gt;

&lt;p&gt;문제는 &lt;a href=&quot;https://redis.io/commands/role&quot;&gt;ROLE&lt;/a&gt; 명령과 복제 섹션에 대한 &lt;a href=&quot;https://redis.io/commands/info&quot;&gt;INFO&lt;/a&gt; 출력에서 마스터에 연결된 복제본의 IP주소를 보여주는데, 이는 NAT를 사용하는 환경에서 복제본 인스턴스의 논리적 주소(클라이언트가 연결하기 위해 사용해야 하는 주소)와 다를 수 있다는 것이다.&lt;/p&gt;

&lt;p&gt;마찬가지로 포트가 재생성되는 경우, 포워딩 포트와 다를 수 있는 &lt;strong&gt;redis.conf&lt;/strong&gt; 에서 구성된 수신 포트로 복제본이 나열될 것이다. 두 가지 문제를 모두 해결하기 위해, 레디스 3.2.2 이후, 아래 내용처럼 복제본이 마스터에게 임의의 IP 와 포트 쌍을 알리도록 강제하는 것이 가능하다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slave-announce-ip 5.5.5.5
slave-announce-port 1234
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;info-role-명령어&quot;&gt;INFO, ROLE 명령어&lt;/h2&gt;

&lt;p&gt;마스터와 복제본 인스턴스의 현재 복제 매개 변수에 대한 많은 정보를 제공하는 레디스 명령어는 두가지가 있다. 복제 인수를  &lt;a href=&quot;https://redis.io/commands/info&quot;&gt;INFO&lt;/a&gt; 명령어와 함께 호출하면 복제와 관련된 정보만 표시된다. &lt;a href=&quot;https://redis.io/commands/role&quot;&gt;ROLE&lt;/a&gt; 명령어는 마스터와 복제본의 복제 상태를 복제 오프셋, 연결된 복제본 목록등과 함께 제공한다.&lt;/p&gt;

&lt;h2 id=&quot;재시작-및-페일오버-이후-부분-재동기화&quot;&gt;재시작 및 페일오버 이후 부분 재동기화&lt;/h2&gt;
&lt;p&gt;레디스 4.0 이후 장애 조치 이후에 인스턴스가 마스터로 승격될 때, 여전히 이전 마스터의 복제본들과 부분적인 재동기화를 수행할 수 있을 것이다. 이를 위해 복제본은 이전의 복지 IP와 이전 마스터의 오프셋을 기억하기 때문에, 이전 복제 IP를 요청하더라도 백로그의 일부를 연결된 복제본에게 제공할 수 있다.&lt;/p&gt;

&lt;p&gt;하지만 승격된 복제본의 새 복제 ID는 데이터셋의 다른 기록을 구성하기 때문에 다를 것이다. 예를 들어 마스터는 사용 가능한 상태로 반환할 수 있으며 한동안 쓰기를 계속 허용할 수 있으므로 승격된 복제본에서 동일한 복제 ID를 사용하면 복제 ID와 오프셋 쌍이 단일 데이터셋만 식별한다는 규칙을 위반할 수 있다.&lt;/p&gt;

&lt;p&gt;게다가, 정상적으로 전원을 종료했다가 다시 시작할 때, 복제본은 마스터와 다시 동기화하기 위해 필요한 정보를 RDB파일에 저장할 수 있다. 이것은 업그레이드의 경우에 유용하다. 이 작업이 필요할 때는 복제본에 대해 저장 및 종료 작업을 수행하기 위해 SHUTDOWN 명령을 사용하는 것이 좋다.&lt;/p&gt;

&lt;p&gt;AOF 파일을 통해 재시작된 복제본을 부분적으로 다시 동기화하는 것은 올바르지 않다. 하지만 인스턴스 종료 전 RDB persistence 옵션을 켠 후, 재시작을 하고, 마지막으로 AOF를 다시 활성화 할 수 있다.&lt;/p&gt;
</description>
        <pubDate>Sun, 19 May 2019 21:02:00 +0000</pubDate>
        <link>http://garimoo.github.io/database/2019/05/19/redis_replication.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/database/2019/05/19/redis_replication.html</guid>
        
        
        <category>Database</category>
        
      </item>
    
      <item>
        <title>[Redis] 센티널 구성과 파이썬 패키지를 이용한 클라이언트 테스트</title>
        <description>&lt;h2 id=&quot;question&quot;&gt;Question&lt;/h2&gt;
&lt;h3 id=&quot;센티널을-이용한-레디스-환경에서-application은-어떻게-masterdb를-접속-하는가-ex-ap---센티널---레디스-형태로의-통신&quot;&gt;센티널을 이용한 레디스 환경에서 Application은 어떻게 MasterDB를 접속 하는가? ex) AP - 센티널 - 레디스 형태로의 통신?&lt;/h3&gt;

&lt;h3 id=&quot;이-상태에서-master-서버의-nw문제로-통신이-불가할-경우-ap는-어떻게-master를-찾아-연결-하는가&quot;&gt;이 상태에서 Master 서버의 NW문제로 통신이 불가할 경우, AP는 어떻게 Master를 찾아 연결 하는가?&lt;/h3&gt;

&lt;h1 id=&quot;레디스-구성&quot;&gt;레디스 구성&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190619/1.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;sentinel-info&quot;&gt;Sentinel info&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=10.162.29.33:6371,slaves=2,sentinels=3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;테스트1-마스터-프로세스-킬&quot;&gt;테스트1: 마스터 프로세스 킬&lt;/h1&gt;
&lt;h2 id=&quot;마스터-킬-후-슬레이브-상황&quot;&gt;마스터 킬 후 슬레이브 상황&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;슬레이브1: 마스터로 승격
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.162.29.36:6371&amp;gt; info replication
# Replication
role:master
connected_slaves:1
slave0:ip=10.162.29.37,port=6371,state=online,offset=89041,lag=0
master_replid:fe476f74f3f980067686f0e53a1431b2259fb8bd
master_replid2:87869ad90965d8d2c319e96d3b6885705fe47038
master_repl_offset:89041
second_repl_offset:87337
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1821
repl_backlog_histlen:87221
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;슬레이브 2
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.162.29.37:6371&amp;gt; info replication
# Replication
role:slave
master_host:10.162.29.33
master_port:6371
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
.
.
.
10.162.29.37:6371&amp;gt; info replication
# Replication
role:slave
master_host:10.162.29.36
master_port:6371
master_link_status:up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;페일오버-된-상태에서-새로운-데이터-입력&quot;&gt;페일오버 된 상태에서 새로운 데이터 입력&lt;/h2&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.162.29.36:6371&amp;gt; set newkey newdata
OK
10.162.29.36:6371&amp;gt; set newkey2 newdata2
OK
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;죽은-마스터가-다시-올라오면&quot;&gt;죽은 마스터가 다시 올라오면?&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;기존 마스터가 새로운 마스터의 슬레이브로 연결
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.162.29.33:6371&amp;gt; info replication
# Replication
role:slave
master_host:10.162.29.36
master_port:6371
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;h2 id=&quot;새로-입력했던-데이터-조회&quot;&gt;새로 입력했던 데이터 조회&lt;/h2&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.162.29.33:6371&amp;gt; get newkey
&quot;newdata&quot;
10.162.29.33:6371&amp;gt; get newkey2
&quot;newdata2&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;테스트-2-클라이언트-테스트&quot;&gt;테스트 2: 클라이언트 테스트&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190619/2.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;테스트-시나리오&quot;&gt;테스트 시나리오&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;마스터에 붙어있는 클라이언트&lt;/li&gt;
  &lt;li&gt;마스터 Down(redis.io 문서에서는 partition 이라고 표현, 즉 shutdown, power down, network 단절 등)&lt;/li&gt;
  &lt;li&gt;연결이 끊긴 상태에서 클라이언트가 인식하는 마스터 확인&lt;/li&gt;
  &lt;li&gt;연결이 끊긴 상태에서 (구) 마스터에게 클라이언트가 데이터 입력&lt;/li&gt;
  &lt;li&gt;재연결된 상태에서 클라이언트가 입력한 데이터 확인&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;클라이언트-연결&quot;&gt;클라이언트 연결&lt;/h2&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;redis&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RedisError&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;redis.sentinel&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SentinelConnectionPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ConnectionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MasterNotFoundError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SlaveNotFoundError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;listSentinel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'10.162.29.33'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;27001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'10.162.29.36'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;27002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'10.162.29.37'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;27003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;strServiceName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'mymaster'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;nMaxUser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;socket_timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# No need for this
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;#sentinel.discover_master(strServiceName)
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;#sentinel.discover_slaves(strServiceName)
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;master&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;master_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strServiceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;socket_timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;slave&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentinel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slave_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strServiceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;socket_timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;센티널들의 정보를 리스트로 입력&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sentinel.discover_master(strServiceName)&lt;/code&gt; 으로 마스터 정보 확인 가능&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;마스터에서-슬레이브-아이피-차단&quot;&gt;마스터에서 슬레이브 아이피 차단&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;네트워크 단절을 테스트하기 위해 iptables rule을 추가해 슬레이브 ip 차단
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[irteamsu@data-redis-wa903 ~]$ sudo iptables -A INPUT -s 10.162.29.33 -j DROP
[irteamsu@data-redis-wa903 ~]$ sudo iptables -A INPUT -s 10.162.29.36 -j DROP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;슬레이브-상태&quot;&gt;슬레이브 상태&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;master는 변하지 않고, master_link_status는 down 상태 유지.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;master_host:10.162.29.37
master_port:6371
master_link_status:down
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;센티널-상태&quot;&gt;센티널 상태&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;마스터에 연결된 slave를 0으로 인식
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=odown,address=10.162.29.37:6371,slaves=0,sentinels=2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;&lt;strong&gt;슬레이브에서 오는 연결은 차단됐지만, 센티널에서는 계속 정상으로 모니터링 되어서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;계속해서 마스터로 살아있음&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;센티널-모니터링-연결-차단&quot;&gt;센티널 모니터링 연결 차단&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;센티널이 떠있는 포트 차단
    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;irteamsu@data-redis-wa903 ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; INPUT &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; tcp &lt;span class=&quot;nt&quot;&gt;--sport&lt;/span&gt; 27001 &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; DROP
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;irteamsu@data-redis-wa903 ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; INPUT &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; tcp &lt;span class=&quot;nt&quot;&gt;--sport&lt;/span&gt; 27002 &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; DROP
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;irteamsu@data-redis-wa903 ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; INPUT &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; tcp &lt;span class=&quot;nt&quot;&gt;--sport&lt;/span&gt; 27003 &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; DROP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;h2 id=&quot;연결-상태&quot;&gt;연결 상태&lt;/h2&gt;
    &lt;p&gt;&lt;img src=&quot;/assets/20190619/3.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;
    &lt;h2 id=&quot;센티널-상태-1&quot;&gt;센티널 상태&lt;/h2&gt;
  &lt;/li&gt;
  &lt;li&gt;마스터 모니터링이 안되기 때문에 슬레이브를 마스터로 승격시킴
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=10.162.29.36:6371,slaves=2,sentinels=2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;레디스-상태&quot;&gt;레디스 상태&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;네트워크 단절된 마스터
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Replication
role:master
connected_slaves:0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;(신) 마스터(36)
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Replication
role:master
connected_slaves:1
slave0:ip=10.162.29.33,port=6371,state=online,offset=9554376,lag=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;각-마스터에-데이터-입력&quot;&gt;각 마스터에 데이터 입력&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;신 마스터에 iam master 입력
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.162.29.36:6371&amp;gt; set iam master
OK
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;구 마스터에 iwas master 입력&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.162.29.37:6371&amp;gt; set iwas master
OK
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;차단-복구&quot;&gt;차단 복구&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[irteamsu@data-redis-wa903 ~]$ sudo iptables -F
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;연결-상태-1&quot;&gt;연결 상태&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190619/4.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;마스터-상태&quot;&gt;마스터 상태&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;단절이 해결된 마스터가 신 마스터의 슬레이브로 연결됨.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.162.29.36:6371&amp;gt; info replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.162.29.33,port=6371,state=online,offset=10210046,lag=1
slave1:ip=10.162.29.37,port=6371,state=online,offset=10210046,lag=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;입력된-데이터-확인&quot;&gt;입력된 데이터 확인&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;연결 단절된 마스터에 입력했던 데이터는 사라짐.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.162.29.37:6371&amp;gt; get iam
&quot;master&quot;
10.162.29.37:6371&amp;gt; get iwas
(nil)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 19 May 2019 18:57:00 +0000</pubDate>
        <link>http://garimoo.github.io/database/2019/05/19/redis_client_test.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/database/2019/05/19/redis_client_test.html</guid>
        
        
        <category>Database</category>
        
      </item>
    
      <item>
        <title>[Redis] 데이터 구조와 명령어</title>
        <description>&lt;h2 id=&quot;데이터-구조&quot;&gt;데이터 구조&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;지원하는 데이터형
    &lt;ul&gt;
      &lt;li&gt;String&lt;/li&gt;
      &lt;li&gt;Hashes&lt;/li&gt;
      &lt;li&gt;Sets&lt;/li&gt;
      &lt;li&gt;Sorted Sets&lt;/li&gt;
      &lt;li&gt;Lists&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;논리적 데이터 저장 방식: Map 구조&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/20190511/1.png&quot; alt=&quot;Image&quot; /&gt;
&lt;img src=&quot;/assets/20190511/2.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;string&quot;&gt;String&lt;/h3&gt;
&lt;h4 id=&quot;setters--getters-명령어&quot;&gt;Setters / Getters 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Get key: key에 해당하는 value 반환&lt;/li&gt;
  &lt;li&gt;Set key: key에 value 저장&lt;/li&gt;
  &lt;li&gt;SETNX key: key가 없을 때 value 저장. 실패시 0 반환&lt;/li&gt;
  &lt;li&gt;GETSET key: 새로운 value 저장, 이전 value 반환. (새로 저장시 nil 반환)&lt;/li&gt;
  &lt;li&gt;MGET key1 key: 여러개의 key에 대응하는 value 반환&lt;/li&gt;
  &lt;li&gt;MSET key: 여러개의 value 저장&lt;/li&gt;
  &lt;li&gt;MSETNX key: 여러개의 value 저장. 이미 존재하는 key가 하나라도 있다면 전체 실패.
    &lt;h4 id=&quot;data-clean-명령어&quot;&gt;Data Clean 명령어&lt;/h4&gt;
  &lt;/li&gt;
  &lt;li&gt;SET option
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET key value [NX|XX] [EX second] [PX millisecond]&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;NX: 해당 key가 없는 경우에만 value 저장 (=SETNX)&lt;/li&gt;
          &lt;li&gt;XX: 해당 key가 있는 경우에만 value 저장&lt;/li&gt;
          &lt;li&gt;EX second: second 이후에 데이터 지워짐 (=SETEX)&lt;/li&gt;
          &lt;li&gt;PX millisecond: millisecond 이후에 데이터 지워짐 (=PSETEX)&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; setex key 5 value
OK
127.0.0.1:6379&amp;gt; ttl key
(integer) 2
127.0.0.1:6379&amp;gt; get key
&quot;value&quot;
127.0.0.1:6379&amp;gt; get key
(nil)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;utility-명령어&quot;&gt;Utility 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;APPEND: 기존 value 뒤에 새로운 value 추가. (key가 없는 경우 set과 같음)&lt;/li&gt;
  &lt;li&gt;STRLEN: string 길이 반환&lt;/li&gt;
  &lt;li&gt;SETRANGE: 주어진 offset에 해당하는 string 을 변환&lt;/li&gt;
  &lt;li&gt;GETRANGE: 일부 문자열 조회
    &lt;ul&gt;
      &lt;li&gt;start/end 는 둘다 offset을 의미 (end가 문자열 길이 X)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; set key1 Hello
OK
127.0.0.1:6379&amp;gt; append key1 Redis
(integer) 10
127.0.0.1:6379&amp;gt; get key1
&quot;HelloRedis&quot;
127.0.0.1:6379&amp;gt; set key &quot;This is MySQL Server&quot;
OK
127.0.0.1:6379&amp;gt; setrange key 8 Redis
(integer) 20
127.0.0.1:6379&amp;gt; get key
&quot;This is Redis Server&quot;
------------------------------------------------------
127.0.0.1:6379&amp;gt; setrange keyy 3 Redis
(integer) 8
127.0.0.1:6379&amp;gt; get keyy
&quot;\x00\x00\x00Redis&quot;
새로운 key에 setrange 명령어를 통해 padding 기능 사용 가능
127.0.0.1:6379&amp;gt; set key &quot;This is Redis Server&quot;
OK
127.0.0.1:6379&amp;gt; getrange key 0 3
&quot;This&quot;
127.0.0.1:6379&amp;gt; getrange key 8 100
&quot;Redis Server&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;bitset--bitmap-명령어&quot;&gt;BitSet / Bitmap 명령어&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190511/3.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SETBIT: 해당 offset에 value 저장&lt;/li&gt;
  &lt;li&gt;GETBIT: 해당 offset 위치의 값 반환&lt;/li&gt;
  &lt;li&gt;BITCOUNT: value 중 1의 갯수 반환&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;숫자형-명령어&quot;&gt;숫자형 명령어&lt;/h4&gt;
&lt;p&gt;Getter/Setter, Data Clean 명령어 모두 string 명령어와 같음&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;DECR&lt;/li&gt;
  &lt;li&gt;DECRBY: 지정한 숫자만큼 감소&lt;/li&gt;
  &lt;li&gt;INCR&lt;/li&gt;
  &lt;li&gt;INCRBY: 지정한 숫자만큼 증가&lt;/li&gt;
  &lt;li&gt;INCRBYFLOAT: 지정한 숫자(정수/유리수) 만큼 증가.
    &lt;ul&gt;
      &lt;li&gt;감소의 경우 음수 입력 가능&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;hashes&quot;&gt;Hashes&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190511/4.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;키 하나에 여러개의 (field/value) 쌍이 매핑&lt;/li&gt;
  &lt;li&gt;key 하나에 field와 value 쌍을 40억개(4,294,967,295)까지 저장 가능&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/20190511/5.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;h5 id=&quot;table--hash-유사점&quot;&gt;Table / Hash 유사점&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Hashes가 field와 value로 구성된다는 면에서 RDB의 table과 비슷&lt;/li&gt;
  &lt;li&gt;Hash key는 table의 PK, field는 column, value는 value로&lt;/li&gt;
  &lt;li&gt;Key가 PK와 같은 역할을 하기 때문에 key 하나는 table의 한 row와 같음&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;table--hash-차이점&quot;&gt;Table / Hash 차이점&lt;/h5&gt;

&lt;ul&gt;
  &lt;li&gt;Table의 column 수는 일반적으로 제한이 있는 반면, Hash의 field 수는 40억개로 거의 무제한&lt;/li&gt;
  &lt;li&gt;Table에서 column을 추가하려면 alter문으로 미리 table을 변경해야 하나, Hash에서는 자유로운 field의 추가/삭제&lt;/li&gt;
  &lt;li&gt;Field의 추가/삭제는 해당 key에만 영향을 미침&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;setters--getters-명령어-1&quot;&gt;Setters / Getters 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;HGET: key, field로 value 조회&lt;/li&gt;
  &lt;li&gt;HGETALL: key에 속한 모든 field와 value를 조회&lt;/li&gt;
  &lt;li&gt;HSET: key에 여러개의 field와 value를 저장 가능. 기존에 같은 field가 있으면 덮어씀.&lt;/li&gt;
  &lt;li&gt;HMGET: 여러개의 value 조회&lt;/li&gt;
  &lt;li&gt;HMSET : 버전 4.0.0부터는 hmset 대신 hset을 사용할 것을 권장&lt;/li&gt;
  &lt;li&gt;HVALS: key에 속한 모든 value를 조회&lt;/li&gt;
  &lt;li&gt;HSETNX: field가 기존에 없으면 저장. 있으면 실패(-1)&lt;/li&gt;
  &lt;li&gt;HKEYS: key에 속한 모든 field name을 조회&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;data-clean-명령어-1&quot;&gt;Data Clean 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;HDEL: 지정한 field와 value를 삭제한다. field를 여러개 지정 가능&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;utility-명령어-1&quot;&gt;Utility 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;HEXISTS: field가 있는지 조회. 있으면 1, 없으면 0 반환&lt;/li&gt;
  &lt;li&gt;HINCRBY: value를 increment 만큼 증가 또는 감소. 해당 field가 없으면 increment 값을 set.&lt;/li&gt;
  &lt;li&gt;HINCRBYFLOAT&lt;/li&gt;
  &lt;li&gt;HLEN: field의 개수 조회&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;lists&quot;&gt;Lists&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190511/6.png&quot; alt=&quot;Image&quot; /&gt;
key와 value가 일 대 다 관계&lt;/p&gt;

&lt;h4 id=&quot;setters--getters-명령어-2&quot;&gt;Setters / Getters 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;LPUSH: 리스트의 왼쪽에 데이터를 저장&lt;/li&gt;
  &lt;li&gt;LPUSHX: 키가 이미 있을 경우에만 리스트의 왼쪽에 데이터를 저장&lt;/li&gt;
  &lt;li&gt;RPUSHX: 키가 이미 있을 경우에만 리스트의 오른쪽에 데이터를 저장&lt;/li&gt;
  &lt;li&gt;LINSERT: 값으로 특정 위치에 데이터 넣기
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linsert key BEFORE | AFTER pivot value&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;기준이 되는 데이터(pivot)을 기준으로 전.후에 value를 넣는다.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/linsert_ani1.php&quot;&gt;예시&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;LSET: index의 위치에 있는 데이터를 새로운 데이터로 변환
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lset key index value&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;LRANGE: 인덱스로 범위를 지정해서 리스트 조회&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;data-clean-명령어-2&quot;&gt;Data Clean 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;LTRIM: 인덱스로 지정한 범위 밖의 데이터를 삭제. 지정한 범위의 데이터만 남기고 나머지를 모두 삭제시 유용.&lt;/li&gt;
  &lt;li&gt;RPOP: 리스트 오른쪽에서 데이터를 꺼내옴.&lt;/li&gt;
  &lt;li&gt;LREM:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lrem key count value&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;count가 양수이면 지정한 value를 리스트의 왼쪽에서 부터 count 만큼 삭제&lt;/li&gt;
      &lt;li&gt;count가 0이면 지정한 value를 모두 삭제합니다. 삭제된 value 개수를 리턴&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/lrem_ani1.php&quot;&gt;예시1&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;count가 음수이면 오른쪽에서 부터 count 만큼 삭제&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/lrem_ani2.php&quot;&gt;예시2&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;LPOP: 리스트 왼쪽에서 데이터를 꺼내옴&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;utility-명령어-2&quot;&gt;Utility 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;LINDEX: 인덱스로 특정 위치의 데이터를 조회
    &lt;ul&gt;
      &lt;li&gt;index를 왼쪽부터 지정할때는 (0, 1, 2), 오른쪽 부터 지정할때는 (-1, -2, -3) 순으로&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;LLEN: 리스트에서 value의 개수를 조회&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;advanced-명령어&quot;&gt;Advanced 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;BLPOP
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blpop key timeout&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;리스트에 데이터가 이미 있을 경우에는 LPOP와 같음. 데이터가 없을 경우에는 timeout(초) 만큼 기다림. timeout이 0이면 들어올 때까지 기다림.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/blpop_ani1.php&quot;&gt;예시3&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;여러 키 지정 가능. 지정한 순서대로 리스트를 확인해서 데이터가 있으면 하나만 가져오고 종료.&lt;/li&gt;
      &lt;li&gt;key를 여러 개 지정했다고 여러 개 데이터를 가져오는 것은 아님.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/blpop_ani3.php&quot;&gt;예시4&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;BRPOP&lt;/li&gt;
  &lt;li&gt;RPOPLPUSH: 리스트 오른쪽에서 데이터를 꺼내서 왼쪽에 넣음.
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/rpoplpush_ani1.php&quot;&gt;예시5&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;BRPOPLPUSH: 데이터가 들어오면 리스트 오른쪽에서 데이터를 꺼내서 왼쪽에 넣음.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/brpoplpush_ani1.php&quot;&gt;예시6&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sets&quot;&gt;Sets&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Value는 입력된 순서와 상관없이 저장되며, 중복되지 않음.&lt;/li&gt;
  &lt;li&gt;value를 member라 부름 (집합의 의미여서)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;setters--getters-명령어-3&quot;&gt;Setters / Getters 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;SADD: 집합에 데이터를 추가. 여러개 지정 가능&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;data-clean-명령어-3&quot;&gt;Data Clean 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;SPOP: random member 1개 삭제&lt;/li&gt;
  &lt;li&gt;SREM: 해당 member를 삭제. 여러개 삭제 가능.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;utility-명령어-3&quot;&gt;Utility 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;SCARD: 집합에 속한 member의 개수를 조회&lt;/li&gt;
  &lt;li&gt;SISMEMBER: 집합에 member 존재하는지 확인&lt;/li&gt;
  &lt;li&gt;SMOVE: 한 집합의 member를 다른 집합으로 이동.
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;smove source_key destination_key member&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;source key의 member에서는 지워진다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;SRANDOMMEMBER: 랜덤 멤버 조회&lt;/li&gt;
  &lt;li&gt;SDIFF: 첫번째 집합에서 나머지 집합에 있는 member들을 제거 (차집합)&lt;/li&gt;
  &lt;li&gt;SDIFFSTORE: 차집합을 구해서 새로운 집합에 저장
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdiffstore destination_key source_key1 source_key2&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;SINTER: 교집합&lt;/li&gt;
  &lt;li&gt;SINTERSTORE: 교집합을 구해서 새로운 집합에 저장&lt;/li&gt;
  &lt;li&gt;SUNION: 합집합&lt;/li&gt;
  &lt;li&gt;SUNIONSTORE: 합집합을 구해서 새로운 집합에 저장&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sorted-sets-zsets&quot;&gt;Sorted Sets (ZSets)&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Value는 score로 sort되며, 중복되지 않음.&lt;/li&gt;
  &lt;li&gt;score가 같으면 value로 sort (알파벳순)&lt;/li&gt;
  &lt;li&gt;정렬이 필요한 곳에서 사용&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;setters--getters-명령어-4&quot;&gt;Setters / Getters 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;ZADD: score와 함께 member 저장
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zadd key score member&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ZRANGE: score 오름차순으로 member list 조회
    &lt;ul&gt;
      &lt;li&gt;withscores 옵션을 사용하면 score 표시됨&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ZRANGEBYSCORE: score로 범위를 지정해서 조회
    &lt;ul&gt;
      &lt;li&gt;모두 조회하려면 -inf, +inf 사용&lt;/li&gt;
      &lt;li&gt;포함하지 않게 하려면 min, max에 ( 를 사용
        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/zrangebyscore_ani2.php&quot;&gt;예시7&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;limit offset count 사용 가능&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/zrangebyscore_ani3.php&quot;&gt;예시8&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ZREVRANGEBYSCORE&lt;/li&gt;
  &lt;li&gt;ZRANK: member값을 입력하고 index 반환&lt;/li&gt;
  &lt;li&gt;ZREVRANK&lt;/li&gt;
  &lt;li&gt;ZREVRANGE&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;data-clean-명령어-4&quot;&gt;Data Clean 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;ZREM: member 삭제&lt;/li&gt;
  &lt;li&gt;ZREMRANGEBYRANK: index 범위로 member를 삭제
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zremrangebyrank key start stop&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/zremrangebyrank_ani.php&quot;&gt;예시9&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ZREMRANGEBYSCORE: score로 범위를 지정해서 member 삭제
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zremrangebyscore key min max&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;모두 삭제하려면 -inf, +inf를 사용&lt;/li&gt;
      &lt;li&gt;min, max 값을 제외하려면 앞에 ‘(‘ 를 사용&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/zremrangebyscore_ani.php&quot;&gt;예시10&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;utility-명령어-4&quot;&gt;Utility 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;ZCARD: 집합에 속한 member 개수를 리턴&lt;/li&gt;
  &lt;li&gt;ZSCORE: member의 score를 리턴&lt;/li&gt;
  &lt;li&gt;ZCOUNT: score로 범위를 지정해서 개수 조회
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zcount key min max&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/zcount_ani.php&quot;&gt;예시11&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ZINCRBY: score 증가, 감소
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zincrby key increment member&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/zincrby_ani1.php&quot;&gt;예시12&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ZINTERSTORE: 교집합을 구해서 새로운 집합에 저장
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zinterstore dest_key 집합수 src_key1 src_key2&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;결과 집합은 지정한 dest_key에 저장됨&lt;/li&gt;
      &lt;li&gt;각 member의 score는 더해짐&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/zinterstore_ani1.php&quot;&gt;예시13&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zinterstore dest_key 집합수 src_key1 src_key2 weights 2 3&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;가중치 더해서 새로운 집합에 저장&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/zinterstore_ani2.php&quot;&gt;예시14&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zinterstore dest_key 2 src_key1 src_key2 aggregate sum|min|max&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/zinterstore_ani3.php&quot;&gt;예시15&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ZUNIONSTORE&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;new-feature&quot;&gt;New Feature&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;ZPOPMIN: 작은 값부터 출력
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zpopmin key [count]&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;기본 count는 1&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ZPOPMAX&lt;/li&gt;
  &lt;li&gt;BZPOPMIN: 데이터가 들어오면 작은 값부터 출력
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bzpopmin key [key ...] timeout&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;BZPOPMAX&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;key-관리&quot;&gt;KEY 관리&lt;/h3&gt;
&lt;h4 id=&quot;조회-명령어&quot;&gt;조회 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;KEYS: keys+pattern *
    &lt;ul&gt;
      &lt;li&gt;: 모든 문자 매치&lt;/li&gt;
      &lt;li&gt;?: 1개 문자 매치&lt;/li&gt;
      &lt;li&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;EXISTS: 키가 존재하는지 확인&lt;/li&gt;
  &lt;li&gt;SCAN: Key들을 일정 단위 개수 만큼씩 조회
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SCAN cursor [MATCH pattern] [COUNT count]&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;SORT: 데이터를 sort 하여 조회
    &lt;ul&gt;
      &lt;li&gt;limit 옵션&lt;/li&gt;
      &lt;li&gt;get 옵션
        &lt;ul&gt;
          &lt;li&gt;다른 key의 value를 조회할 경우 get 사용&lt;/li&gt;
          &lt;li&gt;RDB의 join과 유사&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/sort_ani1.php&quot;&gt;예시16&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;by 옵션
        &lt;ul&gt;
          &lt;li&gt;지정한 key의 value로 sort&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/command/sort_ani3.php&quot;&gt;예시17&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;삭제변경-명령어&quot;&gt;삭제/변경 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;RENAME: key 이름 변경.
    &lt;ul&gt;
      &lt;li&gt;new_key가 이미 존재하면 삭제됨.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;RENAMENX: new_key가 존재하지 않을 경우에만 key 이름을 변경&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;만료-처리-명령어&quot;&gt;만료 처리 명령어&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;EXPIRE: 지정된 시간(초) 후 key 자동 삭제&lt;/li&gt;
  &lt;li&gt;TTL: 남은 expire time(seconds)을 조회&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;key-설계&quot;&gt;KEY 설계&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;RDB
&lt;img src=&quot;/assets/20190511/7.png&quot; alt=&quot;Image&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;REDIS
&lt;img src=&quot;/assets/20190511/8.png&quot; alt=&quot;Image&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;참고-문서&quot;&gt;참고 문서&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.redisgate.com/redis/introduction/redis_intro.php&quot;&gt;Redis Introduction&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 11 May 2019 19:18:00 +0000</pubDate>
        <link>http://garimoo.github.io/database/2019/05/11/rediscommand.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/database/2019/05/11/rediscommand.html</guid>
        
        
        <category>Database</category>
        
      </item>
    
      <item>
        <title>[ORACLE] Library Lock, Pin and Load Lock</title>
        <description>&lt;h2 id=&quot;lock-pin-and-load-lock&quot;&gt;Lock, Pin and Load Lock&lt;/h2&gt;
&lt;h3 id=&quot;library-cache-lock&quot;&gt;Library cache lock&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;object handle의 lock을 획득하여 클라이언트 간의 library cache에 대한 동시성을 제어한다.
    &lt;ul&gt;
      &lt;li&gt;같은 object에 대해 한 client와 다른 client가 액세스하지 못하게 할 수 있다.&lt;/li&gt;
      &lt;li&gt;한 클라이언트가 오랫동안 dependency를 유지하게 할 수 있다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;library cache에서 object를 찾기 위해 확보된다.&lt;/li&gt;
  &lt;li&gt;이 잠금은 SQL 또는 PL/SQL문의 구문 분석이나 컴파일 중에 참조되는 object에서 가져온다. lock은 컴파일이 끝날 때 해제된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;library-cache-pin&quot;&gt;Library cache pin&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;library cache의 동시성을 관리한다.
    &lt;ul&gt;
      &lt;li&gt;개체를 pinning 하면 heap이 메모리에 로드된다.&lt;/li&gt;
      &lt;li&gt;클라이언트가 object를 수정, 검사하려는 경우 client는 lock 후에 pin을 확보해야 한다.&lt;/li&gt;
      &lt;li&gt;pin은 &lt;strong&gt;NULL, SHARE, EXCLUSIVE&lt;/strong&gt; 모드로 획득할 수 있다.&lt;/li&gt;
      &lt;li&gt;library cache pin을 기다리는 것은 다른 세션이 pin을 호환되지 않는 모드로 유지한다는 것을 의미한다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;두-종류의-lock이-필요한-이유&quot;&gt;두 종류의 lock이 필요한 이유&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;library cache의 object에 액세스하기 위해서 lock과 pin이 모두 필요하다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;lock은 프로세스간의 동시성을 관리, pin은 캐시의 일관성을 관리&lt;/strong&gt; 한다.&lt;/li&gt;
  &lt;li&gt;PL/SQL 의 컴파일, 파싱 등을 위해 Library Cache Lock, Library Cache Pin이 필요한 이유는 컴파일, 파싱 작업 중 object의 정의를 변경하고, object를 삭제하고, 새로 작성하는 동안 아무도 그 object를 사용하지 않도록 하기 위함이다.&lt;/li&gt;
  &lt;li&gt;SQL문이 세션에 의해 하드파싱 될 때에도 다른 세션이 object에 액세스하거나 수정하지 못하도록 Library Cache Lock을 획득해야 한다.&lt;/li&gt;
  &lt;li&gt;하드 파싱과 별개로, 세션이 SQL에 지정된 object의 정의를 변경하거나 수정을 한다면 Libray cache Pin과 함께 Library Cache Lock을 획득해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;library-cache-load-lock&quot;&gt;Library Cache Load Lock&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;세션은 object를 load하기 위해서 object에 대한 load lock을 찾으려고 시도함.&lt;/li&gt;
  &lt;li&gt;load lock은 항상 exclusive mode에서 획득되므로, 다른 프로세스가 동일한 객체를 load할 수 없음. 세션은 잠금이 사용 가능해 질 때 까지 이벤트를 기다림.&lt;/li&gt;
  &lt;li&gt;동일한 object의 load를 요청하는 multiple process를 방지하기 위해, object를 메모리에 로드할 때 lock이 사용중이면 다른 세션은 library cache load lock을 기다려야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;library-cache-pin과-library-load-lock의-관계&quot;&gt;Library Cache pin과 Library Load lock의 관계&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;PL/SQL 의 컴파일 등의 과정에서 Library Cache Pin과 load lock이 발생할 수 있다.&lt;/li&gt;
  &lt;li&gt;컴파일은 항상 명시적이고, object의 invalidation에 의해 object의 재컴파일이 일어날 수 있다.
&lt;br /&gt;&lt;/li&gt;
  &lt;li&gt;object가 invalidation 된 후, oracle은 object에 처음 access할 때 object를 재컴파일 하려고 시도한다.&lt;/li&gt;
  &lt;li&gt;이 때 다른 세션이 library cache에 객체를 pin 시키면 문제가 발생할 수 있다.&lt;/li&gt;
  &lt;li&gt;객체의 재 컴파일을 기다리는 동안 액세스를 시도하는 모든 세션을 차단하는 데 몇 시간이 걸릴 수도 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;참고-문서&quot;&gt;참고 문서&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/cd/E11882_01/server.112/e41084/ap_locks002.htm#SQLRF55509&quot;&gt;Automatic Locks in DDL Operations&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/cd/B28359_01/server.111/b28318/dependencies.htm#BEGIN&quot;&gt;Schema Object Dependencies&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.oracle.com/epmos/faces/DocumentDisplay?_afrLoop=396224682066885&amp;amp;parent=SrDetailText&amp;amp;sourceId=3-18443773491&amp;amp;id=444560.1&amp;amp;_afrWindowMode=0&amp;amp;_adf.ctrl-state=1bcu4fowxv_195&quot;&gt;Document 444560.1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 14 Jan 2019 15:24:00 +0000</pubDate>
        <link>http://garimoo.github.io/database/2019/01/14/librarycache.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/database/2019/01/14/librarycache.html</guid>
        
        
        <category>Database</category>
        
      </item>
    
      <item>
        <title>[STUDY] 오라클 성능 고도화 원리와 해법2 - 01장 인덱스 원리와 활용</title>
        <description>&lt;h1 id=&quot;chapter-1-인덱스-원리와-활용&quot;&gt;[Chapter 1] 인덱스 원리와 활용&lt;/h1&gt;

&lt;h2 id=&quot;인덱스-구조&quot;&gt;인덱스 구조&lt;/h2&gt;

&lt;h3 id=&quot;범위-스캔&quot;&gt;범위 스캔&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;인덱스는 key 순으로 정렬되어 있어서 range scan이 가능&lt;/li&gt;
  &lt;li&gt;IOT를 제외하면 일반적인 테이블(heap 구조)에서는 불가&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;인덱스-기본-구조&quot;&gt;인덱스 기본 구조&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;B*Tree 구조
&lt;img src=&quot;/assets/20190103/1.png&quot; alt=&quot;Image&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;branch block은 &lt;strong&gt;DBA(Data Block Access)&lt;/strong&gt; 정보를, leaf block은 &lt;strong&gt;key column, rowid&lt;/strong&gt; 를 갖음&lt;/li&gt;
  &lt;li&gt;lmc(left most child): 각 브랜치 노드의 첫 번째 엔트리. 키 값을 가진 첫 번째 엔트리보다 작은 값을 의미.&lt;/li&gt;
  &lt;li&gt;테이블 값이 갱신되면 리프노드 인덱스 키 값도 같이 갱신. 브랜치 노드까지 바뀌지는 않음.&lt;/li&gt;
  &lt;li&gt;브랜치 노드는 인덱스 분할에 의해 새로운 블록이 추가되거나 삭제될 때만 갱신.
    &lt;h3 id=&quot;인덱스-탐색&quot;&gt;인덱스 탐색&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;수직적 탐색: range scan. 수평적 탐색을 위한 시작점을 찾는 과정.&lt;/li&gt;
  &lt;li&gt;수평적 탐색: leaf blokc을 좌, 우로 스캔.&lt;/li&gt;
  &lt;li&gt;브랜치 블록 탐색
  &lt;img src=&quot;/assets/20190103/2.png&quot; alt=&quot;Image&quot; /&gt;
    &lt;ul&gt;
      &lt;li&gt;탐색은 뒤에서부터 스캔하고, 찾고자 하는 값보다 키가 작은 엔트리를 따라 내려가야 함.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;결합 인덱스 구조
  &lt;img src=&quot;/assets/20190103/3.png&quot; alt=&quot;Image&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;rowid-포맷&quot;&gt;ROWID 포맷&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;data file 번호, block 번호, row 번호같은  테이블 레코드의 물리적 위치정보를 포함.&lt;/li&gt;
  &lt;li&gt;테이블 레코드를 찾아가는데 필요하므로 index에 저장. 테이블에는 저장되어있지 않는 pseudo column&lt;/li&gt;
  &lt;li&gt;데이터 블록 헤더에 물리적인 정보들(object, datafile 번호 등)이 저장되어 있어서 rowid를 읽을 때 가공 가능&lt;/li&gt;
  &lt;li&gt;크기
    &lt;ul&gt;
      &lt;li&gt;~ver. 7: 6바이트
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;restricted rowid format&lt;/strong&gt;: 구분자 포함 18자리
            &lt;ul&gt;
              &lt;li&gt;datafile 번호 (4)&lt;/li&gt;
              &lt;li&gt;block 번호 (8)&lt;/li&gt;
              &lt;li&gt;row 번호 (4)&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;ver.8 ~: 10바이트 (하지만 파티션되지 않은 인덱스는 6바이트)
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;extended rowid format&lt;/strong&gt;: 구분자 없이 18자리
            &lt;ul&gt;
              &lt;li&gt;data object 번호 (6)&lt;/li&gt;
              &lt;li&gt;datafile 번호 (3)&lt;/li&gt;
              &lt;li&gt;block 번호 (6)&lt;/li&gt;
              &lt;li&gt;row 번호 (3)&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;인덱스-기본-원리&quot;&gt;인덱스 기본 원리&lt;/h2&gt;
&lt;h3 id=&quot;인덱스-사용이-불가능하거나-범위-스캔이-불가능한-경우&quot;&gt;인덱스 사용이 불가능하거나 범위 스캔이 불가능한 경우&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;index full scan은 가능, index range scan이 불가능한 경우
    &lt;ul&gt;
      &lt;li&gt;인덱스 컬럼을 조건절에서 가공하는 경우&lt;/li&gt;
      &lt;li&gt;부정형 비교: where 직업 &amp;lt;&amp;gt; ‘학생’&lt;/li&gt;
      &lt;li&gt;is not null 조건&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;index 사용이 불가능한 경우
    &lt;ul&gt;
      &lt;li&gt;is null 조건
        &lt;ul&gt;
          &lt;li&gt;하지만, not null 조건 column을 is null 조건으로 검색하면 index scan에 가서 공집합을 반환.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;인덱스-컬럼의-가공&quot;&gt;인덱스 컬럼의 가공&lt;/h3&gt;
&lt;h4 id=&quot;컬럼-가공-사례&quot;&gt;컬럼 가공 사례&lt;/h4&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;컬럼 가공&lt;/th&gt;
      &lt;th&gt;튜닝 방안&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;where substr(업체명,1,2) = ‘대한’&lt;/td&gt;
      &lt;td&gt;where 업체명 like ‘대한%’&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;where 월급여 * 12 = 360000&lt;/td&gt;
      &lt;td&gt;where 월급여 = 360000/12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;where to_char(일시,’yyyymmdd’) = :dt&lt;/td&gt;
      &lt;td&gt;where 일시 &amp;gt;= to_date(:’dt, ‘yyyymmdd’) &lt;br /&gt; and 일시 &amp;lt; to_date (:dt, ‘yyyymmdd’) + 1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;where 연령 || 직업 = '30공무원'&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;where 연령 = 30 and 직업 = ‘공무원’&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;where 회원번호 || 지점번호 =: str&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;where 회원번호 = substr(:str,1,2)&lt;br /&gt;and 지점번호 = substr(:str,3,4)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;튜닝-사례&quot;&gt;튜닝 사례&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;인덱스 구성
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PK: 수신번호
INDEX : 정정대상접수번호 + 금감원접수번호
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;튜닝 전 쿼리
    &lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;    &lt;span class=&quot;err&quot;&gt;접수정보파일&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;정정대상접수번호&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;금감원접수번호&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;정정대상접수번호&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;접수번호&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;튜닝 후 쿼리: decode 조건절 재구성
    &lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;    &lt;span class=&quot;err&quot;&gt;접수정보파일&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;   &lt;span class=&quot;err&quot;&gt;정정대상접수번호&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;접수번호&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt;     &lt;span class=&quot;err&quot;&gt;금감원접수번호&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;정정대상접수번호&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;접수번호&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;금감원접수번호&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;묵시적-형변환&quot;&gt;묵시적 형변환&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;숫자형과 문자형이 비교될 때는 숫자형이 우선됨.
    &lt;ul&gt;
      &lt;li&gt;=로 비교하기 전에 to_char를 통해 문자형 비교로 변경&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;성능 문제 뿐 아니라 쿼리 수행 도중 에러 발생 가능.
    &lt;ul&gt;
      &lt;li&gt;형변환에 의해 숫자로 변환될 때, 변환되지 못하는 문자열이 있을 수 있음.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;decode(a, b, c, d) 함수에서 출력되는 값의 데이터 타입은 c에 의해 결정됨.
    &lt;ul&gt;
      &lt;li&gt;만약 c가 null이면 varchar2로 출력됨.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;FBI(Function Based Index) 활용: 급한 불을 끌 때 사용.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;다양한-인덱스-스캔-방식&quot;&gt;다양한 인덱스 스캔 방식&lt;/h2&gt;
&lt;h3 id=&quot;index-range-scan&quot;&gt;Index Range Scan&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;수직적 탐색(root block ~ leaf block) 후 수평 탐색(leaf block)&lt;/li&gt;
  &lt;li&gt;일반적인 액세스 방식&lt;/li&gt;
  &lt;li&gt;실행 계획에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INDEX (RANGE SCAN)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;index-full-scan&quot;&gt;Index Full Scan&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;수직적 탐색 없이 leaf block을 처음부터 끝까지 수평적으로 탐색하는 방식.
    &lt;ul&gt;
      &lt;li&gt;실제로는 첫 번째 leaf block을 찾기 위해 제일 왼쪽에서 수직적 탐색이 일어남.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;최적의 인덱스가 없을 때 차선으로 선택됨&lt;/li&gt;
  &lt;li&gt;실행 계획에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INDEX (FULL SCAN)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;index scan 단계에서 대부분 레코드의 필터링이 가능하다면 유리. 하지만 index에서 대부분의 레코드가 선택되면 불리.
    &lt;h3 id=&quot;index-unique-scan&quot;&gt;Index Unique Scan&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;수직탐색으로만 데이터를 찾는 방식. ‘=’조건으로 탐색하는 경우에만 작동&lt;/li&gt;
  &lt;li&gt;한 건의 데이터를 찾는 순간 더이상의 탐색은 없음.&lt;/li&gt;
  &lt;li&gt;실행 계획에 ‘INDEX (UNIQUE SCAN)’&lt;/li&gt;
  &lt;li&gt;unique index더라도, ‘&amp;gt;=’ 등의 조건을 사용하면 range scan을 이용.
    &lt;h3 id=&quot;index-skip-scan&quot;&gt;Index Skip Scan&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;조건절에 빠진 인덱스 선두 컬럼의 distinct value 개수가 적고, 후행 컬럼의 distinct value 개수가 많을 때 유용
&lt;img src=&quot;/assets/20190103/4.png&quot; alt=&quot;Image&quot; /&gt;
    &lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;사원&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;연봉&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;between&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Index Skip Scan은 첫 번째 리프 블록을 항상 방문하고, 마지막 리프 블록도 항상 방문.
    &lt;ul&gt;
      &lt;li&gt;여기에서는 ‘남’보다 작은 성별이나, ‘여’보다 큰 성별 값이 존재하는지 확인해 보기 위해서 방문.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Index Skip Scan이 작동하기 위한 조건
    &lt;ul&gt;
      &lt;li&gt;3개 컬럼의 index가 존재할 때, 최선두 컬럼은 입력하고, 중간 컬럼에 대한 조건절이 누락된 경우.&lt;/li&gt;
      &lt;li&gt;distinct value 개수가 적은 두 개의 선두컬럼이 모두 누락된 경우&lt;/li&gt;
      &lt;li&gt;선두컬럼이 범위조건일 때&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;index-fast-full-scan&quot;&gt;Index Fast Full Scan&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Index Fast Scan보다 빠름. 인덱스 트리구조를 무시하고 인덱스 세그먼트 전체를 multiblock read 방식으로 스캔.&lt;/li&gt;
  &lt;li&gt;인덱스를 읽지만 결과집합 순서 보장 안됨.&lt;/li&gt;
  &lt;li&gt;병렬스캔 가능.&lt;/li&gt;
  &lt;li&gt;인덱스에 포함된 컬럼으로만 조회할 때 사용 가능.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;index-range-scan-descending&quot;&gt;Index Range Scan Descending&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;index range scan과 동일한 방식, 내림차순으로 정렬된 결과집합을 얻는다는 것만 다름&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;and-equal-index-combine-index-join&quot;&gt;And-Equal, Index Combine, Index join&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;and-equal: 10g부터 폐기.&lt;/li&gt;
  &lt;li&gt;index combine: 데이터 분포도가 좋지 않은 두 개 이상의 인덱스를 결합해 테이블 random 액세스량을 줄이는 데에 목적이 있음.
    &lt;ul&gt;
      &lt;li&gt;b* index를 스캔하면서 각 조건을 만족하는 레코드의 rowid 목록을 얻음&lt;/li&gt;
      &lt;li&gt;이 rowid 목록을 가지고 비트맵인덱스 구조를 만듬&lt;/li&gt;
      &lt;li&gt;bit-wize 오퍼레이션 수행&lt;/li&gt;
      &lt;li&gt;true인 비트 값들을 rowid로 환산해 최종 방문할 rowid 목록 얻음&lt;/li&gt;
      &lt;li&gt;rowid 이용해서 테이블 액세스&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;index join: 한 테이블에 속한 여러 인덱스를 이용해 테이블 액세스 없이 결과집합을 만들 때 사용하는 인덱스 스캔 방식
    &lt;ul&gt;
      &lt;li&gt;쿼리에 사용된 컬럼들이 인덱스에 모두 포함될 때만 작동&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;테이블-random-액세스-부하&quot;&gt;테이블 Random 액세스 부하&lt;/h2&gt;
&lt;h3 id=&quot;인덱스-rowid에-의한-테이블-액세스&quot;&gt;인덱스 ROWID에 의한 테이블 액세스&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TABLE ACCESS (BY INDEX ROWID)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;오라클은 DBA(Data Block Address)를 해시 키 값으로 삼아 해싱 알고리즘을 통해 버퍼 블록을 찾음. 매번 위치가 달라지더라도 (버퍼에서 밀려났다가 다시 캐싱되기 때문에) 캐싱되는 해시 버킷만은 고정적.&lt;/li&gt;
  &lt;li&gt;인덱스 rowid는 테이블 레코드와 물리적으로 연결되어 있지 않기 때문에 인덱스를 통한 테이블 액세스는 고비용구조.
    &lt;ul&gt;
      &lt;li&gt;모든 데이터가 메모리에 캐싱돼 있더라도 매번 DBA를 해싱하고 래치 획득 과정을 반복해야 하기 때문. buffer lock도 고려.
        &lt;h3 id=&quot;인덱스-클러스터링-팩터&quot;&gt;인덱스 클러스터링 팩터&lt;/h3&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;같은 값을 갖는 데이터가 모여 있는 정도를 의미.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;-- object_id로 정렬하면서 테이블 생성&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_objects&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rownum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- object_id에 대한 인덱스 생성&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_object_id_idx&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- object_name에 대한 인덱스 생성&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_object_name_idx&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- 통계정보 수집&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbms_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather_table_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'T'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- 클러스터링 팩터 출력&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blocks&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_blocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clustering_factor&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_tables&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_indexes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'T'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190103/5.png&quot; alt=&quot;Image&quot; /&gt;
clustering_factor 수치가 테이블 블록에 가까울수록 데이터가 잘 정렬되어 있음.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;물리적 I/O: CF가 좋은 인덱스라면 index-range scan 후 레코드도 가까운 시점에서 읽힐 가능성이 높다. 물리적인 I/O 횟수가 감소.&lt;/li&gt;
  &lt;li&gt;논리적 I/O: 버퍼 Pinning 효과에 의해 접근하는 논리적 block 수가 감소함.
    &lt;ul&gt;
      &lt;li&gt;buffer Pinning: 방금 액세스한 버퍼에 대한 Pin을 즉각 해제하지 않고 데이터베이스 call 내에서 계속 유지하는 기능. 연속된 레코드가 같은 block을 가리키면 latch 획득 과정을 생략하기 때문에 logical read 수가 증가하지 않음.
        &lt;h3 id=&quot;인덱스-손익분기점&quot;&gt;인덱스 손익분기점&lt;/h3&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;index range scan에 의한 table access가 table full scan보다 느려지는 지점을 손익분기점이라 함.&lt;/li&gt;
  &lt;li&gt;index rowid에 의한 테이블 액세스는 random인데, full table scan은 sequential 방식으로 이루어짐.&lt;/li&gt;
  &lt;li&gt;디스크 I/O시 index rowid는 single block read, full table scan은 multiblock read 방식.&lt;/li&gt;
  &lt;li&gt;index를 사용한다고 언제나 좋은 효율을 내는 것은 아니다.&lt;/li&gt;
  &lt;li&gt;손익분기점을 극복하기 위한 방법들
    &lt;ul&gt;
      &lt;li&gt;IOT&lt;/li&gt;
      &lt;li&gt;클러스터 테이블&lt;/li&gt;
      &lt;li&gt;파티셔닝&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;테이블-random-액세스-최소화-튜닝&quot;&gt;테이블 Random 액세스 최소화 튜닝&lt;/h2&gt;
&lt;h3 id=&quot;인덱스-컬럼추가&quot;&gt;인덱스 컬럼추가&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;기존 인덱스에 컬럼을 추가하는 것만으로 효과를 볼 수도 있음&lt;/li&gt;
  &lt;li&gt;인덱스 스캔량은 줄지 않지만 테이블 random access 횟수를 줄이기 때문
    &lt;h3 id=&quot;pk인덱스에-컬럼추가&quot;&gt;PK인덱스에 컬럼추가&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;단일 테이블을 PK로 액세스 할 때는 단 한건만 조회, NL조인에서 Inner Table은 random access 부하가 많이 발생함.&lt;/li&gt;
  &lt;li&gt;PK+필터조건을 포함한 새로운 non-unique index 추가해서 pk 제약 설정하면 인덱스 개수 줄일 수 있음.
    &lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;alter&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dept&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;drop&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;primary&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dept_x01&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dept&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deptno&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;alter&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dept&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;constraint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dept_pk&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;primary&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deptno&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dept_x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;h3 id=&quot;컬럼-추가에-따른-클러스터링-팩터-변화&quot;&gt;컬럼 추가에 따른 클러스터링 팩터 변화&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;기존 인덱스에 컬럼을 추가했을 때 클러스터링 팩터가 나빠질 수 있음.&lt;/li&gt;
  &lt;li&gt;object_type처럼 변별력이 좋지 않은 컬럼 뒤에 object_name처럼 변별력이 좋은 컬럼을 추가하면 rowid 이전에 object_name 순으로 정렬되기 때문.
    &lt;h3 id=&quot;인덱스만-읽고-처리&quot;&gt;인덱스만 읽고 처리&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;table access가 발생하지 않도록 모든 필요한 컬럼을 인덱스에 포함시키는 방법
    &lt;h3 id=&quot;버퍼-pinning-효과-활용&quot;&gt;버퍼 Pinning 효과 활용&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;한번 입력된 레코드는 rowid가 바뀌지 않음.&lt;/li&gt;
  &lt;li&gt;rowid를 이용한 레코드 조회가 가능. (where rowid = ~)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TABLE ACCESS (BY USER ROWID)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;select from (select ~ order by rowid) 를 통해 테이블 액세스 가능.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;수동으로-클러스터링-팩터-높히기&quot;&gt;수동으로 클러스터링 팩터 높히기&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;테이블에는 데이터가 무작위로 입력되고, 인덱스는 key순으로 정렬되므로 대게 CF가 좋지 않음.&lt;/li&gt;
  &lt;li&gt;인덱스를 기준으로 테이블을 재생성하여 인위적으로 CF를 좋게 만드는 방법&lt;/li&gt;
  &lt;li&gt;가장 자주 사용되는 인덱스를 기준, 다른 인덱스를 사용하는 쿼리에 영향을 주지 않는지 체크 필요&lt;/li&gt;
  &lt;li&gt;데이터 이관 과정에서도 CF가 나빠질 수 있다.
    &lt;ul&gt;
      &lt;li&gt;기존 입력 시에는 트랜잭션이 발생하는 순서대로 데이터 입력, 데이터를 이관할 때에는 병렬 쿼리를 이용해서 데이터를 흩어놓기 때문.
        &lt;h2 id=&quot;iot-클러스터-테이블-활용&quot;&gt;IOT, 클러스터 테이블 활용&lt;/h2&gt;
        &lt;h3 id=&quot;iot란&quot;&gt;IOT란?&lt;/h3&gt;
        &lt;p&gt;&lt;img src=&quot;/assets/20190103/6.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;
        &lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;primary&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;organization&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;정렬된 상태로 모여 있기 때문에 sequential 방식으로 액세스 가능. 넓은 범위를 액세스 할 때 유리&lt;/li&gt;
  &lt;li&gt;FULL TABLE SCAN 시 자동적으로 ORDERING이 이루어짐.&lt;/li&gt;
  &lt;li&gt;인덱스 세그먼트를 생성하지 않아도 돼 저장공간 절약&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;단점&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;PK로만 정렬이 가능. 추가적인 index 생성 불가능&lt;/li&gt;
  &lt;li&gt;데이터 입력시 성능이 느림&lt;/li&gt;
  &lt;li&gt;데이터 삽입시 인덱스 split 발생빈도가 높아지면 성능이 느려짐.
    &lt;ul&gt;
      &lt;li&gt;IOT가 PK 이외에 많은 컬럼을 갖는다면 리프 블록에 저장해야 할 데이터가 늘어나 split 빈도도 높아짐.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;클러스터링 테이블 불가&lt;/li&gt;
  &lt;li&gt;병렬 작업 불가&lt;/li&gt;
  &lt;li&gt;분산, 복제, 분할 불가&lt;/li&gt;
  &lt;li&gt;LOB불가
    &lt;h3 id=&quot;iot-언제-사용할-것인가&quot;&gt;IOT, 언제 사용할 것인가&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;크기가 작고 NL 조인으로 반복 Lookup 하는 테이블&lt;/li&gt;
  &lt;li&gt;row 수가 많고, column 수가 적은 테이블
    &lt;ul&gt;
      &lt;li&gt;관계형 테이블(방문일시 등)에서 PK는 어짜피 생성해야 하므로, 테이블과 거의 중복된 데이터를 갖게 됨. 차라리 IOT로 구성하는 게 좋다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;넓은 범위를 주로 검색하는 테이블
    &lt;ul&gt;
      &lt;li&gt;Between, Like처럼 넓은 범위를 검색하는 테이블 (통계성 테이블 등)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;데이터 입력과 조회 패턴이 다른 테이블
    &lt;ul&gt;
      &lt;li&gt;등록은 일자별로, 조회는 번호별로&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;partitioned-iot&quot;&gt;Partitioned IOT&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;데이터 수가 늘어나면 인덱스를 사용해도 부담스러움.&lt;/li&gt;
  &lt;li&gt;월별 파티셔닝 등
    &lt;h3 id=&quot;overflow-영역&quot;&gt;Overflow 영역&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;PK 이외 컬럼이 많은 테이블이면 IOT로 구성하기 부적합. 하지만 이용하려면 분리 저장.&lt;/li&gt;
  &lt;li&gt;옵션
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OVERFLOW TABLESPACE&lt;/code&gt;: Overflow 세그먼트가 저장될 테이블스페이스 지정&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PCTHRESHOLD&lt;/code&gt;: 이 값을 초과하면 뒤쪽 컬럼은 overflow 세그먼트에 저장&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INCLUDING&lt;/code&gt;: 여기에 지정된 컬럼까지만 인덱스 블록에 저장. 나머지는 overflow 영역에 저장.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;overflow 영역에도 버퍼 pinning 효과가 나타나기 때문에 연속적으로 같은 overflow 영역을 읽으면 random 블록 I/O 최소화&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;secondary-인덱스&quot;&gt;Secondary 인덱스&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;IOT 레코드 위치는 영구적이지 않기 때문에 secondary 인덱스로부터 IOT 레코드를 가리킬 때 물리적 주소 대신 logical rowid 사용
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Logical Rowid = PK + physical guess&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;physical guess란 인덱스를 생성한 시점에 IOT 레코드가 위치했던 DBA. 분할되면 갱신되지 않음.
&lt;img src=&quot;/assets/20190103/7.png&quot; alt=&quot;Image&quot; /&gt;
①은 physical guess를 통해 레코드 직접 액세스
②는 PK를 통해 IOT 탐색&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PCT_DIRECT ACCESS&lt;/code&gt;: 유효한 physical guess를 가진 비율. 100% 미만이면 바로 PK를 이용해 IOT를 탐색. 재생성하면 100%&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;비휘발성 IOT&lt;/strong&gt; 인 경우 pct_direct_access 값을 100으로 유지.
    &lt;ul&gt;
      &lt;li&gt;읽기전용이라면 항상 100이겠지만, 우측에 지속적으로 값을 입력하는 경우에는 통계정보 수집이 필수.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;휘발성 IOT&lt;/strong&gt; 인 경우 주기적으로 physical guess를 갱신.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;right-growing-iot에서-pct_direct_access가-100미만으로-떨어지는-이유&quot;&gt;Right-Growing IOT에서 pct_direct_access가 100미만으로 떨어지는 이유&lt;/h5&gt;

&lt;p&gt;&lt;img src=&quot;/assets/20190103/8.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;100번 블록이 꽉 차면 101번 블록에 모두 복제하고 100은 루트로 올라감. 새로 추가되는 값들은 102에 입력됨. (physical guess 오류)
&lt;img src=&quot;/assets/20190103/9.png&quot; alt=&quot;Image&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;100번 정보를 103에게 넘겨줄 뿐, 다른 리프 블록에 변화가 없음.(physical guess 영향없음)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;root block은 특별하기 때문에 항상 같은 block이도록 함.&lt;/p&gt;
&lt;h3 id=&quot;인덱스-클러스터-테이블&quot;&gt;인덱스 클러스터 테이블&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;클러스터 키 값이 같은 레코드가 한 블록에 모이도록 저장하는 구조&lt;/li&gt;
  &lt;li&gt;한 블록에 모두 담을 수 없을 때에는 새로운 블럭을 할당해 클러스터 체인으로 연결&lt;/li&gt;
  &lt;li&gt;이미 클러스터값의 기준으로 JOIN되어있는 구조&lt;/li&gt;
  &lt;li&gt;넓은 범위를 검색할 때 유리&lt;/li&gt;
  &lt;li&gt;클러스터 인덱스를 ‘=’ 조건으로 액세스 할 때는 항상 unique scan&lt;/li&gt;
  &lt;li&gt;성능 이슈
    &lt;ul&gt;
      &lt;li&gt;DML 성능 떨어짐. (없던 값을 입력할 때에는 블록 새로 입력받아야 해서 더 느림)&lt;/li&gt;
      &lt;li&gt;전체 데이터 지울 때 Truncate 불가. (클러스터를 truncate해야 함)&lt;/li&gt;
      &lt;li&gt;다중 테이블 클러스터 fullscan시 다른 테이블 데이터까지 스캔&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;SIZE 옵션
    &lt;ul&gt;
      &lt;li&gt;하나의 블록에 여러 클러스터 키가 담길 수 있게. SIZE는 하나의 블록에 담을 최대 클러스터 키 개수를 결정지음.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;해시-클러스터-테이블&quot;&gt;해시 클러스터 테이블&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;해시 함수가 인덱스 역할을 대신하는 것.&lt;/li&gt;
  &lt;li&gt;’=’ 검색만 가능&lt;/li&gt;
  &lt;li&gt;물리적 인덱스를 갖지 않기 때문에 블록 I/O가 덜 발생.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;인덱스-스캔-효율&quot;&gt;인덱스 스캔 효율&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;Sequential 액세스 선택도 높이고, Random 액세스 발생량 줄이기&lt;/p&gt;
  &lt;h3 id=&quot;비교-연산자-종류와-컬럼-순서에-따른-인덱스-레코드의-군집성&quot;&gt;비교 연산자 종류와 컬럼 순서에 따른 인덱스 레코드의 군집성&lt;/h3&gt;
  &lt;ul&gt;
    &lt;li&gt;첫 번째 나타나는 범위검색 조건까지만 만족하는 인덱스 레코드는 모두 연속되게 모여 있지만, 그 이하 조건가지 만족하는 레코드는 비교 연산자 종류에 상관없이 흩어짐.
      &lt;h3 id=&quot;인덱스-선행-컬럼이-가-아닐-때-발생하는-비효율&quot;&gt;인덱스 선행 컬럼이 (=)가 아닐 때 발생하는 비효율&lt;/h3&gt;
    &lt;/li&gt;
    &lt;li&gt;선두 컬럼이 적게 잡힐 수 있는 인덱스 구조를 채택해야 함.
      &lt;h3 id=&quot;between-조건을-in-list로-바꾸었을-때-인덱스-스캔-효율&quot;&gt;Between 조건을 IN-List로 바꾸었을 때 인덱스 스캔 효율&lt;/h3&gt;
    &lt;/li&gt;
    &lt;li&gt;in-list 안에 있는 레코드만큼 수직적 탐색이 일어남.&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INLIST ITERATOR&lt;/code&gt; -&amp;gt; union all과 같다.&lt;/li&gt;
    &lt;li&gt;In list 개수가 많고, 인덱스 높이가 높을 때 비효율이 크다.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;index-skip-scan을-이용한-비효율-해소&quot;&gt;Index Skip Scan을 이용한 비효율 해소&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;인덱스 선두 컬럼이 between이더라도 index skip scan으로 효율성을 높일 수 있음.
&lt;img src=&quot;/assets/20190103/10.png&quot; alt=&quot;Image&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;index(판매월, 판매구분)&lt;/li&gt;
  &lt;li&gt;where 판매월 between ‘200801’ and ‘200802’ and 판매구분=’A’
    &lt;h3 id=&quot;범위검색-조건을-남용할-때-발생하는-비효율&quot;&gt;범위검색 조건을 남용할 때 발생하는 비효율&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;’=’검색으로 하면 짧게 검색할 상황에, like 연산자를 사용해서 조회 성능에 영향을 끼칠 수도 있다.
    &lt;h3 id=&quot;같은-컬럼에-두-개의-범위검색-조건-시-주의사항&quot;&gt;같은 컬럼에 두 개의 범위검색 조건 시 주의사항&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;rowid를 conctenation하면 &lt;em&gt;rowidtochar&lt;/em&gt; 함수를 통해 문자열로 변환되어서, rowid 그대로 비교할 때랑 정렬 순서가 다름.&lt;/li&gt;
  &lt;li&gt;인덱스를 스캔하면서 rowid를 필터링할 때에는 비효율 발생.
    &lt;ul&gt;
      &lt;li&gt;인덱스 rowid는 leaf 블록에만 있기 때문에 이를 필터링하려면 일단 다른 조건으로 leaf block을 찾아가야 함. 거기서 rowid를 필터링해야 함.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;between과-like-스캔-범위-비교&quot;&gt;Between과 Like 스캔 범위 비교&lt;/h3&gt;
&lt;p&gt;결론: &lt;strong&gt;between이 like보다 더 넓은 범위를 스캔하는 경우는 없다.&lt;/strong&gt;
&lt;img src=&quot;/assets/20190103/11.png&quot; alt=&quot;Image&quot; /&gt;
&lt;strong&gt;쿼리 1&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매집계&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매월&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;between&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'200901'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'200902'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매구분&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'A'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;쿼리 2&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매집계&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매월&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;like&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2009%'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매구분&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'A'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;쿼리 3&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매집계&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매월&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'200901'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매월&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'200903'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매구분&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'A'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190103/12.png&quot; alt=&quot;Image&quot; /&gt;
&lt;strong&gt;쿼리 4&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매집계&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매월&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;between&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'200901'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'200912'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매구분&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'B'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;판매월=200901&lt;/strong&gt;이고, 판매구분=B인 레코드를 목표로 수직적 탐색.
&lt;strong&gt;쿼리 5&lt;/strong&gt;
    &lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매집계&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매월&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;like&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2009%'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매구분&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'B'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;판매월=2009&lt;/strong&gt;이고, 판매구분=B인 레코드를 목표로 수직적 탐색.
&lt;strong&gt;쿼리 6&lt;/strong&gt;
    &lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매집계&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매월&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;between&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'200900'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'200902'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;판매구분&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'B'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;h3 id=&quot;선분이력의-인덱스-스캔-효율&quot;&gt;선분이력의 인덱스 스캔 효율&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;과거데이터 조회시 &lt;strong&gt;시작일 + 종료일&lt;/strong&gt; 로 인덱스 구성&lt;/li&gt;
  &lt;li&gt;최근데이터 조회시 &lt;strong&gt;종료일 + 시작일&lt;/strong&gt; 로 인덱스 구성
&lt;img src=&quot;/assets/20190103/13.png&quot; alt=&quot;Image&quot; /&gt;
    &lt;h3 id=&quot;access-predicate와-filter-predicate&quot;&gt;Access Predicate와 Filter Predicate&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;인덱스를 경유할 때에는
    &lt;ul&gt;
      &lt;li&gt;인덱스 단계에서의 access predicate&lt;/li&gt;
      &lt;li&gt;인덱스 단계에서의 filter predicate&lt;/li&gt;
      &lt;li&gt;테이블 단계에서의 filter predicate&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;인덱스를 경유하지 않을 때에는
    &lt;ul&gt;
      &lt;li&gt;테이블 단계에서의 filter predicate&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;인덱스 단계의 access predicate는 인덱스 스캔 범위를 결정하는 데에 영향을 미치는 조건절
    &lt;ul&gt;
      &lt;li&gt;아래 경우에는 인덱스 스캔 범위를 결정하는 데에 영향을 미치지 않으므로 access predicate에서 제외됨.
        &lt;ul&gt;
          &lt;li&gt;좌변 컬럼 가공&lt;/li&gt;
          &lt;li&gt;왼쪽, 양쪽 ‘%’ 사용하는 like 조건절&lt;/li&gt;
          &lt;li&gt;같은 컬럼 조건절이 두개 이상일 때, 인덱스를 타지 않는 조건절&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;이 경우를 제외하면 &lt;strong&gt;수직적 탐색 과정에서 모든 인덱스 컬럼을 비교 조건으로 사용&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;인덱스 단계에서의 filter predicate는 테이블로의 액세스 여부를 결정짓는 조건절&lt;/li&gt;
  &lt;li&gt;테이블 단계에서의 filter predicate는 테이블을 액세스하고 나서 최종 결과집합으로 포함여부를 결정짓는 조건절
    &lt;h3 id=&quot;index-fragmentation&quot;&gt;Index Fragmentation&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;Index Skew: 인덱스 엔트리가 왼쪽, 오른쪽에 치우치는 현상. Index Full Scan시 성능이 나빠짐.&lt;/li&gt;
  &lt;li&gt;Index Spare: skew처럼 블록이 비면 freelist로 반환되겠지만, spare는 반환되지 않고 남아있는 상태.&lt;/li&gt;
  &lt;li&gt;Index Rebuild: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alter index t_idx coalesce;&lt;/code&gt; 를 통해 여러 인덱스 블록을 merge하고, 남은 블록을 freelist에 반환.
    &lt;ul&gt;
      &lt;li&gt;하지만 index block에는 어느정도 공간을 남겨두는 것이 좋다. 전혀 없으면 인덱스 분할이 일어나서 TX 이벤트 발생.&lt;/li&gt;
      &lt;li&gt;index rebuild를 고려하면 좋을 경우
        &lt;ul&gt;
          &lt;li&gt;인덱스 분할에 의한 contention이 높을 때&lt;/li&gt;
          &lt;li&gt;자주 사용되는 인덱스 스캔 효율을 높일 때. 인덱스 height이 증가했을 때&lt;/li&gt;
          &lt;li&gt;대량의 delete 작업 후 다시 레코드 입력까지 오래걸릴 때&lt;/li&gt;
          &lt;li&gt;총 레코드 수가 일정한데도 인덱스가 계속 커질 때
            &lt;h2 id=&quot;인덱스-설계&quot;&gt;인덱스 설계&lt;/h2&gt;
            &lt;h3 id=&quot;필수-선택-기준&quot;&gt;필수 선택 기준&lt;/h3&gt;
            &lt;p&gt;Index Range Scan을 고려해야 함.&lt;/p&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;조건절에 항상 사용되거나, 자주 사용되는 컬럼들을 선택&lt;/li&gt;
  &lt;li&gt;’=’조건으로 자주 조회되는 컬럼들을 앞쪽에 배치
    &lt;h3 id=&quot;결합-인덱스-컬럼-순서-결정&quot;&gt;결합 인덱스 컬럼 순서 결정&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;인덱스 생성 여부를 결정할 때, 선택도가 충분히 낮은지 확인. 선택도가 높은 인덱스는 효용가치가 없음.
    &lt;ul&gt;
      &lt;li&gt;선택도가 높다 = 변별력이 떨어진다
        &lt;h5 id=&quot;선택도가-액세스-효율에-영향을-주지-않는-경우&quot;&gt;선택도가 액세스 효율에 영향을 주지 않는 경우&lt;/h5&gt;
        &lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IDX01&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;고객등급&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;고객번호&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;거래일자&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;거래유형&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;상품번호&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;고객등급, 고객번호는 ‘=’조건, 거래일자는 between 일 때 위와 같은 인덱스
    &lt;ul&gt;
      &lt;li&gt;between인 거래일자 뒤의 거래유형, 상품번호는 어짜피 인덱스 필터 조건으로 사용되므로 변별력을 따질 필요가 없음.&lt;/li&gt;
      &lt;li&gt;’=’, ‘=’, ‘between’이므로 범위 검색조건 전까지 인덱스가 다 모여있음.&lt;/li&gt;
      &lt;li&gt;변별력이 좋지 않은 고객등급을 앞에 둬도 스캔 범위가 어짜피 최소화.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;상황에-따라-유--불리가-바뀌는-경우&quot;&gt;상황에 따라 유 · 불리가 바뀌는 경우&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190103/14.png&quot; alt=&quot;Image&quot; /&gt;
&lt;img src=&quot;/assets/20190103/15.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;선택도가 높은 컬럼(고객등급)을 선두에 두면 누락되거나, 범위검색 조건에 사용되더라도 Index Skip Scan이나 IN-List를 활용할 수 있어서 유리.&lt;/li&gt;
  &lt;li&gt;선택도가 낮은 컬럼을 선두에 두면 범위검색일 때 불리하지만, 입력 값의 범위가 좁다면 유리.
    &lt;h3 id=&quot;sort-생략을-위한-컬럼-추가&quot;&gt;Sort 생략을 위한 컬럼 추가&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;인덱스를 이용해 소트 연산을 대체하려면 인덱스 컬럼 순서와 같은 순서로 누락 없이 order by에 기술해야 함.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;비트맵-인덱스&quot;&gt;비트맵 인덱스&lt;/h2&gt;
&lt;h3 id=&quot;비트맵-인덱스-기본-구조&quot;&gt;비트맵 인덱스 기본 구조&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/assets/20190103/16.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;키 값별로 하나의 비트맵 레코드를 갖음. 비트맵 상의 각 비트가 하나의 테이블 레코드와 매핑됨.&lt;/li&gt;
  &lt;li&gt;키 값의 수가 많으면 B*Index 구조를 띄게 되고, 일반적인 인덱스보다 더 많은 공간을 차지할 수 있어 부적합.&lt;/li&gt;
  &lt;li&gt;비트맵 압축: 압축으로 인해 시작 rowid와 종료 rowid가 달라질 수 있음. checksum을 이용한 압축도 가능. 시작과 종료 rowid만 알고 있으면 bitwise 연산에는 문제가 없음.
    &lt;h3 id=&quot;비트맵-인덱스-활용&quot;&gt;비트맵 인덱스 활용&lt;/h3&gt;
  &lt;/li&gt;
  &lt;li&gt;distinct value가 적을 때(성별) 효율이 좋다.&lt;/li&gt;
  &lt;li&gt;하나의 비트맵 인덱스보다, 여러 비트맵 인덱스를 동시에 사용할 수 있을 때 효율이 크다.
&lt;img src=&quot;/assets/20190103/17.png&quot; alt=&quot;Image&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 03 Jan 2019 18:46:00 +0000</pubDate>
        <link>http://garimoo.github.io/study/2019/01/03/oraclebookstudy_01.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/study/2019/01/03/oraclebookstudy_01.html</guid>
        
        
        <category>Study</category>
        
      </item>
    
      <item>
        <title>[ORACLE] RANK, DENSE_RANK, ROW_NUMBER 함수</title>
        <description>&lt;p&gt;RANK, DENSE_RANK, ROW_NUMBER 함수는 오라클에서 모두 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;순위를 반환하는 함수&lt;/code&gt; 이다.
하지만 중복 값이 발생했을 때 반환하는 값에 차이가 있다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;RANK(): 중복 순위 다음은 항상 개수만큼 건너뛰고 반환&lt;/li&gt;
  &lt;li&gt;DENSE_RANK(): 중복 순위 다음은 개수 상관 없이 다음 순서 반환&lt;/li&gt;
  &lt;li&gt;ROW_NUMBER: 중복 상관 없이 반환
&lt;img src=&quot;/assets/20181206/1.png&quot; alt=&quot;Image&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;위의 결과는 salary 값으로 정렬하였고, ranking1은 RANK()함수, ranking2는 DENSE_RANK()함수, ranking3은 ROW_NUMBER() 함수로 뽑은 결과이다.&lt;/p&gt;
</description>
        <pubDate>Thu, 06 Dec 2018 15:16:00 +0000</pubDate>
        <link>http://garimoo.github.io/database/2018/12/06/ranking.html</link>
        <guid isPermaLink="true">http://garimoo.github.io/database/2018/12/06/ranking.html</guid>
        
        
        <category>Database</category>
        
      </item>
    
  </channel>
</rss>
