<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>MySQL 主從架構｜林鼎淵 Dean Lin</title>
    <link>https://deanlin.net/series/mysql-replication/</link>
    <image><url>https://deanlin.net/images/author_hu_aa256fc934e9a932.jpg</url><title>MySQL 主從架構｜林鼎淵 Dean Lin</title><link>https://deanlin.net/series/mysql-replication/</link><width>144</width><height>144</height></image>
    <description>MySQL 主從式架構實作：設定 Replication、用 Atlas 讀寫分離、sysbench 壓測，以及解決複製延遲。</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <copyright>© 2020–2026 林鼎淵 Dean Lin</copyright>
    <lastBuildDate>Mon, 29 Mar 2021 15:27:42 +0800</lastBuildDate>
    <atom:link href="https://deanlin.net/series/mysql-replication/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>解決 MySQL 主從複製的延遲問題 (Replication lag)</title>
      <link>https://deanlin.net/blog/202103/mysql-replication-lag/</link>
      <pubDate>Mon, 29 Mar 2021 15:27:42 +0800</pubDate>
      <guid>https://deanlin.net/blog/202103/mysql-replication-lag/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>Slave DB 上的資料從 Master DB 複製過來時會有延遲，所以會造成用戶看到舊的資訊；在過去想解決這個問題真的需要 18 般武藝，但到了 MySQL 5.7 版後官方說只要調整一個設定就能解決，具體做法讓我們看下去。</description>
      <media:content url="https://deanlin.net/blog/202103/mysql-replication-lag/images/mysql-replication-lag-og_hu_e6c9419343a55c95.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p>在『MySQL Master Slave Replication 主從式架構設定教學』這篇文章的結尾有提到因為 Slave DB 上的資料從 Master DB 複製過來時會有延遲，所以會造成用戶看到舊的資訊；在過去想解決這個問題真的需要 18 般武藝，但到了 MySQL 5.7 版後官方說只要調整一個設定就能解決，具體做法讓我們看下去。</p>
<p><img src="https://deanlin.net/blog/202103/mysql-replication-lag/images/mysql-replication-lag-01.webp" width="1400" height="783" alt="解決 MySQL 主從複製的延遲問題 (Replication lag)（圖 1）" fetchpriority="high" decoding="async"></p>
<pre tabindex="0"><code>一、模擬主從複製延遲的情境
二、了解發生問題的原因
三、解決方案：開啟 MySQL 5.7 的多 process 的功能
四、確認 Slave 已經沒有延遲
</code></pre><h2 id="一模擬主從複製延遲的情境">一、模擬主從複製延遲的情境<a class="h-anchor" href="#%e4%b8%80%e6%a8%a1%e6%93%ac%e4%b8%bb%e5%be%9e%e8%a4%87%e8%a3%bd%e5%bb%b6%e9%81%b2%e7%9a%84%e6%83%85%e5%a2%83" aria-label="這個段落的連結"></a></h2>
<blockquote>
<p>這裡我們利用 sysbench 進行情境模擬，如果不清楚如何操作的朋友請參考這篇文章：『<a href="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/">用 sysbench 測試 MySQL 性能 — 在 mac &amp; ubuntu 上的安裝與使用</a>』</p>
</blockquote>
<p>下面是我在 ubuntu 的測試指令，如果你是 mac 需要自己更換 .lua 的路徑</p>
<pre tabindex="0"><code>sysbench /usr/share/sysbench/oltp_write_only.lua \
--mysql-host=127.0.0.1 \
--mysql-port=3306 \
--mysql-user=user_name \
--mysql-password=user_password \
--mysql-db=test \
--db-driver=mysql \
--tables=10 \
--table_size=100000 \
--report-interval=10 \
--threads=128 \
--time=120 \
prepare
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/46688419066279598ee33553f5ae9c45" rel="noopener">sysbench_lag.cmd</a>（GitHub Gist）</p>
<p>指令輸入後你就會發現資料庫好像頓住不會動了…</p>
<p><img src="https://deanlin.net/blog/202103/mysql-replication-lag/images/mysql-replication-lag-02.webp" width="916" height="912" alt="一、模擬主從複製延遲的情境（圖 2）" loading="lazy" decoding="async"></p>
<p>一段時間後我們進入到 Slave 的 MySQL 中會發現他與 Master 的延遲很嚴重</p>
<pre tabindex="0"><code>mysql&gt; show slave status\G
</code></pre><ul>
<li><strong>Seconds_Behind_Master</strong>：延遲的時間</li>
</ul>
<p><img src="https://deanlin.net/blog/202103/mysql-replication-lag/images/mysql-replication-lag-03.webp" width="874" height="236" alt="一、模擬主從複製延遲的情境（圖 3）" loading="lazy" decoding="async"></p>
<h2 id="二了解發生問題的原因">二、了解發生問題的原因<a class="h-anchor" href="#%e4%ba%8c%e4%ba%86%e8%a7%a3%e7%99%bc%e7%94%9f%e5%95%8f%e9%a1%8c%e7%9a%84%e5%8e%9f%e5%9b%a0" aria-label="這個段落的連結"></a></h2>
<p>因為我們模擬的情境會製作出相當大量的 SQL command，執行內容包含：table 內容大量更新、index 建立；可是 Slave 去讀取 Master 的 process 卻只有一個，這樣使 Master 累積了太多尚未同步到 Slave 的 SQL command，因此造成了兩邊資料庫的內容不一致。</p>
<h2 id="三解決方案開啟-mysql-57-的多-process-的功能">三、解決方案：開啟 MySQL 5.7 的多 process 的功能<a class="h-anchor" href="#%e4%b8%89%e8%a7%a3%e6%b1%ba%e6%96%b9%e6%a1%88%e9%96%8b%e5%95%9f-mysql-57-%e7%9a%84%e5%a4%9a-process-%e7%9a%84%e5%8a%9f%e8%83%bd" aria-label="這個段落的連結"></a></h2>
<p><strong>STEP 1</strong>：先暫停 Slave，不然無法修改變數</p>
<pre tabindex="0"><code>mysql&gt; stop slave;
</code></pre><p><strong>STEP 2</strong>：更改 slave_parallel_type</p>
<p>先看看預設值</p>
<pre tabindex="0"><code>mysql&gt; show variables like ‘slave_parallel%’;
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-replication-lag/images/mysql-replication-lag-04.webp" width="403" height="163" alt="三、解決方案：開啟 MySQL 5.7 的多 process 的功能（圖 4）" loading="lazy" decoding="async"></p>
<p>修改變數</p>
<pre tabindex="0"><code>mysql&gt; set global slave_parallel_type=&#39;logical_clock&#39;;
mysql&gt; set global slave_parallel_workers=10; #根據實際需求設定
</code></pre><p>啟動 Slave 功能</p>
<pre tabindex="0"><code>mysql&gt; start slave;
</code></pre><p>查看剛剛設定的多 process 是否生效</p>
<pre tabindex="0"><code>mysql&gt; show processlist;
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-replication-lag/images/mysql-replication-lag-05.webp" width="1206" height="373" alt="三、解決方案：開啟 MySQL 5.7 的多 process 的功能（圖 5）" loading="lazy" decoding="async"></p>
<h2 id="四確認-slave-已經沒有延遲">四、確認 Slave 已經沒有延遲<a class="h-anchor" href="#%e5%9b%9b%e7%a2%ba%e8%aa%8d-slave-%e5%b7%b2%e7%b6%93%e6%b2%92%e6%9c%89%e5%bb%b6%e9%81%b2" aria-label="這個段落的連結"></a></h2>
<p>在上面的設定後稍等一段時間再去查看 Slave 的延遲狀態</p>
<pre tabindex="0"><code>mysql&gt; show slave status\G
</code></pre><p>就發現他已經變成 0 嚕～</p>
<p><img src="https://deanlin.net/blog/202103/mysql-replication-lag/images/mysql-replication-lag-06.webp" width="482" height="129" alt="四、確認 Slave 已經沒有延遲（圖 6）" loading="lazy" decoding="async"></p>
<hr>
<pre tabindex="0"><code>如果你想了解資料庫主從式架構的設定歡迎參考我的文章：
👍 MySQL Master Slave Replication 主從式架構設定教學
👍 Ubuntu 用 Atlas 實現 MySQL Proxy 讀寫分離 read/write splitting
</code></pre><p><strong>參考資訊</strong>：<a href="https://jerryw.blog/2021/01/07/mysql-master-slave-%E8%A4%87%E5%AF%AB%E5%BB%B6%E6%BB%AF%E5%90%8C%E6%AD%A5%E5%95%8F%E9%A1%8C/">mysql master-slave 複寫延滯同步問題</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>用 sysbench 測試 MySQL 性能 — 在 mac &amp; ubuntu 上的安裝與使用</title>
      <link>https://deanlin.net/blog/202103/mysql-benchmark-sysbench/</link>
      <pubDate>Mon, 29 Mar 2021 14:43:32 +0800</pubDate>
      <guid>https://deanlin.net/blog/202103/mysql-benchmark-sysbench/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>sysbench 是我目前使用上最順手的一個測試工具，安裝、使用都非常容易，除了資料庫的測試外他還能對 CPU、Memory…做測試，本篇會詳細說明如何使用它來做 MySQL 性能測試，其他的功能可以參考文末附的連結。</description>
      <media:content url="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-og_hu_19a351296783d12c.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p>sysbench 是我目前使用上最順手的一個測試工具，安裝、使用都非常容易，除了資料庫的測試外他還能對 CPU、Memory…做測試，本篇會詳細說明如何使用它來做 MySQL 性能測試，其他的功能可以參考文末附的連結。</p>
<p><img src="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-01.webp" width="1400" height="785" alt="用 sysbench 測試 MySQL 性能 — 在 mac &amp; ubuntu 上的安裝與使用（圖 1）" fetchpriority="high" decoding="async"></p>
<h2 id="一在-mac-上操作-sysbench">一、在 Mac 上操作 sysbench<a class="h-anchor" href="#%e4%b8%80%e5%9c%a8-mac-%e4%b8%8a%e6%93%8d%e4%bd%9c-sysbench" aria-label="這個段落的連結"></a></h2>
<h3 id="-安裝-sysbench">安裝 sysbench<a class="h-anchor" href="#-%e5%ae%89%e8%a3%9d-sysbench" aria-label="這個段落的連結"></a></h3>
<blockquote>
<p>如果還沒安裝過 brew 請<a href="https://brew.sh/zh-tw/">參考官網</a></p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>brew install sysbench
</span></span></code></pre></div><p>安裝完成後輸入指令確認安裝完成</p>
<pre tabindex="0"><code>sysbench — version
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-02.webp" width="880" height="92" alt="安裝 sysbench（圖 2）" loading="lazy" decoding="async"></p>
<p>我安裝的版本為 <code>1.0.20</code> 這個會跟下面的指令路徑有關，如果你安裝的是其他版本要自行修改這個版本路徑</p>
<h3 id="-測試-mysql-性能">測試 MySQL 性能<a class="h-anchor" href="#-%e6%b8%ac%e8%a9%a6-mysql-%e6%80%a7%e8%83%bd" aria-label="這個段落的連結"></a></h3>
<blockquote>
<p>mac 的範例是本地端的測試，針對 read 這塊</p>
</blockquote>
<p><strong>STEP 1</strong>：先建立好測試用的 DB</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff7b72">create</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">database</span><span style="color:#6e7681"> </span>test;<span style="color:#6e7681">
</span></span></span></code></pre></div><p><strong>STEP 2</strong>：建立測試資料</p>
<ul>
<li><strong>mysql-user、mysql-password</strong>：改成自己 MySQL 的使用者</li>
<li><strong>mysql-db</strong>：測試的資料庫</li>
<li><strong>range_size、table_size、tables</strong>：測試的規模</li>
<li><strong>prepare</strong>：建立測資的意思</li>
</ul>
<pre tabindex="0"><code>sysbench /usr/local/Cellar/sysbench/1.0.20/share/sysbench/oltp_read_only.lua \
--db-driver=mysql \
--mysql-user=user_name \
--mysql-password=user_password \
--mysql-db=test \
--range_size=100 \
--table_size=10000 \
--tables=2 \
--threads=1 \
--events=0 \
--time=60 \
--rand-type=uniform \
prepare
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/d26df5b62cc8b790085ef4d6ee468883" rel="noopener">sysbench_prepare.cmd</a>（GitHub Gist）</p>
<p><img src="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-03.webp" width="1032" height="334" alt="測試 MySQL 性能（圖 3）" loading="lazy" decoding="async"></p>
<p><strong>STEP 3</strong>：進行測試</p>
<blockquote>
<p>只是把指令最後面改成 run</p>
</blockquote>
<pre tabindex="0"><code>sysbench /usr/local/Cellar/sysbench/1.0.20/share/sysbench/oltp_read_only.lua \
--db-driver=mysql \
--mysql-user=user_name \
--mysql-password=user_password \
--mysql-db=test \
--range_size=100 \
--table_size=10000 \
--tables=2 \
--threads=1 \
--events=0 \
--time=60 \
--rand-type=uniform \
run
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/a3093daa95c2b7cae4dd327fcae4d9ba" rel="noopener">sysbench_run.cmd</a>（GitHub Gist）</p>
<p><img src="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-04.webp" width="1330" height="1378" alt="測試 MySQL 性能（圖 4）" loading="lazy" decoding="async"></p>
<p><strong>STEP 4</strong>：清除測試數據</p>
<blockquote>
<p>只是把指令最後面改成 cleanup</p>
</blockquote>
<pre tabindex="0"><code>sysbench /usr/local/Cellar/sysbench/1.0.20/share/sysbench/oltp_read_only.lua \
--db-driver=mysql \
--mysql-user=user_name \
--mysql-password=user_password \
--mysql-db=test \
--range_size=100 \
--table_size=10000 \
--tables=2 \
--threads=1 \
--events=0 \
--time=60 \
--rand-type=uniform \
cleanup
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/24b62f7153920bd3fb4773d64f461945" rel="noopener">sysbench_cleanup.cmd</a>（GitHub Gist）</p>
<p><img src="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-05.webp" width="992" height="152" alt="測試 MySQL 性能（圖 5）" loading="lazy" decoding="async"></p>
<h2 id="一在-ubuntu-上操作-sysbench">一、在 Ubuntu 上操作 sysbench<a class="h-anchor" href="#%e4%b8%80%e5%9c%a8-ubuntu-%e4%b8%8a%e6%93%8d%e4%bd%9c-sysbench" aria-label="這個段落的連結"></a></h2>
<h3 id="-安裝-sysbench-1">安裝 sysbench<a class="h-anchor" href="#-%e5%ae%89%e8%a3%9d-sysbench-1" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt-get install sysbench -y
</span></span></code></pre></div><p>安裝完成後輸入指令確認安裝完成</p>
<pre tabindex="0"><code>sysbench — version
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-06.webp" width="696" height="82" alt="安裝 sysbench（圖 6）" loading="lazy" decoding="async"></p>
<h3 id="-測試-mysql-性能-1">測試 MySQL 性能<a class="h-anchor" href="#-%e6%b8%ac%e8%a9%a6-mysql-%e6%80%a7%e8%83%bd-1" aria-label="這個段落的連結"></a></h3>
<blockquote>
<p>ubuntu 的範例是遠端的測試，針對 write這塊</p>
</blockquote>
<p><strong>STEP 1</strong>：先建立好測試用的 DB</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff7b72">create</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">database</span><span style="color:#6e7681"> </span>test;<span style="color:#6e7681">
</span></span></span></code></pre></div><p><strong>STEP 2</strong>：建立測試資料</p>
<p>如果你的設定跟我的範例一樣可能要稍微等一下，因為範例的測資比較大筆</p>
<ul>
<li><strong>mysql-host</strong>：目標測試的 ip 或是 host name</li>
<li><strong>mysql-port</strong>：目標測試的 MySQL port</li>
<li><strong>mysql-user、mysql-password</strong>：改成自己 MySQL 的使用者</li>
<li><strong>mysql-db</strong>：測試的資料庫</li>
<li><strong>range_size、table_size、tables</strong>：測試的規模</li>
<li><strong>prepare</strong>：建立測資的意思</li>
</ul>
<pre tabindex="0"><code>sysbench /usr/share/sysbench/oltp_write_only.lua \
--mysql-host=127.0.0.1 \
--mysql-port=3306 \
--mysql-user=user_name \
--mysql-password=user_password \
--mysql-db=test \
--db-driver=mysql \
--tables=10 \
--table_size=10000 \
--report-interval=10 \
--threads=128 \
--time=120 \
prepare
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/4adca32bb1877a41e51c23ff3ff127bf" rel="noopener">ubuntu_sysbench_prepare.cmd</a>（GitHub Gist）</p>
<p><img src="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-07.webp" width="1026" height="1340" alt="測試 MySQL 性能（圖 7）" loading="lazy" decoding="async"></p>
<p><strong>STEP 3</strong>：進行測試</p>
<blockquote>
<p>只是把指令最後面改成 run</p>
</blockquote>
<pre tabindex="0"><code>sysbench /usr/share/sysbench/oltp_write_only.lua \
--mysql-host=127.0.0.1 \
--mysql-port=3306 \
--mysql-user=user_name \
--mysql-password=user_password \
--mysql-db=test \
--db-driver=mysql \
--tables=10 \
--table_size=10000 \
--report-interval=10 \
--threads=128 \
--time=120 \
run
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/24b504d1f2056b6645b63a55ac2856c4" rel="noopener">ubuntu_sysbench_run.cmd</a>（GitHub Gist）</p>
<p><img src="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-08.webp" width="1104" height="895" alt="測試 MySQL 性能（圖 8）" loading="lazy" decoding="async"></p>
<p><strong>STEP 4</strong>：清除測試數據</p>
<blockquote>
<p>只是把指令最後面改成 cleanup</p>
</blockquote>
<pre tabindex="0"><code>sysbench /usr/share/sysbench/oltp_write_only.lua \
--mysql-host=127.0.0.1 \
--mysql-port=3306 \
--mysql-user=user_name \
--mysql-password=user_password \
--mysql-db=test \
--db-driver=mysql \
--tables=10 \
--table_size=10000 \
--report-interval=10 \
--threads=128 \
--time=120 \
cleanup
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/957b162ca5a0bbe57a86e7c792e2a085" rel="noopener">ubuntu_sysbench_cleanup.cmd</a>（GitHub Gist）</p>
<p><img src="https://deanlin.net/blog/202103/mysql-benchmark-sysbench/images/mysql-benchmark-sysbench-09.webp" width="458" height="226" alt="測試 MySQL 性能（圖 9）" loading="lazy" decoding="async"></p>
<hr>
<blockquote class="pullquote"><p>以上是今天的分享，想了解更多功能請參考<a href="https://github.com/akopytov/sysbench">官方的 github</a></p>
</blockquote>
<p><strong>參考資源</strong>：Ubuntu 16.04中進行Sysbench環境搭建、<a href="https://blog.51cto.com/13990437/2400940">sysbench安装和使用</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Ubuntu 用 Atlas 實現 MySQL Proxy 讀寫分離 read/write splitting</title>
      <link>https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/</link>
      <pubDate>Mon, 29 Mar 2021 08:50:19 +0800</pubDate>
      <guid>https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>如果單純把 DB 分成 Master 及 Slave 並不能真正解決平衡負載問題，因為每台 server 是獨立的 ip，使用者在存取資料庫的時候並不知道要使用哪一台 server 😅；這篇文章會教你如何設定一台 Proxy server，準確地將 SQL 指令正確分流</description>
      <media:content url="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-og_hu_bbca886f617e582f.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p>在『<a href="https://deanlin.net/blog/202103/mysql-master-slave-replication/">MySQL Master Slave Replication 主從式架構設定教學</a>』這篇文章的結尾有提到如果單純把 DB 分成 Master 及 Slave 並不能真正解決平衡負載問題，因為每台 server 是獨立的 ip，使用者在存取資料庫的時候並不知道要使用哪一台 server 😅 ，除非你的 Slave server 只是用來做資料分析 🤔；這篇文章會教你如何設定一台 Proxy server，準確地將 SQL 指令分流到正確的 server。</p>
<figure class="figure"><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-01.webp" width="1400" height="761" alt="此為設計架構圖" fetchpriority="high" decoding="async">
  <figcaption>此為設計架構圖</figcaption>
</figure>

<pre tabindex="0"><code>一、前置作業
二、MySQL Proxy 的選擇
三、Atlas 功能介紹
四、在 Ubunut 安裝 Atlas
五、連到 Atlas 管理 server
六、實際測試透過 Atlas Proxy 是否達成讀寫分離
</code></pre><h2 id="一前置作業">一、前置作業<a class="h-anchor" href="#%e4%b8%80%e5%89%8d%e7%bd%ae%e4%bd%9c%e6%a5%ad" aria-label="這個段落的連結"></a></h2>
<blockquote>
<p>OS version、MySQL 是筆者安裝時的環境，ip 是給大家參考對應的</p>
</blockquote>
<h3 id="-準備一台要當-mysql-proxy-的-ubuntu-server">準備一台要當 MySQL Proxy 的 ubuntu server<a class="h-anchor" href="#-%e6%ba%96%e5%82%99%e4%b8%80%e5%8f%b0%e8%a6%81%e7%95%b6-mysql-proxy-%e7%9a%84-ubuntu-server" aria-label="這個段落的連結"></a></h3>
<ul>
<li>OS version：ubuntu 16.04</li>
<li>MySQL：5.7</li>
<li>ip ：172.16.9.39</li>
</ul>
<h3 id="-準備一台-master-server-負責寫入">準備一台 Master server 負責寫入<a class="h-anchor" href="#-%e6%ba%96%e5%82%99%e4%b8%80%e5%8f%b0-master-server-%e8%b2%a0%e8%b2%ac%e5%af%ab%e5%85%a5" aria-label="這個段落的連結"></a></h3>
<ul>
<li>OS version：ubuntu 16.04</li>
<li>MySQL：5.7</li>
<li>ip ：172.16.9.32</li>
</ul>
<h3 id="-準備一台以上的-slave-server-負責讀取">準備一台以上的 Slave server 負責讀取<a class="h-anchor" href="#-%e6%ba%96%e5%82%99%e4%b8%80%e5%8f%b0%e4%bb%a5%e4%b8%8a%e7%9a%84-slave-server-%e8%b2%a0%e8%b2%ac%e8%ae%80%e5%8f%96" aria-label="這個段落的連結"></a></h3>
<blockquote>
<p>準備兩台以上測試 load balance 才會準確，這塊有興趣的讀者可以自己嘗試</p>
</blockquote>
<ul>
<li>OS version：ubuntu 16.04</li>
<li>MySQL：5.7</li>
<li>ip ：172.16.9.32</li>
</ul>
<h3 id="-在-master-server-裡面增加給-proxy-server-使用的使用者並創建測試用-table">在 Master Server 裡面增加給 Proxy server 使用的使用者，並創建測試用 table<a class="h-anchor" href="#-%e5%9c%a8-master-server-%e8%a3%a1%e9%9d%a2%e5%a2%9e%e5%8a%a0%e7%b5%a6-proxy-server-%e4%bd%bf%e7%94%a8%e7%9a%84%e4%bd%bf%e7%94%a8%e8%80%85%e4%b8%a6%e5%89%b5%e5%bb%ba%e6%b8%ac%e8%a9%a6%e7%94%a8-table" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">/*建立proxy server使用者*/</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>mysql<span style="color:#ff7b72;font-weight:bold">&gt;</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">CREATE</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">USER</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">&#39;proxy_account&#39;</span><span style="color:#ff7b72;font-weight:bold">@</span><span style="color:#a5d6ff">&#39;%&#39;</span><span style="color:#6e7681"> </span>IDENTIFIED<span style="color:#6e7681"> </span><span style="color:#ff7b72">BY</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">&#39;proxy_password&#39;</span>;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">/*給使用者權限*/</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>mysql<span style="color:#ff7b72;font-weight:bold">&gt;</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">GRANT</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">ALL</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">ON</span><span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">*</span>.<span style="color:#ff7b72;font-weight:bold">*</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">TO</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">&#39;proxy_account&#39;</span><span style="color:#ff7b72;font-weight:bold">@</span><span style="color:#a5d6ff">&#39;%&#39;</span>;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">/*使權限生效*/</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>mysql<span style="color:#ff7b72;font-weight:bold">&gt;</span><span style="color:#6e7681"> </span>FLUSH<span style="color:#6e7681"> </span><span style="color:#ff7b72">PRIVILEGES</span>;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">/*創建測試用資料庫*/</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">CREATE</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">DATABASE</span><span style="color:#6e7681"> </span>proxy_test;<span style="color:#6e7681">
</span></span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/3c818b9e9a1294cd7e33f108985884e0" rel="noopener">create_proxy_user.sql</a>（GitHub Gist）</p>
<h2 id="二mysql-proxy-的選擇">二、MySQL Proxy 的選擇<a class="h-anchor" href="#%e4%ba%8cmysql-proxy-%e7%9a%84%e9%81%b8%e6%93%87" aria-label="這個段落的連結"></a></h2>
<ul>
<li><strong>MySQL Proxy</strong>：原本想使用官方工具來處理讀寫分離，但一上<strong>官網發現連他自己都不推薦使用</strong>😱，所以就不在多費心力研究。</li>
</ul>
<p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-02.webp" width="1296" height="398" alt="二、MySQL Proxy 的選擇（圖 2）" loading="lazy" decoding="async"></p>
<ul>
<li><strong>MySQL Router</strong>：根據官方的指引認真的研究這個工具後發現雖然設定上簡單，但並不是很符合需求，他的<strong>讀寫分離是同一台伺服器分成兩個 port 來處理</strong>，雖然現在主流框架都支援這樣的操作，但我認為這樣的方式並不優雅</li>
<li><strong>kingshard</strong>：是基於 Go 語言開發的 MySQL Proxy，在 github 上面擁有高人氣；但實際使用心得真的是滿滿的陰影…<strong>官方安裝指引非常的爛</strong>，鬼才知道要自己切換 branch 才能使用啊？設定檔根本要用錯誤嘗試法，沒說清楚必填選填，上面這些問題也就罷了，安裝成功後居然告訴我只有 windows 的電腦可以測試 <a href="https://github.com/flike/kingshard/issues/591#issuecomment-776367532">mac 電腦不行</a>😡，作者是在開玩笑嗎？總之我非常的不推薦。</li>
<li><strong>Atlas</strong>：是基於 MySQL-Proxy 0.8.2 版本開發的工具，由 360 公司推出，在 github 上面找到最新的版本是 2015 年推出的，至於為什麼不再更新的理由，360 公司表示因為這個工具已經夠棒了🥴，沒什麼繼續優化的必要性了；事實上這個工具也是筆者最後選用的，儘管安裝過程滿滿的坑😓</li>
</ul>
<h2 id="三atlas-功能介紹">三、Atlas 功能介紹<a class="h-anchor" href="#%e4%b8%89atlas-%e5%8a%9f%e8%83%bd%e4%bb%8b%e7%b4%b9" aria-label="這個段落的連結"></a></h2>
<ol>
<li>讀寫分離</li>
<li>slave server 的 load balance</li>
<li>IP filter</li>
<li>DBA 可以輕鬆的上下線 DB（有指令支持）</li>
<li>自動移除當機的 DB（但如果當機的是 master server，就只剩讀取功能）</li>
</ol>
<h2 id="四在-ubunut-安裝-atlas">四、在 Ubunut 安裝 Atlas<a class="h-anchor" href="#%e5%9b%9b%e5%9c%a8-ubunut-%e5%ae%89%e8%a3%9d-atlas" aria-label="這個段落的連結"></a></h2>
<blockquote>
<p>筆者在安裝過程中感受到滿滿的惡意</p>
</blockquote>
<p><strong>STEP 1</strong>：下載 &amp; 安裝 Atlas</p>
<p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-03.webp" width="1259" height="340" alt="四、在 Ubunut 安裝 Atlas（圖 3）" loading="lazy" decoding="async"></p>
<p>你可以在官方 <a href="https://github.com/Qihoo360/Atlas/releases">github release </a>中找到 deb 的安裝包下載他</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>wget https://github.com/Qihoo360/Atlas/releases/download/2.2/Atlas-2.2-debian6.0-x86_64.deb
</span></span></code></pre></div><p>執行安裝</p>
<pre tabindex="0"><code>dpkg -i Atlas-2.2-debian6.0-x86_64.deb
</code></pre><p><strong>STEP 2</strong>：調整 Atlas 的設定檔</p>
<p>Atlas 的安裝路徑是/usr/local/mysql-proxy ，執行時的參數會參考 conf 目錄下的 test.cnf 檔案</p>
<pre tabindex="0"><code>vi /usr/local/mysql-proxy/conf/test.cnf
</code></pre><p>下面幾個需要特別調整，其他用默認的就可以了</p>
<ul>
<li><strong>proxy-backend-addresses</strong>：填寫 Master server 的 ip</li>
</ul>
<p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-04.webp" width="1196" height="88" alt="四、在 Ubunut 安裝 Atlas（圖 4）" loading="lazy" decoding="async"></p>
<ul>
<li><strong>proxy-read-only-backend-addresses</strong>：填寫 Slave server 的 ip</li>
</ul>
<p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-05.webp" width="1400" height="122" alt="四、在 Ubunut 安裝 Atlas（圖 5）" loading="lazy" decoding="async"></p>
<ul>
<li><strong>pwds</strong>：請使用於前置作業 Master server 新增專門給 Proxy Server 使用的帳號密碼，密碼要用下面的指令作加密</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 前往加密的目錄</span>
</span></span><span style="display:flex;"><span>cd /usr/local/mysql-proxy/bin
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 對密碼加密</span>
</span></span><span style="display:flex;"><span>./encrypt proxy_password
</span></span></code></pre></div><p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-06.webp" width="1332" height="126" alt="四、在 Ubunut 安裝 Atlas（圖 6）" loading="lazy" decoding="async"></p>
<p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-07.webp" width="1400" height="136" alt="四、在 Ubunut 安裝 Atlas（圖 7）" loading="lazy" decoding="async"></p>
<ul>
<li><strong>sql-log</strong>：預設是 OFF 不會紀錄，但為了方便觀察讀寫是否分離，我們將它設定為 REALTIME</li>
</ul>
<p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-08.webp" width="1400" height="138" alt="四、在 Ubunut 安裝 Atlas（圖 8）" loading="lazy" decoding="async"></p>
<p><strong>STEP 3</strong>：啟動 Atlas</p>
<p>輸入下面指令即可啟動 Atlas</p>
<pre tabindex="0"><code>/usr/local/mysql-proxy/bin/mysql-proxyd test start
</code></pre><p>不過應該有 99% 的人會在這個步驟失敗</p>
<figure class="figure"><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-09.webp" width="1400" height="90" alt="這是另外一種啟動方式，反正只是給大家看會有什麼錯誤而已" loading="lazy" decoding="async">
  <figcaption>這是另外一種啟動方式，反正只是給大家看會有什麼錯誤而已</figcaption>
</figure>

<p>你可以在 log 裡面看錯誤訊息</p>
<pre tabindex="0"><code>vi /usr/local/mysql-proxy/log/test.log
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-10.webp" width="1400" height="192" alt="四、在 Ubunut 安裝 Atlas（圖 10）" loading="lazy" decoding="async"></p>
<p><strong>解決方式：</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 安裝套件相關套件</span>
</span></span><span style="display:flex;"><span>apt-get install libmysqlclient-dev
</span></span><span style="display:flex;"><span>apt-get install mysql-client
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 下載缺失的 libmysqlclient.so.16 套件，我是在這個網站找到的</span>
</span></span><span style="display:flex;"><span>wget http://files.directadmin.com/services/debian_5.0_64/libmysqlclient.so.16
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 將 libmysqlclient.so.16 搬移到 lib 目錄下</span>
</span></span><span style="display:flex;"><span>mv libmysqlclient.so.16 /usr/lib/x86_64-linux-gnu/
</span></span></code></pre></div><p>再次啟動 Atlas</p>
<pre tabindex="0"><code>/usr/local/mysql-proxy/bin/mysql-proxyd test start
</code></pre><p>看到 <strong>OK: MySQL-Proxy of test is started</strong> 的提示就成功嚕！</p>
<p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-11.webp" width="1400" height="73" alt="四、在 Ubunut 安裝 Atlas（圖 11）" loading="lazy" decoding="async"></p>
<p>檢查 Atlas 服務狀態</p>
<pre tabindex="0"><code>/usr/local/mysql-proxy/bin/mysql-proxyd test status
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-12.webp" width="1400" height="104" alt="四、在 Ubunut 安裝 Atlas（圖 12）" loading="lazy" decoding="async"></p>
<h2 id="五連到-atlas-管理-server">五、連到 Atlas 管理 server<a class="h-anchor" href="#%e4%ba%94%e9%80%a3%e5%88%b0-atlas-%e7%ae%a1%e7%90%86-server" aria-label="這個段落的連結"></a></h2>
<p>Atlas 預設的管理 port 為 2345，ip 填寫 Proxy server ip，如果 admin 的帳號密碼沒換就是跟下面一樣</p>
<pre tabindex="0"><code>mysql -h172.16.9.39 -P2345 -uuser -ppwd
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-13.webp" width="1400" height="553" alt="五、連到 Atlas 管理 server（圖 13）" loading="lazy" decoding="async"></p>
<h3 id="-查看-atlas-的管理命令">查看 Atlas 的管理命令<a class="h-anchor" href="#-%e6%9f%a5%e7%9c%8b-atlas-%e7%9a%84%e7%ae%a1%e7%90%86%e5%91%bd%e4%bb%a4" aria-label="這個段落的連結"></a></h3>
<pre tabindex="0"><code>mysql&gt; select * from help;
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-14.webp" width="1400" height="529" alt="查看 Atlas 的管理命令（圖 14）" loading="lazy" decoding="async"></p>
<h3 id="-查看-mysql-讀寫分離設定是否生效">查看 MySQL 讀寫分離設定是否生效<a class="h-anchor" href="#-%e6%9f%a5%e7%9c%8b-mysql-%e8%ae%80%e5%af%ab%e5%88%86%e9%9b%a2%e8%a8%ad%e5%ae%9a%e6%98%af%e5%90%a6%e7%94%9f%e6%95%88" aria-label="這個段落的連結"></a></h3>
<p>rw 為讀寫權限，ro 代表只有讀的權限</p>
<pre tabindex="0"><code>mysql&gt; select * from backends;
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-15.webp" width="942" height="320" alt="查看 MySQL 讀寫分離設定是否生效（圖 15）" loading="lazy" decoding="async"></p>
<p>到這步其實就可以完結撒花惹 😊😊😊😊😊😊</p>
<h2 id="六實際測試透過-atlas-proxy-是否達成讀寫分離">六、實際測試透過 Atlas Proxy 是否達成讀寫分離<a class="h-anchor" href="#%e5%85%ad%e5%af%a6%e9%9a%9b%e6%b8%ac%e8%a9%a6%e9%80%8f%e9%81%8e-atlas-proxy-%e6%98%af%e5%90%a6%e9%81%94%e6%88%90%e8%ae%80%e5%af%ab%e5%88%86%e9%9b%a2" aria-label="這個段落的連結"></a></h2>
<p>Atlas 預設的proxy port 為 1234，ip 填寫 Proxy server ip，登入的帳號密碼填寫你在 Master 申請給 Atlas Proxy 使用的那組</p>
<pre tabindex="0"><code>mysql -h172.16.9.39 -P1234 -uproxy_account -pproxy_password
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-16.webp" width="1400" height="536" alt="六、實際測試透過 Atlas Proxy 是否達成讀寫分離（圖 16）" loading="lazy" decoding="async"></p>
<h3 id="-測試寫入功能">測試寫入功能<a class="h-anchor" href="#-%e6%b8%ac%e8%a9%a6%e5%af%ab%e5%85%a5%e5%8a%9f%e8%83%bd" aria-label="這個段落的連結"></a></h3>
<pre tabindex="0"><code># 先選擇之前創建的測試資料庫
mysql&gt; use proxy_test;
# 創建表並塞入一寫資料
mysql&gt; CREATE TABLE department (id INT,name varchar(50));
mysql&gt; INSERT INTO department VALUES (1, &#39;Sales&#39;);
mysql&gt; INSERT INTO department VALUES (2, &#39;Operator&#39;);
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-17.webp" width="1062" height="308" alt="測試寫入功能（圖 17）" loading="lazy" decoding="async"></p>
<h3 id="-測試讀取功能">測試讀取功能<a class="h-anchor" href="#-%e6%b8%ac%e8%a9%a6%e8%ae%80%e5%8f%96%e5%8a%9f%e8%83%bd" aria-label="這個段落的連結"></a></h3>
<pre tabindex="0"><code># 先選擇之前創建的測試資料庫
mysql&gt; use proxy_test;
# 讀取剛剛創建的 table
mysql&gt; SELECT * FROM department;
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-18.webp" width="536" height="308" alt="測試讀取功能（圖 18）" loading="lazy" decoding="async"></p>
<h3 id="-觀察-sql-運行的-log">觀察 sql 運行的 log<a class="h-anchor" href="#-%e8%a7%80%e5%af%9f-sql-%e9%81%8b%e8%a1%8c%e7%9a%84-log" aria-label="這個段落的連結"></a></h3>
<blockquote>
<p>記得把 Atla 設定檔的 ssql-log 參數調整為 REALTIME 才會有喔！</p>
</blockquote>
<p>前往 log file</p>
<pre tabindex="0"><code>tail /usr/local/mysql-proxy/log/sql_test.log
</code></pre><p>從下圖我們可以觀察到，負責變更資料庫的是 Master server(172.16.3.32)，讀取的則是 Slave Server(172.16.3.33)，恭喜自己大功告成</p>
<p><img src="https://deanlin.net/blog/202103/mysql-read-write-splitting-atlas/images/mysql-read-write-splitting-atlas-19.webp" width="1400" height="530" alt="觀察 sql 運行的 log（圖 19）" loading="lazy" decoding="async"></p>
<hr>
<p>希望這篇文章有幫助到使用 Ubuntu 架設 MySQL 並且想做到讀寫分離的開發人員，網路上對這塊的資訊相對來說比較零散，有些甚至都是錯誤，整理起來這些文件也真的很費力，如果對你有幫助的話可以幫我按個拍手👏給我一點繼續分享技術文章的動力。</p>
<pre tabindex="0"><code>如果你想了解資料庫主從式架構的設定歡迎參考我的文章：
👍 MySQL Master Slave Replication 主從式架構設定教學
👍 在 MySQL 5.7 解決主從複製的延遲問題
</code></pre><hr>
<p><strong>參考資訊</strong>：<a href="https://web.archive.org/web/20170127083654/http://files.directadmin.com:80/services/debian_5.0_64/libmysqlclient.so.16" title="原網頁已失效，連到網際網路檔案館的存檔">下載libmysqlclient.so.16檔案</a>、<a href="https://www.jianshu.com/p/67d4b34b2af4">讀寫分離測試</a>、<a href="https://zhuanlan.zhihu.com/p/94279867">Mysql访问中间件 — Atlas初探</a>、<a href="https://www.jianshu.com/p/b68e429d09c7">Atlas</a>、<a href="https://github.com/Qihoo360/Atlas/blob/master/README_ZH.md">Atlas Github</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>MySQL Master Slave Replication 主從式架構設定教學</title>
      <link>https://deanlin.net/blog/202103/mysql-master-slave-replication/</link>
      <pubDate>Thu, 25 Mar 2021 16:45:58 +0800</pubDate>
      <guid>https://deanlin.net/blog/202103/mysql-master-slave-replication/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>Master Slave Replication 是一個 Web 工程師需要具備的基礎概念，我們可以透過這個技術有效的降低 Database server 負荷，同時給使用者提供更穩定順暢的體驗；本文除了讓你了解這個技術的原理外還會以 MySQL…</description>
      <media:content url="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-og_hu_19e34a7748bd18bb.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p><img src="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-01.webp" width="1400" height="783" alt="MySQL Master Slave Replication 主從式架構設定教學（圖 1）" fetchpriority="high" decoding="async"></p>
<p>Master Slave Replication 是一個 Web 工程師需要具備的基礎概念，我們可以透過這個技術有效的降低 Database server 負荷，同時給使用者提供更穩定順暢的體驗；本文除了讓你了解這個技術的原理外還會以 MySQL 這門資料庫語言一步步透過實作讓你理解如何實現它。</p>
<pre tabindex="0"><code>一、30 秒認識 Master Slave Replication 是什麼
  ∙ 為什麼需要 Master Slave Replication 的架構？
  ∙ Master Slave Replication 為什麼能夠解決這些問題？
  ∙ Master Slave Replication 的架構
二、實作 MySQL Master Slave replication
  ∙ 前置作業
  ∙ 建立MySQL Replication
三、驗證 Master、Slave DB 是否同步
</code></pre><h2 id="一30-秒認識-master-slave-replication-是什麼">一、30 秒認識 Master Slave Replication 是什麼<a class="h-anchor" href="#%e4%b8%8030-%e7%a7%92%e8%aa%8d%e8%ad%98-master-slave-replication-%e6%98%af%e4%bb%80%e9%ba%bc" aria-label="這個段落的連結"></a></h2>
<h3 id="為什麼需要-master-slave-replication-的架構">為什麼需要 Master Slave Replication 的架構？<a class="h-anchor" href="#%e7%82%ba%e4%bb%80%e9%ba%bc%e9%9c%80%e8%a6%81-master-slave-replication-%e7%9a%84%e6%9e%b6%e6%a7%8b" aria-label="這個段落的連結"></a></h3>
<ul>
<li><strong>網站需要面對大流量</strong>：新聞媒體類的網站無論哪個時段都會被大量的檢索，如果資料庫只用一個 server 來處理會無法負荷瞬間的高流量。</li>
<li><strong>資料庫內容需要被分析</strong>：公司如果有資料分析部門，他們也會隨時監控資料庫的變化來產生報表，但如果跟一般使用者一樣 query 同一台 server 會使這台 server 增加不必要的負荷導致使用者體驗下降。</li>
</ul>
<h3 id="master-slave-replication-為什麼能夠解決這些問題">Master Slave Replication 為什麼能夠解決這些問題？<a class="h-anchor" href="#master-slave-replication-%e7%82%ba%e4%bb%80%e9%ba%bc%e8%83%bd%e5%a4%a0%e8%a7%a3%e6%b1%ba%e9%80%99%e4%ba%9b%e5%95%8f%e9%a1%8c" aria-label="這個段落的連結"></a></h3>
<ul>
<li><strong>主從式架構</strong>：將資料庫的 server 區分成 Master(1台) 與 Slave(N台) 兩種，Master 負責資料寫入，Slave 提供資料檢索；這樣就把壓力分散到幾台 Server 上面。</li>
<li><strong>資料庫同步</strong>：Slave 會同步 Master 的資料，這樣除了分散 server 壓力外，資料也獲得了備份。</li>
</ul>
<h3 id="master-slave-replication-的架構">Master Slave Replication 的架構<a class="h-anchor" href="#master-slave-replication-%e7%9a%84%e6%9e%b6%e6%a7%8b" aria-label="這個段落的連結"></a></h3>
<ol>
<li>在 master 的 server 執行的 SQL command 會被記錄在 Binary Log 裡面</li>
<li>master 將 Binary Log 傳送到 slave 的 Relay Log</li>
<li>slave 依據 Relay Log 做資料的變更</li>
</ol>
<figure class="figure"><img src="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-02.webp" width="1194" height="738" alt="Relay Log 與 Binary Log檔案格式、內容都一樣，不同在於 slave 在執行完 Relay Log 的 SQL 之後，會自己刪除當前的 Relay Log" loading="lazy" decoding="async">
  <figcaption>Relay Log 與 Binary Log檔案格式、內容都一樣，不同在於 slave 在執行完 Relay Log 的 SQL 之後，會自己刪除當前的 Relay Log</figcaption>
</figure>

<h2 id="二實作-mysql-master-slave-replication">二、實作 MySQL Master Slave replication<a class="h-anchor" href="#%e4%ba%8c%e5%af%a6%e4%bd%9c-mysql-master-slave-replication" aria-label="這個段落的連結"></a></h2>
<h2 id="前置作業">前置作業<a class="h-anchor" href="#%e5%89%8d%e7%bd%ae%e4%bd%9c%e6%a5%ad" aria-label="這個段落的連結"></a></h2>
<h3 id="-準備兩台-ubuntu-server-自己實驗建議使用-vmware筆者環境配置如下">準備兩台 ubuntu server (自己實驗建議使用 VMWARE)，筆者環境配置如下<a class="h-anchor" href="#-%e6%ba%96%e5%82%99%e5%85%a9%e5%8f%b0-ubuntu-server-%e8%87%aa%e5%b7%b1%e5%af%a6%e9%a9%97%e5%bb%ba%e8%ad%b0%e4%bd%bf%e7%94%a8-vmware%e7%ad%86%e8%80%85%e7%92%b0%e5%a2%83%e9%85%8d%e7%bd%ae%e5%a6%82%e4%b8%8b" aria-label="這個段落的連結"></a></h3>
<blockquote>
<p>硬體配置一定會影響到效能，筆者只是建置一個提供實驗的環境而已</p>
</blockquote>
<ul>
<li>OS version：ubuntu 16.04</li>
<li>RAM：1G</li>
<li>ROM：8G</li>
</ul>
<h3 id="-給兩台-sever-安裝-mysql">給兩台 sever 安裝 MySQL<a class="h-anchor" href="#-%e7%b5%a6%e5%85%a9%e5%8f%b0-sever-%e5%ae%89%e8%a3%9d-mysql" aria-label="這個段落的連結"></a></h3>
<p>安裝指令如下</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt update
</span></span><span style="display:flex;"><span>sudo apt install mysql-server
</span></span></code></pre></div><p>安裝完成後你可以先登入 MySQL 看看安裝版本資訊</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mysql -u root -p
</span></span></code></pre></div><p><img src="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-03.webp" width="1400" height="491" alt="給兩台 sever 安裝 MySQL（圖 3）" loading="lazy" decoding="async"></p>
<h2 id="建立mysql-replication">建立MySQL Replication<a class="h-anchor" href="#%e5%bb%ba%e7%ab%8bmysql-replication" aria-label="這個段落的連結"></a></h2>
<h3 id="-master-server-設定">Master server 設定<a class="h-anchor" href="#-master-server-%e8%a8%ad%e5%ae%9a" aria-label="這個段落的連結"></a></h3>
<p><strong>STEP 1</strong>：設定 Master server 的 my.cnf</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo vi /etc/mysql/my.cnf
</span></span></code></pre></div><pre tabindex="0"><code>[mysqld]
# 在這裡設定已蓋過預設的127.0.0.1(只能連本機)
bind-address = 0.0.0.0

# id為唯一值，每個server不可重覆
server-id = 1

# 指定產生bin log檔案名稱，使MySQL記錄所有DB交易記錄
log-bin=mysql-bin
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/a555262a58eb9245c7ffcbcf891791ca" rel="noopener">my.cnf</a>（GitHub Gist）</p>
<p>儲存後，重啟 MySQL 才會生效</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo /etc/init.d/mysql restart
</span></span></code></pre></div><p><strong>STEP 2</strong>：在 Master server 建立給 Slave 複製資料的帳號</p>
<p>登入 MySQL</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mysql -u root -p
</span></span></code></pre></div><p>建立有 REPLICATION、SLAVE 權限的 user 帳號</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">/*建立使用者*/</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>mysql<span style="color:#ff7b72;font-weight:bold">&gt;</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">CREATE</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">USER</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">&#39;account&#39;</span><span style="color:#ff7b72;font-weight:bold">@</span><span style="color:#a5d6ff">&#39;ip&#39;</span><span style="color:#6e7681"> </span>IDENTIFIED<span style="color:#6e7681"> </span><span style="color:#ff7b72">BY</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">&#39;password&#39;</span>;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">/*給使用者權限*/</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>mysql<span style="color:#ff7b72;font-weight:bold">&gt;</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">GRANT</span><span style="color:#6e7681"> </span>REPLICATION<span style="color:#6e7681"> </span>SLAVE<span style="color:#6e7681"> </span><span style="color:#ff7b72">ON</span><span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">*</span>.<span style="color:#ff7b72;font-weight:bold">*</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">TO</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">&#39;account&#39;</span><span style="color:#ff7b72;font-weight:bold">@</span><span style="color:#a5d6ff">&#39;ip&#39;</span>;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">/*使權限生效*/</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>mysql<span style="color:#ff7b72;font-weight:bold">&gt;</span><span style="color:#6e7681"> </span>FLUSH<span style="color:#6e7681"> </span><span style="color:#ff7b72">PRIVILEGES</span>;<span style="color:#6e7681">
</span></span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/68f42b828e24df0209600dee7a7030c7" rel="noopener">create_slave_user.sql</a>（GitHub Gist）</p>
<p><strong>STEP 3</strong>：備份 Master server 的 DB</p>
<blockquote>
<p>執行此步驟時如果系統已經上線，一定要發出公告說明系統維護時間</p>
</blockquote>
<p>在完成 DB Replication 前，為了保證 Master 與 Slave 的資料相同，你需要先把 DB Lock 成 read only：</p>
<pre tabindex="0"><code>mysql&gt; FLUSH TABLES WITH READ LOCK;
</code></pre><p>備份指令：</p>
<pre tabindex="0"><code>mysqldump -uroot -p  --all-databases &gt; master.sql
</code></pre><p>解除 read only：</p>
<pre tabindex="0"><code>mysql&gt; UNLOCK TABLES;
</code></pre><p><strong>STEP 4</strong>：檢查 Master 是否已經啟動</p>
<pre tabindex="0"><code>mysql&gt; SHOW MASTER STATUS \G
</code></pre><p><img src="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-04.webp" width="1142" height="330" alt="Master server 設定（圖 4）" loading="lazy" decoding="async"></p>
<h3 id="-slave-server-設定">Slave server 設定<a class="h-anchor" href="#-slave-server-%e8%a8%ad%e5%ae%9a" aria-label="這個段落的連結"></a></h3>
<p><strong>STEP 1</strong>：將剛剛備份的 <code>master.sql</code> 還原到 Slave server 的 DB</p>
<p>先複製 master.sql 到 Slave server 的目錄下</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo scp master.sql &lt;username&gt;@&lt;IP or hostname&gt;:&lt;Destination&gt;
</span></span></code></pre></div><p>下指令還原 DB</p>
<pre tabindex="0"><code>mysql -u root -p — default-character-set=utf8 &lt; master.sql
</code></pre><h3 id="step-2設定-slave-server-的-mycnf">STEP 2：設定 Slave server 的 my.cnf<a class="h-anchor" href="#step-2%e8%a8%ad%e5%ae%9a-slave-server-%e7%9a%84-mycnf" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo vi /etc/mysql/my.cnf
</span></span></code></pre></div><pre tabindex="0"><code>[mysqld]
#注意：不可與剛剛設定的 Master 重複
server-id=2 

#Slave server 的 DB 開啟唯獨避免被寫入
read-only=on
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/54144c2099368bfc91f2681a47f9bf94" rel="noopener">slave_my.cnf</a>（GitHub Gist）</p>
<p>儲存後，重啟 MySQL 才會生效</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo /etc/init.d/mysql restart
</span></span></code></pre></div><p><strong>STEP 3</strong>：設定 SlaveDB，指定 Master DB 登入資訊，並啟動 Replication</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">/* 這裡的帳號密碼就上面 Master 設定的那組 */</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>mysql<span style="color:#ff7b72;font-weight:bold">&gt;</span><span style="color:#6e7681"> </span>CHANGE<span style="color:#6e7681"> </span>MASTER<span style="color:#6e7681"> </span><span style="color:#ff7b72">TO</span><span style="color:#6e7681"> </span>MASTER_HOST<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;master_ip&#39;</span>,<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">              </span>MASTER_USER<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;account&#39;</span>,<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">              </span>MASTER_PASSWORD<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;password&#39;</span>;<span style="color:#6e7681">
</span></span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/e09739d509230e0b270f7b374c8ba553" rel="noopener">setting_slave_info.sql</a>（GitHub Gist）</p>
<p>啟動 Slave Replication：</p>
<pre tabindex="0"><code>mysql&gt; START SLAVE;
</code></pre><p><strong>STEP 4</strong>：檢查 replication 狀態</p>
<pre tabindex="0"><code>SHOW SLAVE STATUS \G
</code></pre><p>如果下圖紅框皆顯示 Yes 就代表你成功嚕！</p>
<p><img src="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-05.webp" width="582" height="268" alt="STEP 2：設定 Slave server 的 my.cnf（圖 5）" loading="lazy" decoding="async"></p>
<p>如果你看到下面這個畫面，可能是因為你的 Master MySQL 的設定有問題，最有可能是『使用者帳號、密碼錯誤』、『bind-address 沒有調整』</p>
<p><img src="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-06.webp" width="582" height="268" alt="STEP 2：設定 Slave server 的 my.cnf（圖 6）" loading="lazy" decoding="async"></p>
<h2 id="三驗證-masterslave-db-是否同步">三、驗證 Master、Slave DB 是否同步<a class="h-anchor" href="#%e4%b8%89%e9%a9%97%e8%ad%89-masterslave-db-%e6%98%af%e5%90%a6%e5%90%8c%e6%ad%a5" aria-label="這個段落的連結"></a></h2>
<p>先在 Master 確認是不是真的有跟 Slave 串接成功</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff7b72">show</span><span style="color:#6e7681"> </span>slave<span style="color:#6e7681"> </span>hosts;<span style="color:#6e7681">
</span></span></span></code></pre></div><p><img src="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-07.webp" width="1400" height="274" alt="三、驗證 Master、Slave DB 是否同步（圖 7）" loading="lazy" decoding="async"></p>
<p>確認連接成功後我們在 Master server 上面新增一個 DB</p>
<p><img src="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-08.webp" width="342" height="479" alt="三、驗證 Master、Slave DB 是否同步（圖 8）" loading="lazy" decoding="async"></p>
<p>回到 Slave server 就會看到 DB 被同步過來嚕～</p>
<p><img src="https://deanlin.net/blog/202103/mysql-master-slave-replication/images/mysql-master-slave-replication-09.webp" width="342" height="217" alt="三、驗證 Master、Slave DB 是否同步（圖 9）" loading="lazy" decoding="async"></p>
<hr>
<p>這篇文章是針對 MySQL Master Slave Replication 做說明，但你依照上面的做法完成後還需要思考幾個問題：</p>
<ul>
<li>我們知道 Master server 是用來更新資料，Slave server 是用來檢索資料；但他們都是獨立的 ip，總不能每個 api 執行時都連不一樣的 ip 吧？</li>
<li>Slave 的資料是從 Master 上複製過來的，會複製就代表會有延遲，要如何保持資料的一致性呢？</li>
</ul>
<pre tabindex="0"><code>如果你想深入了解上面的問題如何解決可以參考我的文章：
👍 在 MySQL 5.7 解決主從複製的延遲問題
👍 Ubuntu 用 Atlas 實現 MySQL Proxy 讀寫分離 read/write splitting
</code></pre><hr>
<p>參考資源：<a href="https://dotblogs.com.tw/eric_obay_talk/2018/10/24/160017">MySQL Master Slave replication</a>、<a href="https://ithelp.ithome.com.tw/articles/10236724">打造MySQL Replication</a></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
