<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>DATA 전문가로 가는 길</title>
    <link>https://estenpark.tistory.com/</link>
    <description>데이터 분석을 활용한 정보를 공유하는 블로그</description>
    <language>ko</language>
    <pubDate>Fri, 10 Apr 2026 09:16:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>EstenPark</managingEditor>
    <item>
      <title>[MySQL/MariaDB] 대용량 페이징 처리 활용 방안(Pagination, 커버링 인덱스)</title>
      <link>https://estenpark.tistory.com/389</link>
      <description>&lt;p&gt;대용량 테이블에서 페이징 처리 시 맨 뒤쪽으로 갈수록 느려지는 현상을 보실 수 있습니다.&amp;nbsp; 그러한&amp;nbsp;이유는&amp;nbsp;어느 순간까지는&amp;nbsp;인덱스를&amp;nbsp;활용해서&amp;nbsp;결과를&amp;nbsp;출력&amp;nbsp;가능&amp;nbsp;하지만,&amp;nbsp;MySQL/MariaDB에서는&amp;nbsp;filesort가&amp;nbsp;발생하면서&amp;nbsp;성능&amp;nbsp;저하가&amp;nbsp;발생합니다. &lt;br /&gt;&lt;br /&gt;옵티마이저는 정렬을 위해 인덱스 사용이 가능한지 확인하고, 가능하다면 'Filesort' 과정 없이 인덱스 순으로 결과를 반환하게 됩니다. 인덱스를 사용할 수 없다면 WHERE 조건에 일치하는 레코드를 검색해 정렬 버퍼에 저장하면서 정렬 처리(Filesort)를 합니다.&amp;nbsp; Order&amp;nbsp;by,&amp;nbsp;Group&amp;nbsp;By를&amp;nbsp;처리할&amp;nbsp;때&amp;nbsp;인덱스를&amp;nbsp;처리하지&amp;nbsp;못할&amp;nbsp;경우에는&amp;nbsp;filesort&amp;nbsp;알고리즘을&amp;nbsp;통해&amp;nbsp;정렬하게&amp;nbsp;됩니다.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Data&amp;nbsp;-&amp;gt;&amp;nbsp;Sort&amp;nbsp;Buffer&amp;nbsp;-&amp;gt;&amp;nbsp;Temp&amp;nbsp;File&amp;nbsp;-&amp;gt;&amp;nbsp;Result&amp;nbsp;File&amp;nbsp;-&amp;gt;&amp;nbsp;Read&amp;nbsp;Random&amp;nbsp;Buffer&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.&amp;nbsp;테스트&amp;nbsp;환경&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;OS&amp;nbsp;:&amp;nbsp;CentOS&amp;nbsp;7.5(CPU&amp;nbsp;:&amp;nbsp;Intel(R)&amp;nbsp;Xeon(R)&amp;nbsp;CPU&amp;nbsp;E3-1220&amp;nbsp;v3&amp;nbsp;@&amp;nbsp;3.10GHz(4&amp;nbsp;core),&amp;nbsp;Memory&amp;nbsp;:&amp;nbsp;32GB) &lt;br /&gt;DB&amp;nbsp;:&amp;nbsp;MariaDB&amp;nbsp;10.3.8 &lt;br /&gt;Parameter&amp;nbsp;:&amp;nbsp; &lt;br /&gt;innodb_buffer_pool_size&amp;nbsp;=15G &lt;br /&gt;sort_buffer_size&amp;nbsp;=&amp;nbsp;1048576 &lt;br /&gt;innodb_sort_buffer_size&amp;nbsp;=&amp;nbsp;1048576 &lt;br /&gt;max_length_for_sort_data&amp;nbsp;=&amp;nbsp;1024 &lt;br /&gt;read_rnd_buffer_size&amp;nbsp;=&amp;nbsp;8388608 &lt;br /&gt;&lt;br /&gt;Table&amp;nbsp;및&amp;nbsp;인덱스&amp;nbsp;정보&amp;nbsp;:&amp;nbsp; &lt;br /&gt;EST_USER_CONN_LOGIN&amp;nbsp;전체&amp;nbsp;건수&amp;nbsp;:&amp;nbsp;2000546&lt;/p&gt;
&lt;p&gt;EST_USER_CONN_LOGIN&amp;nbsp;Primary&amp;nbsp;Key &lt;br /&gt;&amp;nbsp;&amp;nbsp;- USER_CONN_HIST_SEQ_NO, type=int(9), auto_increment&lt;br /&gt;EST_USER_CONN_LOGIN_IX02&amp;nbsp;인덱스&amp;nbsp;(결합 인덱스) &lt;br /&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;USER_LOGIN_DTTM,&amp;nbsp;type=datetime &lt;br /&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;USER_LOGIN_RST_TP_CODE,&amp;nbsp;type=varchar&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2.&amp;nbsp;일반적인&amp;nbsp;페이징&amp;nbsp;쿼리&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;약 200만 건 테이블에서 뒤쪽에 있는 데이터를 추출하려고 할 때 상당히 오래 걸리는 것을 보실 수 있습니다. 그&amp;nbsp;이유는&amp;nbsp;인덱스를&amp;nbsp;제대로&amp;nbsp;사용하지&amp;nbsp;못하고,&amp;nbsp;데이터&amp;nbsp;블록을&amp;nbsp;정렬해서&amp;nbsp;가져오기&amp;nbsp;때문입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT    A.USER_LOGIN_DTTM, A.USER_ID, A.USER_NM
FROM      EST_USER_CONN_LOGIN A
ORDER BY  A.USER_LOGIN_DTTM
LIMIT     0, 10    /* 앞쪽 데이터를 엑세스 할 경우 속도 영향 없습니다. */
;

수행 시간 : 2ms

SELECT    A.USER_LOGIN_DTTM, A.USER_ID, A.USER_NM
FROM      EST_USER_CONN_LOGIN A
ORDER BY  A.USER_LOGIN_DTTM
LIMIT     2000536, 10 /* 맨 뒤쪽 데이터를 엑세스 할 경우 속도 지연 현상이 발생 합니다.*/
;

수행 시간 : 1m 44s

ANALYZE   FORMAT=JSON 결과
{
  &quot;query_block&quot;: {
    &quot;select_id&quot;: 1,
    &quot;r_loops&quot;: 1,
    &quot;r_total_time_ms&quot;: 3347.9,
    &quot;read_sorted_file&quot;: {
      &quot;r_rows&quot;: 2e6,
      &quot;filesort&quot;: {                       -&amp;gt;&amp;gt; Filesort 발생!!
        &quot;sort_key&quot;: &quot;A.USER_LOGIN_DTTM&quot;,
        &quot;r_loops&quot;: 1,
        &quot;r_total_time_ms&quot;: 2968.6,
        &quot;r_limit&quot;: 2000546,
        &quot;r_used_priority_queue&quot;: false,
        &quot;r_output_rows&quot;: 2000546,
        &quot;r_sort_passes&quot;: 84,
        &quot;r_buffer_size&quot;: &quot;1023Kb&quot;,
        &quot;table&quot;: {
          &quot;table_name&quot;: &quot;A&quot;,
          &quot;access_type&quot;: &quot;ALL&quot;,
          &quot;r_loops&quot;: 1,
          &quot;rows&quot;: 1950976,
          &quot;r_rows&quot;: 2e6,
          &quot;r_total_time_ms&quot;: 693.89,
          &quot;filtered&quot;: 100,
          &quot;r_filtered&quot;: 100
        }
      }
    }
  }
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blog_01.png&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;166&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tutR5/btqFTSHtdzX/3qvJvDVef5QnN5sWlmP6Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tutR5/btqFTSHtdzX/3qvJvDVef5QnN5sWlmP6Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tutR5/btqFTSHtdzX/3qvJvDVef5QnN5sWlmP6Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtutR5%2FbtqFTSHtdzX%2F3qvJvDVef5QnN5sWlmP6Tk%2Fimg.png&quot; data-filename=&quot;blog_01.png&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;166&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. 튜닝한 페이징 쿼리(Covering Index)&lt;/b&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;Covering&amp;nbsp;Index(커버링&amp;nbsp;인덱스)를&amp;nbsp;활용해서&amp;nbsp;대용량&amp;nbsp;데이터를&amp;nbsp;처리하는&amp;nbsp;방법으로&amp;nbsp;성능을&amp;nbsp;높일&amp;nbsp;수&amp;nbsp;있습니다. &lt;br /&gt;커버링&amp;nbsp;인덱스는&amp;nbsp;데이터를&amp;nbsp;인덱스에서만&amp;nbsp;추출할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;인덱스를&amp;nbsp;의미하며,&amp;nbsp;B-Tree&amp;nbsp;스캔만으로&amp;nbsp;원하는&amp;nbsp;데이터를&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;있습니다.(데이터&amp;nbsp;블록&amp;nbsp;사용&amp;nbsp;안 함)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT    B.USER_LOGIN_DTTM, B.USER_ID, B.USER_NM
FROM      (
          SELECT    A.USER_CONN_HIST_SEQ_NO
          FROM      EST_USER_CONN_LOGIN A
          ORDER BY  A.USER_LOGIN_DTTM
          LIMIT     2000536, 10
          ) A
          STRAIGHT_JOIN EST_USER_CONN_LOGIN B ON (A.USER_CONN_HIST_SEQ_NO = B.USER_CONN_HIST_SEQ_NO)
          ;

수행 시간 : 389ms

ANALYZE   FORMAT=JSON 결과
{
  &quot;query_block&quot;: {
    &quot;select_id&quot;: 1,
    &quot;r_loops&quot;: 1,
    &quot;r_total_time_ms&quot;: 0.067,
    &quot;table&quot;: {
      &quot;table_name&quot;: &quot;&quot;,
      &quot;access_type&quot;: &quot;ALL&quot;,
      &quot;r_loops&quot;: 1,
      &quot;rows&quot;: 10,
      &quot;r_rows&quot;: 10,
      &quot;r_total_time_ms&quot;: 0.0026,
      &quot;filtered&quot;: 100,
      &quot;r_filtered&quot;: 100,
      &quot;materialized&quot;: {
        &quot;query_block&quot;: {
          &quot;select_id&quot;: 2,
          &quot;r_loops&quot;: 1,
          &quot;r_total_time_ms&quot;: 0.0261,
          &quot;table&quot;: {
            &quot;table_name&quot;: &quot;A&quot;,
            &quot;access_type&quot;: &quot;index&quot;,
            &quot;key&quot;: &quot;EST_USER_CONN_LOGIN_IX02&quot;,
            &quot;key_length&quot;: &quot;10&quot;,
            &quot;used_key_parts&quot;: [&quot;USER_LOGIN_DTTM&quot;, &quot;USER_LOGIN_RST_TP_CODE&quot;],
            &quot;r_loops&quot;: 1,
            &quot;rows&quot;: 1950976,
            &quot;r_rows&quot;: 10,
            &quot;r_total_time_ms&quot;: 0.0157,
            &quot;filtered&quot;: 100,
            &quot;r_filtered&quot;: 100,
            &quot;using_index&quot;: true
          }
        }
      }
    },
    &quot;table&quot;: {
      &quot;table_name&quot;: &quot;B&quot;,
      &quot;access_type&quot;: &quot;eq_ref&quot;,
      &quot;possible_keys&quot;: [&quot;PRIMARY&quot;],
      &quot;key&quot;: &quot;PRIMARY&quot;,
      &quot;key_length&quot;: &quot;4&quot;,
      &quot;used_key_parts&quot;: [&quot;USER_CONN_HIST_SEQ_NO&quot;],
      &quot;ref&quot;: [&quot;A.USER_CONN_HIST_SEQ_NO&quot;],
      &quot;r_loops&quot;: 10,
      &quot;rows&quot;: 1,
      &quot;r_rows&quot;: 1,
      &quot;r_total_time_ms&quot;: 0.0201,
      &quot;filtered&quot;: 100,
      &quot;r_filtered&quot;: 100
    }
  }
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blog_02.png&quot; data-origin-width=&quot;1769&quot; data-origin-height=&quot;420&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XuIR1/btqFRAAxpUV/0IwQ3tC0ZjO5iWl2WHaHs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XuIR1/btqFRAAxpUV/0IwQ3tC0ZjO5iWl2WHaHs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XuIR1/btqFRAAxpUV/0IwQ3tC0ZjO5iWl2WHaHs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXuIR1%2FbtqFRAAxpUV%2F0IwQ3tC0ZjO5iWl2WHaHs0%2Fimg.png&quot; data-filename=&quot;blog_02.png&quot; data-origin-width=&quot;1769&quot; data-origin-height=&quot;420&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Covering Index를 사용하기 전에는 100000건부터 10건을 가져올 때 &quot;Filesort&quot;가 발생했지만, Covering Index를 활용하게 되면 &quot;Using Index&quot;를 확인할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가장&amp;nbsp;마지막에&amp;nbsp;존재하는&amp;nbsp;데이터를&amp;nbsp;가져올&amp;nbsp;때&amp;nbsp;1m&amp;nbsp;44s에서&amp;nbsp;389ms로&amp;nbsp;단축된&amp;nbsp;것을&amp;nbsp;보실&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;결과적으로&amp;nbsp;Primary&amp;nbsp;Key를&amp;nbsp;가져와서&amp;nbsp;인덱스&amp;nbsp;접근만으로&amp;nbsp;데이터를&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;한&amp;nbsp;것이&amp;nbsp;큰&amp;nbsp;효과를&amp;nbsp;보게&amp;nbsp;되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;4. 데이터 범위&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 내용은 화면 기획자, 개발 담당자와 긴밀한 협업이 필요한 내용입니다. 데이터 모델링을 할 때 데이터의 범위를 미리 생각해서 인덱스를 활용하는 방법입니다. 전체를 대상으로 데이터를 가져오는 경우는 많이 적기 때문에 화면을 통해서 제어를 한다면 전체 데이터 200만 건을 모두 확인할 필요 없이 적절한 범위 내에서 데이터를 가져올 수 있습니다. 조회 조건에서 기간을 3개월만 조회 가능하도록 정의하면&amp;nbsp;아래처럼 WHERE 조건절에 범위를 줄일 수 있습니다.&amp;nbsp; 커버링&amp;nbsp;인텍스를&amp;nbsp;활용한다고&amp;nbsp;해도&amp;nbsp;데이터&amp;nbsp;양이&amp;nbsp;늘어날수록&amp;nbsp;조금씩&amp;nbsp;느려지게&amp;nbsp;되는데&amp;nbsp;아래와&amp;nbsp;같이&amp;nbsp;범위를&amp;nbsp;정하게&amp;nbsp;되면&amp;nbsp;동일한&amp;nbsp;속도를&amp;nbsp;유지할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;최종적으로는&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;55ms&lt;/b&gt;&lt;/span&gt;로&amp;nbsp;줄이게&amp;nbsp;되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT    B.USER_LOGIN_DTTM, B.USER_ID, B.USER_NM
FROM      (
          SELECT    A.USER_CONN_HIST_SEQ_NO
          FROM      EST_USER_CONN_LOGIN A
          WHERE     A.USER_LOGIN_DTTM BETWEEN STR_TO_DATE( CONCAT('20200201', '00:00'), '%Y%m%d%H:%i')   /* 조회 조건 활용 */
                                      AND STR_TO_DATE(CONCAT('20200430', '23:59'),'%Y%m%d%H:%i')
          ORDER BY  A.USER_LOGIN_DTTM
          LIMIT     200000, 10
          ) A
          STRAIGHT_JOIN EST_USER_CONN_LOGIN B ON (A.USER_CONN_HIST_SEQ_NO = B.USER_CONN_HIST_SEQ_NO)
          ;

수행 시간 : 55ms

&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;5. ROW_COUNT( ), FOUND_ROWS( ) 적절한 활용&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;목록을&amp;nbsp;보여주는&amp;nbsp;화면의&amp;nbsp;경우&amp;nbsp;현재 건수/전체 건수와&amp;nbsp;같이&amp;nbsp;표현하는&amp;nbsp;경우가&amp;nbsp;많은데&amp;nbsp;그럴&amp;nbsp;때&amp;nbsp;전체&amp;nbsp;조회건수를&amp;nbsp;동작하기&amp;nbsp;위해서&amp;nbsp;같은&amp;nbsp;쿼리를&amp;nbsp;COUNT(*)&amp;nbsp;함수를&amp;nbsp;활용해서&amp;nbsp;실행하게&amp;nbsp;되면&amp;nbsp;비효율이&amp;nbsp;발생할&amp;nbsp;수&amp;nbsp;있습니다. &lt;br /&gt;그러한&amp;nbsp;부분을&amp;nbsp;개선해주기&amp;nbsp;위해서&amp;nbsp;ROW_COUNT(&amp;nbsp;),&amp;nbsp;FOUND_ROWS(&amp;nbsp;)&amp;nbsp;적절히&amp;nbsp;사용해도&amp;nbsp;좋습니다..&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;FOUND_ROWS()&amp;nbsp;함수는&amp;nbsp;SELECT&amp;nbsp;수행&amp;nbsp;후&amp;nbsp;결과를&amp;nbsp;리턴&amp;nbsp;받으며, &lt;br /&gt;ROW_COUNT()&amp;nbsp;함수는&amp;nbsp;DML(Delete,&amp;nbsp;Update,&amp;nbsp;Insert)&amp;nbsp;문장을&amp;nbsp;수행 시&amp;nbsp;결과를&amp;nbsp;리턴합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;6. 결론&amp;nbsp; &lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;MySQL/MariaDB에서 대용량 데이터를 페이징 할 때는 커버링 인덱스를 적극적으로 활용하고, 화면 기획 담당자, 개발 담당자 협업을 통해서 조건에 대한 범위를 줄이고자 노력한다면 데이터가 늘어나도 동일한 속도를 유지할 수 있습니다.&amp;nbsp; 전체&amp;nbsp;건수를&amp;nbsp;활용할&amp;nbsp;때는&amp;nbsp;ROW_COUNT(&amp;nbsp;),&amp;nbsp;FOUND_ROW(&amp;nbsp;)&amp;nbsp;활용해보는&amp;nbsp;것도&amp;nbsp;좋습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;7. 참고&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://ktdsoss.tistory.com/423&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ktdsoss.tistory.com/423&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gywn.net/2012/04/mysql-covering-index/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;gywn.net/2012/04/mysql-covering-index/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.lulab.net/database/optimize-pagination-sql-by-join-instead-of-limit/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;blog.lulab.net/database/optimize-pagination-sql-by-join-instead-of-limit/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://elky84.github.io/2018/10/05/mysql/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;elky84.github.io/2018/10/05/mysql/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Data Architecture/Tunning</category>
      <category>index</category>
      <category>mariaDB</category>
      <category>MySQL</category>
      <category>pagination</category>
      <category>tunning</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/389</guid>
      <comments>https://estenpark.tistory.com/389#entry389comment</comments>
      <pubDate>Tue, 21 Jul 2020 14:17:21 +0900</pubDate>
    </item>
    <item>
      <title>[MySQL/MariaDB] 불필요한 Table Access와 인덱스 컬럼 변형에 대한 튜닝 사례</title>
      <link>https://estenpark.tistory.com/388</link>
      <description>&lt;p&gt;MySQL/MariaDB에서는 UPDATE 실행 계획을 볼 수 없습니다. 그래서 UPDATE 문법을 SELECT 문법으로 변경해서 실행 계획을 확인해야 합니다. 주기적으로 수행(매 10초)하는 쿼리라서 최대한 Sending&amp;nbsp;data를 줄이지 않으면 성능에 문제가 발생할 수 있었습니다. 개발자가 작성한 쿼리를 튜닝한 사례입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 EST_DB_SVC_CPS 테이블의 인덱스 정보입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Primary Key : EST_NO, EST_SVC_NO&lt;/li&gt;
&lt;li&gt;Non&amp;nbsp;Unique&amp;nbsp;Index&amp;nbsp;:&amp;nbsp;CONN_TEST_TASK_UPDT_DTTM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;1. 원본 쿼리&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1.1. 실행 계획 해석&lt;/b&gt;&lt;br /&gt;실행계획을&amp;nbsp;보면&amp;nbsp;&quot;EST_DB_SVC_CPS&quot;&amp;nbsp;테이블을&amp;nbsp;Table&amp;nbsp;Full&amp;nbsp;Scan&amp;nbsp;하고&amp;nbsp;있습니다.(type=ALL) &lt;br /&gt;처음&amp;nbsp;조인하는&amp;nbsp;테이블에서&amp;nbsp;다음&amp;nbsp;읽어야&amp;nbsp;할&amp;nbsp;테이블의&amp;nbsp;Primary&amp;nbsp;Key,&amp;nbsp;Unique&amp;nbsp;Key&amp;nbsp;컬럼을&amp;nbsp;사용해서&amp;nbsp;두 번째&amp;nbsp;테이블에서&amp;nbsp;출력되는&amp;nbsp;레코드가&amp;nbsp;1건으로&amp;nbsp;판단합니다.(type=eq_ref) &lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.2. 성능 문제점&lt;/b&gt;&lt;br /&gt;EST_DB_SVC_CPS&amp;nbsp;테이블을&amp;nbsp;두번&amp;nbsp;액세스&amp;nbsp;하는&amp;nbsp;비효율이&amp;nbsp;발생했습니다.&amp;nbsp; &lt;br /&gt;&quot;DATE_ADD(SQ2.CONN_TEST_TASK_UPDT_DTTM&amp;nbsp;,&amp;nbsp;INTERVAL&amp;nbsp;+&amp;nbsp;180&amp;nbsp;SECOND&amp;nbsp;)&quot;인덱스&amp;nbsp;컬럼을&amp;nbsp;변경하면서&amp;nbsp;적절하게&amp;nbsp;인덱스를&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;없었습니다. &lt;br /&gt;&amp;nbsp;
 &lt;pre class=&quot;brush:sql&quot;&gt;
/* UPDATE 원본 쿼리 */
UPDATE    EST_DB_SVC_CPS A
SET       A.CONN_TEST_TASK_GRP_NO = NULL
WHERE     EXISTS
            (SELECT    'O'
             FROM      (SELECT    SQ2.EST_NO, SQ2.EST_SVC_NO
                        FROM      EST_DB_SVC_CPS SQ2
                        WHERE     SQ2.CONN_TEST_TASK_GRP_NO IS NOT NULL
                        AND       DATE_ADD(SQ2.CONN_TEST_TASK_UPDT_DTTM , INTERVAL + 180 SECOND ) &lt; CURRENT_TIMESTAMP() ) SQ1 
             WHERE     SQ1.EST_NO = A.EST_NO
             AND       SQ1.EST_SVC_NO = A.EST_SVC_NO);

/* 튜닝을 위해서 SELECT로 변경한 원본 쿼리 */
EXPLAIN   EXTENDED 
SELECT    A.CONN_TEST_TASK_GRP_NO
FROM      EST_DB_SVC_CPS A
WHERE     EXISTS
            (SELECT    'O'
             FROM      (SELECT    SQ2.EST_NO, SQ2.EST_SVC_NO
                        FROM      EST_DB_SVC_CPS SQ2
                        WHERE     SQ2.CONN_TEST_TASK_GRP_NO IS NOT NULL
                        AND       DATE_ADD(SQ2.CONN_TEST_TASK_UPDT_DTTM , INTERVAL + 180 SECOND ) &lt; CURRENT_TIMESTAMP() ) SQ1 
             WHERE     SQ1.EST_NO = A.EST_NO
             AND       SQ1.EST_SVC_NO = A.EST_SVC_NO);
          
id|select_type|table|type  |possible_keys                    |key    |key_len|ref                                |ROWS  |filtered|Extra      |
--|-----------|-----|------|---------------------------------|-------|-------|-----------------------------------|------|--------|-----------|
 1|PRIMARY    |A    |ALL   |PRIMARY,EST_DB_SVC_CPS_FK01      |       |       |                                   | 12041|     100|           |
 1|PRIMARY    |SQ2  |eq_ref|PRIMARY,EST_DB_SVC_CPS_FK01      |PRIMARY|8      |ESTDB.A.EST_NO,ESTDB.A.EST_SVC_NO  |   1  |     100|Using where|

MariaDB [ESTDB]&gt; show profile for query 4;
+------------------------+----------+
| Status                 | Duration |
+------------------------+----------+
| Starting               | 0.000050 |
| Checking permissions   | 0.000014 |
| Opening tables         | 0.000012 |
| After opening tables   | 0.000013 |
| System lock            | 0.000004 |
| Table lock             | 0.000005 |
| Init                   | 0.000046 |
| Optimizing             | 0.000030 |
| Statistics             | 0.000027 |
| Preparing              | 0.000017 |
| Executing              | 0.000012 |
| Sending data           | 0.081115 |   -&gt;&gt; 해당 항목을 줄이는 게 튜닝 목표
| End of update loop     | 0.000014 |
| Query end              | 0.000004 |
| Commit                 | 0.000006 |
| Closing tables         | 0.000003 |
| Removing tmp table     | 0.000005 |
| Closing tables         | 0.000004 |
| Unlocking tables       | 0.000003 |
| Closing tables         | 0.000018 |
| Starting cleanup       | 0.000003 |
| Freeing items          | 0.000027 |
| Updating status        | 0.000015 |
| Reset for next command | 0.000011 |
+------------------------+----------+
24 rows in set (0.000 sec)
&lt;/pre&gt; 
  
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. 쿼리 튜닝&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;불필요한&amp;nbsp;테이블&amp;nbsp;엑세스를&amp;nbsp;줄이고,&amp;nbsp;인덱스&amp;nbsp;컬럼&amp;nbsp;부분을&amp;nbsp;변형하지&amp;nbsp;않고,&amp;nbsp;대입되는&amp;nbsp;컬럼을&amp;nbsp;상수 부분에&amp;nbsp;적용합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
EXPLAIN   EXTENDED
SELECT    A.CONN_TEST_TASK_GRP_NO
FROM      EST_DB_SVC_CPS A
WHERE     A.CONN_TEST_TASK_UPDT_DTTM &lt; DATE_ADD(CURRENT_TIMESTAMP() , INTERVAL - 180 SECOND )
AND       A.CONN_TEST_TASK_GRP_NO IS NOT NULL

id|select_type|table|type |possible_keys            |key                      |key_len|ref|rows|filtered|Extra                             |
--|-----------|-----|-----|-------------------------|-------------------------|-------|---|----|--------|----------------------------------|
 1|SIMPLE     |A    |range|EST_DB_SVC_CPS_IX01|EST_DB_SVC_CPS_IX01            |5      |   | 450|     100|Using index condition; Using where|
 
MariaDB [ESTDB]&gt; show profile for query 7;
+------------------------+----------+
| Status                 | Duration |
+------------------------+----------+
| Starting               | 0.000036 |
| Checking permissions   | 0.000005 |
| Opening tables         | 0.000011 |
| After opening tables   | 0.000004 |
| System lock            | 0.000004 |
| Table lock             | 0.000005 |
| Init                   | 0.000012 |
| Optimizing             | 0.000009 |
| Statistics             | 0.000008 |
| Preparing              | 0.000010 |
| Executing              | 0.000003 |
| Sending data           | 0.017332 |  -&gt;&gt; 튜닝 전(0.081115), 튜닝 후 (0.017332)
| End of update loop     | 0.000005 |
| Query end              | 0.000003 |
| Commit                 | 0.000004 |
| Closing tables         | 0.000003 |
| Unlocking tables       | 0.000003 |
| Closing tables         | 0.000005 |
| Starting cleanup       | 0.000003 |
| Freeing items          | 0.000005 |
| Updating status        | 0.000122 |
| Reset for next command | 0.000019 |
+------------------------+----------+
22 rows in set (0.000 sec)

/* 최종 UPDATE 튜닝 쿼리 */
UPDATE    EST_DB_SVC_CPS A
SET       A.CONN_TEST_TASK_GRP_NO = NULL
WHERE     A.CONN_TEST_TASK_UPDT_DTTM &lt; DATE_ADD(CURRENT_TIMESTAMP() , INTERVAL - 180 SECOND )
AND       A.CONN_TEST_TASK_GRP_NO IS NOT NULL
;
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. 결과&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;보통 날짜 데이터에 대해서 쿼리를 작성 할 때 컬럼을 변경해서 대입하는 경우가 상당히 많습니다. 그렇게 작성하는 것보다 인덱스 컬럼은 최대한 변경하지 않은 상태에서 상수 값을 대입하면 인덱스를 적절하게 사용 가능합니다.&lt;/p&gt;
&lt;p&gt;그리고 테이블을 여러 번 액세스 해야 하는 경우가 아니라면, 테이블을 한 번만 액세스 해서 성능을 최적화하는 것이 좋습니다.&lt;/p&gt;</description>
      <category>Data Architecture/Tunning</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/388</guid>
      <comments>https://estenpark.tistory.com/388#entry388comment</comments>
      <pubDate>Fri, 10 Jul 2020 10:42:23 +0900</pubDate>
    </item>
    <item>
      <title>[Admin] MySQL/MariaDB 대용량 데이터 마이그레이션 성능 비교(CTAS, mysqldump, INTO OUTFILE~LOAD DATA LOCAL INFILE)</title>
      <link>https://estenpark.tistory.com/387</link>
      <description>&lt;p&gt;MySQL/MariaDB에서 대용량 데이터를 이관하기 위해서 다양한 방법이 존재합니다. 저는 그중에서 전통적인 방식 CTAS와 mysqldump 유틸리티 그리고 INTO OUTFILE LOAD DATA LOCAL INFILE입니다. 대용량 데이터를 빠르게 이관할 때 유용하게 사용 가능합니다. 다만, 서비스를 운영하는 시점에 적용하는 게 아니라 점검 시간이나 다운타임이 발생할 때 가능합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1. 테스트 환경&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;OS : CentOS 7.5(CPU : Intel(R) Xeon(R) CPU E3-1220 v3 @ 3.10GHz(4 core), Memory : 32GB)&lt;/p&gt;
&lt;p&gt;DB : MariaDB 10.3.8&lt;/p&gt;
&lt;p&gt;Parameter :&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;innodb_buffer_pool_size =15G&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;innodb_flush_log_at_trx_commit=0&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;sync_binlog=0&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;tx_isolation=READ-COMMITTED&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;innodb_flush_log_at_trx_commit는&amp;nbsp;장애 시&amp;nbsp;commit이&amp;nbsp;완료된&amp;nbsp;트랜잭션이&amp;nbsp;유실되는&amp;nbsp;것을&amp;nbsp;방지하기&amp;nbsp;위해&amp;nbsp;필요한&amp;nbsp;파라미터인데&amp;nbsp;성능을&amp;nbsp;높이기&amp;nbsp;위해서는&amp;nbsp;0으로&amp;nbsp;설정하여&amp;nbsp;매&amp;nbsp;트랜잭션&amp;nbsp;commit마다&amp;nbsp;InnoDB&amp;nbsp;log를&amp;nbsp;디스크로&amp;nbsp;sync하지&amp;nbsp;않도록&amp;nbsp;해줍니다.&amp;nbsp; &lt;br /&gt;sync_binlog를&amp;nbsp;0으로&amp;nbsp;세팅하면&amp;nbsp;binary&amp;nbsp;log&amp;nbsp;flush를&amp;nbsp;OS가&amp;nbsp;알아서&amp;nbsp;하게&amp;nbsp;하므로&amp;nbsp;성능을&amp;nbsp;높일&amp;nbsp;수&amp;nbsp;있습니다 &lt;br /&gt;innodb_support_xa는&amp;nbsp;InnoDB&amp;nbsp;log와&amp;nbsp;binary&amp;nbsp;log의&amp;nbsp;일관성을&amp;nbsp;보장하기&amp;nbsp;위해서&amp;nbsp;필요하다고&amp;nbsp;하는데&amp;nbsp;여기서는&amp;nbsp;필요&amp;nbsp;없기 때문에&amp;nbsp;0으로&amp;nbsp;설정하였습니다.&amp;nbsp; &lt;br /&gt;MySQL의&amp;nbsp;Transaction&amp;nbsp;Isolation&amp;nbsp;Level&amp;nbsp;기본&amp;nbsp;값은&amp;nbsp;REPEATABLE&amp;nbsp;READ입니다.&amp;nbsp;하지만&amp;nbsp;이를&amp;nbsp;보장하기&amp;nbsp;위해서&amp;nbsp;Gap&amp;nbsp;Lock을&amp;nbsp;사용하는데&amp;nbsp;이를&amp;nbsp;피하려면&amp;nbsp;Transaction&amp;nbsp;Isolation&amp;nbsp;Level을&amp;nbsp;READ&amp;nbsp;COMMITTED나&amp;nbsp;READ-UNCOMMITTED로&amp;nbsp;변경하면&amp;nbsp;됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. CTAS(Create Table As Select)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;OLDDB 데이터베이스에서 ESTDB 데이터베이스로 동일한 테이블을 옮겨보도록 하겠습니다. CTAS 데이터를 옮기게 되면 테이블 기본적인 스키마(PK, FK, UK, INDEX 제외)를 생성하고, 데이터를 이관하게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
MariaDB [(none)]&gt; CREATE DATABASE ESTDB;
Query OK, 1 row affected (0.021 sec)

MariaDB [ESTDB]&gt; CREATE TABLE LARGE_DB_INSPT AS
    -&gt; SELECT * FROM OLDDB.LARGE_DB_INSPT;
Query OK, 1858594 rows affected (20.309 sec)
Records: 1858594  Duplicates: 0  Warnings: 0     -&gt;&gt; 데이터 이관 완료
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;OLDDB 데이터베이스 LARGE_DB_INSPT 테이블에서 있었던 Primary Key를 생성합니다. 대용량 테이블일 경우 ALTER 문법을 진행할 때는 반드시 여러 번 고민해보고 적용하시기 바랍니다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
MariaDB [ESTDB]&gt; ALTER TABLE LARGE_DB_INSPT ADD PRIMARY KEY (SOLT_INSPT_ID, INSPT_EVENT_ID, EQMT_IP);
Connection id:    1338530
Current database: ESTDB

Query OK, 0 rows affected (2 min 30.731 sec)
Records: 0  Duplicates: 0  Warnings: 0            -&gt;&gt; 정상적으로 PK 생성 완료
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. mysqldump(mysqldump&amp;nbsp;&amp;nbsp;Ver 10.16 Distrib 10.3.8-MariaDB)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;mysqldump 유틸리티에서 데이터 백업할 때 필요한 옵션에 대해서 정리해보겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;--no-autocommit=1 : autocommit을 끄고 개의 테이블 입력 완료 후 commit 수행, 오류 발생 시 다시 처음부터 시작합니다.&lt;/li&gt;
&lt;li&gt;--single-transaction=1 : 작업 후에 변경된 데이터의 내역을 다시 적용하지 않습니다.&lt;/li&gt;
&lt;li&gt;--extended-insert=1&amp;nbsp;:&amp;nbsp;&amp;nbsp;INSERT&amp;nbsp;구문이&amp;nbsp;늘어나는&amp;nbsp;것을&amp;nbsp;차단합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;time 명령어를 활용해서 수행 시간을 확인합니다. 작업이 완료되면 습관적으로 tail -1으로 파일을 확인해보면 &quot;Dump completed on 2020-07-08 15:57:00&quot;으로 나오게 됩니다. head 명령어로 앞쪽 스크립트를 확인하는 것도 권장합니다.&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
[estdbusr@estsvr01 sspark]$ 
time mysqldump -hlocalhost -uroot -proot --databases OLDDB \
--tables LARGE_DB_INSPT --no-autocommit=1 --single-transaction=1 --extended-insert=1 \
&gt; LARGE_DB_INSPT.sql

real    0m3.324s
user    0m2.344s
sys     0m0.169s
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 데이터를 복원해보겠습니다. 여기에서 작업을 시작하기 전에 MySQL/MariaDB 글로벌 파라미터를 변경해서 성능을 높여 줍니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;SET global net_buffer_length=1000000;&lt;/li&gt;
&lt;li&gt;SET global max_allowed_packet=1000000000;&lt;/li&gt;
&lt;li&gt;SET foreign_key_checks = 0;&lt;/li&gt;
&lt;li&gt;SET UNIQUE_CHECKS = 0;&lt;/li&gt;
&lt;li&gt;SET&amp;nbsp;AUTOCOMMIT&amp;nbsp;=&amp;nbsp;0;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
[estdbusr@estsvr01 sspark]$ 
time mysql -hlocalhost -uroot -proot ESTDB &lt; LARGE_DB_INSPT.sql

real    1m4.211s
user    0m2.491s
sys     0m0.114s

&lt;/pre&gt;
&lt;p&gt;데이터 복원을 정상적으로 마쳤을 경우에는 아래와 같이 파라미터 설정을 다시 변경합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;SET foreign_key_checks = 1;&lt;/li&gt;
&lt;li&gt;SET UNIQUE_CHECKS = 1;&lt;/li&gt;
&lt;li&gt;SET&amp;nbsp;AUTOCOMMIT&amp;nbsp;=&amp;nbsp;1;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;4. &lt;span style=&quot;color: #333333;&quot;&gt;INTO OUTFILE LOAD DATA LOCAL INFILE&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;배치 프로세스 개발할 때 많이 사용하는 방법으로 개발할 때 유연성을 보장해주는 장점이 있습니다. 그리고 무엇보다 빠르게 데이터 마이그레이션을 할 수 있습니다. 하지만 단점은 파일 안에 있는 데이터에 특수문자를 제어해야 합니다. 만약에 &quot;FIELDS TERMINATED BY '|'&quot; 옵션을 사용했는데 파일안에 '|' 문자가 존재한다면 에러가 나서 동작하지 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;데이터를 백업해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
MariaDB [ESTDB]&gt; 
SELECT    SOLT_INSPT_ID       
         ,INSPT_EVENT_ID      
         ,EQMT_IP             
         ,EQMT_HOST_NM        
         ,DB_DUPX_TP_CODE     
         ,SOLT_INSPT_RST_CODE 
         ,SOLT_INSPT_RST_CNTS 
         ,REG_USER_NO         
         ,REG_DTTM            
INTO OUTFILE '/home/estdbusr/sspark/LARGE_DB_INSPT.csv'
CHARACTER SET euckr
FIELDS TERMINATED BY '|' LINES TERMINATED BY '\n'
FROM OLDDB.LARGE_DB_INSPT;
Query OK, 1861555 rows affected (4.903 sec)
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;데이터를 복원해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
MariaDB [ESTDB]&gt; 
CREATE TABLE &quot;LARGE_DB_INSPT&quot; (
  &quot;SOLT_INSPT_ID&quot; varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ,
  &quot;INSPT_EVENT_ID&quot; varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ,
  &quot;EQMT_IP&quot; varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ,
  &quot;EQMT_HOST_NM&quot; varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ,
  &quot;DB_DUPX_TP_CODE&quot; varchar(1) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL ,
  &quot;SOLT_INSPT_RST_CODE&quot; varchar(1) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ,
  &quot;SOLT_INSPT_RST_CNTS&quot; text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL ,
  &quot;REG_USER_NO&quot; decimal(9,0) NOT NULL DEFAULT 1 ,
  &quot;REG_DTTM&quot; datetime NOT NULL DEFAULT current_timestamp() ,
  PRIMARY KEY (&quot;SOLT_INSPT_ID&quot;,&quot;INSPT_EVENT_ID&quot;,&quot;EQMT_IP&quot;)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4  


MariaDB [ESTDB]&gt; 
LOAD DATA LOCAL INFILE '/home/estdbusr/sspark/LARGE_DB_INSPT.csv' 
INTO TABLE ESTDB.LARGE_DB_INSPT CHARACTER SET euckr FIELDS TERMINATED BY '|' LINES TERMINATED BY '\n';
Query OK, 1862836 rows affected (23.350 sec)
Records: 1862836  Deleted: 0  Skipped: 0  Warnings: 0
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&quot;LINES TERMINATED BY '\r\n'&quot; \r 옵션을 사용하면 아래와 같이 warnings이 발생 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
MariaDB [ESTDB]&gt; 
LOAD DATA LOCAL INFILE '/home/estdbusr/sspark/LARGE_DB_INSPT.csv' 
INTO TABLE ESTDB.LARGE_DB_INSPT CHARACTER SET euckr FIELDS TERMINATED BY '|' LINES TERMINATED BY '\r\n';
Query OK, 1 row affected, 1 warning (1.422 sec)
Records: 1  Deleted: 0  Skipped: 0  Warnings: 1

MariaDB [ESTDB]&gt; show warnings;
+---------+------+-----------------------------------------------+
| Level   | Code | Message                                       |
+---------+------+-----------------------------------------------+
| Warning | 1265 | Data truncated for column 'REG_DTTM' at row 1 |
+---------+------+-----------------------------------------------+
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;5. 결과&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 78px;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 40%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;데이터 이관 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;수행 시간(ms)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;CPU(4 Core)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;건수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;용량(size)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 40%; height: 18px;&quot;&gt;CTAS&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;2분 40초&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;40%&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 58px; text-align: center;&quot; rowspan=&quot;3&quot;&gt;1,858,594&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 58px; text-align: center;&quot; rowspan=&quot;3&quot;&gt;308 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;mysqldump&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;1분 7초&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;90%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 20px;&quot;&gt;INTO OUTFILE&lt;br /&gt;LOAD DATA LOCAL INFILE&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;28초&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;170%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;결과적으로는 속도는 INTO OUTFILE LOAD DATA를 활용하는 게 좋습니다. 하지만, 큰 용량에 따라서 성능 저하를 가져올 수 있습니다. 속도와 성능을 고려한다면 mysqldump를 활용하는 것을 권장합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;6. 참고&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://treeple3.tistory.com/entry/ERROR-Warning-Code-1265&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;treeple3.tistory.com/entry/ERROR-Warning-Code-1265&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blackbull.tistory.com/8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;blackbull.tistory.com/8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lalwr.blogspot.com/2017/12/mysqldump.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;lalwr.blogspot.com/2017/12/mysqldump.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mobiinside.co.kr/2018/02/09/buzzvil-migration-mysql/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.mobiinside.co.kr/2018/02/09/buzzvil-migration-mysql/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Data Architecture/Admin</category>
      <category>CTAS</category>
      <category>into outfile</category>
      <category>LOAD DATA</category>
      <category>mariaDB</category>
      <category>MySQL</category>
      <category>mysqldump</category>
      <category>대용량</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/387</guid>
      <comments>https://estenpark.tistory.com/387#entry387comment</comments>
      <pubDate>Thu, 9 Jul 2020 12:23:58 +0900</pubDate>
    </item>
    <item>
      <title>[Admin] MySQL/MariaDB AUTO_INCREMENT</title>
      <link>https://estenpark.tistory.com/386</link>
      <description>&lt;p&gt;&lt;b&gt;1. Auto increment 란?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;테이블 컬럼에 auto_increment를 설정하게 되면, 데이터가 삽입 되는 순간 자동으로 증가 됩니다. 데이터 타입은 INT, BIGINT를 주로 많이 사용 합니다. InnoDB 엔진의 경우 Primary Key 생성과 동시에 인덱스 사이즈에 영향을 주기 때문에 테이블 설계시 많이 권고 하는 방법 입니다. MySQL 5.7 이전 버전에서는 MyISAM 엔진의 경우 auto_increment 값이 파일에 저장 해서 관리 되는 반면, InnoDB 인젠의 경우 메모리 기반으로 관리 됩니다. MySQL 8.0 에서는 InnoDB 엔진의 테이블 정보 저장 공간에 auto_increment 카운터 정보를 보관 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;주로 많이 활용되는 방법은 PK + auto_increment를 결합해서 사용 합니다.(아래 샘플 예제)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
CREATE TABLE `EST_INFO` (
  `USER_NO` int(11) NOT NULL AUTO_INCREMENT,
  `USER_ID` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `USER_NM` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`USER_NO`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
;
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. Persistent Auto Increment Value&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;auto_increment의 초기화 방식은 MySQL 8.0에서 변경 되었습니다. 자동 증가 값이 변경될 때마다 redo log에 매번 기록하고, 각 체크포인트를 관리하는 스토리지 엔진의 시스템 테이블에 해당 내용을 저장하게 되었습니다. 그동안 문제가 되었던 InnoDB 엔진의 auto_increment 삭제 후 재시작 할 경우에 문제가 된 부분도 해결 되었습니다. Data Dictionary 시스템 테이블에 저장된 가장 큰 자동 증가값을 사용해 메모리의 auto_increment 카운터를 초기화 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;MySQL 5.7 및 이전 버전에서 롤백 직후 서버를 다시 시작하면 롤백 된 트랜잭션에 할당된 자동 증가 값을 재사용 하지만, MySQL 8.0에서는 현재 최대 자동 증가 값이 유지되므로 이전에 할당된 값을 재활용 할 수 없게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2.1. Auto Increment&amp;nbsp; 카운트 점검(MySQL 8.0.17)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;EST_INFO 테이블을 생성하고 AUTO_INCREMENT를 1로 설정 합니다.&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
mysql&gt; CREATE TABLE `EST_INFO` (
  `USER_NO` int(11) NOT NULL AUTO_INCREMENT,
  `USER_ID` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `USER_NM` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`USER_NO`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
;
&lt;/pre&gt;
&lt;p&gt;EST_INFO 테이블에 3 row를 생성 후 auto_increment 증가된 상황을 확인 합니다.&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
mysql&gt; INSERT INTO EST_INFO(USER_ID, USER_NM) values('user01', 'estenpark01');
Query OK, 1 row affected (0.00 sec)

mysql&gt; INSERT INTO EST_INFO(USER_ID, USER_NM) values('user02', 'estenpark02');
Query OK, 1 row affected (0.01 sec)

mysql&gt; INSERT INTO EST_INFO(USER_ID, USER_NM) values('user03', 'estenpark03');
Query OK, 1 row affected (0.01 sec)

mysql&gt; SELECT  *  FROM EST_INFO;
+---------+---------+-------------+
| USER_NO | USER_ID | USER_NM     |
+---------+---------+-------------+
|       1 | user01  | estenpark01 |   &gt;&gt; auto_increment 1 증가
|       2 | user02  | estenpark02 |   &gt;&gt; auto_increment 2 증가
|       3 | user03  | estenpark03 |   &gt;&gt; auto_increment 3 증가
+---------+---------+-------------+
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;테스트를 위해서 EST_INFO 테이블에 모든 데이터를 삭제 하고, DDL 결과를 확인 합니다.&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
mysql&gt; DELETE FROM EST_INFO;
Query OK, 3 rows affected (0.01 sec)

mysql&gt; SELECT  *  FROM EST_INFO;
Empty set (0.00 sec)

mysql&gt; show create table EST_INFO;
+----------+-------------------------------------------------------------------------------+
| Table    | Create Table                                                                  |
+----------+-------------------------------------------------------------------------------+
| EST_INFO | CREATE TABLE `EST_INFO` (
  `USER_NO` int(11) NOT NULL AUTO_INCREMENT,
  `USER_ID` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `USER_NM` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`USER_NO`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+----------+-------------------------------------------------------------------------------+
1 row in set (0.00 sec)
&lt;/pre&gt;
&lt;p&gt;MySQL 서비스를 재 시작 합니다.&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
root@localhost /]# systemctl stop mysqld;
[root@localhost /]# systemctl start mysqld;
&lt;/pre&gt;
&lt;p&gt;EST_INFO 테이블에 2 row를 생성 하게 되면, 이전 버전과 다르게 4, 5로 증가된 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
mysql&gt; INSERT INTO EST_INFO(USER_ID, USER_NM) values('user04', 'estenpark04');
Query OK, 1 row affected (0.02 sec)

mysql&gt; INSERT INTO EST_INFO(USER_ID, USER_NM) values('user05', 'estenpark05');
Query OK, 1 row affected (0.00 sec)

mysql&gt; SELECT  *  FROM EST_INFO;
+---------+---------+-------------+
| USER_NO | USER_ID | USER_NM     |
+---------+---------+-------------+
|       4 | user04  | estenpark04 |   &gt;&gt; auto_increment 4 증가(초기화 되지 않고 증가 됨)
|       5 | user05  | estenpark05 |   &gt;&gt; auto_increment 5 증가(초기화 되지 않고 증가 됨)
+---------+---------+-------------+
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;EST_INFO 테이블을 TRUNCATE(DDL 문법) 수행할 경우에는 초기화 된 결과가 나오게 됩니다..&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;
mysql&gt; TRUNCATE TABLE EST_INFO;
Query OK, 0 rows affected (0.04 sec)

mysql&gt; INSERT INTO EST_INFO(USER_ID, USER_NM) values('user06', 'estenpark06');
Query OK, 1 row affected (0.00 sec)

mysql&gt; select * from EST_INFO;
+---------+---------+-------------+
| USER_NO | USER_ID | USER_NM     |
+---------+---------+-------------+
|       1 | user06  | estenpark06 |   &gt;&gt; auto_increment 1 증가(초기화)
+---------+---------+-------------+
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. Auto Increment 결론&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;MySQL 5.7, MariaDB 10.1.12 버전은 데이터를 삭제하고, DB를 재시작 할 경우 초기화 된 정보가 나오게 되며, MySQL 8.0, MariaDB 10.3.8 버전은 데이터를 삭제하고, DB를 재 시작 한다고 해도 초기화 되지 않고 계속 증가하게 됩니다. 이러한 버전에 특성을 이해 하면 장애를 최소화 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;4. 참고&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://mariadb.com/kb/en/auto_increment/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;mariadb.com/kb/en/auto_increment/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.guru99.com/auto-increment.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.guru99.com/auto-increment.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gywn.net/2013/02/mysql-innodb-auto-increment/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;gywn.net/2013/02/mysql-innodb-auto-increment/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.letmecompile.com/mysql-innodb-auto-increment-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.letmecompile.com/mysql-innodb-auto-increment-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/naver-cloud-platform/%EC%9D%B4%EB%A0%87%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EC%84%B8%EC%9A%94-mysql-8-0-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%8B%A0%EA%B7%9C-%EA%B8%B0%EB%8A%A5-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0-1-innodb-d638a3e4fde9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;medium.com/naver-cloud-platform/%EC%9D%B4%EB%A0%87%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EC%84%B8%EC%9A%94-mysql-8-0-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%8B%A0%EA%B7%9C-%EA%B8%B0%EB%8A%A5-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0-1-innodb-d638a3e4fde9&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Data Architecture/Admin</category>
      <category>Auto_increment</category>
      <category>mariaDB</category>
      <category>MySQL</category>
      <category>SEQUENCE</category>
      <category>자동순번</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/386</guid>
      <comments>https://estenpark.tistory.com/386#entry386comment</comments>
      <pubDate>Mon, 6 Jul 2020 13:16:26 +0900</pubDate>
    </item>
    <item>
      <title>[Admin] MySQL/MariaDB Primary Key vs Unique Key</title>
      <link>https://estenpark.tistory.com/385</link>
      <description>&lt;p&gt;&lt;b&gt;1. Primary Key 란&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Primary Key는 데이터베이스 행을 고유하게 식별할 수 있는 습니다. 테이블 단일 컬럼으로 식별할 수 있고, 복합 컬럼으로 식별할 수 있는데 성능적으로는 단일 컬럼으로 생성해야 Clustered Index를 견고하게 사용 가능합니다. 또한 NULL을 허용하지 않으며, 데이터에 대한 일관성을 보장합니다. 데이터 중복이 발생하지 않아 빠르게 쿼리를 수행 가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;InnoDB engine기준으로 Primary Key는 항상 Clustered Index이며, Primary Key가 존재하지 않을 경우 Unique Key(Unique Index)를 선택하게 됩니다. 후보가 없을 경우 Auto_Increment 속성의 컬럼을 활용하게 되는데 이는 테이블에 Primary Key를 생성하지 않으면 시스템 적으로 고유값을 찾아서 그 값을 생성하게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. Unique Key(Unique Index) 란&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Unique Key는 데이터베이스 행을 고유하게 식별할 수 있는 구조는 동일하지만, NULL 허용하게 되면 중복된 데이터를 가질 수 있습니다. 대체적으로 논리적으로 보조 식별자를 구분하기 위해서 사용하게 되며, 테이블 기준으로 여러 개를 생성 가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;InnoDB 기준으로 Unique Key는 Non-Clustered Index입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. Primary Key와 Unique Key 차이점&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 100px;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 20px;&quot;&gt;Primary Key&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 20px;&quot;&gt;Unique Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;테이블에 반드시 하나만 존재&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;테이블에 여러 개 존재 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;NULL 허용 하지 않음&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;NULL 허용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;기본 Clustered Index&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;기본 Non-Clustered Index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;데이터 무결성 보장&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;데이터 무결성 보장(NULL은 여러 개 존재 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;CREATE&amp;nbsp;TABLE&amp;nbsp;employee &lt;br /&gt;( &lt;br /&gt;emp_no&amp;nbsp;int&amp;nbsp;PRIMARY&amp;nbsp;KEY, &lt;br /&gt;name&amp;nbsp;varchar(255),&amp;nbsp; &lt;br /&gt;city&amp;nbsp;varchar(150) &lt;br /&gt;) ;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;br /&gt;CREATE&amp;nbsp;TABLE&amp;nbsp;employee &lt;br /&gt;( &lt;br /&gt;emp_no&amp;nbsp;int&amp;nbsp;PRIMARY&amp;nbsp;KEY, &lt;br /&gt;emp_id&amp;nbsp;&amp;nbsp;int, &lt;br /&gt;name&amp;nbsp;varchar(255),&amp;nbsp; &lt;br /&gt;city&amp;nbsp;varchar(150) &lt;br /&gt;) &lt;br /&gt;; &lt;br /&gt;&lt;br /&gt;CREATE UNIQUE INDEX employee_idx01 &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ON employee(emp_id);&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;제약 조건(constrainnt)&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;제약 조건 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;4. Primary Key 반드시 필요한가?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;데이터 모델링 관점&lt;/p&gt;
&lt;p&gt;관계형 데이터베이스에서 릴레이션을 가지기 위해서는 반드시 필요합니다. 만약 부서와 사원 엔티티가 존재한다고 할 때 부서 엔티티에 식별자를 &quot;부서 코드&quot;를 사원 엔티티에서 부서 코드를 FK로 참조 가능합니다. 부서 엔티티에 식별자가 존재하지 않는다면 이렇게 릴레이션이 연결될 수 없게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;데이터 관점&lt;/p&gt;
&lt;p&gt;테이블에 Primary Key가 존재하지 않는다면 중복된 데이터로 인해 데이터 정제 작업이 필요하며, 각 컬럼에 값을 어떤 우선순위에 따라 결과를 추출해야 합니다. 데이터 품질 향상에 도움을 줄 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;성능 관점&lt;/p&gt;
&lt;p&gt;Primary Key를 생성하게 되면 최소의 Cost를 사용할 수밖에 없습니다. MySQL, MariaDB의 InnoDB engine은 기본적으로 데이터를 저장할 때 인덱스를 활용하기 위해서 후보가 될 수 있는 Primary Key를 생성하게 됩니다. InnoDB engine은 Unique 값을 가진 컬럼을 찾아서 보이지 않는 Clustered Index를 만들게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;동기화 관점(HA)&lt;/p&gt;
&lt;p&gt;MHA는 데이터 동기화를 진행할 때 테이블 PK 기준으로 맞추게 됩니다. 또한 db compare 유틸리티를 할 때도 Primary Key 기준으로 양쪽 데이터를 비교해서 INSERT, UPDATE, DELETE를 추출하게 됩니다. 양쪽 데이터를 비교할 때 모든 컬럼을 기준으로 비교해서 데이터를 이관한다고 하면 그만큼 성능 저하가 발생하게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;결론&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Primary Key는 선택 사항이 아니라 필수 사항입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;5. Primary Key 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;유일한 값을 가질 수 있는 컬럼&lt;/li&gt;
&lt;li&gt;Null을 허용하지 않은 컬럼&lt;/li&gt;
&lt;li&gt;데이터 변경이 발생하지 않은 컬럼&lt;/li&gt;
&lt;li&gt;int, bigint datatype 형태 컬럼(auto_increment와 같이 순차적으로 생성)&lt;/li&gt;
&lt;li&gt;업무적 유일한 컬럼(사원번호, 부서코드, 종목번호, 회원ID 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;6. 참고&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gurubee.net/article/51454&quot;&gt;http://www.gurubee.net/article/51454&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.guru99.com/difference-between-primary-key-and-unique-key.html&quot;&gt;https://www.guru99.com/difference-between-primary-key-and-unique-key.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hodongman.github.io/2019/01/14/Database-PK%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0.html&quot;&gt;https://hodongman.github.io/2019/01/14/Database-PK%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://purumae.tistory.com/209&quot;&gt;https://purumae.tistory.com/209&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Data Architecture/Admin</category>
      <category>mariaDB</category>
      <category>MySQL</category>
      <category>PRIMARY KEY</category>
      <category>Unique Index</category>
      <category>Unique Key</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/385</guid>
      <comments>https://estenpark.tistory.com/385#entry385comment</comments>
      <pubDate>Thu, 2 Jul 2020 15:30:18 +0900</pubDate>
    </item>
    <item>
      <title>[Admin] MySQL/MariaDB Clustered vs Non-Clustered Index</title>
      <link>https://estenpark.tistory.com/384</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;1. 인덱스란?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;인덱스는 테이블 또는 View에서 행을 가져오는 속도를 높이기 위해서 데이터베이스의 하나 이상의 열에서 작성된 키입니다. 이는 Oracle, SQL Server, MySQL, MariaDB 등과 같은 데이터베이스가 키 값과 관련된 행을 신속하게 찾을 수 있도록 합니다. Clustered Index, Non-Clustered Index 두 가지 형태로 나눠지게 됩니다. MySQL/MariaDB에서 Clustered Index를 사용할 수 있는 엔진은 InnoDB 뿐입니다. MyISAM, Memory, Archive NDB 엔진은 사용할 수 없습니다. InnoDB Buffer Pool 메모리에 존재하지 않을 경우 디스크에서 페이지를 가져오게 됩니다. 한번 가져온 페이지는&amp;nbsp;&amp;nbsp;대부분은 InnoDB Buffer Pool에 존재합니다. 속도 관점에서는 최대한 디스크에서 페이지를 가져오는 것보다는 메모리에서 가져오는 게 좋을 수밖에 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p2Njx/btqFjFPt6yj/tbaNZyeLZWaYT4sTk4UCx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p2Njx/btqFjFPt6yj/tbaNZyeLZWaYT4sTk4UCx1/img.png&quot; data-alt=&quot;B+Tree Structure of a Clustered Index&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p2Njx/btqFjFPt6yj/tbaNZyeLZWaYT4sTk4UCx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp2Njx%2FbtqFjFPt6yj%2FtbaNZyeLZWaYT4sTk4UCx1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;B+Tree Structure of a Clustered Index&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;2. Clustered Index 란?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;클러스터 인덱스는 키 값에 대한 테이블의 데이터 행을 정렬하는 인덱스의 한 유형입니다. 데이터베이스에는 테이블당 클러스터 인덱스가 하나만 존재해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;클러스터 인덱스는 테이블의 데이터를 정렬해서 저장되는 순서를 정의합니다. 따라서 테이블마다 클러스터링된 인덱스가 하나만 있습니다. RDBMS에서는 일반적으로 Primary Key를 사용하여 특정 열을 기반으로 클러스터링 된 인덱스를 만들 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;3. Non-Clustered Index 란?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;Non-Clustered Index는 데이터와 인덱스를 각각 다른 위치에 저장합니다. 인덱스는 해당 데이터의 위치에 대한 포인터를 포함하고 있습니다. Non-Clustered Index의 인덱스가 서로 다른 위치에 저장되므로 단일 테이블에는 많은 Non-Clustered Index가 있을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;예를 들면, 책 한권은 내용을 빠르게 찾기 위해서 보통 두 가지 이상의 색인을 가지게 됩니다. 앞쪽에는 &quot;목차&quot; 기준으로 정렬되어 있고, 뒤쪽에는 &quot;찾아보기&quot;와 같은 용어에 대한 알파벳 순으로 표시하는 것을 볼 수 있습니다. 책을 테이블이라고 하고 목차, 찾아보기를 Non-Clustered Index로 생각하면 이해가 될 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;4. Clustered Index와 Non-Clustered Index 차이점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 0%; height: 180px;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Parameters&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Clustered Index&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Non-Clustered Index&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Use For&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;물리적으로 행을 재배열&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;논리적으로 행을 재배열(물리적으로 데이터 파일 포인터 활용 하며 재배열은 안함)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Storing method&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;인덱스의 리프 노드에 데이터 페이지를 저장&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;인덱스의 리프 노드에 데이터 페이지 저장 안함&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Size&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;용량 작다&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;용량 크다&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Data accessing&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Faster(빠름)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Clustered Index에 비해 느림&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Additional&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;disk space&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;필요하지 않음&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;인덱스를 별도로 지정하는 데 필요&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Type of Key&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기본적으로 테이블 Primary Key는 Clustered Index 사용&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;복합키 역할을 하는 데이블의 고유한 제약조건과 함께 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Main&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Feature&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터 검색 속도 향상&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;조인에 사용되는 열에 생성&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;N per TABLE&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;테이블 당 1개&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;테이블 당 249개&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;engine&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;InnoDB&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;InnoDB&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;5. Clustered Index 장점/단점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;5.1. 장점&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Primary Key로 검색할 경우 성능 빠름&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;테이블의 모든 보조 인덱스가 Primary Key를 가지고 있기 때문에 인덱스만으로 처리 가능(커버링 인덱스)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;MIN, MAX, COUNT 쿼리를 사용 하는 범위 또는 그룹에 성능 최적화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;데이터의 특정 지점으로 바로 이동하거나 그 위치부터 순차적으로 읽기 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;페이지 전송 최소화, 캐시 히트 최적화&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;5.2.&amp;nbsp; 단점&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;정렬 되지 않은 데이터를 적재할 경우(PK를 컬럼을 선정할 때 Auto_increment와 유사한 정렬 순서를 보장하면 좋음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;INSERT, UPDATE, DELETE(DML) 시 추가 작업 증가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Clustered Index 필드가 변경 될 때 업데이트하는 데 시간 오래 걸림&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;리프 노드는 대부분 Clustered Index의 데이터 페이지를 포함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;6. Non-Clustered Index 장점/단점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;6.1. 장점&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;테이블에 여러 개의 인덱스를 생성 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Clustered Index Overhead 비용 최소화&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;6,2. 단점&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;논리적 순서로 데이터를 저장하는 데 도움을 주지만, 데이터 행을 물리적으로 정렬하지 안 함(물리적으로 I/O 발생)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;검색할 때 많은 비용 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;불필요한 디스크 사용량 증가&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;7. 주의 사항&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Clustered Index 생성 시 Key에 대한 크기를 작게 해야 좋습니다. 따라서 인덱스 설계할 때 데이터 타입과 사이즈를 적절하게 사용하는 것을 권장 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;테이블에 Primary Key를 반드시 명시합니다. 이는 HA 동기화를 할 때 내부적으로는 Primary Key로 Master Node에서 Slave Node로 데이터를 이관하게 됩니다. Clustered Index가 아니더라도 꼭 필요한 부분입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;테이블에 Non-Clustered Index를 여러 개 생성하게 되면, DML 작업에 취약하며, 시스템 성능 저하가 발생합니다. 꼭 필요한 인덱스를 추가해야 하며, 주기적으로 운영 DB에서 사용하지 않은 인덱스를 찾아서 제거하시기 바랍니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;8. 참고&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.guru99.com/clustered-vs-non-clustered-index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.guru99.com/clustered-vs-non-clustered-index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://umumble.com/blogs/mysql/mysql-(innodb)-clustered-and-non_clustered-indexes-/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;umumble.com/blogs/mysql/mysql-(innodb)-clustered-and-non_clustered-indexes-/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://12bme.tistory.com/149&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;12bme.tistory.com/149&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lng1982.tistory.com/144&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;lng1982.tistory.com/144&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.blog.naver.com/PostView.nhn?blogId=jevida&amp;amp;logNo=221139771581&amp;amp;proxyReferer=https:%2F%2Fwww.google.com%2F&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;m.blog.naver.com/PostView.nhn?blogId=jevida&amp;amp;logNo=221139771581&amp;amp;proxyReferer=https:%2F%2Fwww.google.com%2F&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mongyang.tistory.com/75&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;mongyang.tistory.com/75&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;dev.mysql.com/doc/refman/8.0/en/storage-engines.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Data Architecture/Admin</category>
      <category>clustered index</category>
      <category>mariaDB</category>
      <category>MySQL</category>
      <category>Non-Clustered index</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/384</guid>
      <comments>https://estenpark.tistory.com/384#entry384comment</comments>
      <pubDate>Wed, 1 Jul 2020 13:57:09 +0900</pubDate>
    </item>
    <item>
      <title>[Admin] ORA-30556 함수 기반 인덱스, 컬럼(타입) 사이즈 변경 에러</title>
      <link>https://estenpark.tistory.com/381</link>
      <description>&lt;p&gt;오라클 문법중에서 컬럼 사이즈 변경 할 때 &quot;ALTER TABLE &lt;span style=&quot;color: #ee2323;&quot;&gt;테이블명&lt;/span&gt;&amp;nbsp; MODIFY (&lt;span style=&quot;color: #ee2323;&quot;&gt;컬럼명&lt;/span&gt; VARCHAR2(&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;128&lt;/span&gt;&lt;/b&gt;))&quot; 명령어로 변경 하게 됩니다. 일반적인 상황에서는 문제 없이 변경이 가능 하지만, 함수 기반 인덱스로 활용된 경우에는 아래와 같은 에러가 발생 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. ORA Error&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - EST_TABLE 테이블의 EST_TABLE_IDX02는 함수기반 인덱스는 &quot;LOWER(&quot;USER_NM&quot;)&quot;으로 되어있어서 일반적인 컬럼 속성 변경할 때 문제가 발생 할 수 있습니다. 에러는 아래를 참고 바랍니다.&lt;/p&gt;
&lt;pre class=&quot;brush: sql&quot;&gt;/* SQL 쿼리 수행 */
ALTER TABLE EST_TABLE MODIFY USER_NM VARCHAR2(128);
/* SQL 쿼리 수행 후 에러 메시지 */
  -- ORA-30556: either functional or bitmap join index is defined on the column to be modified 
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 해결 방법&lt;/p&gt;
&lt;pre class=&quot;brush: sql&quot;&gt;/* 에러가 발생한 함수 기반 인덱스를 제거 합니다. */
DROP INDEX EST_TABLE_IDX02;

/* EST_TABLE 테이블에서 USER_NM 컬럼 사이즈를 128 Byte로 변경 합니다. */
ALTER TABLE EST_TABLE MODIFY USER_NM VARCHAR2(128);

/* EST_TABLE 테이블 함수 기반 인덱스 추가 합니다. */
CREATE INDEX EST_TABLE_IDX02
 ON  EST_TABLE (
  LOWER(&quot;USER_NM&quot;) ASC
 )
 NOLOGGING
 TABLESPACE  EST_INDEX
;
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. 결론&lt;/p&gt;
&lt;p&gt;결론은 함수기반 컬럼은 내장 함수, 직접 작성된 함수 모두 사용해서 인덱스를 만들었다면 컬럼 속성을 변경 할 때 인덱스를 제외 하고, 컬럼 속성을 변경하고, 다시 원래 인덱스를 생성해줘야 합니다.&lt;/p&gt;</description>
      <category>Data Architecture/Admin</category>
      <category>ORA</category>
      <category>oracle</category>
      <category>oracle error</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/381</guid>
      <comments>https://estenpark.tistory.com/381#entry381comment</comments>
      <pubDate>Fri, 20 Sep 2019 13:00:28 +0900</pubDate>
    </item>
    <item>
      <title>[Admin] Oralce 파라미터 설정 자동 가이드(audit_trail, processes, archive log directory, _diag_daemon, memory_target)</title>
      <link>https://estenpark.tistory.com/380</link>
      <description>&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;오라클을 구축 후 운영단계에서 퍼포먼스 튜닝을 진행해야 합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Oracle&amp;nbsp;11g&amp;nbsp;이상&amp;nbsp;또는&amp;nbsp;Single&amp;nbsp;설치에서만&amp;nbsp;진행&amp;nbsp;하시기&amp;nbsp;바라며,&amp;nbsp;파라미터는&amp;nbsp;반드시&amp;nbsp;백업&amp;nbsp;받고&amp;nbsp;진행&amp;nbsp;해주시기&amp;nbsp;바랍니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;파라미터&amp;nbsp;적용&amp;nbsp;시&amp;nbsp;DB&amp;nbsp;종료가&amp;nbsp;필요하니&amp;nbsp;DB&amp;nbsp;종료&amp;nbsp;가능&amp;nbsp;여부도&amp;nbsp;같이&amp;nbsp;확인&amp;nbsp;하시기&amp;nbsp;바랍니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;1.&amp;nbsp;주요&amp;nbsp;변경&amp;nbsp;사항&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;audit_trail &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;주로&amp;nbsp;감사(Audition)에서&amp;nbsp;활용되며,&amp;nbsp;데이터베이스&amp;nbsp;작업을&amp;nbsp;모니터링,&amp;nbsp;기록을&amp;nbsp;수집하는&amp;nbsp;기능입니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;모든&amp;nbsp;추척&amp;nbsp;내용을&amp;nbsp;감사&amp;nbsp;로그로&amp;nbsp;기록하기&amp;nbsp;때문에&amp;nbsp;시스템의&amp;nbsp;속도를&amp;nbsp;저하&amp;nbsp;시킵니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;processes &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;프로세스를&amp;nbsp;변경하기&amp;nbsp;위해서&amp;nbsp;일반적으로&amp;nbsp;&quot;processes&quot;&amp;nbsp;파라미터&amp;nbsp;값을&amp;nbsp;변경&amp;nbsp;합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;프로세스는&amp;nbsp;기본적으로&amp;nbsp;150으로&amp;nbsp;되어있지만,&amp;nbsp;운영&amp;nbsp;하다보면&amp;nbsp;점점&amp;nbsp;늘어나기&amp;nbsp;때문에&amp;nbsp;표준을&amp;nbsp;1000&amp;nbsp;기준으로&amp;nbsp;합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;log_archive_dest_1 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;archive&amp;nbsp;log&amp;nbsp;mode를&amp;nbsp;확인해서&amp;nbsp;Default&amp;nbsp;Directory일&amp;nbsp;경우&amp;nbsp;변경&amp;nbsp;합니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;_diag_daemon &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;분석&amp;nbsp;데몬의&amp;nbsp;자동&amp;nbsp;시작&amp;nbsp;유무&amp;nbsp;결정하는&amp;nbsp;것이며,&amp;nbsp;Diagnosability&amp;nbsp;Daemon은&amp;nbsp;process와&amp;nbsp;instance&amp;nbsp;failure에&amp;nbsp;관련한&amp;nbsp;진단&amp;nbsp;정보를&amp;nbsp;포착해서&amp;nbsp;systemstate&amp;nbsp;dump를&amp;nbsp;생성하는&amp;nbsp;역할을&amp;nbsp;합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;memory_target &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;총&amp;nbsp;메모리&amp;nbsp;량을&amp;nbsp;지정하는&amp;nbsp;파라미터&amp;nbsp;입니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;memory_target&amp;nbsp;PGA&amp;nbsp;및&amp;nbsp;SGA&amp;nbsp;메모리를&amp;nbsp;자동으로&amp;nbsp;조정&amp;nbsp;해주는&amp;nbsp;역할&amp;nbsp;합니다.(11g&amp;nbsp;부터&amp;nbsp;가능) &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;memory_target,&amp;nbsp;memory_max_target&amp;nbsp;동일하게&amp;nbsp;사이즈를&amp;nbsp;설정하는&amp;nbsp;게&amp;nbsp;좋습니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;2.&amp;nbsp;Oracle&amp;nbsp;Parameter&amp;nbsp;check&amp;nbsp;PL/SQL&lt;br /&gt;&lt;/b&gt;&amp;nbsp; - 전체 운영체제에 존재하는 메모리 사이즈를 확인 후 최소 40~60% 오라클 메모리 사이즈를 지정해주시기 바라며, 해당 사이즈에 맞게 변경된 출력 결과를 얻게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: sql&quot;&gt;SPOOL /tmp/Parameter_Result.txt  /* 경로는 운영체제 환경에 맞게 조정 해주시기 바랍니다. */

SET LINE 100
SET SERVEROUTPUT ON

DECLARE

  IN_DB_MEMORY   NUMBER;
  V_DB_MEMORY    NUMBER;
  V_VERSION      VARCHAR2(100);
  V_PAM_FILE     VARCHAR2(100);
  V_AUDIT_RS     VARCHAR2(100);
  V_PROCESS_RS   NUMBER;
  V_AR_RS        VARCHAR2(100);
  V_AR_DIR_RS    VARCHAR2(100);
  V_MR_RS        NUMBER;
  V_MR_REC       NUMBER;
  V_DIAG_DAEMON  VARCHAR2(100);
  V_MR_RT_10     NUMBER := 0.7;
  V_MR_RT_11     NUMBER := 0.8; /*SGA_MAX_TARGET 비율*/

  
BEGIN
  DBMS_OUTPUT.PUT_LINE('/*********DB가 사용할 수 있는 메모리를 입력하시기 바랍니다.[단위 G](ex. 8G -&amp;gt; 8)*********/'); 
  
  IN_DB_MEMORY := &amp;amp;in_memory; /*[입력]G 단위로 입력*/
  
  V_DB_MEMORY := IN_DB_MEMORY * 1024;
  
  DBMS_OUTPUT.PUT_LINE(' - DB Memory : '||V_DB_MEMORY||'MB');
  DBMS_OUTPUT.PUT_LINE(' '); 

  DBMS_OUTPUT.PUT_LINE('/****************************************************************************************/'); 
  DBMS_OUTPUT.PUT_LINE('                    파라미터 변경 시 DB 종료가 필요합니다.');
  DBMS_OUTPUT.PUT_LINE('                   DB 종료 가능 여부를 파악한 후에 진행하시고');  
  DBMS_OUTPUT.PUT_LINE('        권고 작업 진행 전, Parameter file(spfile[SID].ora)을 백업하시기 바랍니다.');  
  DBMS_OUTPUT.PUT_LINE('/****************************************************************************************/'); 
  DBMS_OUTPUT.PUT_LINE(' '); 
  DBMS_OUTPUT.PUT_LINE(' '); 
  /*Oracle Version 확인*/
  DBMS_OUTPUT.PUT_LINE('/*************************************Oracle Version*************************************/');   
  DECLARE CURSOR ORACLE_VERSION IS
  SELECT    A.BANNER
  FROM      V$VERSION A; 
  BEGIN
    FOR ORACLE_VERSION_LIST IN ORACLE_VERSION LOOP
      DBMS_OUTPUT.PUT_LINE(ORACLE_VERSION_LIST.BANNER);  
    END LOOP;
  END;
  DBMS_OUTPUT.PUT_LINE('/****************************************************************************************/'); 
  DBMS_OUTPUT.PUT_LINE(' '); 

  /*Oracle Version 확인*/
  SELECT    TO_NUMBER(REGEXP_SUBSTR(A.VERSION,'[^.]+',1,1)) AS VERSION
  INTO      V_VERSION
  FROM      V$INSTANCE A; 
 
  IF V_VERSION &amp;lt; 10 THEN
    DBMS_OUTPUT.PUT_LINE('Oracle Version 10g 이상부터 사용할 수 있습니다.'); 
    RETURN; 
  END IF;
  
  /*Parameter File 확인*/  
  DBMS_OUTPUT.PUT_LINE('Check Parameter File');   
  SELECT    A.VALUE
  INTO      V_PAM_FILE
  FROM      V$PARAMETER A
  WHERE     A.NAME = 'spfile';
  IF V_PAM_FILE IS NULL THEN
    DBMS_OUTPUT.PUT_LINE(' - Parameter File을 spfile로 변경하신 후 재작업하시기 바랍니다.');  
    DBMS_OUTPUT.PUT_LINE(' '); 
    DBMS_OUTPUT.PUT_LINE(' [권고 작업] ');
    DBMS_OUTPUT.PUT_LINE('  CREATE SPFILE FROM PFILE;');
    DBMS_OUTPUT.PUT_LINE('  DB 종료');
    DBMS_OUTPUT.PUT_LINE('  PFILE(INIT[SID].ORA) 파일 제거');
    DBMS_OUTPUT.PUT_LINE('  DB 시작');
    RETURN; 
  ELSE
    DBMS_OUTPUT.PUT_LINE(' - Pass');   
  END IF;
  DBMS_OUTPUT.PUT_LINE(' ');  
  /*audit_trail 확인*/
  DBMS_OUTPUT.PUT_LINE('Check audit_trail');  
  SELECT    A.VALUE
  INTO      V_AUDIT_RS
  FROM      V$PARAMETER A
  WHERE     A.NAME = 'audit_trail';
  IF V_AUDIT_RS != 'NONE' THEN
    DBMS_OUTPUT.PUT_LINE(' - audit_trail을 NONE으로 변경하시기 바랍니다.');
    DBMS_OUTPUT.PUT_LINE(' [현재 audit_trail 설정값]'); 
    DBMS_OUTPUT.PUT_LINE('  - '||V_AUDIT_RS);
    DBMS_OUTPUT.PUT_LINE(' '); 
    DBMS_OUTPUT.PUT_LINE(' [권고 audit_trail 설정값]'); 
    DBMS_OUTPUT.PUT_LINE('  ALTER SYSTEM SET audit_trail=NONE SCOPE=SPFILE;'); 
    DBMS_OUTPUT.PUT_LINE('  DB 종료');      
    DBMS_OUTPUT.PUT_LINE('  DB 시작'); 
  ELSE
    DBMS_OUTPUT.PUT_LINE(' - Pass');
  END IF;
  DBMS_OUTPUT.PUT_LINE(' ');  
  /*processes 확인*/  
  DBMS_OUTPUT.PUT_LINE('Check processes');  
  SELECT    A.VALUE
  INTO      V_PROCESS_RS
  FROM      V$PARAMETER A
  WHERE     A.NAME = 'processes';
  IF V_PROCESS_RS &amp;lt; 1000 THEN
    DBMS_OUTPUT.PUT_LINE(' - processes 개수를 1000개로 변경하시기 바랍니다.');
    DBMS_OUTPUT.PUT_LINE(' [현재 processes 설정값]');  
    DBMS_OUTPUT.PUT_LINE('  '||V_PROCESS_RS);  
    DBMS_OUTPUT.PUT_LINE(' ');     
    DBMS_OUTPUT.PUT_LINE(' [권고 작업] ');
    DBMS_OUTPUT.PUT_LINE('  ALTER SYSTEM SET processes=1000 SCOPE=SPFILE;');
    DBMS_OUTPUT.PUT_LINE('  DB 종료');      
    DBMS_OUTPUT.PUT_LINE('  DB 시작');  
  ELSE
    DBMS_OUTPUT.PUT_LINE(' - Pass');
  END IF;
  DBMS_OUTPUT.PUT_LINE(' '); 
  /*archive log mode 확인 - Default Directory일 경우, 변경문구출력*/
  SELECT    A.LOG_MODE
  INTO      V_AR_RS
  FROM      V$DATABASE A;
  IF V_AR_RS = 'ARCHIVELOG' THEN
    DBMS_OUTPUT.PUT_LINE('Check archive log directory');  
    SELECT    A.VALUE
    INTO      V_AR_DIR_RS
    FROM      V$PARAMETER A
    WHERE     A.NAME = 'log_archive_dest_1';
    IF V_AR_DIR_RS IS NULL THEN
      DBMS_OUTPUT.PUT_LINE(' - archive log 위치를 변경해주시기 바랍니다.'); 
      DBMS_OUTPUT.PUT_LINE(' [현재 Archive log 디렉토리 설정값]');  
      DBMS_OUTPUT.PUT_LINE('  db_recovery_file_dest'); 
      DBMS_OUTPUT.PUT_LINE(' ');     
      DBMS_OUTPUT.PUT_LINE(' [권고 작업] ');
      DBMS_OUTPUT.PUT_LINE('  ALTER SYSTEM SET LOG_ARCHIVE_DEST_1=''location=[directory 경로]'' ; /*directory 경로 변경*/');
    ELSE
      DBMS_OUTPUT.PUT_LINE(' - Pass');    
    END IF;
    DBMS_OUTPUT.PUT_LINE(' '); 
  END IF;
  /*diag_daemon 확인*/
  DBMS_OUTPUT.PUT_LINE('Check _diag_daemon'); 
  SELECT    Y.KSPPSTVL
  INTO      V_DIAG_DAEMON
  FROM      X$KSPPI X
           ,X$KSPPCV Y
  WHERE     X.INDX = Y.INDX
  AND       X.KSPPINM = '_diag_daemon';
  IF V_DIAG_DAEMON = 'TRUE' THEN
    DBMS_OUTPUT.PUT_LINE(' - 파라미터를 FALSE로 변경하시기 바랍니다.'); 
    DBMS_OUTPUT.PUT_LINE(' [현재 _diag_daemon 설정값]');  
    DBMS_OUTPUT.PUT_LINE('  '||V_DIAG_DAEMON);  
    DBMS_OUTPUT.PUT_LINE(' ');     
    DBMS_OUTPUT.PUT_LINE(' [권고 작업] ');
    DBMS_OUTPUT.PUT_LINE('  ALTER SYSTEM SET &quot;_diag_daemon&quot;=FALSE SCOPE=SPFILE;');
    DBMS_OUTPUT.PUT_LINE('  DB 종료');      
    DBMS_OUTPUT.PUT_LINE('  DB 시작');  
  ELSE
    DBMS_OUTPUT.PUT_LINE(' - Pass');     
  END IF;
  DBMS_OUTPUT.PUT_LINE(' ');   
  /*Oracle Memory 확인*/
  DBMS_OUTPUT.PUT_LINE('Check Oracle Memory');  
  SELECT    A.BYTE_VALUE / 1024 / 1024, V_DB_MEMORY AS VALUE
  INTO      V_MR_RS, V_MR_REC
  FROM      (SELECT    CASE
                         WHEN V_VERSION = 10
                         THEN
                           SUM(CASE
                                 WHEN A.NAME IN ('sga_target', 'pga_aggregate_target') THEN TO_NUMBER(A.VALUE)
                                 ELSE 0
                               END)
                         ELSE
                           MAX(CASE
                                 WHEN A.NAME = 'memory_target' THEN TO_NUMBER(A.VALUE)
                                 ELSE 0
                               END)
                       END
                         AS BYTE_VALUE
             FROM      V$PARAMETER A
             WHERE     A.NAME IN ('memory_target', 'sga_target', 'pga_aggregate_target')) A;

  IF V_MR_RS = 0 OR V_MR_RS &amp;lt; V_MR_REC THEN
    DBMS_OUTPUT.PUT_LINE(' - 파라미터를 재설정하시기 바랍니다.');     
    DBMS_OUTPUT.PUT_LINE(' [현재 메모리 설정값]'); 
    DECLARE CURSOR ORACLE_MEMORY IS
    SELECT    A.NAME AS PARAMETER_NAME, A.VALUE AS BYTE_VALUE, ROUND(A.VALUE / 1024 / 1024, 2) || 'MB' AS MB_VALUE
    FROM      V$PARAMETER A
    WHERE     A.NAME IN
                ('sga_max_size'
                ,'sga_target'
                ,'java_pool_size'
                ,'streams_pool_size'
                ,'large_pool_size'
                ,'shared_pool_size'
                ,'shared_pool_reserved_size'
                ,'db_cache_size'
                ,'pga_aggregate_target'
                ,'memory_target'
                ,'memory_max_target')
    ORDER BY  A.NAME;
    
    BEGIN
      FOR ORACLE_MEMORY_LIST IN ORACLE_MEMORY LOOP
        DBMS_OUTPUT.PUT_LINE('  - '||RPAD(ORACLE_MEMORY_LIST.PARAMETER_NAME,25,' ')||' : '||ORACLE_MEMORY_LIST.MB_VALUE);  
      END LOOP;
    END;
    DBMS_OUTPUT.PUT_LINE(' ');  
    DBMS_OUTPUT.PUT_LINE(' [권고 작업] ');  
    
    IF V_VERSION = 10 THEN
      DECLARE CURSOR ORACLE_MEMORY_RT IS
      SELECT    'ALTER SYSTEM SET '|| A.PARAMETER_NAME ||' = '||A.VALUE||'M SCOPE=SPFILE;' AS STMT
      FROM      (SELECT    'sga_max_size' AS PARAMETER_NAME, TRUNC(V_DB_MEMORY * V_MR_RT_10) AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'sga_target' AS PARAMETER_NAME, TRUNC(V_DB_MEMORY * V_MR_RT_10) AS VALUE
                 FROM      DUAL               
                 UNION ALL
                 SELECT    'pga_aggregate_target' AS PARAMETER_NAME, V_DB_MEMORY - TRUNC(V_DB_MEMORY * V_MR_RT_10) AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'java_pool_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'streams_pool_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'large_pool_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'shared_pool_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'shared_pool_reserved_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 ) A    ;
      BEGIN
        FOR ORACLE_MEMORY_STMT IN ORACLE_MEMORY_RT LOOP
          DBMS_OUTPUT.PUT_LINE('  '||ORACLE_MEMORY_STMT.STMT);  
        END LOOP;
      END;
             
      DBMS_OUTPUT.PUT_LINE('  DB 종료');      
      DBMS_OUTPUT.PUT_LINE('  DB 시작');        
      DBMS_OUTPUT.PUT_LINE('  ALTER SYSTEM SET db_cache_size = 0M ;'); 
    
    ELSIF V_VERSION &amp;gt;= 11 THEN
      DECLARE CURSOR ORACLE_MEMORY_RT IS
      SELECT    'ALTER SYSTEM SET '|| A.PARAMETER_NAME ||' = '||A.VALUE||'M SCOPE=SPFILE;' AS STMT
      FROM      (SELECT    'memory_max_target' AS PARAMETER_NAME, V_DB_MEMORY AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'memory_target' AS PARAMETER_NAME, V_DB_MEMORY AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'sga_max_size' AS PARAMETER_NAME, TRUNC(V_DB_MEMORY * V_MR_RT_11) AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'sga_target' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL               
                 UNION ALL
                 SELECT    'pga_aggregate_target' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'java_pool_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'streams_pool_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'large_pool_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'shared_pool_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 UNION ALL
                 SELECT    'shared_pool_reserved_size' AS PARAMETER_NAME, 0 AS VALUE
                 FROM      DUAL
                 ) A    ;
      BEGIN
        FOR ORACLE_MEMORY_STMT IN ORACLE_MEMORY_RT LOOP
          DBMS_OUTPUT.PUT_LINE('  '||ORACLE_MEMORY_STMT.STMT);  
        END LOOP;
      END;
      
      DBMS_OUTPUT.PUT_LINE('  DB 종료');      
      DBMS_OUTPUT.PUT_LINE('  DB 시작');        
      DBMS_OUTPUT.PUT_LINE('  ALTER SYSTEM SET db_cache_size = 0M ;'); 
      
    END IF;
  ELSE
    DBMS_OUTPUT.PUT_LINE(' - Pass');   
  END IF;
END;
/

SPOOL OFF
SET SERVEROUTPUT OFF
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;3.&amp;nbsp;&lt;b&gt;Oracle&amp;nbsp;Parameter&amp;nbsp;check&lt;/b&gt; 결과&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: sql&quot;&gt;Check audit_trail
 - audit_trail을 NONE으로 변경하시기 바랍니다.
 [현재 audit_trail 설정값]
  - DB
 
 [권고 audit_trail 설정값]
  ALTER SYSTEM SET audit_trail=NONE SCOPE=SPFILE;
  DB 종료
  DB 시작
 
Check processes
 - processes 개수를 1000개로 변경하시기 바랍니다.
 [현재 processes 설정값]
  150
 
 [권고 작업] 
  ALTER SYSTEM SET processes=1000 SCOPE=SPFILE;

Check archive log directory
 - archive log 위치를 변경해주시기 바랍니다.
 [현재 Archive log 디렉토리 설정값]
  db_recovery_file_dest
 
 [권고 작업] 
  ALTER SYSTEM SET LOG_ARCHIVE_DEST_1='location=[directory 경로]';

Check _diag_daemon
 - 파라미터를 FALSE로 변경하시기 바랍니다.
 [현재 _diag_daemon 설정값]
  TRUE
 
 [권고 작업] 
  ALTER SYSTEM SET &quot;_diag_daemon&quot;=FALSE SCOPE=SPFILE;
  DB 종료
  DB 시작
 
Check Oracle Memory
 - 파라미터를 재설정하시기 바랍니다.
 [현재 메모리 설정값]
  - db_cache_size             : 0MB
  - java_pool_size            : 0MB
  - large_pool_size           : 0MB
  - memory_max_target         : 616MB
  - memory_target             : 616MB
  - pga_aggregate_target      : 0MB
  - sga_max_size              : 412MB
  - sga_target                : 0MB
  - shared_pool_reserved_size : 13.6MB
  - shared_pool_size          : 0MB
  - streams_pool_size         : 0MB
 
 [권고 작업] 
  ALTER SYSTEM SET memory_max_target = 26M SCOPE=SPFILE;
  ALTER SYSTEM SET memory_target = 26M SCOPE=SPFILE;
  ALTER SYSTEM SET sga_max_size = 20M SCOPE=SPFILE;
  ALTER SYSTEM SET sga_target = 0M SCOPE=SPFILE;
  ALTER SYSTEM SET pga_aggregate_target = 0M SCOPE=SPFILE;
  ALTER SYSTEM SET java_pool_size = 0M SCOPE=SPFILE;
  ALTER SYSTEM SET streams_pool_size = 0M SCOPE=SPFILE;
  ALTER SYSTEM SET large_pool_size = 0M SCOPE=SPFILE;
  ALTER SYSTEM SET shared_pool_size = 0M SCOPE=SPFILE;
  ALTER SYSTEM SET shared_pool_reserved_size = 0M SCOPE=SPFILE;
  DB 종료
  DB 시작
  ALTER SYSTEM SET db_cache_size = 0M ;
  
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;해당 내용은 자체적으로 테스트하고 운영하면서 변경된 최적화 내용을 토대로 전달해드립니다.&amp;nbsp;&lt;br /&gt;운영 환경과 오라클 버전에 따라서 조금씩 차이가 날 수 있습니다. 반드시 내부 테스트를 거쳐서 반영하시기 바랍니다.&lt;br /&gt;&lt;br /&gt;메모리에 대한 변경은 11G 이상부터 적용가능 합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Data Architecture/Admin</category>
      <category>memory</category>
      <category>oracle</category>
      <category>oracle parameter</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/380</guid>
      <comments>https://estenpark.tistory.com/380#entry380comment</comments>
      <pubDate>Fri, 16 Aug 2019 12:13:26 +0900</pubDate>
    </item>
    <item>
      <title>[Admin] MariaDB/MySQL Linux Filesystem Cache(unmap) 메모리 초기화</title>
      <link>https://estenpark.tistory.com/378</link>
      <description>&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;1. unmap RPM 목록&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;
&lt;p&gt;&lt;span&gt;gdbm-devel-1.10-8.el7.x86_64.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;libdb-5.3.21-20.el7.x86_64.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;libdb-devel-5.3.21-20.el7.x86_64.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;libdb-utils-5.3.21-20.el7.x86_64.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-Digest-1.17-245.el7.noarch.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-Digest-MD5-2.52-3.el7.x86_64.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-ExtUtils-Install-1.58-292.el7.noarch.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-ExtUtils-MakeMaker-6.68-3.el7.noarch.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-ExtUtils-Manifest-1.61-244.el7.noarch.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-ExtUtils-ParseXS-3.18-3.el7.noarch.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-Inline-0.53-4.el7.noarch.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-Parse-RecDescent-1.967009-5.el7.noarch.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-Test-Harness-3.28-3.el7.noarch.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;perl-devel-5.16.3-292.el7.x86_64.rpm&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;pyparsing-1.5.6-9.el7.noarch.rpm&lt;/span&gt;&lt;/p&gt;
&lt;span&gt;systemtap-sdt-devel-3.1-3.el7.x86_64.rpm&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/os9wX/btqxkVgi0K2/GF5xfO3m7asPM9HGddfEx0/unmap_RPM_CentOS%207.1511.zip?attach=1&amp;amp;knm=tfile.zip&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;unmap_RPM_CentOS 7.1511.zip&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;2.53MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;2. unmap perl source&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - 원본 :&amp;nbsp;&lt;a href=&quot;https://github.com/gywndi/kkb/tree/master/mysql_cache_unmap&quot;&gt;https://github.com/gywndi/kkb/tree/master/mysql_cache_unmap&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - MariaDB/MySQL 설치된 경로가 다를 수 있습니다. 아래 처럼 목적에 맞게 변경 바랍니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; -&amp;nbsp;my&amp;nbsp;@target_dirs&amp;nbsp;=&amp;nbsp;(&amp;nbsp;'/estdb/dbms/data',&amp;nbsp;'/estdb/dbms/log/binary',&amp;nbsp;'/estdb/dbms/log/relay'&amp;nbsp;);&lt;/p&gt;
&lt;pre class=&quot;brush:python perl&quot;&gt;#!/usr/bin/perl

#  Copyright (C) 2011 DeNA Co.,Ltd.
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#  Foundation, Inc.,
#  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use strict;
use warnings FATAL =&amp;gt; 'all';
use Inline C       =&amp;gt; 'DATA';

my @target_dirs = ( '/estdb/dbms/data', '/estdb/dbms/log/binary', '/estdb/dbms/log/relay' );
my @fully_unmap_logs = ('ib_logfile');
my $binary_log_name  = 'mysql-bin';
my $relay_log_name   = 'relay-bin';

&amp;amp;main();

sub unmap_binary_logs {
  my $binlog_name = shift;
  foreach my $dir (@target_dirs) {
    opendir DIR, $dir;
    my @files =
      grep { m/$binlog_name\.[0-9][0-9][0-9][0-9][0-9][0-9]/ } readdir DIR;
    @files = sort @files;
    for ( my $i = 0 ; $i &amp;lt; $#files + 1 ; $i++ ) {
      my $fpath = $dir . &quot;/&quot; . $files[$i];

      # The tail of the latest binary/relay logs are read from binlog dump
      # thread or SQL thread. To keep the tail in cache, we don't
      # unmap all area, but unmap 90% of the file (the 10% tail is cached).
      if ( $i == $#files ) {
        my $filesize = -s $fpath;
        unmap_log( $fpath, 0, int( $filesize * 0.9 ) );
      }
      else {
        unmap_log_all($fpath);
      }
    }
    closedir DIR;

    #binlog is under this directry. We don't need to search more
    last if ( $#files + 1 &amp;gt; 0 );
  }
}

sub unmap_logs {
  foreach my $dir (@target_dirs) {
    opendir DIR, $dir;
    foreach my $target (@fully_unmap_logs) {
      my @files = grep { m/$target/ } readdir DIR;
      foreach my $file (@files) {
        my $fpath = $dir . &quot;/&quot; . $file;
        unmap_log_all($fpath);
      }
    }
  }
}

sub main {
  unmap_binary_logs($binary_log_name);
  unmap_binary_logs($relay_log_name);
  unmap_logs();
}

__DATA__
__C__

#define _XOPEN_SOURCE 600
#define _FILE_OFFSET_BITS 64
#include 
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;sys/stat.h&amp;gt;
#include 
#include 
#include 

int unmap_log(const char *fpath, size_t start, size_t len)
{
  int fd = open(fpath, O_RDONLY);
  if (fd &amp;lt; 0) {
    fprintf(stderr, &quot;Failed to open %s\n&quot;, fpath);
    return 1;
  }
  int r = posix_fadvise(fd, start, len, POSIX_FADV_DONTNEED);
  if (r != 0) {
    fprintf(stderr, &quot;posix_fadvice failed for %s\n&quot;, fpath);
  }
  close(fd);
  return 0;
}

int unmap_log_all(const char *fpath)
{
  return unmap_log(fpath, 0, 0);
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;3. 테스트 환경&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - OS : CentOS 7&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - CPU :&amp;nbsp;Intel(R)&amp;nbsp;Xeon(R)&amp;nbsp;CPU&amp;nbsp;E3-1220&amp;nbsp;v3&amp;nbsp;@&amp;nbsp;3.10GHz&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - Memory : 32Gb&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - Database :&amp;nbsp;MariaDB&amp;nbsp;10.1.12&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;4. MariaDB 메모리 스왑(Swap)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Linux&amp;nbsp;계열의&amp;nbsp;OS사용시&amp;nbsp;MariaDB&amp;nbsp;(or&amp;nbsp;MySQL)에서&amp;nbsp;사용하는&amp;nbsp;Innodb_Buffer는&amp;nbsp;할당된&amp;nbsp;공간을&amp;nbsp;미리&amp;nbsp;확보하는&amp;nbsp;것이&amp;nbsp;아니기&amp;nbsp;때문에&amp;nbsp;다른&amp;nbsp;곳에서&amp;nbsp;메모리를&amp;nbsp;선점해&amp;nbsp;버린다면&amp;nbsp;Innodb_Buffer&amp;nbsp;사용량이&amp;nbsp;증가함에&amp;nbsp;따라&amp;nbsp;Swap&amp;nbsp;사용량이&amp;nbsp;늘어납니다. &lt;br /&gt;리눅스&amp;nbsp;계열은&amp;nbsp;FileSystem&amp;nbsp;Cache를&amp;nbsp;가장&amp;nbsp;우선시&amp;nbsp;하고&amp;nbsp;있어서&amp;nbsp;실제&amp;nbsp;서버의&amp;nbsp;free&amp;nbsp;memory가&amp;nbsp;없을&amp;nbsp;경우&amp;nbsp;FileSystem&amp;nbsp;Cache의&amp;nbsp;일부를&amp;nbsp;반환하는&amp;nbsp;것이&amp;nbsp;아닌&amp;nbsp;memory의&amp;nbsp;가장&amp;nbsp;많이&amp;nbsp;사용하고&amp;nbsp;있는&amp;nbsp;application의&amp;nbsp;일정량을&amp;nbsp;Swap로&amp;nbsp;내리게&amp;nbsp;됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;5.&amp;nbsp;Linux&amp;nbsp;Filesystem&amp;nbsp;Cache Swap 메모리 문제 해결&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;해결&amp;nbsp;방법으로는&amp;nbsp;메모리&amp;nbsp;초기화나&amp;nbsp;Cache&amp;nbsp;unmap를&amp;nbsp;주기적으로&amp;nbsp;실행해야&amp;nbsp;한다.&amp;nbsp;메모리&amp;nbsp;전체&amp;nbsp;초기화는&amp;nbsp;테스트&amp;nbsp;용으로&amp;nbsp;진행&amp;nbsp;했으나&amp;nbsp;DB운영&amp;nbsp;중에&amp;nbsp;문제가&amp;nbsp;발생할&amp;nbsp;수&amp;nbsp;있으므로&amp;nbsp;FileSystem&amp;nbsp;Caching&amp;nbsp;대상에서&amp;nbsp;제외하라는&amp;nbsp;명령을&amp;nbsp;통해서&amp;nbsp;메모리&amp;nbsp;Swap&amp;nbsp;를&amp;nbsp;관리할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chPYZf/btqxlx659l2/6XcIqaU4pD9NDCTqIc52K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chPYZf/btqxlx659l2/6XcIqaU4pD9NDCTqIc52K0/img.png&quot; data-alt=&quot;swap 초기화 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chPYZf/btqxlx659l2/6XcIqaU4pD9NDCTqIc52K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchPYZf%2Fbtqxlx659l2%2F6XcIqaU4pD9NDCTqIc52K0%2Fimg.png&quot; data-filename=&quot;blob&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;swap 초기화 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;6. unmap 설치 가이드&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:bash lsl&quot;&gt;# 디렉토리 및 구성
[root@estdb01 /root]# cd /estdb/dbms
[root@estdb01 /estdb/dbms]# mkdir ./scripts
[root@estdb01 /estdb/dbms]# cd scripts
[root@estdb01 /estdb/dbms/scripts]# mkdir conf lib logs
[root@estdb01 /estdb/dbms]# chown -R estdbusr.estdba /estdb/dbms/scripts

# RPM 설치
[root@estdb01 /opt]# cd ./rpm
[root@estdb01 /rpm]# rpm -ivh --force gdbm-devel-1.10-8.el7.x86_64.rpm libdb-5.3.21-20.el7.x86_64.rpm libdb-devel-5.3.21-20.el7.x86_64.rpm libdb-utils-5.3.21-20.el7.x86_64.rpm perl-Digest-1.17-245.el7.noarch.rpm perl-Digest-MD5-2.52-3.el7.x86_64.rpm perl-ExtUtils-Install-1.58-292.el7.noarch.rpm perl-ExtUtils-MakeMaker-6.68-3.el7.noarch.rpm perl-ExtUtils-Manifest-1.61-244.el7.noarch.rpm perl-ExtUtils-ParseXS-3.18-3.el7.noarch.rpm perl-Inline-0.53-4.el7.noarch.rpm perl-Parse-RecDescent-1.967009-5.el7.noarch.rpm perl-Test-Harness-3.28-3.el7.noarch.rpm perl-devel-5.16.3-292.el7.x86_64.rpm pyparsing-1.5.6-9.el7.noarch.rpm systemtap-sdt-devel-3.1-3.el7.x86_64.rpm 

# unmap_mysql_log  파일의 권한을 변경 및 실행 권한 부여(위 소스 참고)
[root@estdb01 ~]# cd /opt
[root@estdb01 /opt]# chmod 755 ./unmap_mysql_logs
[root@estdb01 /opt]# chown estdbusr.estdba ./unmap_mysql_logs
[root@estdb01 /opt]# mv ./unmap_mysql_logs /estdb/dbms/scripts/
[root@estdb01 /opt]# su &amp;ndash; estdbusr
[estdbusr@estdb01 ~]$ cd /estdb/dbms/scripts/
[estdbusr@estdb01 /estdb/dbms/scripts]$ ./unmap_mysql_logs
[estdbusr@estdb01 /estdb/dbms/scripts]$ ls
_Inline  conf  lib  logs  unmap_mysql_log 

# crontab 등록(매 10분 마다)
[root@estdb01 /root]# crontab -e
*/10 * * * * /estdb/dbms/scripts/unmap_mysql_logs
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;7. 결과&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - 10분 주기로 스크립트를 통해서 메모리를 초기화 할 경우 아래와 같이 Swap 사이즈가 증가하지 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - unmap_mysql_logs 쉘 스크립트 사용 필수&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - DB 파라미터 변경 : table_open_cache=4096&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; - &lt;span style=&quot;color: #333333;&quot;&gt;DB&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;파라미터 변경 : performance_schema=off&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; - OS 파라미터 : vm.swappiness=1 (검증되지 않았으며, 운영 서버에 바로 반영 하면 안됩니다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/spMPE/btqxlv9vpTA/TwAoO4UelNvLaQUuSohf01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/spMPE/btqxlv9vpTA/TwAoO4UelNvLaQUuSohf01/img.png&quot; data-alt=&quot;주기적으로 수행 시 swap 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/spMPE/btqxlv9vpTA/TwAoO4UelNvLaQUuSohf01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FspMPE%2Fbtqxlv9vpTA%2FTwAoO4UelNvLaQUuSohf01%2Fimg.png&quot; data-filename=&quot;blob&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주기적으로 수행 시 swap 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;8. 참고 문서&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://small-dbtalk.blogspot.com/2013/09/mysql-linux-filesystem-cache-2.html&quot;&gt;http://small-dbtalk.blogspot.com/2013/09/mysql-linux-filesystem-cache-2.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/gywndi/kkb/tree/master/mysql_cache_unmap&quot;&gt;https://github.com/gywndi/kkb/tree/master/mysql_cache_unmap&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Data Architecture/Admin</category>
      <category>cache</category>
      <category>mariaDB</category>
      <category>memory</category>
      <category>MySQL</category>
      <category>Unmap</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/378</guid>
      <comments>https://estenpark.tistory.com/378#entry378comment</comments>
      <pubDate>Fri, 9 Aug 2019 14:57:33 +0900</pubDate>
    </item>
    <item>
      <title>[Admin] MariaDB/MySQL InnoDB 테이블 압축(Compression)</title>
      <link>https://estenpark.tistory.com/377</link>
      <description>&lt;p&gt;&lt;b&gt;1.&amp;nbsp;테이블&amp;nbsp;압축&amp;nbsp;개요&lt;/b&gt; &lt;br /&gt;증가하는&amp;nbsp;데이터로&amp;nbsp;인해서&amp;nbsp;서버의&amp;nbsp;저장공간이&amp;nbsp;부족&amp;nbsp;혹은&amp;nbsp;추가적인&amp;nbsp;HDD의&amp;nbsp;증설을&amp;nbsp;줄이려는&amp;nbsp;방법으로&amp;nbsp;데이터&amp;nbsp;수정(UPDATE)이&amp;nbsp;발생하지&amp;nbsp;않는&amp;nbsp;로그&amp;nbsp;테이블에&amp;nbsp;적용하여&amp;nbsp;조회&amp;nbsp;속도 향상&amp;nbsp;및&amp;nbsp;저장&amp;nbsp;공간을&amp;nbsp;줄일&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;기법.&amp;nbsp;테이블&amp;nbsp;압축은&amp;nbsp;테이블&amp;nbsp;데이터를&amp;nbsp;압축해서&amp;nbsp;보관함으로&amp;nbsp;파일&amp;nbsp;I/O를&amp;nbsp;감소시키는&amp;nbsp;것이&amp;nbsp;가장&amp;nbsp;큰&amp;nbsp;목적입니다.&amp;nbsp;(반대로&amp;nbsp;압축을&amp;nbsp;하게&amp;nbsp;되면&amp;nbsp;수정&amp;nbsp;시에는&amp;nbsp;속도가&amp;nbsp;느림) &lt;br /&gt;&lt;br /&gt;테이블 압축의&amp;nbsp;옵션인&amp;nbsp;블록 사이즈는&amp;nbsp;2,&amp;nbsp;4,&amp;nbsp;8,&amp;nbsp;16KB로&amp;nbsp;나눠지게&amp;nbsp;됩니다.&amp;nbsp;(기본값&amp;nbsp;:&amp;nbsp;16KB)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. 테이블 압축 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;MariaDB [HIWDB]&amp;gt; ALTER TABLE EST_CONN_LOGIN_2 ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=2;
Query OK, 0 rows affected (1 min 36.00 sec) 
Records: 0  Duplicates: 0  Warnings: 0

MariaDB [HIWDB]&amp;gt; ALTER TABLE EST_CONN_LOGIN_4 ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
Query OK, 0 rows affected (54.58 sec)               
Records: 0  Duplicates: 0  Warnings: 0

MariaDB [HIWDB]&amp;gt; ALTER TABLE EST_CONN_LOGIN_8 ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;
Query OK, 0 rows affected (39.06 sec)               
Records: 0  Duplicates: 0  Warnings: 0

MariaDB [HIWDB]&amp;gt; ALTER TABLE EST_CONN_LOGIN_16 ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=16;
Query OK, 0 rows affected (25.48 sec)
Records: 0  Duplicates: 0  Warnings: 0
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. 테이블 압축 결과 비교&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - 테이블 압축은 100만건을 대상으로 진행했으며, 블록 사이즈가 줄이면 압축률이 좋고 조회시간도 줄어듭니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;0&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;항목&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;123&quot;&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;용량&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;113&quot;&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;압축률&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;151&quot;&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;조회시간&lt;span&gt;(5&lt;/span&gt;회 평균&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;데이터 건수&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;123&quot;&gt;
&lt;p&gt;&lt;span&gt;1,004,092&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;113&quot;&gt;
&lt;p&gt;&lt;span&gt;-&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;151&quot;&gt;
&lt;p&gt;&lt;span&gt;-&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축 전 용량&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;123&quot;&gt;
&lt;p&gt;&lt;span&gt;413,696KB&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;113&quot;&gt;
&lt;p&gt;&lt;span&gt;-&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;151&quot;&gt;
&lt;p&gt;&lt;span&gt;0.425&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축&lt;span&gt;(&lt;/span&gt;블록 사이즈 &lt;span&gt;2KB)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;123&quot;&gt;
&lt;p&gt;&lt;span&gt;98,304KB&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;113&quot;&gt;
&lt;p&gt;&lt;span&gt;76.2%&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;151&quot;&gt;
&lt;p&gt;&lt;span&gt;0.390&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축(블록 사이즈 &lt;span&gt;4&lt;span style=&quot;color: #333333;&quot;&gt;KB&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;123&quot;&gt;
&lt;p&gt;&lt;span&gt;98,304KB&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;113&quot;&gt;
&lt;p&gt;&lt;span&gt;76.2%&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;151&quot;&gt;
&lt;p&gt;&lt;span&gt;0.377&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축(블록 사이즈 &lt;span&gt;8&lt;span style=&quot;color: #333333;&quot;&gt;KB&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;123&quot;&gt;
&lt;p&gt;&lt;span&gt;180,224KB&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;113&quot;&gt;
&lt;p&gt;&lt;span&gt;56.4%&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;151&quot;&gt;
&lt;p&gt;&lt;span&gt;0.398&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축(블록 사이즈 &lt;span&gt;16&lt;span style=&quot;color: #333333;&quot;&gt;KB&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;123&quot;&gt;
&lt;p&gt;&lt;span&gt;352,256KB&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;113&quot;&gt;
&lt;p&gt;&lt;span&gt;14.9%&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;151&quot;&gt;
&lt;p&gt;&lt;span&gt;0.433&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;4.&amp;nbsp;테이블압축&amp;nbsp;추가&amp;nbsp;및&amp;nbsp;수정&amp;nbsp;테스트&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - DML에서 INSERT, UPDATE, DELETE 각각 변경 사이즈에 따른 수행 결과를 확인했습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;0&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;항목&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Insert&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Update&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Delete&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축 전&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;2.83&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;0.02&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;3.59&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축(블록 사이즈 &lt;span&gt;2&lt;span style=&quot;color: #333333;&quot;&gt;KB&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;3.5&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;1.19&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;6.75&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축(블록 사이즈 &lt;span&gt;4&lt;span style=&quot;color: #333333;&quot;&gt;KB&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;3.2&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;0.63&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;5.63&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축(블록 사이즈 &lt;span&gt;8&lt;span style=&quot;color: #333333;&quot;&gt;KB&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;3.15&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;0.32&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;4.34&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width=&quot;180&quot;&gt;
&lt;p&gt;&lt;span&gt;압축(블록 사이즈 &lt;span&gt;16&lt;span style=&quot;color: #333333;&quot;&gt;KB&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;3.09&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;0.19&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width=&quot;129&quot;&gt;
&lt;p&gt;&lt;span&gt;3.95&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;5. 결론&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; - 장점은 일반 테이블 보다 파일 크기를 줄일 수 있고, 데이터 조회 속도는 큰 차이가 없습니다. 단점은 DML 수행 속도가 느려지며, 과거 데이터에 대한 관리 방법이 필요하다고 판단 합니다.&lt;/p&gt;</description>
      <category>Data Architecture/Admin</category>
      <category>mariaDB</category>
      <category>MySQL</category>
      <category>Table compression</category>
      <category>테이블 압축</category>
      <author>EstenPark</author>
      <guid isPermaLink="true">https://estenpark.tistory.com/377</guid>
      <comments>https://estenpark.tistory.com/377#entry377comment</comments>
      <pubDate>Wed, 7 Aug 2019 10:19:05 +0900</pubDate>
    </item>
  </channel>
</rss>