<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>隨意記事</title>
  
  
  <link href="https://sujunmin.github.io/blog/atom.xml" rel="self"/>
  
  <link href="https://sujunmin.github.io/blog/"/>
  <updated>2026-09-26T03:17:55.356Z</updated>
  <id>https://sujunmin.github.io/blog/</id>
  
  <author>
    <name>Su, Jun-Ming</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>用 Graylog 取代 Splunk 紀錄 (下)</title>
    <link href="https://sujunmin.github.io/blog/2018/06/12/%E7%94%A8%20Graylog%20%E5%8F%96%E4%BB%A3%20Splunk%20%E7%B4%80%E9%8C%84%20(%E4%B8%8B)/"/>
    <id>https://sujunmin.github.io/blog/2018/06/12/%E7%94%A8%20Graylog%20%E5%8F%96%E4%BB%A3%20Splunk%20%E7%B4%80%E9%8C%84%20(%E4%B8%8B)/</id>
    <published>2018-06-12T07:50:55.000Z</published>
    <updated>2026-09-26T03:17:55.356Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>有了前一篇的架設，這邊來說明要怎麼從 Splunk 把報表轉到 Graylog。</p><h2 id="先前準備"><a href="#先前準備" class="headerlink" title="先前準備"></a>先前準備</h2><p>因為在 Graylog 的 Web 界面是沒辦法直接拉報表，必須透過 Graylog Restful API 來做介接取得資料後再自己 Render 出來，所以以下的基本知識需要先了解。</p><ol><li><a href="https://ithelp.ithome.com.tw/users/20091343/ironman/762">REST&#x2F;RESTFul API 介紹</a>，怎麼操作 API</li><li><a href="https://blog.csdn.net/caisini_vc/article/details/53377756">Lucene Query Syntax</a>，最重要的核心，如何去找到你要的東西</li><li>報表系統使用的平台，必須要能夠吃 Graylog 產生出來的 JSON</li></ol><p>因為在 <a href="https://github.com/sujunmin/SQLServerTraceFileParser">SQL Server</a>、<a href="https://github.com/sujunmin/MariaDBDailyCheckScripts">MariaDB</a> 的經驗，這次還是選擇 Windows 上面的 Powershell 來實做報表。</p><h3 id="Graylog-RESTFul-API"><a href="#Graylog-RESTFul-API" class="headerlink" title="Graylog RESTFul API"></a>Graylog RESTFul API</h3><p><a href="http://docs.graylog.org/en/2.4/pages/configuration/rest_api.html">官方文件</a>就是只叫用戶到 System&#x2F;Node 底下的 API Browser 去看，說實在的，就只是這樣而已，因此大部分的時間都在在這個用 Swagger 做出來的 API Browser 上。</p><p>登入帳號密碼以後可以看到列表，點開每一個項目會有說明，下面的圖應該就是寫報表的主體。</p><p><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/graylog_3.png"></p><p>點開一個方法就會有每個方法的介紹。</p><p><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/graylog_4.png"></p><p>其中幾個重要的點。</p><ol><li>Implement Note: 注意說明跟參數格式</li><li>Response Class: 回傳資料格式</li><li>Parameters: 參數，注意必須項目</li><li>Try It Out: 我覺得最棒的項目了，直接試一次，會有以下的結果</li></ol><p><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/graylog_5.png"></p><ol><li>Request URL: 就是後續用一樣的連結就可以得到一樣的東西，可以加到報表程式裡</li><li>Response Body: 回應資料，精華所在，測試的時候可以先把他放在檔案裡供程式測試，等到上線測試再走 Request URL。</li></ol><h3 id="回到搜尋"><a href="#回到搜尋" class="headerlink" title="回到搜尋"></a>回到搜尋</h3><p>大家在看到之前的 Search API 的時候，實在是太多了，到底要用哪一個?</p><p>其實這個是見仁見智的，要看需求是什麼，但是我覺得 Graylog 開了很多一樣結果的不一樣 API，應該就是要怎麼樣都能搜尋吧 XD</p><ol><li>如果很明確的時間就 <code>/Search/Absolute</code></li><li>如果不明確的時間 (昨天，兩天前)就 <code>Search/Keyword</code></li><li>如果你已經在 Graylog GUI 存了一個覺得不賴的報表，想再叫出來用，就 <code>Search/Saved</code></li><li>…</li></ol><p>每個項目還有對於原來的值 (<code>absloute</code>)，簡單統計(<code>histogram</code>，<code>stats</code>)都能得到想要的，都要試試看才會知道。</p><h3 id="Splunk-報表轉到-Graylog"><a href="#Splunk-報表轉到-Graylog" class="headerlink" title="Splunk 報表轉到 Graylog"></a>Splunk 報表轉到 Graylog</h3><p>這邊就拿一個最常用跟最基本來分享給大家。</p><h4 id="原來的-Splunk-報表"><a href="#原來的-Splunk-報表" class="headerlink" title="原來的 Splunk 報表"></a>原來的 Splunk 報表</h4><p>有一個功能是每天凌晨零點寄送某個帳號每一臺機器的登入登出統計，在 Splunk 是這樣做的</p><p><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/graylog_6.jpg"></p><h4 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h4><p>在查詢一下他的語法以後，我們可以透過<code>/Search/Absolute</code>來拿資料 (程式碼請參考<a href="https://gist.github.com/sujunmin/d606bfdfd17e7207eaef48dd0cc66d54">這個</a>)</p><ol><li>透過 <code>/Search/Absolute</code> 拿昨天的資料</li><li>如果 <code>total_results</code> 沒東西就結束</li><li>反之存一份 JSON 當附件</li><li>拿出所有的 <code>messages.message.full_message</code></li><li>拿到 IP</li><li>放到一個 Dictionary 裡面統計</li><li>輸出</li></ol><p>以下來說明一下實際上怎麼做</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="variable">$UserToken</span> = <span class="string">&quot;token&quot;</span></span><br><span class="line"><span class="variable">$base64AuthInfo</span> = [<span class="type">Convert</span>]::ToBase64String([<span class="type">Text.Encoding</span>]::ASCII.GetBytes((<span class="string">&quot;&#123;0&#125;:token&quot;</span> <span class="operator">-f</span> <span class="variable">$UserToken</span>)))</span><br><span class="line">...</span><br><span class="line"><span class="variable">$rawdata</span> = (<span class="built_in">Invoke-WebRequest</span> <span class="literal">-Uri</span> <span class="variable">$uri</span> <span class="literal">-Method</span> Get <span class="literal">-Headers</span> <span class="selector-tag">@</span>&#123;<span class="string">&quot;Content-Type&quot;</span>=<span class="string">&quot;application/json&quot;</span>;<span class="string">&quot;Authorization&quot;</span> = (<span class="string">&quot;Basic &#123;0&#125;&quot;</span> <span class="operator">-f</span> <span class="variable">$base64AuthInfo</span>)&#125;).Content</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>這個部份是認證，因為有限制系統使用者存取，所以要擺認證資料進去。</p><p>可以跟官方文件的資料一樣，送帳號密碼進去也行，但是寫在 Script 裡總是怪怪的。</p><p>我們可透過新增 Session Token 的方法來傳送驗證。</p><p>可以透過 curl 或是 API Browser 直接來新增 (<code>Users:/users/&#123;username&#125;/tokens/&#123;name&#125;</code>)，username 是帳號名稱，name 就是隨便取的好記名字 (例如 report)。</p><p><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/graylog_7.png"></p><p>會得到一個 token，就可以拿進來用了。</p><p>另外這邊使用到 <code>Invoke-WebRequest</code>，為什麼不用 <code>Invoke-RestMethod</code> 呢? 因為 <code>Invoke-RestMethod</code> 只會取<a href="https://stackoverflow.com/questions/42889221/invoke-restmethod-in-powershell-only-returning-inner-entry-elements-and-not-feed">第一層</a>的緣故，所以透過 <code>Invoke-WebRequest</code> 拿資料。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="variable">$resultcount</span> = (<span class="variable">$rawdata</span> | <span class="built_in">ConvertFrom-Json</span>).total_results</span><br><span class="line">...</span><br><span class="line"><span class="variable">$test</span> = <span class="variable">$rawdata</span> | <span class="built_in">ConvertFrom-Json</span> | <span class="built_in">select</span> <span class="literal">-expand</span> messages | <span class="built_in">select</span> <span class="literal">-expand</span> message | <span class="built_in">select</span> full_message</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>這邊就是取實際資料出來的樣子，如果是從檔案(測試資料)出來的，記得再 Convert JSON 先。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="variable">$resultcount</span> = (<span class="variable">$rawdata</span> | <span class="built_in">ConvertTo-Json</span> | <span class="built_in">ConvertFrom-Json</span>).total_results</span><br><span class="line">...</span><br><span class="line"><span class="variable">$test</span> = <span class="variable">$rawdata</span> | <span class="built_in">ConvertTo-Json</span> |  <span class="built_in">ConvertFrom-Json</span> | <span class="built_in">select</span> <span class="literal">-expand</span> messages | <span class="built_in">select</span> <span class="literal">-expand</span> message | <span class="built_in">select</span> full_message</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>接下來分析 IP，從<code>Original Address=</code> 拿出來。用個 Dictionary 統計 (沒有的新增，有的加 1)</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$test</span> | <span class="built_in">Foreach-Object</span> &#123;</span><br><span class="line">      <span class="variable">$test3</span> = [<span class="type">regex</span>]::Matches(<span class="variable">$_</span>.full_message, <span class="string">&quot;Original Address=([^\s]+)&quot;</span>)</span><br><span class="line">      <span class="variable">$test4</span> = <span class="variable">$test3</span>[<span class="number">0</span>].value.split(<span class="string">&quot;=&quot;</span>)</span><br><span class="line">      <span class="variable">$d</span>[<span class="variable">$test4</span>[<span class="number">1</span>]] = <span class="variable">$d</span>[<span class="variable">$test4</span>[<span class="number">1</span>]] + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>顯示結果與印出來。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$outputdata</span> = <span class="string">&quot;Hi all, &lt;br&gt; <span class="variable">$_</span> 在 &quot;</span> + (<span class="string">&#x27;&#123;0:yyyy-MM-dd&#125;&#x27;</span> <span class="operator">-f</span> <span class="variable">$reportdate</span>) + <span class="string">&quot; 事件數量紀錄如下&lt;br/&gt;&quot;</span> </span><br><span class="line">   <span class="variable">$d</span> | <span class="built_in">Format-Table</span>  <span class="selector-tag">@</span>&#123;L=<span class="string">&#x27;Host(s)&#x27;</span>;E=&#123;<span class="variable">$_</span>.key&#125;&#125;, <span class="selector-tag">@</span>&#123;L=<span class="string">&#x27;Event Count(s)&#x27;</span>;E=&#123;<span class="variable">$_</span>.value&#125;&#125; <span class="literal">-auto</span> | <span class="built_in">out-file</span> <span class="string">&quot;.\ooo.txt&quot;</span></span><br><span class="line">   <span class="built_in">Get-Content</span> <span class="string">&quot;.\ooo.txt&quot;</span> | <span class="built_in">Foreach-Object</span> &#123;</span><br><span class="line">      <span class="variable">$outputdata</span> = <span class="variable">$outputdata</span> + <span class="variable">$_</span></span><br><span class="line">      <span class="variable">$outputdata</span> = <span class="variable">$outputdata</span> + <span class="string">&quot;&lt;br/&gt;&quot;</span></span><br><span class="line"></span><br><span class="line">   &#125;</span><br><span class="line">  </span><br><span class="line">   <span class="variable">$outputdata</span> = <span class="variable">$outputdata</span> + <span class="string">&quot;&lt;br/&gt;詳細請參考附件資料。&quot;</span></span><br><span class="line">   </span><br><span class="line">   <span class="variable">$d</span>.Clear()</span><br><span class="line"></span><br><span class="line">   <span class="built_in">Send-MailMessage</span> <span class="literal">-To</span> <span class="variable">$MailTo</span> <span class="literal">-From</span> <span class="variable">$MailFrom</span> <span class="literal">-Subject</span> ((<span class="string">&#x27;&#123;0:yyyy-MM-dd&#125;&#x27;</span> <span class="operator">-f</span> <span class="variable">$reportdate</span>) + <span class="string">&quot; <span class="variable">$_</span> 事件紀錄統計&quot;</span>) <span class="literal">-Body</span> <span class="string">&quot;<span class="variable">$outputdata</span>&quot;</span> <span class="literal">-BodyAsHtml</span> <span class="literal">-SmtpServer</span> <span class="variable">$MailServer</span> <span class="literal">-Encoding</span> ([<span class="type">System.Text.Encoding</span>]::UTF8) <span class="literal">-Attachments</span> <span class="string">&quot;.\<span class="variable">$_</span>.log&quot;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>然後設排程跑這個 Script，結果應該是這樣。</p><p><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/graylog_8.jpg"></p><p>這個只是個簡單的報表，當然可以再加點 HTML&#x2F;CSS，不過先知道要做什麼，拿到資料，後面做報表就不是很大的問題了。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;有了前一篇的架設，這邊來說明要怎麼從 Splunk 把報表轉到 Graylog。&lt;/p&gt;
&lt;h2 id=&quot;先前準備&quot;&gt;&lt;a</summary>
        
      
    
    
    
    
    <category term="Networks" scheme="https://sujunmin.github.io/blog/tags/Networks/"/>
    
    <category term="MIS" scheme="https://sujunmin.github.io/blog/tags/MIS/"/>
    
  </entry>
  
  <entry>
    <title>用 Graylog 取代 Splunk 紀錄 (上)</title>
    <link href="https://sujunmin.github.io/blog/2018/05/31/%E7%94%A8%20Graylog%20%E5%8F%96%E4%BB%A3%20Splunk%20%E7%B4%80%E9%8C%84%20(%E4%B8%8A)/"/>
    <id>https://sujunmin.github.io/blog/2018/05/31/%E7%94%A8%20Graylog%20%E5%8F%96%E4%BB%A3%20Splunk%20%E7%B4%80%E9%8C%84%20(%E4%B8%8A)/</id>
    <published>2018-05-31T07:43:12.000Z</published>
    <updated>2026-09-26T03:17:55.356Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>原來負責系統的 Log 架構是這樣的</p><p><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/graylog_1.png"></p><p>因為 Splunk License 的關係，有時候會因為收的量太多而罷工了。</p><p>這次就是想要利用 <a href="https://www.graylog.org/">Graylog</a> 來取代原有的 Splunk，以避免有 Log 沒分析到的問題。</p><h2 id="試用經過"><a href="#試用經過" class="headerlink" title="試用經過"></a>試用經過</h2><h1 id="請注意以下因為一些因素，只換-Splunk-的方式非真正最佳解法，僅提供有類似架構的朋友參考"><a href="#請注意以下因為一些因素，只換-Splunk-的方式非真正最佳解法，僅提供有類似架構的朋友參考" class="headerlink" title="(請注意以下因為一些因素，只換 Splunk 的方式非真正最佳解法，僅提供有類似架構的朋友參考)"></a>(請注意以下因為一些因素，只換 Splunk 的方式非真正最佳解法，僅提供有類似架構的朋友參考)</h1><p>因為一開始對 Graylog 不熟，所以當然要有一些測試。從官網上的 <a href="http://docs.graylog.org/en/latest/pages/installation/virtual_machine_appliances.html">OVA</a> 開始，發現可以有 Log 進來，玩了一下界面，還蠻不錯的。</p><p>不過有一個麻煩的事情(這個在之前系統有購買 Arcsight 的 Solution 也有碰到過)，就是資料是亂碼的問題。</p><p>原因出在從 <a href="https://www.kiwisyslog.com/kiwi-syslog-server">KIWI Syslog Server</a> 拋出來的 Syslog 是 Windows 編碼，到 Linux 上就會變亂碼了。</p><p>找了一些<a href="https://community.graylog.org/t/syslog-encoding-problem/2253">方法</a>，沒找到可以從 Graylog 直接 Conversion 的方法，只能前面加個 <a href="https://www.elastic.co/products/logstash">logstash</a> 轉了。</p><p>原來想裝在官網的 OVA 裡，不過官網的 OVA 一來是 Ubuntu 14.04，系統裡的 Ubuntu 16.04 外，裝好起來效能竟然差到沒法 Parsing 資料，於是就想辦法自己裝了。</p><p>官網的<a href="http://docs.graylog.org/en/2.4/pages/installation/os/ubuntu.html">手動安裝方法</a>寫得很詳細，照做就很容易完成了。</p><h2 id="最後架構"><a href="#最後架構" class="headerlink" title="最後架構"></a>最後架構</h2><p><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/graylog_2.png"></p><p>因為只有 Windows 傳過來的會亂碼，所以在 Graylog 前面多放一個 logstash 轉檔以後再丟到 Graylog 即可，網路設備的不會這樣，就直接送進 Graylog。</p><h3 id="Graylog-Server"><a href="#Graylog-Server" class="headerlink" title="Graylog Server"></a>Graylog Server</h3><h4 id="Ubuntu-16-04"><a href="#Ubuntu-16-04" class="headerlink" title="Ubuntu 16.04"></a>Ubuntu 16.04</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">sudo</span> apt-get update &amp;&amp; <span class="built_in">sudo</span> apt-get upgrade</span><br><span class="line">$ <span class="built_in">sudo</span> apt-get install apt-transport-https openjdk-8-jre-headless uuid-runtime pwgen</span><br></pre></td></tr></table></figure><p>測試一下 Java</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sujunmin@graylog:~$ java -version</span><br><span class="line">java version <span class="string">&quot;1.8.0_171&quot;</span></span><br><span class="line">Java(TM) SE Runtime Environment (build 1.8.0_171-b01)</span><br><span class="line">Java HotSpot(TM) 64-Bit Server VM (build 25.171-b01, mixed mode)</span><br></pre></td></tr></table></figure><h4 id="MongoDB"><a href="#MongoDB" class="headerlink" title="MongoDB"></a>MongoDB</h4><p>安裝最新版 MongoDB</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">sudo</span> apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">&quot;deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.6 multiverse&quot;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/apt/sources.list.d/mongodb-org-3.6.list</span><br><span class="line">$ <span class="built_in">sudo</span> apt-get update</span><br><span class="line">$ <span class="built_in">sudo</span> apt-get install -y mongodb-org</span><br><span class="line">$ <span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> mongod.service</span><br><span class="line">$ <span class="built_in">sudo</span> systemctl restart mongod.service</span><br></pre></td></tr></table></figure><h4 id="Elasticsearch"><a href="#Elasticsearch" class="headerlink" title="Elasticsearch"></a>Elasticsearch</h4><p>Elasticsearch 要用 5.x 版的 (是說我已經有這個了，然後前面又有 logstash，差一個 Kanban 就變成 <a href="https://www.elastic.co/webinars/introduction-elk-stack">ELK</a> 了，還要用 Graylog 嗎 XD)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | <span class="built_in">sudo</span> apt-key add -</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">&quot;deb https://artifacts.elastic.co/packages/5.x/apt stable main&quot;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> -a /etc/apt/sources.list.d/elastic-5.x.list</span><br><span class="line">$ <span class="built_in">sudo</span> apt-get update &amp;&amp; <span class="built_in">sudo</span> apt-get install elasticsearch</span><br></pre></td></tr></table></figure><p>安裝完以後記得設定檔 <code>/etc/elasticsearch/elasticsearch.yml</code> 裡頭的 <code>cluster.name</code> 要設為 <code>graylog</code>。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cluster.name: graylog</span><br></pre></td></tr></table></figure><p>然後設定服務</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> elasticsearch.service</span><br><span class="line">$ <span class="built_in">sudo</span> systemctl restart elasticsearch.service</span><br></pre></td></tr></table></figure><p>測試一下</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">sujunmin@graylog:~$ curl -XGET <span class="string">&#x27;localhost:9200/?pretty&#x27;</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;name&quot;</span> : <span class="string">&quot;-kYzFA9&quot;</span>,</span><br><span class="line">  <span class="string">&quot;cluster_name&quot;</span> : <span class="string">&quot;graylog&quot;</span>,</span><br><span class="line">  <span class="string">&quot;cluster_uuid&quot;</span> : <span class="string">&quot;T3JQKehzSqmLThlVkEKPKg&quot;</span>,</span><br><span class="line">  <span class="string">&quot;version&quot;</span> : &#123;</span><br><span class="line">    <span class="string">&quot;number&quot;</span> : <span class="string">&quot;5.5.1&quot;</span>,</span><br><span class="line">    <span class="string">&quot;build_hash&quot;</span> : <span class="string">&quot;19c13d0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;build_date&quot;</span> : <span class="string">&quot;2018-05-18T20:44:24.823Z&quot;</span>,</span><br><span class="line">    <span class="string">&quot;build_snapshot&quot;</span> : <span class="literal">false</span>,</span><br><span class="line">    <span class="string">&quot;lucene_version&quot;</span> : <span class="string">&quot;6.6.0&quot;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;tagline&quot;</span> : <span class="string">&quot;You Know, for Search&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h4><p>作為前端 Reverse Proxy 到 Graylog Web 的媒介</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt -y install nginx</span><br></pre></td></tr></table></figure><p>修改設定檔</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vi /etc/nginx/sites-available/default</span><br></pre></td></tr></table></figure><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">server</span><br><span class="line">&#123;</span><br><span class="line">    listen <span class="number">80</span> default_server;</span><br><span class="line">    listen [::]:<span class="number">80</span> default_server ipv6only=<span class="keyword">on</span>;</span><br><span class="line">    server_name &lt;實際 IP&gt;;</span><br><span class="line"></span><br><span class="line">    location / &#123;</span><br><span class="line">      proxy_set_header Host $http_host;</span><br><span class="line">      proxy_set_header <span class="keyword">X</span>-Forwarded-Host $host;</span><br><span class="line">      proxy_set_header <span class="keyword">X</span>-Forwarded-Server $host;</span><br><span class="line">      proxy_set_header <span class="keyword">X</span>-Forwarded-For $proxy_add_x_forwarded_for;</span><br><span class="line">      proxy_set_header <span class="keyword">X</span>-Graylog-Server-URL http://$server_name/api;</span><br><span class="line">      proxy_pass       http://<span class="number">127.0</span>.<span class="number">0.1</span>:<span class="number">9000</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>設定服務與啟動</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> graylog-server.service</span><br><span class="line">$ <span class="built_in">sudo</span> systemctl start graylog-server.service</span><br></pre></td></tr></table></figure><h4 id="Graylog"><a href="#Graylog" class="headerlink" title="Graylog"></a>Graylog</h4><p>安裝最新版 Graylog</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ wget https://packages.graylog2.org/repo/packages/graylog-2.4-repository_latest.deb</span><br><span class="line">$ <span class="built_in">sudo</span> dpkg -i graylog-2.4-repository_latest.deb</span><br><span class="line">$ <span class="built_in">sudo</span> apt-get update &amp;&amp; <span class="built_in">sudo</span> apt-get install graylog-server</span><br></pre></td></tr></table></figure><p>以下的設定是一定要設的</p><ol><li><p><code>root_username</code>，預設是 <code>admin</code>，可以改喜歡的</p></li><li><p><code>root_password_sha2</code>，root 的密碼，用以下方式產生</p><p> <code>echo -n yourpassword | sha256sum</code></p></li><li><p><code>password_secret</code>，其他用戶的密碼從這個資料加料產生，用以下方式產生</p><p> <code>pwgen -N 1 -s 96</code></p></li><li><p><code>rest_listen_uri</code>，預設是 <code>0.0.0.0:9000</code>，因為不管是 Web 還是其他應用程式，跟 Graylog 溝通都是透過 <a href="http://docs.graylog.org/en/2.4/pages/configuration/rest_api.html">Graylog RESTFul API</a>，這個就是溝通的橋樑</p></li><li><p><code>rest_transport_uri</code>，這個通常跟上面的一樣，不一樣的部份是如果你想要 expose 這個服務到其他不同段的 IP，這邊就要特別設定再外面看到的 IP，前面 Nginx 的 <code>server_name</code> 會跟這個一樣</p></li><li><p><code>root_timezone</code> 設定時區</p></li><li><p><code>web_enable</code> 是 <code>true</code></p></li><li><p><code>web_listen_uri</code> 跟 <code>rest_listen_uri</code> 一樣</p></li></ol><p>設定服務與啟動</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> graylog-server.service</span><br><span class="line">$ <span class="built_in">sudo</span> systemctl start graylog-server.service</span><br></pre></td></tr></table></figure><p>登入看看，記得把原來的預設收 514 的 syslog input 的 Port 改成 12345。</p><h3 id="logstash"><a href="#logstash" class="headerlink" title="logstash"></a>logstash</h3><p>安裝 logstash</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt-get install logstash</span><br></pre></td></tr></table></figure><p>安裝 <code>logstash-output-syslog</code> (轉換過的 syslog 吐給 Graylog)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> /usr/share/logstash/bin/logstash-plugin install logstash-output-syslog</span><br></pre></td></tr></table></figure><p>設定 pipeline.conf</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vi /etc/logstash/conf.d/pipeline.conf</span><br></pre></td></tr></table></figure><p>Input 從 514 的 syslog 編碼為 CP950 (Windows) 轉到 12345，編碼是 UTF-8</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">input</span> &#123;</span><br><span class="line">    syslog &#123;</span><br><span class="line">      port =&gt; <span class="number">514</span></span><br><span class="line">      codec =&gt; plain &#123;</span><br><span class="line">        charset =&gt; <span class="string">&quot;CP950&quot;</span></span><br><span class="line">      &#125;  </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">output &#123;</span><br><span class="line">    syslog &#123;</span><br><span class="line">        host =&gt; <span class="string">&quot;127.0.0.1&quot;</span></span><br><span class="line">        port =&gt; <span class="number">12345</span></span><br><span class="line">        codec =&gt; plain &#123;</span><br><span class="line">          charset =&gt; <span class="string">&quot;UTF-8&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Input 設在 514 用預設的 logstash 使用者沒法聽，所以要改啟動參數</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vi /etc/logstash/startup.options</span><br></pre></td></tr></table></figure><p>找到 <code>LS_USER</code> 與 <code>LS_GROUP</code> 都設成 <code>root</code></p><p>重新安裝設定檔</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> /usr/share/logstash/bin/system-install</span><br></pre></td></tr></table></figure><p>設定服務與啟動</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> logstash.service</span><br><span class="line"><span class="built_in">sudo</span> systemctl start logstash.service</span><br></pre></td></tr></table></figure><h2 id="下一步"><a href="#下一步" class="headerlink" title="下一步"></a>下一步</h2><p>安裝測試完成以後，就是原來 Splunk 的報表能夠轉移。</p><p>剛剛有說到所有跟 Graylog 溝通都是透過 Graylog RESTFul API 來做處理，下一篇文章就是要來說明如何透過 Graylog RESTFul API 來做簡單的報表。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;原來負責系統的 Log 架構是這樣的&lt;/p&gt;
&lt;p&gt;&lt;img</summary>
        
      
    
    
    
    
    <category term="Networks" scheme="https://sujunmin.github.io/blog/tags/Networks/"/>
    
    <category term="MIS" scheme="https://sujunmin.github.io/blog/tags/MIS/"/>
    
  </entry>
  
  <entry>
    <title>Ubuntu 16.04 安裝與使用 Flashcache 流程紀錄</title>
    <link href="https://sujunmin.github.io/blog/2017/08/31/Ubuntu%2016.04%20%E5%AE%89%E8%A3%9D%E8%88%87%E4%BD%BF%E7%94%A8%20Flashcache%20%E6%B5%81%E7%A8%8B%E7%B4%80%E9%8C%84/"/>
    <id>https://sujunmin.github.io/blog/2017/08/31/Ubuntu%2016.04%20%E5%AE%89%E8%A3%9D%E8%88%87%E4%BD%BF%E7%94%A8%20Flashcache%20%E6%B5%81%E7%A8%8B%E7%B4%80%E9%8C%84/</id>
    <published>2017-08-31T04:31:57.000Z</published>
    <updated>2026-09-26T03:17:55.350Z</updated>
    
    <content type="html"><![CDATA[<p>因為系統效能的關係，嘗試一下在 Ubuntu 16.04 下用 SSD 當硬碟的快取碟，看效能有沒有比較好，<a href="http://wubx.net/ubuntu-flashcache/">這篇</a>已經寫的十分清楚，但是有一些我做起來不太一樣的，以下紀錄安裝與使用方式。</p><h2 id="安裝"><a href="#安裝" class="headerlink" title="安裝"></a>安裝</h2><p>原文中使用原碼編譯安裝，但其實 Ubuntu 16.04 裡頭已經有這個 Package 了。</p><p><code>sudo apt install flashcache-dkms flashcache-utils</code></p><p>載入 flashcache</p><p><code>sudo modprobe flashcache</code></p><p>看有沒有成功</p><p><code>sudo lsmod |grep flashcache</code></p><p>開機自動載入</p><p><code>echo &#39;flashcache&#39; | sudo tee --append /etc/modules</code></p><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><p>如同原文方式，首先要確認 Partitions，原來的磁碟跟要作為快取的 SSD 磁碟。</p><p>假設原來的狀態如下</p><p>原來的磁碟 <code>/dev/sdb1</code>，UUID 為 <code>[UUID_sdb1]</code> (可從<code>ls -la /dev/disk/by-uuid/</code>查看對應) mount 在 <code>/test</code> 上</p><p>要做快取的 SSD 磁碟 <code>/dev/sdc1</code>，未來名稱為 <code>sdb1_cache</code></p><ol><li><p>把 <code>/test</code> 裡頭的資料移到其他地方，因為要清空</p></li><li><p>對 SSD 磁碟做 <code>flash_create</code>，這個會依照大小等一段時間</p><p><code>sudo flashcache_create -v -p back sdb1_cache /dev/sdc1 /dev/disk/by-uuid/[UUID_sdb1]</code></p></li><li><p>試著 mount 看看</p><p><code>sudo mount /dev/mapper/sdb1_cache /test</code></p></li><li><p>成功就可以試著寫寫看 (把先前移到別的地方的資料移回來)，透過 <code>flashstat</code> 可以看使用狀況</p></li><li><p>如果要開機可以 mount，寫入 <code>/etc/fstab</code> 就可以了 (範例)</p><p><code>/dev/mapper/sdb1_cache /test ext4 defaults 0 0</code></p></li></ol><p>如果要打掉的話就</p><ol><li><code>/test</code> 的資料移動到別的地方</li><li><code>sudo umount /test</code></li><li><code>sudo dmsetup remove sdb1_cache</code> </li><li><code>sudo flashcache_destory /dev/sdc1</code></li><li><code>/test</code> 的 mounting point 改回原來的 (<code>/dev/sdb1</code>)</li><li>資料搬回 <code>/test</code></li></ol><h2 id="後記"><a href="#後記" class="headerlink" title="後記"></a>後記</h2><p>我覺得這個還蠻好用的，它是對 Partition 做 cache，都不用管實際怎麼運作就能用了，十分方便好用。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;因為系統效能的關係，嘗試一下在 Ubuntu 16.04 下用 SSD 當硬碟的快取碟，看效能有沒有比較好，&lt;a</summary>
        
      
    
    
    
    
    <category term="MIS" scheme="https://sujunmin.github.io/blog/tags/MIS/"/>
    
    <category term="Ubuntu" scheme="https://sujunmin.github.io/blog/tags/Ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>IE 11 因為 KB2729094 不能安裝的問題</title>
    <link href="https://sujunmin.github.io/blog/2017/07/27/IE%2011%20%E5%9B%A0%E7%82%BA%20KB2729094%20%E4%B8%8D%E8%83%BD%E5%AE%89%E8%A3%9D%E7%9A%84%E5%95%8F%E9%A1%8C/"/>
    <id>https://sujunmin.github.io/blog/2017/07/27/IE%2011%20%E5%9B%A0%E7%82%BA%20KB2729094%20%E4%B8%8D%E8%83%BD%E5%AE%89%E8%A3%9D%E7%9A%84%E5%95%8F%E9%A1%8C/</id>
    <published>2017-07-27T10:31:45.000Z</published>
    <updated>2026-09-26T03:17:55.350Z</updated>
    
    <content type="html"><![CDATA[<p>最近碰到一台電腦的更新卡在 IE 11 無法更新，查了很多資料看 Log 都是安裝程式判定 KB2729094 沒有安裝的問題，但是明明就有安裝的狀態下該怎麼解決。</p><p>找了很多資料，只有<a href="https://www.symantec.com/connect/articles/deploy-internet-explorer-11-prerequisite-patches-swd-or-task">這篇</a>寫的我可以拿來解決，分享給大家。 </p><p>步驟大概如下：</p><ol><li><p>從<a href="http://www.microsoft.com/en-us/download/internet-explorer-11-for-windows-7-details.aspx">這裡</a>下載 IE11 獨立安裝包</p></li><li><p><code>IE11-Windows6.1-x86-en-us.exe /x:C:\temp\IE10_x86</code></p><p><code>IE11-Windows6.1-x64-en-us.exe /x:C:\temp\IE10_x64</code></p></li><li><p>他說會有如他說的資料夾架構，但我解開只有 <code>IE11_x64</code> 或 <code>IE11_x86</code> 裡頭的內容，所以編一個跟他一樣的架構</p></li><li><p>把 <a href="https://www.microsoft.com/en-us/download/details.aspx?id=30521">KB2729094</a> 下載下來放在 <code>PreReq</code> 裡(只差這一個)</p></li><li><p>下載 <a href="https://www.symantec.com/connect/sites/default/files/IE11Installer-Win7-script.zip">IE11INSTALLER-WIN7-SCRIPT.ZIP</a>，放在該放的地方</p></li><li><p><code>cscript.exe //nologo IE11Installer-Win7.vbs</code></p></li><li><p>等完成收工</p></li></ol>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;最近碰到一台電腦的更新卡在 IE 11 無法更新，查了很多資料看 Log 都是安裝程式判定 KB2729094 沒有安裝的問題，但是明明就有安裝的狀態下該怎麼解決。&lt;/p&gt;
&lt;p&gt;找了很多資料，只有&lt;a</summary>
        
      
    
    
    
    
    <category term="MIS" scheme="https://sujunmin.github.io/blog/tags/MIS/"/>
    
  </entry>
  
  <entry>
    <title>在 Pinebook 上安裝 Visual Studio Code</title>
    <link href="https://sujunmin.github.io/blog/2017/07/02/%E5%9C%A8%20Pinebook%20%E4%B8%8A%E5%AE%89%E8%A3%9D%20Visual%20Studio%20Code/"/>
    <id>https://sujunmin.github.io/blog/2017/07/02/%E5%9C%A8%20Pinebook%20%E4%B8%8A%E5%AE%89%E8%A3%9D%20Visual%20Studio%20Code/</id>
    <published>2017-07-02T15:53:43.000Z</published>
    <updated>2026-09-26T03:17:55.353Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://code.visualstudio.com/">Visual Studio Code</a>是一個很好用的工具，剛好看到<a href="https://forum.pine64.org/showthread.php?tid=4491">論壇</a>有人如何安裝，但是實際上卻碰釘子了，因此做這篇文章來紀錄如何解決。 </p><h1 id="原來的方式"><a href="#原來的方式" class="headerlink" title="原來的方式"></a>原來的方式</h1><p>文章的意思就是如下</p><ol><li>設定 MultiArch</li><li>到網站下最新的安裝包</li><li>安裝它</li></ol><p>在 3 的地方發生了一個很嚴重的問題</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">&gt; sudo dpkg -i code-oss_1.14.0-1497990172_armhf.deb </span><br><span class="line">[sudo] password for sujunmin: </span><br><span class="line">選取了原先未選的套件 code-oss:armhf。</span><br><span class="line">（讀取資料庫 ... 目前共安裝了 193100 個檔案和目錄。）</span><br><span class="line">準備解開 code-oss_1.14.0-1497990172_armhf.deb ...</span><br><span class="line">解開 code-oss:armhf (1.14.0-1497990172) 中...</span><br><span class="line">dpkg: 因相依問題，無法設定 code-oss:armhf：</span><br><span class="line"> code-oss:armhf 相依於 apt.</span><br><span class="line"></span><br><span class="line">dpkg: error processing package code-oss:armhf (--install):</span><br><span class="line"> 相依問題 - 保留未設定</span><br><span class="line">Processing triggers for desktop-file-utils (0.22-1ubuntu5.1) ...</span><br><span class="line">Processing triggers for bamfdaemon (0.5.3~bzr0+16.04.20160824-0ubuntu1) ...</span><br><span class="line">Rebuilding /usr/share/applications/bamf-2.index...</span><br><span class="line">Processing triggers for mime-support (3.59ubuntu1) ...</span><br><span class="line">處理時發生錯誤：</span><br><span class="line"> code-oss:armhf</span><br></pre></td></tr></table></figure><p>然後要使用 <code>sudo apt install -f</code> 來處理的時候</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">&gt; sudo apt install -f</span><br><span class="line">正在讀取套件清單... 完成</span><br><span class="line">正在重建相依關係          </span><br><span class="line">正在讀取狀態資料... 完成</span><br><span class="line">正在修正相依關係... 完成</span><br><span class="line">下列的額外套件將被安裝：</span><br><span class="line">  apt:armhf</span><br><span class="line">建議套件：</span><br><span class="line">  aptitude:armhf | synaptic:armhf | wajig:armhf apt-doc:armhf python-apt:armhf</span><br><span class="line">下列套件將會被【移除】：</span><br><span class="line">  apt apt-utils</span><br><span class="line">下列【新】套件將會被安裝：</span><br><span class="line">  apt:armhf</span><br><span class="line">【警告】：下列的基本套件都將被移除。</span><br><span class="line">除非您很清楚您在做什麼，否則請勿輕易嘗試！</span><br><span class="line">  apt</span><br><span class="line">升級 0 個，新安裝 1 個，移除 2 個，有 0 個未被升級。</span><br><span class="line">1 個沒有完整得安裝或移除。</span><br><span class="line">需要下載 1,014 kB 的套件檔。</span><br><span class="line">此操作完成之後，會空出 1,042 kB 的磁碟空間。</span><br><span class="line">您所進行的操作可能會帶來危險。</span><br><span class="line">請輸入 &#x27;Yes, do as I say!&#x27; 這個句子以繼續進行</span><br><span class="line"> ?] </span><br></pre></td></tr></table></figure><p>天阿，竟然會移除掉 <code>apt</code>，那時想這只是不同的 arch 應該還是可以用，結果就繼續下去，變成更多的 error</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">&gt; sudo apt upgrade</span><br><span class="line">正在讀取套件清單... 完成</span><br><span class="line">正在重建相依關係          </span><br><span class="line">正在讀取狀態資料... 完成</span><br><span class="line">您也許得執行 &#x27;apt-get -f install&#x27; 以修正這些問題。</span><br><span class="line">下列的套件有未滿足的相依關係：</span><br><span class="line"> account-plugin-facebook : 相依關係: libaccount-plugin-generic-oauth 但它卻尚未安裝或</span><br><span class="line">                                         ubuntu-system-settings-online-accounts 但它卻尚未安裝</span><br><span class="line"> account-plugin-flickr : 相依關係: libaccount-plugin-generic-oauth 但它卻尚未安裝或</span><br><span class="line">                                       ubuntu-system-settings-online-accounts 但它卻尚未安裝</span><br><span class="line"> account-plugin-google : 相依關係: libaccount-plugin-google 但它卻尚未安裝或</span><br><span class="line">                                       ubuntu-system-settings-online-accounts 但它卻尚未安裝</span><br><span class="line"> adduser : 相依關係: perl-base (&gt;= 5.6.0) 但它卻尚未安裝</span><br><span class="line">           相依關係: passwd (&gt;= 1:4.1.5.1-1.1ubuntu6) 但它卻尚未安裝</span><br><span class="line"> adwaita-icon-theme : 相依關係: libgtk-3-bin 但它卻尚未安裝</span><br><span class="line">                      相依關係: librsvg2-common 但它卻尚未安裝</span><br><span class="line"> alsa-base : 相依關係: kmod (&gt;= 17-1) 但它卻尚未安裝</span><br><span class="line">             相依關係: udev 但它卻尚未安裝</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>幾乎整個系統都要轉成 <code>armhf</code> 架構了，真糟糕。</p><h1 id="解決的方法"><a href="#解決的方法" class="headerlink" title="解決的方法"></a>解決的方法</h1><p>中間有試過很多方法(重編，安裝 arm64 版本…)都失敗。最後還是想到這樣的方式解決。</p><p>首先要把 <code>apt:arm64</code> 裝回來，然後確定 apt database 沒問題以後，解開下載的安裝包，手動放在該放的目錄底下。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&gt; dpkg -x code-oss_1.14.0-1497990172_armhf.deb code</span><br><span class="line">&gt; cd code</span><br><span class="line">&gt; sudo cp -R usr/* /usr/</span><br><span class="line">&gt; sudo ln -s /usr/share/code-oss/code-oss /usr/bin/code-oss</span><br><span class="line">&gt; sudo wget https://raw.githubusercontent.com/stevedesmond-ca/vscode-arm/master/code.png -O /usr/share/code-oss/resources/app/resources/linux/code.png </span><br></pre></td></tr></table></figure><p>最後一個因為我還是比較喜歡 Windows 上面的 Icon，不一定要改。</p><p>接著把它加入到 WM 的功能表裡就完成了。</p><p>你可以加入他們家的 repository，不是要真的更新，而是有更新了可以知道，然後用這樣的方式更新 XD</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&gt; curl -s https://packagecloud.io/install/repositories/headmelted/codebuilds/script.deb.sh | sudo bash</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt;是一個很好用的工具，剛好看到&lt;a</summary>
        
      
    
    
    
    
    <category term="Ubuntu" scheme="https://sujunmin.github.io/blog/tags/Ubuntu/"/>
    
    <category term="Pinebook" scheme="https://sujunmin.github.io/blog/tags/Pinebook/"/>
    
  </entry>
  
  <entry>
    <title>Pinebook 開源筆電開箱</title>
    <link href="https://sujunmin.github.io/blog/2017/06/29/Pinebook%20%E9%96%8B%E6%BA%90%E7%AD%86%E9%9B%BB%E9%96%8B%E7%AE%B1/"/>
    <id>https://sujunmin.github.io/blog/2017/06/29/Pinebook%20%E9%96%8B%E6%BA%90%E7%AD%86%E9%9B%BB%E9%96%8B%E7%AE%B1/</id>
    <published>2017-06-29T02:20:18.000Z</published>
    <updated>2026-09-26T03:17:55.350Z</updated>
    
    <content type="html"><![CDATA[<p>之前看到<a href="https://www.pine64.org/?page_id=3707">介紹</a>覺得這個東西不賴，就想買來試試看，調整了一段時間，來作個開箱文吧。</p><h1 id="客服與社群"><a href="#客服與社群" class="headerlink" title="客服與社群"></a>客服與社群</h1><p>在等待 Pinebook 來的時候(其實等滿久的)，中途有發生想要加訂 64Gb eMMC 的問題，其中退刷跟重訂花了不少時間與客服溝通，但是重要的是，我的排隊順序不因為重訂而改變，這真是太貼心了，客服溝通的時候也十分不厭其煩的說明(連如何拆機都說得很清楚)，真的很棒。</p><p>社群也十分活躍，也許是我剛用 ARM-based 的產品關係吧，有很多思維都是跟之前用 x86 的機器一樣，社群的開發者與使用者不厭其煩的說明，讓我可以好好調校我的 Pinebook。</p><h1 id="外觀"><a href="#外觀" class="headerlink" title="外觀"></a>外觀</h1><p>白色的塑膠殼，很容易髒，他有送貼紙，但我都貼不好，所以貼點別的。<br><img src=https://sujunmin.github.io/test/pinebook_01.jpg /></p><p>背面，很簡單的兩個喇叭。<br><img src=https://sujunmin.github.io/test/pinebook_02.jpg /></p><p>一邊是電源孔，USB Port 與 HDMI 輸出。<br><img src=https://sujunmin.github.io/test/pinebook_03.jpg /></p><p>另一邊是 SD 卡 (64GB Class 10 U1 卡)，耳機孔跟另一個 USB Port，可以看到 USB Port 有一點傷痕，因為實在是太緊了，有點澀的感覺。<br><img src=https://sujunmin.github.io/test/pinebook_04.jpg /></p><p>鍵盤與觸控板，鍵盤也有點澀，有時候會打不太出字，觸控板很靈敏，有個有趣的小插曲。因為我有升級 64GB eMMC，所以有拆機。然後安裝完以後不管是 Linux 或是 Android 觸控板都沒法用，還在想是不是 driver 的關係，上網問了一下，被問到是不是有拆機，我說沒有，只有換 eMMC，就被發現有拆過了，果然就是因為拆機的關係卡榫鬆掉了，弄好就可以了。<br><img src=https://sujunmin.github.io/test/pinebook_05.jpg /></p><p>簡單的三個燈。<br><img src=https://sujunmin.github.io/test/pinebook_06.jpg /></p><p>兩個麥克風的洞。<br><img src=https://sujunmin.github.io/test/pinebook_07.jpg /></p><p>最後選擇 Ubuntu Mate 來作為作業系統，安裝在 eMMC 裡。<br><img src=https://sujunmin.github.io/test/pinebook_08.jpg /></p><p>拆機的照片，因為用膠帶粘著，小心撕開的時候動到了手寫版的排線，所以不能用，插好就可以用了。<br><img src=https://sujunmin.github.io/test/pinebook_09.jpg /></p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;之前看到&lt;a href=&quot;https://www.pine64.org/?page_id=3707&quot;&gt;介紹&lt;/a&gt;覺得這個東西不賴，就想買來試試看，調整了一段時間，來作個開箱文吧。&lt;/p&gt;
&lt;h1 id=&quot;客服與社群&quot;&gt;&lt;a href=&quot;#客服與社群&quot;</summary>
        
      
    
    
    
    
    <category term="Boutique" scheme="https://sujunmin.github.io/blog/tags/Boutique/"/>
    
    <category term="Ubuntu" scheme="https://sujunmin.github.io/blog/tags/Ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>MariaDB Replication 效能提昇紀錄</title>
    <link href="https://sujunmin.github.io/blog/2017/06/12/MariaDB%20Replication%20%E6%95%88%E8%83%BD%E6%8F%90%E6%98%87%E7%B4%80%E9%8C%84/"/>
    <id>https://sujunmin.github.io/blog/2017/06/12/MariaDB%20Replication%20%E6%95%88%E8%83%BD%E6%8F%90%E6%98%87%E7%B4%80%E9%8C%84/</id>
    <published>2017-06-12T02:50:01.000Z</published>
    <updated>2026-09-26T03:17:55.350Z</updated>
    
    <content type="html"><![CDATA[<p>最近管理 MariaDB 因為有更多的資料庫移轉進駐，Replication Site 開始力不從心，甚至跟 Log 到超過 Expire Day 導致 Master 沒有 Log 的問題，因此是時候來好好解決這個問題了，畢竟 Replication Slave 如果真的差那麼多天其實真的就沒有實際上的意義了，發生問題要使用備援區資料差那麼多不能立刻使用。</p><h2 id="觀察-Replication-的作業情形"><a href="#觀察-Replication-的作業情形" class="headerlink" title="觀察 Replication 的作業情形"></a>觀察 Replication 的作業情形</h2><p>Master Site 是由數個獨立的資料庫組合而成，Slave Site 只讀不寫 (有稽核資料庫用途，但與原來的資料沒有關係)。</p><p>Master Binlog 寫入依照 OS 的處理處理之 (<code>innodb_flush_log_at_trx_commit=1</code>)，每個 Binlog 大小都是預設的 1G，約 2.5 - 3 天換檔一次。</p><p>預設的 Replication 只有一個 Thread 在跑，所以通常都是 Find Raw for Update&#x2F;Delete Events。 </p><p><code>show slave status</code> 看就只看到 Relay Log 與 Master 越差越多(從檔名看出來)。</p><h2 id="Parallel-Repliation"><a href="#Parallel-Repliation" class="headerlink" title="Parallel Repliation"></a>Parallel Repliation</h2><p>從 MariaDB 的官方 blog 看到這篇 <a href="https://mariadb.com/resources/blog/goodbye-replication-lag">Goodbye Replication Lag!</a>，原來 Replication 也有平行處理的，但是設定不是像那篇文章寫的那麼簡單。</p><h3 id="slave-parallel-threads-of-cores"><a href="#slave-parallel-threads-of-cores" class="headerlink" title="slave_parallel_threads = # of cores"></a><code>slave_parallel_threads = # of cores</code></h3><p>這個從<a href="https://mariadb.com/kb/en/mariadb/replication-and-binary-log-server-system-variables/#slave_parallel_threads">官方文件</a>可以了解，依樣畫葫蘆設定完以後得到以下的結果。</p><blockquote><p>都在 <code>Waiting for room in worker thread event queue.</code></p></blockquote><p>這邊先來說明一下這些 workers 是在幹什麼用的。</p><p>執行 Replication 時，會有兩個 Thread，一個是 <code>SQL_THREAD</code>，另一個是 <code>IO_THREAD</code>，顧名思義，<code>SQL_THREAD</code> 就是處理 SQL 作業的 Thread，<code>IO_THREAD</code> 就是管理 Binlog 從 Master 拿來然後放在自己的 Relay Log 的 Thread。</p><p>在沒有 Parallel Replication 時，<code>SQL_THREAD</code> 就要自己分配給自己，也因為沒法先看一下 Binlog 後面的 Transation，也許後面可以先做的，但是還是一個一個做，自然很慢。</p><p>當有 Parallel Replication 時，原來的 <code>SQL_THREAD</code> 除了自己變成分配工作的 Thread 以外，會產生出其他 <code>slave_parallel_threads</code> 數量的 Thread 來處理 Transations，在分配工作石就能知道哪些作業沒有順序關係，哪些一定有順序，這樣就會快多了。</p><p>但是怎麼都會是 <code>Waiting for room in worker thread event queue.</code> 呢? 根本沒有平行處理阿?</p><h3 id="slave-parallel-max-queued"><a href="#slave-parallel-max-queued" class="headerlink" title="slave-parallel-max-queued"></a><code>slave-parallel-max-queued</code></h3><p>文件看一半就不會注意到這個。如果你的 Transation 都在每一個 <code>slave-parallel-max-queued</code> 大小的 Transation Logs 裡都被看透，這樣就有助於平行處理。反之如果都沒法看透那就只能等讀到的先做完，有空間了再讀後面的 Log 並且繼續執行，這樣也許有機會平行處理的都被打亂了。</p><p>所以適當的設定 <code>slave-parallel-max-queued</code> 是必須的，但是它跟前一個參數 <code>slave_parallel_threads</code> 與整體的記憶體使用是有關係的 (<code>slave_parallel_threads</code> * <code>slave_parallel_max_queued</code> 結果太大會導致記憶體用盡)。</p><p>在測試的過程中，有要求過把備援機的記憶體調高 (到 10G)，8 個 Parallel Threads，每個 Thread 512MB，加上原來 Innodb 的設定與作業系統的記憶體使用，在開始 Replication 時 Out of Memory 好幾次，這邊是需要微調的。</p><h3 id="結果"><a href="#結果" class="headerlink" title="結果"></a>結果</h3><p>應該期望的是像這樣</p><img src="https://github.com/sujunmin/sujunmin.github.com/blob/master/test/DBYksplUAAAtgek.jpg-large?raw=true" /><p>最重要的兩個參數，MariaDB <a href="https://mariadb.com/kb/en/mariadb/parallel-replication/">文件</a>裡也寫得很清楚，節錄出來給大家參考。</p><blockquote><p>If this value is set <strong>too high</strong>, and the slave is <strong>far</strong> (eg. gigabytes of binlog) behind the master, then the SQL threads can quickly read all of that and fill up memory with huge amounts of binlog events <strong>faster</strong> than the worker threads can consume them.</p></blockquote><blockquote><p>On the other hand, if set <strong>too low</strong>, the SQL thread might <strong>not</strong> have sufficient space for queuing enough events to keep the worker threads busy, which could <strong>reduce performance</strong>.</p></blockquote><h2 id="監控-Replication-的相關參數與-Seconds-Behind-Master"><a href="#監控-Replication-的相關參數與-Seconds-Behind-Master" class="headerlink" title="監控 Replication 的相關參數與 Seconds_Behind_Master"></a>監控 Replication 的相關參數與 <code>Seconds_Behind_Master</code></h2><p><code>show slave status</code> 應該是大家一定會用到的，不外乎 <code>Master_Log_File</code>，<code>Relay_Log_File</code>，<code>Last_IO[SQL]_Error[Errno]</code>，<code>Gtid_IO_Pos</code> 這些，其中有一個參數 <code>Seconds_Behind_Master</code> 原來我是把它當作是大海裡的一塊浮木，想說已經有這麼方便的參數可以拿來看了，應該是很方便監控了，經過幾天看的經驗其實不然。首先來看他的<a href="https://mariadb.com/kb/en/mariadb/show-slave-status/">定義</a>。</p><blockquote><p>Difference between the timestamp logged on the master for the event that the slave is currently processing, and the current timestamp on the slave. Zero if the slave is not currently processing an event. From MariaDB 10.0.23 and MariaDB 10.1.9, with parallel replication, seconds_behind_master is updated only after transactions commit.</p></blockquote><p>有<a href="https://dba.stackexchange.com/questions/21443/when-is-seconds-behind-master-too-big">一篇</a>寫的很好。</p><blockquote><p><code>Seconds_Behind_Master is really a double-edged sword.</code></p></blockquote><p>因為它只是當下 Slave 讀到的 Relay Log 與正在從 Master 拿到的 Binlog 的時間差，假設我的 Binlog 做的不是現在 Master 那個 (Master 在 123，Slave 在 100)，那些多的差異不會被計算入。</p><p>不過最後的每日資料庫狀態報告還是把它拿進來參考，不能當主要因素，但是能參考一下。</p><h2 id="Update-Delete-rows-log-evet-find-row-到底在存取哪個資料庫"><a href="#Update-Delete-rows-log-evet-find-row-到底在存取哪個資料庫" class="headerlink" title="Update[Delete]_rows_log_evet::find_row() 到底在存取哪個資料庫"></a><code>Update[Delete]_rows_log_evet::find_row()</code> 到底在存取哪個資料庫</h2><p>如果你的 Slave 也像我這麼簡單，系統的資源監視器蠻好用的，補齊 <code>SHOW PROCESSLIST</code> 這時候都沒有資料的問題。</p><img src="https://github.com/sujunmin/sujunmin.github.com/raw/master/test/2017061202.png" /><p>不過我的觀察，大部分都是卡在這個作業上很久，有時候還會卡住其他的 Transaction 無法平行處理。</p><p>但是實際上 Master 更新資料也沒那麼久阿?</p><p>看一下 Binlog Format 是 <code>MIXED</code>，會跑出這個應該是變成 <a href="https://mariadb.com/kb/en/mariadb/binary-log-formats/">Row-based</a> 來儲存了。</p><blockquote><p>The default format from MariaDB 10.2.4. A combination of statement and row-based logging. In mixed logging, statement-based logging is used by default, but when MariaDB determines a statement <strong>may not be safe</strong> for statement-based replication, it will use the <strong>row-based</strong> format instead.</p></blockquote><p>Row-based 好處就是更能完整保存交易先後秩序，但是真的<a href="https://mariadb.com/kb/en/mariadb/row-based-replication-with-no-primary-key/">很慢</a>阿，這個需要資料分析找出可以做 unique 或是 primary key 的資料欄位才能加速。</p><blockquote><p>Beginning in MariaDB 5.3, the slave will try to choose a good index among any available:</p></blockquote><blockquote><ol><li>The primary key is used, if there is one.</li><li>Else, the first unique index without NULL-able columns is used, if there is one.</li><li>Else, a choice is made among any normal indexes on the table (e.g. a FULLTEXT index is not considered).</li></ol></blockquote><p><a href="https://www.percona.com/blog/2014/05/02/how-to-identify-and-cure-mysql-replication-slave-lag/">這裡</a>有 script 可以找出哪些連一個 Unique 或是 Primary Key 都沒有的。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;最近管理 MariaDB 因為有更多的資料庫移轉進駐，Replication Site 開始力不從心，甚至跟 Log 到超過 Expire Day 導致 Master 沒有 Log 的問題，因此是時候來好好解決這個問題了，畢竟 Replication Slave</summary>
        
      
    
    
    
    
    <category term="MariaDB" scheme="https://sujunmin.github.io/blog/tags/MariaDB/"/>
    
  </entry>
  
  <entry>
    <title>SQL Server Management Studio (SSMS) 碰到 Value cannot be null (值不能為 Null) 的解決方式</title>
    <link href="https://sujunmin.github.io/blog/2017/05/18/SQL%20Server%20Management%20Studio%20(SSMS)%20%E7%A2%B0%E5%88%B0%20Value%20cannot%20be%20null%20(%E5%80%BC%E4%B8%8D%E8%83%BD%E7%82%BA%20Null)%20%E7%9A%84%E8%A7%A3%E6%B1%BA%E6%96%B9%E5%BC%8F/"/>
    <id>https://sujunmin.github.io/blog/2017/05/18/SQL%20Server%20Management%20Studio%20(SSMS)%20%E7%A2%B0%E5%88%B0%20Value%20cannot%20be%20null%20(%E5%80%BC%E4%B8%8D%E8%83%BD%E7%82%BA%20Null)%20%E7%9A%84%E8%A7%A3%E6%B1%BA%E6%96%B9%E5%BC%8F/</id>
    <published>2017-05-18T06:05:32.000Z</published>
    <updated>2026-09-26T03:17:55.350Z</updated>
    
    <content type="html"><![CDATA[<p>今天廠商說到他們 SSMS 登入資料庫會有 “值不能為 Null” 的問題，會造成資料表打不開。</p><p>找了一下方法，有人說是 <a href="http://stackoverflow.com/questions/15249398/value-cannot-be-null-parameter-name-viewinfo">Temp Path</a> 的問題，不過後來是按照<a href="http://sqlanddotnetdevelopment.blogspot.tw/2014/01/error-showing-while-opening-ssms-2012.html">這裡面</a>的方法砍掉設定檔重開就好了，紀錄一下。</p><p>真的是很奇怪的問題呀。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;今天廠商說到他們 SSMS 登入資料庫會有 “值不能為 Null” 的問題，會造成資料表打不開。&lt;/p&gt;
&lt;p&gt;找了一下方法，有人說是 &lt;a</summary>
        
      
    
    
    
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
  <entry>
    <title>讓不支援 Web API 的程式支援並使用 LINE Notify 服務</title>
    <link href="https://sujunmin.github.io/blog/2017/03/03/%E8%AE%93%E4%B8%8D%E6%94%AF%E6%8F%B4%20Web%20API%20%E7%9A%84%E7%A8%8B%E5%BC%8F%E6%94%AF%E6%8F%B4%E4%B8%A6%E4%BD%BF%E7%94%A8%20LINE%20Notify%20%E6%9C%8D%E5%8B%99/"/>
    <id>https://sujunmin.github.io/blog/2017/03/03/%E8%AE%93%E4%B8%8D%E6%94%AF%E6%8F%B4%20Web%20API%20%E7%9A%84%E7%A8%8B%E5%BC%8F%E6%94%AF%E6%8F%B4%E4%B8%A6%E4%BD%BF%E7%94%A8%20LINE%20Notify%20%E6%9C%8D%E5%8B%99/</id>
    <published>2017-03-03T05:31:42.000Z</published>
    <updated>2026-09-26T03:17:55.356Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>在開發某個應用的時候，使用 <a href="https://notify-bot.line.me/zh_TW/">LINE Notify</a> 作為通知功能，卻發現到該架構沒有使用 Web API 的函式 (<code>Webrequest</code>, <code>HttpWebRequest</code>, <code>RestGet</code>, …)，透過執行外部程式的方式來達成目的。</p><h2 id="LINE-Notify"><a href="#LINE-Notify" class="headerlink" title="LINE Notify"></a><a href="https://notify-bot.line.me/zh_TW/">LINE Notify</a></h2><p>這個服務蠻方便的，如果要作簡易的通知機器人又不想要作複雜的機器人開發，只要註冊一下就可以使用了，<a href="https://www.google.com.tw/#q=Line+Notify+%E4%BD%BF%E7%94%A8&*">Google</a> 一下有很多人都有使用經驗，是個不錯的服務。</p><h2 id="讓不支援-Web-API-的程式支援"><a href="#讓不支援-Web-API-的程式支援" class="headerlink" title="讓不支援 Web API 的程式支援"></a>讓不支援 Web API 的程式支援</h2><p>如果你的架構能支援使用外部程式，或是可以使用其他類 (如 C++ export function) 函式庫的話，以下的說明可以讓該架構可以使用 Web API。</p><p>我的架構中是沒有支援使用外部程式，但可以用 C++ 的外部函式庫，在 Windows 上面。</p><p>首先，我使用 Windows 都有的 <code>shell32.dll</code>。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#import <span class="string">&quot;shell32.dll&quot;</span> </span></span><br><span class="line">        <span class="type">int</span> <span class="title function_">ShellExecuteW</span><span class="params">( <span class="type">int</span>       hWnd,</span></span><br><span class="line"><span class="params">                           <span class="built_in">string</span>    lpVerb,</span></span><br><span class="line"><span class="params">                           <span class="built_in">string</span>    lpFile,</span></span><br><span class="line"><span class="params">                           <span class="built_in">string</span>    lpParameters,</span></span><br><span class="line"><span class="params">                           <span class="built_in">string</span>    lpDirectory,</span></span><br><span class="line"><span class="params">                           <span class="type">int</span>       nCmdShow</span></span><br><span class="line"><span class="params">                           )</span>;</span><br><span class="line"><span class="meta">#import                 </span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>其中 <a href="https://goo.gl/mhpXpg"><code>ShellExecuteW</code></a> 是拿來執行外部程式的函數。</p><p>接下來使用 <a href="https://curl.haxx.se/download.html">curl</a> 來作為發動 Http Request 的外部程式，這個 curl 支援多種平台，真的很方便。</p><p>因為環境在 Windows 上，所以下載 Windows 版的 (記得裡頭的檔案都要在同個目錄下才能運作)。</p><p>最後在要使用 Web API 的地方加</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">      ...</span><br><span class="line">      <span class="built_in">string</span> cmd = <span class="string">&quot; -X POST -H \&quot;Authorization: Bearer &quot;</span> + LineNotifyAccessToken + <span class="string">&quot;\&quot; -F \&quot;message=&quot;</span> + msg + <span class="string">&quot;\&quot; -F \&quot;imageFile=&quot;</span>+filename+<span class="string">&quot;\&quot; https://notify-api.line.me/api/notify&quot;</span>; </span><br><span class="line">...</span><br><span class="line">ShellExecuteW (<span class="number">0</span>, <span class="string">&quot;Open&quot;</span>, <span class="string">&quot;curl.exe&quot;</span>, cmd, workpath, <span class="number">0</span>);</span><br><span class="line">      ...</span><br></pre></td></tr></table></figure><p>其中 <code>imageFile</code> 的格式是 jpeg&#x2F;png ，自己電腦裡的路徑 <code>@C:/test/abc.png</code>，所以就是 <code>imageFile=@C:/test/abc.png</code>。</p><p>另外這個服務是每個小時有一個<a href="https://notify-bot.line.me/doc/en/">上限</a>，API Rate Limit，可以參考一下。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">F:\Test&gt;curl -X GET -H &quot;Authorization: Bearer &lt;token&gt;&quot; https://notify-api.line.me/api/status -I</span><br><span class="line">HTTP/1.1 200 OK</span><br><span class="line">Server: nginx</span><br><span class="line">Date: Fri, 03 Mar 2017 02:45:18 GMT</span><br><span class="line">Content-Type: application/json;charset=UTF-8</span><br><span class="line">Transfer-Encoding: chunked</span><br><span class="line">Connection: keep-alive</span><br><span class="line">Keep-Alive: timeout=3</span><br><span class="line">X-RateLimit-Limit: 1000</span><br><span class="line">X-RateLimit-ImageLimit: 50</span><br><span class="line">X-RateLimit-Remaining: 946</span><br><span class="line">X-RateLimit-ImageRemaining: 50</span><br><span class="line">X-RateLimit-Reset: 1488511643</span><br></pre></td></tr></table></figure><p>這樣一來就讓不支援 Web API 的程式支援了。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;在開發某個應用的時候，使用 &lt;a</summary>
        
      
    
    
    
    
    <category term="Networks" scheme="https://sujunmin.github.io/blog/tags/Networks/"/>
    
  </entry>
  
  <entry>
    <title>從 SQL Server 到 MariaDB - [11] Audit Log Parser 的實做</title>
    <link href="https://sujunmin.github.io/blog/2016/12/29/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[11]%20Audit%20Log%20Parser%20%E7%9A%84%E5%AF%A6%E5%81%9A/"/>
    <id>https://sujunmin.github.io/blog/2016/12/29/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[11]%20Audit%20Log%20Parser%20%E7%9A%84%E5%AF%A6%E5%81%9A/</id>
    <published>2016-12-29T08:04:48.000Z</published>
    <updated>2026-09-26T03:17:55.353Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>因為公司的稽核項目很多，所以大部分的專案都會買一些 SIEM 的設備 (Arcsight, 趨勢的平台…)，有的時候很好用，但是有的時候太複雜或是很慢，所以就有自己作 Audit Log Parser 的動機。</p><p>對於 SQL Server 的 Audit Log (Trace File)有個自己作的 <a href="https://github.com/sujunmin/SQLServerTraceFileParser">Parser</a>，對於 MariaDB 應該也要有一樣的方式處理。</p><h2 id="基本架構"><a href="#基本架構" class="headerlink" title="基本架構"></a>基本架構</h2><p>我的環境除了正式機以外，還有備援機 (Slave)，原來是想在備援機上的 relayed binlog 來作處理，結果大失所望。那些從 master relayed 來的 binlog 完全不會有<a href="https://jira.mariadb.org/browse/MDEV-11618">紀錄</a>，這樣就不能從備援機正式機的 Audit Log 了。</p><p>我的 SQL Server 正式機因為怕效能損耗不會在正式機上 Parsing Trace Files，而是把資料透過網芳存到備援機，由備援機來 Parsing，那 MariaDB 可不可以這樣做呢?</p><p>答案是可以的。</p><p>在 <code>server_audit_file_path</code> 設定 <code>////remotehost//dir//audit.log</code> 這樣就能達到遠端存資料了 (記得反斜線的數量)。</p><p>接下就是設計的問題。仍然使用好用的 Powershell 來達成目標。</p><p>架構大概是這樣</p><ul><li>先前處理<ul><li>建立相關資料夾與環境</li><li>匯入 log</li><li>分析 log</li><li>如有重要資料需儲存的需放到資料庫</li><li>如有重要資料需要即時告警的需發送</li></ul></li></ul><p>接下來就來介紹如何分析與處理。</p><h2 id="Audit-Log-的過程與實做"><a href="#Audit-Log-的過程與實做" class="headerlink" title="Audit Log 的過程與實做"></a>Audit Log 的過程與實做</h2><p>首先從<a href="https://mariadb.com/kb/en/mariadb/about-the-mariadb-audit-plugin/">官方文件</a>可以看到結構。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[timestamp],[serverhost],[username],[host],[connectionid],[queryid],[operation],[database],[object],[retcode]</span><br></pre></td></tr></table></figure><p>有這些資料就能分析了(以下可能有更好的解法，這個<a href="https://github.com/sujunmin/MariaDBAuditFileParser/blob/master/TraceFileParser.ps1">解法</a>只是其中一種)。</p><h3 id="以-timestamp-分段"><a href="#以-timestamp-分段" class="headerlink" title="以 [timestamp], 分段"></a>以 <code>[timestamp],</code> 分段</h3><p>因為檔案不是用 <code>\r\n</code> 或 <code>\n</code> 來分隔一行，而是用空白，在<code>[timestamp]</code> 與 <code>[object]</code> 也會有空白，所以需要先分段。透過 <code>[timestamp],</code> 來分段。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$matches</span> = ([<span class="type">regex</span>] <span class="string">&#x27;(\d&#123;4&#125;)(\d&#123;2&#125;)(\d&#123;2&#125;) (\d&#123;2&#125;):(\d&#123;2&#125;):(\d&#123;2&#125;),&#x27;</span>).Matches(<span class="variable">$auditfilecontent</span>)</span><br></pre></td></tr></table></figure><h3 id="每一段以-分欄位"><a href="#每一段以-分欄位" class="headerlink" title="每一段以 , 分欄位"></a>每一段以 <code>,</code> 分欄位</h3><p>接下來就是每一個 <code>,</code> 欄位，到 <code>[object]</code> 前都是這樣分的。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$submatches</span> = ([<span class="type">regex</span>] <span class="string">&#x27;,&#x27;</span>).Matches(<span class="variable">$line</span>)  </span><br><span class="line"><span class="variable">$timestamp</span>    = <span class="variable">$line</span>.SubString(<span class="number">0</span>, <span class="variable">$submatches</span>[<span class="number">0</span>].Index)</span><br><span class="line"><span class="variable">$serverhost</span>   = <span class="variable">$line</span>.SubString(<span class="variable">$submatches</span>[<span class="number">0</span>].Index+<span class="number">1</span>, <span class="variable">$submatches</span>[<span class="number">1</span>].Index - <span class="variable">$submatches</span>[<span class="number">0</span>].Index<span class="literal">-1</span>)</span><br><span class="line"><span class="variable">$username</span>     = <span class="variable">$line</span>.SubString(<span class="variable">$submatches</span>[<span class="number">1</span>].Index+<span class="number">1</span>, <span class="variable">$submatches</span>[<span class="number">2</span>].Index - <span class="variable">$submatches</span>[<span class="number">1</span>].Index<span class="literal">-1</span>)</span><br><span class="line"><span class="variable">$hosts</span>        = <span class="variable">$line</span>.SubString(<span class="variable">$submatches</span>[<span class="number">2</span>].Index+<span class="number">1</span>, <span class="variable">$submatches</span>[<span class="number">3</span>].Index - <span class="variable">$submatches</span>[<span class="number">2</span>].Index<span class="literal">-1</span>)</span><br><span class="line"><span class="variable">$connectionid</span> = <span class="variable">$line</span>.SubString(<span class="variable">$submatches</span>[<span class="number">3</span>].Index+<span class="number">1</span>, <span class="variable">$submatches</span>[<span class="number">4</span>].Index - <span class="variable">$submatches</span>[<span class="number">3</span>].Index<span class="literal">-1</span>)</span><br><span class="line"><span class="variable">$queryid</span>      = <span class="variable">$line</span>.SubString(<span class="variable">$submatches</span>[<span class="number">4</span>].Index+<span class="number">1</span>, <span class="variable">$submatches</span>[<span class="number">5</span>].Index - <span class="variable">$submatches</span>[<span class="number">4</span>].Index<span class="literal">-1</span>)</span><br><span class="line"><span class="variable">$operation</span>    = <span class="variable">$line</span>.SubString(<span class="variable">$submatches</span>[<span class="number">5</span>].Index+<span class="number">1</span>, <span class="variable">$submatches</span>[<span class="number">6</span>].Index - <span class="variable">$submatches</span>[<span class="number">5</span>].Index<span class="literal">-1</span>)</span><br><span class="line"><span class="variable">$database</span>     = <span class="variable">$line</span>.SubString(<span class="variable">$submatches</span>[<span class="number">6</span>].Index+<span class="number">1</span>, <span class="variable">$submatches</span>[<span class="number">7</span>].Index - <span class="variable">$submatches</span>[<span class="number">6</span>].Index<span class="literal">-1</span>)</span><br><span class="line"><span class="variable">$retcode</span>      = <span class="variable">$line</span>.SubString(<span class="variable">$submatches</span>[<span class="variable">$submatches</span><span class="type">.Count</span>-<span class="number">1</span>].Index+<span class="number">1</span>,<span class="variable">$line</span>.length - <span class="variable">$submatches</span>[<span class="variable">$submatches</span><span class="type">.Count</span>-<span class="number">1</span>].Index<span class="literal">-1</span>).Trim()</span><br></pre></td></tr></table></figure><p>到了 <code>[object]</code>，因為有可能裡頭也有 <code>,</code> 所以不能直接拿 <code>split</code> 的值來用，取最後一個 <code>,</code> 之前的當作是 <code>[object]</code>。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="variable">$submatches</span>[<span class="variable">$submatches</span><span class="type">.Count</span>-<span class="number">1</span>].Index - <span class="variable">$submatches</span>[<span class="number">7</span>].Index- <span class="number">3</span> <span class="operator">-lt</span> <span class="number">0</span>) </span><br><span class="line"> &#123;</span><br><span class="line">   <span class="variable">$object</span> = <span class="string">&quot;&quot;</span></span><br><span class="line"> &#125;<span class="keyword">else</span></span><br><span class="line"> &#123;</span><br><span class="line">   <span class="variable">$object</span> = <span class="variable">$line</span>.SubString(<span class="variable">$submatches</span>[<span class="number">7</span>].Index+<span class="number">2</span>, <span class="variable">$submatches</span>[<span class="variable">$submatches</span><span class="type">.Count</span>-<span class="number">1</span>].Index - <span class="variable">$submatches</span>[<span class="number">7</span>].Index<span class="literal">-3</span>)  </span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>有了資料就能作其他用途了。</p><h2 id="處理的方式"><a href="#處理的方式" class="headerlink" title="處理的方式"></a>處理的<a href="https://github.com/sujunmin/MariaDBAuditFileParser/blob/master/TraceFileParser.ps1">方式</a></h2><p>基本上的需求如下</p><ul><li>原始資料保存</li><li>人員使用的帳號(客服或帳務處理)作業需在每日統計報表裡呈現</li><li>資料庫管理者使用的帳號作業需在每日統計報表裡呈現</li><li>DCL 與 DDL 作業時需即時發送通知給主管</li><li>每日使用狀態報表</li></ul><h3 id="重要資料紀錄-DB"><a href="#重要資料紀錄-DB" class="headerlink" title="重要資料紀錄 DB"></a>重要資料紀錄 DB</h3><p>針對可得到的資料要作一個<a href="https://github.com/sujunmin/MariaDBAuditFileParser/blob/master/auditdb.sql">紀錄</a>，用資料庫處理。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> DATABASE IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `auditdb`;</span><br><span class="line">USE `auditdb`;</span><br><span class="line"></span><br><span class="line"><span class="keyword">CREATE TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `auditdata` (</span><br><span class="line">  `<span class="type">timestamp</span>` datetime <span class="keyword">NOT NULL</span>,</span><br><span class="line">  `serverhost` <span class="type">char</span>(<span class="number">100</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">  `username` <span class="type">char</span>(<span class="number">100</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">  `hosts` <span class="type">char</span>(<span class="number">100</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">  `connectionid` <span class="type">char</span>(<span class="number">100</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">  `queryid` <span class="type">char</span>(<span class="number">100</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">  `operation` <span class="type">char</span>(<span class="number">100</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">  `database` <span class="type">char</span>(<span class="number">100</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">  `object` <span class="type">varchar</span>(<span class="number">10000</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">  `retcode` <span class="type">char</span>(<span class="number">10</span>) <span class="keyword">NOT NULL</span></span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8;</span><br></pre></td></tr></table></figure><h3 id="相關人員帳號使用情形紀錄"><a href="#相關人員帳號使用情形紀錄" class="headerlink" title="相關人員帳號使用情形紀錄"></a>相關人員帳號使用情形紀錄</h3><p>看 <code>[username]</code> 欄位即可得到資訊。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="variable">$Users</span> <span class="operator">-contains</span> <span class="variable">$username</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="variable">$sql</span> = <span class="string">&quot;INSERT INTO &quot;</span> + <span class="variable">$AuditDB</span> + <span class="string">&quot;.&quot;</span> + <span class="variable">$TraceFileData</span> + <span class="string">&quot; VALUES (STR_TO_DATE(&#x27;&quot;</span> + <span class="variable">$timestamp</span> + <span class="string">&quot;&#x27;,&#x27;%Y%m%d %H:%i:%S&#x27;), &#x27;&quot;</span> + <span class="variable">$serverhost</span> + <span class="string">&quot;&#x27;, &#x27;&quot;</span> + <span class="variable">$username</span> + <span class="string">&quot;&#x27;, &#x27;&quot;</span> + <span class="variable">$hosts</span> + <span class="string">&quot;&#x27;, &#x27;&quot;</span> + <span class="variable">$connectionid</span> + <span class="string">&quot;&#x27;, &#x27;&quot;</span> + <span class="variable">$queryid</span> + <span class="string">&quot;&#x27;, &#x27;&quot;</span> + <span class="variable">$operation</span> + <span class="string">&quot;&#x27;, &#x27;&quot;</span> + <span class="variable">$database</span> + <span class="string">&quot;&#x27;, &#x27;&quot;</span> + <span class="variable">$object</span> + <span class="string">&quot;&#x27;, &#x27;&quot;</span> + <span class="variable">$retcode</span> +<span class="string">&quot;&#x27;);&quot;</span>    </span><br><span class="line">                    &amp; <span class="variable">$mysqlexe</span> <span class="literal">-u</span> <span class="variable">$AuditDBUserName</span> <span class="literal">--password</span>=<span class="variable">$AuditDBPassword</span> <span class="literal">-h</span> <span class="variable">$AuditDBServer</span>  <span class="literal">-e</span> <span class="string">&quot;<span class="variable">$</span>(<span class="variable">$sql</span>)&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="DCL-與-DDL-作業即時告警"><a href="#DCL-與-DDL-作業即時告警" class="headerlink" title="DCL 與 DDL 作業即時告警"></a>DCL 與 DDL 作業即時告警</h3><p>如果 <code>server_audit.dll</code> 是用<a href="https://sujunmin.github.io/blog/2016/10/03/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[6]%20Audit%20%E7%9A%84%E5%AF%A6%E4%BD%9C/">之前</a>說的方式重新作一個，這邊就很簡單：看 <code>[operation]</code> 是不是 <code>QUERY_DCL</code>，<code>QUERY_DML</code>，<code>QUERY_DDL</code> 就可以了。如果不是用之前的方式，這邊就要分析 <code>[object]</code> 裡頭的內容了。</p><p>把所需的資料放到一個 <code>ArrayList</code> 裡，全部都 Parsing 完以後看看是不是有資料，有的話就發信告警。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="variable">$MailEventClasses</span> <span class="operator">-contains</span> <span class="variable">$operation</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="variable">$DXL</span>.Add((<span class="variable">$timestamp</span>, <span class="variable">$serverhost</span>, <span class="variable">$username</span>, <span class="variable">$hosts</span>, <span class="variable">$connectionid</span>, <span class="variable">$queryid</span>, <span class="variable">$operation</span>, <span class="variable">$database</span>, <span class="variable">$object</span>, <span class="variable">$retcode</span>))</span><br><span class="line">&#125;</span><br><span class="line">...</span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$DXL</span>.Count <span class="operator">-ne</span> <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="variable">$output</span> = <span class="string">&quot;&lt;table border=1 style=border-width: thin; border-spacing: 2px; border-style: solid; border-color: gray; border-collapse: collapse;&gt; </span></span><br><span class="line"><span class="string">                 &lt;tr&gt;&lt;th&gt;StartTime&lt;/th&gt;</span></span><br><span class="line"><span class="string">             &lt;th&gt;LoginName&lt;/th&gt;</span></span><br><span class="line"><span class="string">                 &lt;th&gt;HostName&lt;/th&gt;</span></span><br><span class="line"><span class="string">             &lt;th&gt;ServerName&lt;/th&gt;</span></span><br><span class="line"><span class="string">                 &lt;th&gt;DatabaseName&lt;/th&gt;</span></span><br><span class="line"><span class="string">                 &lt;th&gt;Operation&lt;/th&gt;</span></span><br><span class="line"><span class="string">                     &lt;th&gt;TextData&lt;/th&gt;</span></span><br><span class="line"><span class="string">                     &lt;th&gt;ReturnCode&lt;/th&gt;&lt;/tr&gt;&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="variable">$DXL</span> | <span class="built_in">Foreach-Object</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="variable">$output</span> = <span class="variable">$output</span> + <span class="string">&quot;&lt;tr&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>[<span class="number">0</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> +  <span class="variable">$_</span>[<span class="number">2</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>[<span class="number">3</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>[<span class="number">1</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>[<span class="number">7</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>[<span class="number">6</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>[<span class="number">8</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>[<span class="number">9</span>] + <span class="string">&quot;&lt;/td&gt;&lt;/tr&gt;&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$output</span> = <span class="variable">$output</span> + <span class="string">&quot;&lt;/table&gt;&quot;</span></span><br><span class="line">    <span class="built_in">Send-MailMessage</span> <span class="literal">-To</span> <span class="variable">$MailTo</span> <span class="literal">-From</span> <span class="variable">$MailFrom</span> <span class="literal">-Subject</span> <span class="string">&quot;資料庫特權活動即時告警&quot;</span> <span class="literal">-Body</span> <span class="string">&quot;<span class="variable">$output</span>&quot;</span> <span class="literal">-BodyAsHtml</span> <span class="literal">-SmtpServer</span> <span class="variable">$MailServer</span> <span class="literal">-Encoding</span> ([<span class="type">System.Text.Encoding</span>]::UTF8)        </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="每日匯總報表"><a href="#每日匯總報表" class="headerlink" title="每日匯總報表"></a>每日匯總報表</h2><p>如果之前的作法中，塞資料到資料庫的動作有作的話，這邊就十分簡單。</p><p>先設定一些資料。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$AuditDBServer</span> = <span class="string">&quot;server&quot;</span></span><br><span class="line"><span class="variable">$AuditDB</span> = <span class="string">&quot;AuditDB&quot;</span></span><br><span class="line"><span class="variable">$TraceFileData</span> = <span class="string">&quot;auditdata&quot;</span></span><br><span class="line"><span class="variable">$AuditDBUserName</span> = <span class="string">&quot;username&quot;</span></span><br><span class="line"><span class="variable">$AuditDBPassword</span> = <span class="string">&quot;password&quot;</span></span><br><span class="line"><span class="variable">$mysqlexe</span> = <span class="string">&quot;D:\Program Files\MariaDB 10.1\bin\mysql.exe&quot;</span></span><br><span class="line"><span class="variable">$Users</span> = <span class="string">&quot;&#x27;need&#x27;,&#x27;to&#x27;,&#x27;be&#x27;,&#x27;audit&#x27;,&#x27;users&#x27;&quot;</span></span><br><span class="line"><span class="variable">$MailFrom</span> = <span class="string">&quot;from@abc.com&quot;</span></span><br><span class="line"><span class="variable">$MailEventClasses</span> = <span class="string">&quot;&#x27;QUERY_DDL&#x27;,&#x27;QUERY_DCL&#x27;&quot;</span></span><br><span class="line"><span class="variable">$MailTo</span> = <span class="string">&quot;to@def.com&quot;</span></span><br><span class="line"><span class="variable">$MailServer</span> = <span class="string">&quot;smtp.server&quot;</span></span><br></pre></td></tr></table></figure><p>接下來移除 Audit 資料庫裡頭 3 天前的資料。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$NowTime</span> = <span class="built_in">Get-Date</span></span><br><span class="line"><span class="variable">$TempPath</span> = <span class="string">&quot;D:\AuditFile&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$sql</span> = <span class="string">&quot;delete from &quot;</span> + <span class="variable">$AuditDB</span> + <span class="string">&quot;.&quot;</span> + <span class="variable">$TraceFileData</span> + <span class="string">&quot; where timestamp &lt; &#x27;&quot;</span>+(<span class="string">&quot;&#123;0:yyyy-MM-dd&#125;&quot;</span> <span class="operator">-f</span> <span class="variable">$NowTime</span>.AddDays(<span class="literal">-3</span>))+ <span class="string">&quot;&#x27; or timestamp is Null;&quot;</span></span><br><span class="line">&amp; <span class="variable">$mysqlexe</span> <span class="literal">-u</span> <span class="variable">$AuditDBUserName</span> <span class="literal">--password</span>=<span class="variable">$AuditDBPassword</span> <span class="literal">-h</span> <span class="variable">$AuditDBServer</span>  <span class="literal">-e</span> <span class="string">&quot;<span class="variable">$</span>(<span class="variable">$sql</span>)&quot;</span> </span><br></pre></td></tr></table></figure><p>符合 <code>$MailEventClasses</code> 的列出來，作為每日資料庫系統設定異動報表。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$output</span> = <span class="string">&quot;&lt;table border=1 style=border-width: thin; border-spacing: 2px; border-style: solid; border-color: gray; border-collapse: collapse;&gt;  </span></span><br><span class="line"><span class="string">                 &lt;tr&gt;&lt;th&gt;StartTime&lt;/th&gt;</span></span><br><span class="line"><span class="string">     &lt;th&gt;LoginName&lt;/th&gt;</span></span><br><span class="line"><span class="string">             &lt;th&gt;HostName&lt;/th&gt;</span></span><br><span class="line"><span class="string">     &lt;th&gt;ServerName&lt;/th&gt;</span></span><br><span class="line"><span class="string">             &lt;th&gt;DatabaseName&lt;/th&gt;</span></span><br><span class="line"><span class="string">             &lt;th&gt;Operation&lt;/th&gt;</span></span><br><span class="line"><span class="string">                     &lt;th&gt;TextData&lt;/th&gt;</span></span><br><span class="line"><span class="string">                     &lt;th&gt;ReturnCode&lt;/th&gt;&lt;/tr&gt;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$sql</span>  = <span class="string">&quot;select &#x27;&lt;tr&gt;&lt;td&gt;&#x27;, ``timestamp``, &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``username`` , &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``hosts``, &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``serverhost`` , &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``database`` , &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``operation`` , &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``object``, &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``retcode`` , &#x27;&lt;/td&gt;&lt;/tr&gt;&#x27; from &quot;</span> + <span class="variable">$AuditDB</span> + <span class="string">&quot;.&quot;</span> + <span class="variable">$TraceFileData</span> + <span class="string">&quot; where operation in (&quot;</span> + <span class="variable">$MailEventClasses</span> + <span class="string">&quot;) and timestamp between STR_TO_DATE(&#x27;&quot;</span> +(<span class="string">&quot;&#123;0:yyyy-MM-dd&#125;&quot;</span> <span class="operator">-f</span> <span class="variable">$NowTime</span>.AddDays(<span class="literal">-1</span>))+ <span class="string">&quot;&#x27;, &#x27;%Y-%m-%d&#x27;)  and STR_TO_DATE(&#x27;&quot;</span> +(<span class="string">&quot;&#123;0:yyyy-MM-dd&#125;&quot;</span> <span class="operator">-f</span> <span class="variable">$NowTime</span>)+ <span class="string">&quot;&#x27;, &#x27;%Y-%m-%d&#x27;)&quot;</span></span><br><span class="line"><span class="variable">$output</span> = <span class="variable">$output</span> + (&amp; <span class="variable">$mysqlexe</span> <span class="literal">-u</span> <span class="variable">$AuditDBUserName</span> <span class="literal">--password</span>=<span class="variable">$AuditDBPassword</span> <span class="literal">-h</span> <span class="variable">$AuditDBServer</span> <span class="literal">-sN</span> <span class="literal">-e</span> <span class="string">&quot;<span class="variable">$</span>(<span class="variable">$sql</span>)&quot;</span> ) </span><br><span class="line"><span class="variable">$output</span> = <span class="variable">$output</span> + <span class="string">&quot;&lt;/table&gt;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$Subj</span> = <span class="variable">$</span>(<span class="string">&quot;&#123;0:yyyy-MM-dd&#125;&quot;</span> <span class="operator">-f</span> <span class="variable">$NowTime</span>.AddDays(<span class="literal">-1</span>)) + <span class="string">&quot; 每日資料庫系統設定異動報表&quot;</span></span><br><span class="line"><span class="built_in">Send-MailMessage</span> <span class="literal">-To</span> <span class="variable">$MailTo</span> <span class="literal">-From</span> <span class="variable">$MailFrom</span> <span class="literal">-Subject</span> <span class="string">&quot;<span class="variable">$Subj</span>&quot;</span>  <span class="literal">-Body</span> <span class="string">&quot;<span class="variable">$output</span>&quot;</span> <span class="literal">-BodyAsHtml</span>  <span class="literal">-SmtpServer</span> <span class="variable">$MailServer</span> <span class="literal">-Encoding</span> ([<span class="type">System.Text.Encoding</span>]::UTF8)</span><br></pre></td></tr></table></figure><p>符合 <code>$Users</code> 的列出來，作為每日資料庫使用者使用情形報表。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$output</span> = <span class="string">&quot;&lt;table border=1 style=border-width: thin; border-spacing: 2px; border-style: solid; border-color: gray; border-collapse: collapse;&gt;  </span></span><br><span class="line"><span class="string">                 &lt;tr&gt;&lt;th&gt;StartTime&lt;/th&gt;</span></span><br><span class="line"><span class="string">     &lt;th&gt;LoginName&lt;/th&gt;</span></span><br><span class="line"><span class="string">             &lt;th&gt;HostName&lt;/th&gt;</span></span><br><span class="line"><span class="string">     &lt;th&gt;ServerName&lt;/th&gt;</span></span><br><span class="line"><span class="string">             &lt;th&gt;DatabaseName&lt;/th&gt;</span></span><br><span class="line"><span class="string">             &lt;th&gt;Operation&lt;/th&gt;</span></span><br><span class="line"><span class="string">                     &lt;th&gt;TextData&lt;/th&gt;</span></span><br><span class="line"><span class="string">                     &lt;th&gt;ReturnCode&lt;/th&gt;&lt;/tr&gt;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$sql</span>  = <span class="string">&quot;select &#x27;&lt;tr&gt;&lt;td&gt;&#x27;, ``timestamp``, &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``username`` , &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``hosts``, &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``serverhost`` , &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``database`` , &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``operation`` , &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``object``, &#x27;&lt;/td&gt;&lt;td&gt;&#x27;, ``retcode`` , &#x27;&lt;/td&gt;&lt;/tr&gt;&#x27; from &quot;</span> + <span class="variable">$AuditDB</span> + <span class="string">&quot;.&quot;</span> + <span class="variable">$TraceFileData</span> + <span class="string">&quot; where username in (&quot;</span> + <span class="variable">$Users</span> + <span class="string">&quot;) and timestamp between STR_TO_DATE(&#x27;&quot;</span> +(<span class="string">&quot;&#123;0:yyyy-MM-dd&#125;&quot;</span> <span class="operator">-f</span> <span class="variable">$NowTime</span>.AddDays(<span class="literal">-1</span>))+ <span class="string">&quot;&#x27;, &#x27;%Y-%m-%d&#x27;)  and STR_TO_DATE(&#x27;&quot;</span> +(<span class="string">&quot;&#123;0:yyyy-MM-dd&#125;&quot;</span> <span class="operator">-f</span> <span class="variable">$NowTime</span>)+ <span class="string">&quot;&#x27;, &#x27;%Y-%m-%d&#x27;)&quot;</span></span><br><span class="line"><span class="variable">$sql</span></span><br><span class="line"><span class="variable">$output</span> = <span class="variable">$output</span> + (&amp; <span class="variable">$mysqlexe</span> <span class="literal">-u</span> <span class="variable">$AuditDBUserName</span> <span class="literal">--password</span>=<span class="variable">$AuditDBPassword</span> <span class="literal">-h</span> <span class="variable">$AuditDBServer</span> <span class="literal">-sN</span> <span class="literal">-e</span> <span class="string">&quot;<span class="variable">$</span>(<span class="variable">$sql</span>)&quot;</span> ) </span><br><span class="line"><span class="variable">$output</span> = <span class="variable">$output</span> + <span class="string">&quot;&lt;/table&gt;&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="variable">$Subj</span> = <span class="variable">$</span>(<span class="string">&quot;&#123;0:yyyy-MM-dd&#125;&quot;</span> <span class="operator">-f</span> <span class="variable">$NowTime</span>.AddDays(<span class="literal">-1</span>)) + <span class="string">&quot; 每日資料庫使用者使用情形報表&quot;</span></span><br><span class="line"><span class="built_in">Send-MailMessage</span> <span class="literal">-To</span> <span class="variable">$MailTo</span> <span class="literal">-From</span> <span class="variable">$MailFrom</span> <span class="literal">-Subject</span> <span class="string">&quot;<span class="variable">$Subj</span>&quot;</span>  <span class="literal">-Body</span> <span class="string">&quot;<span class="variable">$output</span>&quot;</span> <span class="literal">-BodyAsHtml</span>  <span class="literal">-SmtpServer</span> <span class="variable">$MailServer</span> <span class="literal">-Encoding</span> ([<span class="type">System.Text.Encoding</span>]::UTF8)</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;因為公司的稽核項目很多，所以大部分的專案都會買一些 SIEM 的設備 (Arcsight,</summary>
        
      
    
    
    
    
    <category term="MariaDB" scheme="https://sujunmin.github.io/blog/tags/MariaDB/"/>
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
  <entry>
    <title>從 SQL Server 到 MariaDB - [10] DBA Daily Check Report 的製作 [2]</title>
    <link href="https://sujunmin.github.io/blog/2016/12/27/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[10]%20DBA%20Daily%20Check%20Report%20%E7%9A%84%E8%A3%BD%E4%BD%9C%20[2]/"/>
    <id>https://sujunmin.github.io/blog/2016/12/27/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[10]%20DBA%20Daily%20Check%20Report%20%E7%9A%84%E8%A3%BD%E4%BD%9C%20[2]/</id>
    <published>2016-12-27T03:45:16.000Z</published>
    <updated>2026-09-26T03:17:55.353Z</updated>
    
    <content type="html"><![CDATA[<h3 id="Event-Status。"><a href="#Event-Status。" class="headerlink" title="Event Status。"></a>Event Status。</h3><p>這個取得資料的方式，除了之前提到的<a href="https://sujunmin.github.io/blog/2016/09/10/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[4]%20SQL%20Agent%20%E8%88%87%20MariaDB%20Event/">擴充 Event</a>  可以得到 Event 結果以外，就是系統的 <code>information_schema.EVENTS</code> 可以得到相關資訊。</p><h4 id="DB-Event-Name-Last-Run-Time"><a href="#DB-Event-Name-Last-Run-Time" class="headerlink" title="DB, Event Name, Last Run Time"></a>DB, Event Name, Last Run Time</h4><p>這些都能從 <code>information_schema.EVENTS</code> 拿 (<code>event_schema</code>, <code>event_name</code>, <code>last_executed</code>)。</p><h4 id="Event-種類"><a href="#Event-種類" class="headerlink" title="Event 種類"></a>Event 種類</h4><p>Event 種類有 <code>ENABLE</code>, <code>DISABLE</code>, <code>SLAVESIDE_DISABLED</code> (Replication 時從 Master 傳來的 Event，不會在 Slave 執行)，根據實際情形作標示。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CASE</span> status </span><br><span class="line">  <span class="keyword">WHEN</span> <span class="string">&#x27;ENABLED&#x27;</span> <span class="keyword">THEN</span> <span class="string">&#x27;&lt;div class=&quot;Healthy&quot;&gt;Yes&lt;/div&gt;&#x27;</span> </span><br><span class="line">  <span class="keyword">WHEN</span> <span class="string">&#x27;SLAVESIDE_DISABLED&#x27;</span> <span class="keyword">THEN</span> <span class="string">&#x27;&lt;div class=&quot;Healthy&quot;&gt;No&lt;/div&gt;&#x27;</span> </span><br><span class="line">  <span class="keyword">ELSE</span> <span class="string">&#x27;&lt;div class=&quot;Warning&quot;&gt;No&lt;/div&gt;&#x27;</span> </span><br><span class="line"><span class="keyword">END</span>, </span><br></pre></td></tr></table></figure><h4 id="Succeed"><a href="#Succeed" class="headerlink" title="Succeed"></a>Succeed</h4><p>根據 <code>master.event</code> 與 <code>information_schema.EVENTS</code> 來 count，沒有 <code>errno</code> 的就是成功的。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CASE</span> (<span class="keyword">select</span> <span class="built_in">count</span>(<span class="operator">*</span>) <span class="keyword">from</span> master.event_history <span class="keyword">where</span> db <span class="operator">=</span> ev.EVENT_SCHEMA <span class="keyword">and</span> name <span class="operator">=</span> ev.EVENT_NAME <span class="keyword">and</span> errno <span class="keyword">is</span> <span class="keyword">null</span> <span class="keyword">and</span> <span class="keyword">start</span> <span class="operator">&gt;=</span> subdate(now(), <span class="variable">@NumDays</span>)) </span><br><span class="line">  <span class="keyword">WHEN</span> <span class="number">0</span> <span class="keyword">THEN</span> CONCAT(<span class="string">&#x27;&lt;div class=&quot;Warning&quot;&gt;&#x27;</span>, (<span class="keyword">select</span> <span class="built_in">count</span>(<span class="operator">*</span>) <span class="keyword">from</span> master.event_history <span class="keyword">where</span> db <span class="operator">=</span> ev.EVENT_SCHEMA <span class="keyword">and</span> name <span class="operator">=</span> ev.EVENT_NAME <span class="keyword">and</span> errno <span class="keyword">is</span> <span class="keyword">null</span> <span class="keyword">and</span> <span class="keyword">start</span> <span class="operator">&gt;=</span> subdate(now(), <span class="variable">@NumDays</span>)), <span class="string">&#x27;&lt;/div&gt;&#x27;</span>) </span><br><span class="line">  <span class="keyword">ELSE</span> (<span class="keyword">select</span> <span class="built_in">count</span>(<span class="operator">*</span>) <span class="keyword">from</span> master.event_history <span class="keyword">where</span> db <span class="operator">=</span> ev.EVENT_SCHEMA <span class="keyword">and</span> name <span class="operator">=</span> ev.EVENT_NAME <span class="keyword">and</span> errno <span class="keyword">is</span> <span class="keyword">null</span> <span class="keyword">and</span> <span class="keyword">start</span> <span class="operator">&gt;=</span> subdate(now(), <span class="variable">@NumDays</span>)) </span><br><span class="line"><span class="keyword">END</span>,</span><br></pre></td></tr></table></figure><h4 id="Failed"><a href="#Failed" class="headerlink" title="Failed"></a>Failed</h4><p>根據 <code>master.event</code> 與 <code>information_schema.EVENTS</code> 來 count，有 <code>errno</code> 的就是失敗的。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CASE</span> (<span class="keyword">select</span> <span class="built_in">count</span>(<span class="operator">*</span>) <span class="keyword">from</span> master.event_history <span class="keyword">where</span> db <span class="operator">=</span> ev.EVENT_SCHEMA <span class="keyword">and</span> name <span class="operator">=</span> ev.EVENT_NAME <span class="keyword">and</span> errno <span class="keyword">is</span> <span class="keyword">not null</span> <span class="keyword">and</span> <span class="keyword">start</span> <span class="operator">&gt;=</span> subdate(now(), <span class="variable">@NumDays</span>)) </span><br><span class="line">  <span class="keyword">WHEN</span> <span class="number">0</span> <span class="keyword">THEN</span> <span class="string">&#x27;&lt;div class=&quot;Healthy&quot;&gt;0&lt;/div&gt;&#x27;</span> </span><br><span class="line">  <span class="keyword">ELSE</span> CONCAT(<span class="string">&#x27;&lt;div class=&quot;Critical&quot;&gt;&#x27;</span>, (<span class="keyword">select</span> <span class="built_in">count</span>(<span class="operator">*</span>) <span class="keyword">from</span> master.event_history <span class="keyword">where</span> db <span class="operator">=</span> ev.EVENT_SCHEMA <span class="keyword">and</span> name <span class="operator">=</span> ev.EVENT_NAME <span class="keyword">and</span> errno <span class="keyword">is</span> <span class="keyword">not null</span> <span class="keyword">and</span> <span class="keyword">start</span> <span class="operator">&gt;=</span> subdate(now(), <span class="variable">@NumDays</span>)),<span class="string">&#x27;&lt;/div&gt;&#x27;</span>) </span><br><span class="line"><span class="keyword">END</span>,</span><br></pre></td></tr></table></figure><h4 id="Next-Run-Time"><a href="#Next-Run-Time" class="headerlink" title="Next Run Time"></a>Next Run Time</h4><p>根據 <code>master.event</code> 可以用算的，但是算的動作不能直接傳入 column 裡頭的值 (<code>select date_add(colA, interval interval_value interval_field)...</code>)，所以寫成醜醜的 <code>CASE</code>。</p><p>這個部份是我覺得最沒有自動化的地方。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">case</span> interval_field</span><br><span class="line">  <span class="keyword">when</span> &quot;YEAR&quot;    <span class="keyword">then</span> date_add(last_executed, <span class="type">interval</span> interval_value <span class="keyword">YEAR</span>)</span><br><span class="line">  <span class="keyword">when</span> &quot;QUARTER&quot;<span class="keyword">then</span> date_add(last_executed, <span class="type">interval</span> interval_value QUARTER)</span><br><span class="line">  <span class="keyword">when</span> &quot;MONTH&quot;    <span class="keyword">then</span> date_add(last_executed, <span class="type">interval</span> interval_value <span class="keyword">MONTH</span>)</span><br><span class="line">  <span class="keyword">when</span> &quot;DAY&quot;    <span class="keyword">then</span> date_add(last_executed, <span class="type">interval</span> interval_value <span class="keyword">DAY</span>)</span><br><span class="line">  <span class="keyword">when</span> &quot;HOUR&quot;    <span class="keyword">then</span> date_add(last_executed, <span class="type">interval</span> interval_value <span class="keyword">HOUR</span>)</span><br><span class="line">  <span class="keyword">when</span> &quot;MINUTE&quot;    <span class="keyword">then</span> date_add(last_executed, <span class="type">interval</span> interval_value <span class="keyword">MINUTE</span>)</span><br><span class="line">  <span class="keyword">when</span> &quot;WEEK&quot;    <span class="keyword">then</span> date_add(last_executed, <span class="type">interval</span> interval_value WEEK)</span><br><span class="line">  <span class="keyword">when</span> &quot;SECOND&quot;    <span class="keyword">then</span> date_add(last_executed, <span class="type">interval</span> interval_value <span class="keyword">SECOND</span>)</span><br><span class="line"><span class="keyword">end</span>,</span><br></pre></td></tr></table></figure><p>(這個 interval 不是全部，我只有寫常用的)</p><h4 id="Last-Result"><a href="#Last-Result" class="headerlink" title="Last Result"></a>Last Result</h4><p>根據 <code>master.event</code> 與 <code>information_schema.EVENTS</code> 來看最後一次結果的內容。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">(<span class="keyword">select</span> <span class="keyword">CASE</span> </span><br><span class="line">          <span class="keyword">WHEN</span> errno <span class="keyword">is</span> <span class="keyword">null</span> <span class="keyword">THEN</span> CONCAT(<span class="string">&#x27;&lt;span class=&quot;Healthy&quot;&gt;&#x27;</span>, message_text, <span class="string">&#x27;&lt;/span&gt;&#x27;</span>) </span><br><span class="line">          <span class="keyword">ELSE</span> CONCAT(<span class="string">&#x27;&lt;span class=&quot;Critical&quot;&gt;&#x27;</span>, message_text, <span class="string">&#x27;&lt;/span&gt;&#x27;</span>) </span><br><span class="line">        <span class="keyword">END</span> </span><br><span class="line"> <span class="keyword">from</span> master.event_history <span class="keyword">where</span> db <span class="operator">=</span> ev.EVENT_SCHEMA <span class="keyword">and</span> name <span class="operator">=</span> ev.EVENT_NAME <span class="keyword">order</span> <span class="keyword">by</span> <span class="keyword">start</span> limit <span class="number">1</span>),</span><br></pre></td></tr></table></figure><h3 id="Fail-Event"><a href="#Fail-Event" class="headerlink" title="Fail Event"></a>Fail Event</h3><p>只要看之前作的 <a href="https://sujunmin.github.io/blog/2016/09/10/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[4]%20SQL%20Agent%20%E8%88%87%20MariaDB%20Event/"><code>master.event_history</code></a> 就可以了。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="string">&#x27;&lt;tr&gt;&lt;td&gt;&#x27;</span>, `db`, <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, `name`, <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, `<span class="keyword">start</span>`, <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, `<span class="keyword">end</span>`, <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, `<span class="keyword">sqlstate</span>`, <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, `errno` , <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, `message_text`, <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, `record_time`, <span class="string">&#x27;&lt;/td&gt;&lt;/tr&gt;&#x27;</span> <span class="keyword">from</span> master.event_history <span class="keyword">where</span> errno <span class="keyword">is</span> <span class="keyword">not null</span> <span class="keyword">and</span> <span class="keyword">start</span> <span class="operator">&gt;=</span> subdate(now(), <span class="variable">@NumDays</span>);</span><br></pre></td></tr></table></figure><h3 id="Database-File-Status"><a href="#Database-File-Status" class="headerlink" title="Database File Status"></a>Database File Status</h3><p>這部份的資料可以從 <code>information_schema.tables</code> 來處理，以下為重要的欄位與說明。</p><table><thead><tr><th align="left">欄位</th><th align="left">說明</th></tr></thead><tbody><tr><td align="left"><code>data_length</code></td><td align="left">資料大小</td></tr><tr><td align="left"><code>index_length</code></td><td align="left">Index大小</td></tr><tr><td align="left"><code>data_free</code></td><td align="left">剩餘空間</td></tr></tbody></table><p>所以一個 table file 的大小 &#x3D; <code>data_length</code> + <code>index_length</code> + <code>data_free</code>。<br>用掉的大小 &#x3D; <code>data_length</code> + <code>index_length</code>。</p><p>知道這些配合一些告警就能完成了。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="string">&#x27;&lt;tr&gt;&lt;td&gt;&#x27;</span>, table_schema, </span><br><span class="line">              <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>,table_name,</span><br><span class="line">              <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, concat(@<span class="variable">@datadir</span>, table_schema, <span class="string">&#x27;\\&#x27;</span>, table_name, <span class="string">&#x27;.*&#x27;</span>),</span><br><span class="line">              <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, Round((data_length <span class="operator">+</span> index_length <span class="operator">+</span> data_free) <span class="operator">/</span> <span class="number">1024</span> <span class="operator">/</span> <span class="number">1024</span> , <span class="number">1</span>),</span><br><span class="line">       <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, Round((data_length <span class="operator">+</span> index_length) <span class="operator">/</span> <span class="number">1024</span> <span class="operator">/</span> <span class="number">1024</span> , <span class="number">1</span>),</span><br><span class="line">       <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, Round((data_free) <span class="operator">/</span> <span class="number">1024</span> <span class="operator">/</span> <span class="number">1024</span> , <span class="number">1</span>),</span><br><span class="line">       <span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, <span class="keyword">CASE</span> <span class="keyword">WHEN</span> Round((data_length <span class="operator">+</span> index_length)<span class="operator">/</span>(data_length <span class="operator">+</span> index_length <span class="operator">+</span> data_free) <span class="operator">*</span> <span class="number">100</span> , <span class="number">2</span>) <span class="operator">&gt;</span> <span class="variable">@CriticalThresholdPCT</span> <span class="keyword">AND</span> Round((data_length <span class="operator">+</span> index_length <span class="operator">+</span> data_free) <span class="operator">/</span> <span class="number">1024</span> <span class="operator">/</span> <span class="number">1024</span> , <span class="number">1</span>) <span class="operator">&gt;</span> <span class="number">1</span> <span class="keyword">THEN</span> CONCAT(<span class="string">&#x27;&lt;div class=&quot;Critical&quot;&gt;&#x27;</span>, Round((data_length <span class="operator">+</span> index_length)<span class="operator">/</span>(data_length <span class="operator">+</span> index_length <span class="operator">+</span> data_free) <span class="operator">*</span> <span class="number">100</span> , <span class="number">2</span>),<span class="string">&#x27;&lt;/div&gt;&#x27;</span>)</span><br><span class="line">                                <span class="keyword">WHEN</span> Round((data_length <span class="operator">+</span> index_length)<span class="operator">/</span>(data_length <span class="operator">+</span> index_length <span class="operator">+</span> data_free) <span class="operator">*</span> <span class="number">100</span> , <span class="number">2</span>) <span class="operator">&gt;</span> <span class="variable">@WarningThresholdPCT</span> <span class="keyword">AND</span> Round((data_length <span class="operator">+</span> index_length <span class="operator">+</span> data_free) <span class="operator">/</span> <span class="number">1024</span> <span class="operator">/</span> <span class="number">1024</span> , <span class="number">1</span>) <span class="operator">&gt;</span> <span class="number">1</span> <span class="keyword">THEN</span> CONCAT(<span class="string">&#x27;&lt;div class=&quot;Warning&quot;&gt;&#x27;</span>, Round((data_length <span class="operator">+</span> index_length)<span class="operator">/</span>(data_length <span class="operator">+</span> index_length <span class="operator">+</span> data_free) <span class="operator">*</span> <span class="number">100</span> , <span class="number">2</span>),<span class="string">&#x27;&lt;/div&gt;&#x27;</span>)</span><br><span class="line">                <span class="keyword">ELSE</span> CONCAT(<span class="string">&#x27;&lt;div class=&quot;Healthy&quot;&gt;&#x27;</span>, Round((data_length <span class="operator">+</span> index_length)<span class="operator">/</span>(data_length <span class="operator">+</span> index_length <span class="operator">+</span> data_free) <span class="operator">*</span> <span class="number">100</span> , <span class="number">2</span>),<span class="string">&#x27;&lt;/div&gt;&#x27;</span>) <span class="keyword">END</span>,</span><br><span class="line">              <span class="string">&#x27;&lt;/td&gt;&lt;/tr&gt;&#x27;</span></span><br><span class="line"><span class="keyword">FROM</span>   information_schema.tables <span class="keyword">where</span> table_schema <span class="keyword">not</span> <span class="keyword">in</span> (<span class="string">&#x27;performance_schema&#x27;</span>, <span class="string">&#x27;mysql&#x27;</span>, <span class="string">&#x27;information_schema&#x27;</span>)</span><br><span class="line"><span class="keyword">order</span> <span class="keyword">by</span> table_schema, table_name;</span><br></pre></td></tr></table></figure><h3 id="Session-List"><a href="#Session-List" class="headerlink" title="Session List"></a>Session List</h3><p>只要看 <code>information_schema.PROCESSLIST</code> 就可以了。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="string">&#x27;&lt;tr&gt;&lt;td&gt;&#x27;</span>, id,</span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, <span class="keyword">user</span>, </span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, host, </span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, db, </span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, command, </span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, REPLACE(REPLACE(info,<span class="string">&#x27;&lt;&#x27;</span>,<span class="string">&#x27;&lt;&#x27;</span>), <span class="string">&#x27;&gt;&#x27;</span>, <span class="string">&#x27;&gt;&#x27;</span>),</span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, state, </span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, memory_used, </span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, <span class="type">time</span>, </span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;td&gt;&#x27;</span>, query_id, </span><br><span class="line"><span class="string">&#x27;&lt;/td&gt;&lt;/tr&gt;&#x27;</span> <span class="keyword">from</span> information_schema.PROCESSLIST <span class="keyword">order</span> <span class="keyword">by</span> id;</span><br></pre></td></tr></table></figure><p>中間的 replace 是因為用到這個 Powershell 時，也是會在 <code>information_schema.PROCESSLIST</code> 呈現的，如果沒有替換一下，就會混亂了，所以要先 escape。</p><h3 id="Replication-Status"><a href="#Replication-Status" class="headerlink" title="Replication Status"></a>Replication Status</h3><p>這個是說希望能在報表中知道 Slave 的基本狀況，尤其是 Slave 如果發生 Error，會卡在那邊，如果 binlog 有 expire 時間的話，太晚知道問題解決完後 binlog 因 expire 會不見，就有機會無法復原。</p><p>首先，先透過 <code>information_schema.GLOBAL_STATUS</code> 的 <code> SLAVE_RUNNING</code> 看該台機器是 Master 還是 Slave (在 <a href="https://github.com/sujunmin/MariaDBDailyCheckScripts/blob/master/CheckSlave.sql"><code>CheckSlave.sql</code></a>中)</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="keyword">case</span> variable_value <span class="keyword">when</span> <span class="string">&#x27;ON&#x27;</span> <span class="keyword">then</span> <span class="string">&#x27;O&#x27;</span> <span class="keyword">else</span> <span class="string">&#x27;&#x27;</span> <span class="keyword">end</span> <span class="keyword">from</span> information_schema.GLOBAL_STATUS <span class="keyword">where</span> variable_name<span class="operator">=</span><span class="string">&#x27;SLAVE_RUNNING&#x27;</span>;</span><br></pre></td></tr></table></figure><p>如果是的話就是 Slave。</p><p>看 Slave 狀態，指令是 <code>show slave status</code> (在 <a href="https://github.com/sujunmin/MariaDBDailyCheckScripts/blob/master/ShowSlaveStatus.sql"><code>ShowSlaveStatus.sql</code></a> 中)，但這個指令我找了很久，完全不知道是 reference 哪個 table 或是 variable，於是只能自己 Parsing 了。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;h2&gt;Slave Status&lt;/h2&gt;&quot;</span></span><br><span class="line">&amp; <span class="variable">$mysqlexe</span> <span class="literal">-u</span> <span class="variable">$rptuser</span> <span class="literal">--password</span>=<span class="variable">$rptpass</span> <span class="literal">-h</span> <span class="variable">$ServerIP</span> <span class="literal">-sN</span> <span class="literal">-e</span> <span class="string">&quot;source .\ShowSlaveStatus.sql&quot;</span> &gt; out.html</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ((<span class="built_in">Get-Item</span> .\out.html).length <span class="operator">-gt</span> <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;table&gt;</span></span><br><span class="line"><span class="string">&lt;tr&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Master Host&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Master Log File&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Relay Master Log File&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Slave IO State&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Last Errno&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Last Error&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Last IO Errno&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Last IO Error&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Last SQL Errno&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;th&gt;Last SQL Error&lt;/th&gt;</span></span><br><span class="line"><span class="string">&lt;/tr&gt;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$stat</span> = [<span class="built_in">string</span>[]](<span class="built_in">Get-Content</span> .\out.html)</span><br><span class="line"></span><br><span class="line"><span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;tr&gt;&lt;td&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">2</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">6</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">10</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">1</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$stat</span>[<span class="number">19</span>] <span class="operator">-eq</span> <span class="string">&quot;0&quot;</span>) &#123;<span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="variable">$stat</span>[<span class="number">19</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">20</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span>&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;<span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;div class=&quot;</span><span class="string">&quot;Critical&quot;</span><span class="string">&quot;&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">19</span>] + <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div class=&quot;</span><span class="string">&quot;Critical&quot;</span><span class="string">&quot;&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">20</span>] + <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$stat</span>[<span class="number">35</span>] <span class="operator">-eq</span> <span class="string">&quot;0&quot;</span>) &#123;<span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="variable">$stat</span>[<span class="number">35</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">36</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span>&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;<span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;div class=&quot;</span><span class="string">&quot;Critical&quot;</span><span class="string">&quot;&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">35</span>] + <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div class=&quot;</span><span class="string">&quot;Critical&quot;</span><span class="string">&quot;&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">36</span>] + <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$stat</span>[<span class="number">37</span>] <span class="operator">-eq</span> <span class="string">&quot;0&quot;</span>) &#123;<span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="variable">$stat</span>[<span class="number">37</span>] + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">38</span>] + <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&quot;</span>&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;<span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;div class=&quot;</span><span class="string">&quot;Critical&quot;</span><span class="string">&quot;&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">37</span>] + <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div class=&quot;</span><span class="string">&quot;Critical&quot;</span><span class="string">&quot;&gt;&quot;</span> + <span class="variable">$stat</span>[<span class="number">38</span>] + <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&quot;</span>&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line"><span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;span class=&quot;</span><span class="string">&quot;Critical&quot;</span><span class="string">&quot;&gt;No Slave Status&lt;/span&gt;&lt;br/&gt;&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>基本上就是分開數位子，就完成了。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h3 id=&quot;Event-Status。&quot;&gt;&lt;a href=&quot;#Event-Status。&quot; class=&quot;headerlink&quot; title=&quot;Event Status。&quot;&gt;&lt;/a&gt;Event Status。&lt;/h3&gt;&lt;p&gt;這個取得資料的方式，除了之前提到的&lt;a</summary>
        
      
    
    
    
    
    <category term="MariaDB" scheme="https://sujunmin.github.io/blog/tags/MariaDB/"/>
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
  <entry>
    <title>從 SQL Server 到 MariaDB - [9] DBA Daily Check Report 的製作 [1]</title>
    <link href="https://sujunmin.github.io/blog/2016/12/27/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[9]%20DBA%20Daily%20Check%20Report%20%E7%9A%84%E8%A3%BD%E4%BD%9C%20[1]/"/>
    <id>https://sujunmin.github.io/blog/2016/12/27/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[9]%20DBA%20Daily%20Check%20Report%20%E7%9A%84%E8%A3%BD%E4%BD%9C%20[1]/</id>
    <published>2016-12-27T03:35:25.000Z</published>
    <updated>2026-09-26T03:17:55.354Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>(這篇與下一篇是因為參加 <a href="http://ithelp.ithome.com.tw/users/20103536/ironman">2017 iT 邦幫幫忙鐵人賽</a>而產生的，原來是沒有那麼快要開始作，但是因為沒稿子了就只能趕快做出來，分享給大家)</p><p>在 SQL Server 與 Oracle DB 都有一個 Daily Check Scripts 來讓我在上班開始的時候作一些自主檢查，並且產出報告，這個對於管理多台機器較為方便。</p><p>在 SQL Server 上我採用<a href="http://www.wisesoft.co.uk/articles/dba_daily_checks_email_report.aspx">這個</a> Scripts， Oracle DB 的部份採用<a href="https://github.com/sujunmin/OracleDBDailyCheckScripts">這個</a>，十分方便，很多東西都能顯示，大部分的問題都能夠馬上發現。</p><p>到了 MariaDB 希望能夠 Porting 這個來方便管理。</p><p>接下來針對這個 <a href="https://github.com/sujunmin/MariaDBDailyCheckScripts">Porting</a> 來說明過程</p><ul><li>PowerShell 基礎架構</li><li>Uptime and Version</li><li>Disk Drive Status</li><li>Backup Status</li><li>Event Status</li><li>Fail Events Status</li><li>Database File Status</li><li>Session List</li><li>Replication Status</li></ul><h3 id="PowerShell-基礎架構"><a href="#PowerShell-基礎架構" class="headerlink" title="PowerShell 基礎架構"></a>PowerShell 基礎架構</h3><p>在作這個 Porting 之前，筆者先到 Google 找有沒有人有不錯的類似 Solution，大部分都是走 Shell Scripts，因為在 Windows 上用 PowerShell 比較方便，就決定用 PowerShell 來兜 Solution 了。</p><p>大致上的架構是這樣</p><ul><li>變數設定</li><li>Html 的頭(CSS, JS…)<ul><li>mysql 執行各項的子 SQL</li><li>將結果放到 out.html</li><li>Html &#x3D; Html + out.html</li><li>執行各項作業</li></ul></li><li>全部都執行完畢後，整理一下寄信給相關人員</li></ul><h3 id="Uptime-and-Version"><a href="#Uptime-and-Version" class="headerlink" title="Uptime and Version"></a>Uptime and Version</h3><p>Uptime 部份，從 <code>information_schema.GLOBAL_STATUS</code> 可以得到資料，接下來整理一下，化成需要的輸出。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> CONCAT(<span class="keyword">CASE</span> <span class="keyword">WHEN</span> <span class="built_in">CAST</span>(VARIABLE_VALUE <span class="keyword">as</span> unsigned <span class="type">integer</span>) <span class="operator">&lt;</span> <span class="variable">@UptimeCritical</span> <span class="operator">*</span> <span class="number">60</span> <span class="keyword">THEN</span> <span class="string">&#x27;&lt;span class=&quot;Critical&quot;&gt;&#x27;</span></span><br><span class="line">                   <span class="keyword">WHEN</span> <span class="built_in">CAST</span>(VARIABLE_VALUE <span class="keyword">as</span> unsigned <span class="type">integer</span>) <span class="operator">&lt;</span> <span class="variable">@UptimeWarning</span> <span class="operator">*</span> <span class="number">60</span> <span class="keyword">THEN</span> <span class="string">&#x27;&lt;span class=&quot;Warning&quot;&gt;&#x27;</span></span><br><span class="line">           <span class="keyword">ELSE</span> <span class="string">&#x27;&lt;span class=&quot;Healthy&quot;&gt;&#x27;</span> <span class="keyword">END</span>,</span><br><span class="line"><span class="built_in">FLOOR</span>(<span class="keyword">HOUR</span>(SEC_TO_TIME(VARIABLE_VALUE)) <span class="operator">/</span> <span class="number">24</span>), <span class="string">&#x27; day(s), &#x27;</span>,</span><br><span class="line"><span class="built_in">MOD</span>(<span class="keyword">HOUR</span>(SEC_TO_TIME(VARIABLE_VALUE)), <span class="number">24</span>), <span class="string">&#x27; hour(s), &#x27;</span>,</span><br><span class="line"><span class="keyword">MINUTE</span>(SEC_TO_TIME(VARIABLE_VALUE)), <span class="string">&#x27; minute(s)&lt;/span&gt;&#x27;</span>)</span><br><span class="line"><span class="keyword">from</span> information_schema.GLOBAL_STATUS <span class="keyword">where</span> VARIABLE_NAME<span class="operator">=</span><span class="string">&#x27;UPTIME&#x27;</span>;</span><br></pre></td></tr></table></figure><p>Version 部份，從系統參數就能知道了。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> @<span class="variable">@version</span> <span class="keyword">from</span> dual;</span><br></pre></td></tr></table></figure><h3 id="Disk-Drives"><a href="#Disk-Drives" class="headerlink" title="Disk Drives"></a>Disk Drives</h3><p>這部份需要用的 Powershell 的 <code>get-wmiobject</code> 功能。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$Disks</span> = <span class="built_in">get-wmiobject</span> <span class="literal">-Class</span> win32_logicaldisk <span class="literal">-Filter</span> <span class="string">&quot;DriveType=&#x27;3&#x27;&quot;</span></span><br></pre></td></tr></table></figure><p>接下就是計算大小，如果低於警告或是危險級就用不一樣的顏色標示。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ((<span class="variable">$d</span>.FreeSpace / <span class="variable">$d</span>.Size * <span class="number">100</span>) <span class="operator">-lt</span> <span class="variable">$FreeDiskSpacePercentCriticalThreshold</span>) &#123;<span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;div class=&quot;</span><span class="string">&quot;Critical&quot;</span><span class="string">&quot;&gt;&quot;</span> + (<span class="string">&quot;&#123;0:N2&#125;&quot;</span> <span class="operator">-f</span> (<span class="variable">$d</span>.FreeSpace / <span class="variable">$d</span>.Size * <span class="number">100</span>))+ <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&quot;</span>&#125;</span><br><span class="line">     <span class="keyword">elseif</span> ((<span class="variable">$d</span>.FreeSpace / <span class="variable">$d</span>.Size * <span class="number">100</span>) <span class="operator">-lt</span> <span class="variable">$FreeDiskSpacePercentWarningThreshold</span>) &#123;<span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;div class=&quot;</span><span class="string">&quot;Warning&quot;</span><span class="string">&quot;&gt;&quot;</span> + (<span class="string">&quot;&#123;0:N2&#125;&quot;</span> <span class="operator">-f</span> (<span class="variable">$d</span>.FreeSpace / <span class="variable">$d</span>.Size * <span class="number">100</span>))+ <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&quot;</span>&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;<span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;div class=&quot;</span><span class="string">&quot;Healthy&quot;</span><span class="string">&quot;&gt;&quot;</span> + (<span class="string">&quot;&#123;0:N2&#125;&quot;</span> <span class="operator">-f</span> (<span class="variable">$d</span>.FreeSpace / <span class="variable">$d</span>.Size * <span class="number">100</span>))+ <span class="string">&quot;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&quot;</span>&#125; </span><br></pre></td></tr></table></figure><p>然後就接到原來要輸出的資料裡，這個部份就完成了。</p><h3 id="Backup-Status"><a href="#Backup-Status" class="headerlink" title="Backup Status"></a>Backup Status</h3><p>這個部份從系統面看不出來有跟所需要的資料有關連，但是從建立資料庫備份的檔案系統可以看得出來，因此也是用 Powershell 來完成這個需求。</p><table><thead><tr><th align="left">檔案資訊</th><th align="left">備份資訊</th><th align="left">Powershell 用法</th></tr></thead><tbody><tr><td align="left">檔案名稱</td><td align="left">備份檔案名稱</td><td align="left"><code>$File.FullName</code></td></tr><tr><td align="left">建立時間</td><td align="left">開始備份的時間</td><td align="left"><code>$File.CreationTime</code></td></tr><tr><td align="left">修改時間</td><td align="left">完成備份時間</td><td align="left"><code>$File.LastWriteTime</code></td></tr></tbody></table><p>這邊的程式結構也是跟之前的一樣，如果有設定完整備份的資料夾，那就去看看那個資料夾狀態。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Get-ChildItem</span> <span class="variable">$FullBackupPath</span> <span class="literal">-Filter</span> FullBackup* |</span><br><span class="line">        <span class="built_in">Foreach-Object</span> &#123;</span><br><span class="line">   <span class="variable">$Html</span> = <span class="variable">$Html</span> + <span class="string">&quot;&lt;tr&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>.FullName + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>.CreationTime + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + <span class="variable">$_</span>.LastWriteTime + <span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span> + (<span class="string">&quot;&#123;0:N2&#125;&quot;</span> <span class="operator">-f</span> (<span class="variable">$_</span>.Length / <span class="number">1048576</span>)) + <span class="string">&quot; MB&lt;/td&gt;&lt;/tr&gt;&quot;</span> </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;(這篇與下一篇是因為參加 &lt;a</summary>
        
      
    
    
    
    
    <category term="MariaDB" scheme="https://sujunmin.github.io/blog/tags/MariaDB/"/>
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
  <entry>
    <title>從 SQL Server 到 MariaDB - [8] User-defined Functions 與擴增 Event 功能</title>
    <link href="https://sujunmin.github.io/blog/2016/11/08/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[8]%20User-defined%20Functions%20%E8%88%87%E6%93%B4%E5%A2%9E%20Event%20%E5%8A%9F%E8%83%BD/"/>
    <id>https://sujunmin.github.io/blog/2016/11/08/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[8]%20User-defined%20Functions%20%E8%88%87%E6%93%B4%E5%A2%9E%20Event%20%E5%8A%9F%E8%83%BD/</id>
    <published>2016-11-08T01:47:22.000Z</published>
    <updated>2026-09-26T03:17:55.354Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>雖然一些排程或是資訊可以透過系統排程來處理，但是原有的 SQL Server 管理方式幾乎都是透過 SQL Server Job 系統執行，加上 SQL Server 有許多可以在 Server 本體就能做完的方式，為了追求一樣的管理方式，導入 User-defined Functions (UDF) 來擴增 Server 功能。</p><h2 id="透過-MariaDB-裡的-Event-System-進行備份"><a href="#透過-MariaDB-裡的-Event-System-進行備份" class="headerlink" title="透過 MariaDB 裡的 Event System 進行備份"></a>透過 MariaDB 裡的 Event System 進行備份</h2><p>基本上 Google 搜尋 MariaDB&#x2F;MySQL 備份，不外乎是利用 cron jobs 或是 Windows tasks 來進行備份，這樣進行備份一則與原來的 SQL Server 管理不一樣，另外一個也沒辦法知道備份的情況 (要登入主機看 Task 狀態)。</p><p>如果能透過 MariaDB 來發動備份 (透過 Event) 感覺能夠與以前的管理方式一樣，Server 要的排程 Server 自己發動，而且也能收集相關資料進行監控。</p><p>但是 MariaDB 沒有什麼介面可以來執行系統作業，這時候剛好找到 <a href="https://mariadb.com/kb/en/mariadb/user-defined-functions/">UDF</a>，還有一大堆神人做好的 <a href="http://www.mysqludf.org/">MySQLUDF</a> Repository，裡頭剛好有我要的與系統處理有關的 <a href="https://github.com/mysqludf/lib_mysqludf_sys#readme">UDF</a>，正好可以拿來用。</p><h2 id="lib-mysqludf-sys-編譯、安裝、與使用"><a href="#lib-mysqludf-sys-編譯、安裝、與使用" class="headerlink" title="lib_mysqludf_sys 編譯、安裝、與使用"></a>lib_mysqludf_sys 編譯、安裝、與使用</h2><h3 id="編譯"><a href="#編譯" class="headerlink" title="編譯"></a>編譯</h3><p>如果連到 lib_mysqludf_sys 的 github，發現到它的 Makefile 都是為 gcc 做的，Windows 用戶有點麻煩。</p><p>網路上可以找到編譯好的 dll，但是覺得有點怕不知道裏面有什麼。</p><p>還好找到一篇也是部份 MySQL UDF 的作者寫的 blog <a href="http://rpbouman.blogspot.tw/2007/09/creating-mysql-udfs-with-microsoft.html">Creating MySQL UDFs with Microsoft Visual C++ Express</a>，裡頭有一些方法可以參考。</p><p>編譯的環境跟之前修改 <a href="https://sujunmin.github.io/blog/2016/10/03/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[6]%20Audit%20%E7%9A%84%E5%AF%A6%E4%BD%9C/">audit log plugin</a> 的環境一樣，需要注意的地方如下</p><h4 id="Configuring-the-Include-path"><a href="#Configuring-the-Include-path" class="headerlink" title="Configuring the Include path"></a>Configuring the Include path</h4><p>Platform SDK 的 Include Files 在 <code>C:\Program Files (x86)\Windows Kits\8.1\Include</code> 。</p><p>MariaDB 的我是 <code>&lt;MariaDB&gt;\include</code> 與 <code>&lt;MariaDB&gt;\include\mysql</code>。</p><p>這兩個資料夾的資料最好複製一份到其他地方，因為等會要改。</p><h4 id="Adding-the-HAVE-DLOPEN-macro"><a href="#Adding-the-HAVE-DLOPEN-macro" class="headerlink" title="Adding the HAVE_DLOPEN macro"></a>Adding the HAVE_DLOPEN macro</h4><p>除了加這個以外，還要加兩個防止 <code>strncpy</code> 的安全問題一直錯的 macro。</p><p><code>HAVE_DLOPEN;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</code></p><h4 id="Configuring-the-library-path"><a href="#Configuring-the-library-path" class="headerlink" title="Configuring the library path"></a>Configuring the library path</h4><p>Platform SDK 的 Lib Files 在 <code>C:\Program Files (x86)\Windows Kits\8.1\Lib</code> 。</p><p>MariaDB 的是 <code>&lt;MariaDB&gt;\lib</code> 。</p><h4 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h4><p>編譯的時候會一直有 <code>snprintf</code> 重複定義的問題，為了減少麻煩，直接把 MariaDB 的 Include Files 裡有相關的定義先拿掉，待編譯完再改回來。</p><h3 id="安裝"><a href="#安裝" class="headerlink" title="安裝"></a>安裝</h3><p>將產生出來的 dll 與 pdf 檔複製到 <code>&lt;MariaDB&gt;\lib\plugin</code>，透過 github 裡的 <a href="https://github.com/mysqludf/lib_mysqludf_sys/blob/master/lib_mysqludf_sys.sql"><code>lib_mysqludf_sys.sql</code></a> 安裝，記得 so 要改成 dll。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">FUNCTION</span> IF <span class="keyword">EXISTS</span> lib_mysqludf_sys_info;</span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">FUNCTION</span> IF <span class="keyword">EXISTS</span> sys_get;</span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">FUNCTION</span> IF <span class="keyword">EXISTS</span> sys_set;</span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">FUNCTION</span> IF <span class="keyword">EXISTS</span> sys_exec;</span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">FUNCTION</span> IF <span class="keyword">EXISTS</span> sys_eval;</span><br><span class="line"></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">FUNCTION</span> lib_mysqludf_sys_info <span class="keyword">RETURNS</span> string SONAME <span class="string">&#x27;lib_mysqludf_sys.dll&#x27;</span>;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">FUNCTION</span> sys_get <span class="keyword">RETURNS</span> string SONAME <span class="string">&#x27;lib_mysqludf_sys.dll&#x27;</span>;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">FUNCTION</span> sys_set <span class="keyword">RETURNS</span> <span class="type">int</span> SONAME <span class="string">&#x27;lib_mysqludf_sys.dll&#x27;</span>;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">FUNCTION</span> sys_exec <span class="keyword">RETURNS</span> <span class="type">int</span> SONAME <span class="string">&#x27;lib_mysqludf_sys.dll&#x27;</span>;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">FUNCTION</span> sys_eval <span class="keyword">RETURNS</span> string SONAME <span class="string">&#x27;lib_mysqludf_sys.dll&#x27;</span>;</span><br></pre></td></tr></table></figure><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><p>可以看 <a href="https://github.com/mysqludf/lib_mysqludf_sys/blob/master/lib_mysqludf_sys.html"><code>lib_mysqludf_sys.html</code></a>，基本上很簡單。</p><table><thead><tr><th>Function Name</th><th>Input</th><th>Output</th></tr></thead><tbody><tr><td><code>lib_mysqludf_sys_info</code></td><td>N&#x2F;A</td><td>版本號碼</td></tr><tr><td><code>sys_get</code></td><td>系統變數名稱</td><td>系統變數值</td></tr><tr><td><code>sys_set</code></td><td>系統變數名稱<br /> 要設定的系統變數值</td><td>0 為成功，其他為失敗</td></tr><tr><td><code>sys_exec</code></td><td>需執行的指令</td><td>該指令執行的 Process 傳回值</td></tr><tr><td><code>sys_eval</code></td><td>需執行的指令</td><td>指令在 Standard Output 產生內容</td></tr></tbody></table><p>來跑一下試試看</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> sys_eval(<span class="string">&#x27;ipconfig&#x27;</span>);</span><br></pre></td></tr></table></figure><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/lib_mysqludf_sys_01.png /><p>竟然是亂碼，但是 cmd 的沒有問題</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/lib_mysqludf_sys_02.png /><p>顯然是 Windows 編碼問題，最後是這樣解決的。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="keyword">convert</span>(<span class="keyword">convert</span>(sys_eval(<span class="string">&#x27;ipconfig&#x27;</span>) <span class="keyword">using</span> big5) <span class="keyword">using</span> utf8);</span><br></pre></td></tr></table></figure><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/lib_mysqludf_sys_03.png /><h2 id="與-Event-實作備份"><a href="#與-Event-實作備份" class="headerlink" title="與 Event 實作備份"></a>與 Event 實作備份</h2><p>首先透過之前做的 <a href="https://sujunmin.github.io/blog/2016/09/10/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[4]%20SQL%20Agent%20%E8%88%87%20MariaDB%20Event/">Event 紀錄系統</a>來做處理，不過因為 <code>sys_eval</code> 與 <code>sys_exec</code> 有珍貴的 Log 資料，所以不能只是紀錄單純的 OK 就好，加上兼容一般 SQL 的排程，所以修改了一下 Event 紀錄系統。</p><h3 id="備份排程"><a href="#備份排程" class="headerlink" title="備份排程"></a>備份排程</h3><p>做一個備份排程如下</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> DEFINER<span class="operator">=</span>`sujunmin`@`<span class="operator">%</span>` EVENT `backups`</span><br><span class="line"><span class="keyword">ON</span> SCHEDULE</span><br><span class="line"><span class="keyword">EVERY</span> <span class="number">1</span> <span class="keyword">MINUTE</span> STARTS <span class="string">&#x27;2016-11-07 13:42:26&#x27;</span></span><br><span class="line"><span class="keyword">ON</span> COMPLETION <span class="keyword">NOT</span> PRESERVE</span><br><span class="line">ENABLE</span><br><span class="line">COMMENT <span class="string">&#x27;&#x27;</span></span><br><span class="line">DO <span class="keyword">BEGIN</span></span><br><span class="line">    <span class="keyword">call</span> master.proc_for_event_history(<span class="string">&#x27;master&#x27;</span>, <span class="string">&#x27;backups&#x27;</span>, <span class="string">&#x27;select sys_eval(\&#x27;</span>&quot;E://MariaDB 10.1//bin//mysqldump.exe&quot; <span class="operator">-</span>u sujunmin <span class="operator">-</span>pabcdef<span class="variable">@12345</span> <span class="comment">--master-data --verbose --all-databases --events --routines --gtid 2&gt;&amp;1 &gt; E://backup//all_db.sql\&#x27;) into @outv;&#x27;);</span></span><br><span class="line"><span class="keyword">END</span></span><br></pre></td></tr></table></figure><p>要注意的是中間那個 <code>2&gt;&amp;1</code>，因為 <code>mysqldump</code> 是拿 <a href="http://dba.stackexchange.com/questions/14305/how-to-log-verbose-output-from-mysqldump">Standard Error</a> 來 Output 訊息，但是 <code>sys_eval</code> 只會送回 Standard Outupt 內容。</p><p>執行完以後看看結果。</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/lib_mysqludf_sys_04.png /><h2 id="資安問題"><a href="#資安問題" class="headerlink" title="資安問題"></a>資安問題</h2><p>網路上有很多使用 MySQL UDF 透過這些 UDF 來存取系統的<a href="https://securitypentester.ninja/mysql-udf-injection/">資安問題</a>，所以要確保以下的防護是否有達成。</p><ol><li>Web 連資料庫不能是 root 等級的帳號，權限愈小愈好。</li><li>AppArmor 或是 UAC 的設定。</li></ol><h2 id="其他紀錄"><a href="#其他紀錄" class="headerlink" title="其他紀錄"></a>其他紀錄</h2><h3 id="2016-11-08"><a href="#2016-11-08" class="headerlink" title="2016&#x2F;11&#x2F;08"></a>2016&#x2F;11&#x2F;08</h3><p>安裝的時候如果碰到不能載入，除了之前那篇寫的內容之外，有可能是 Visual C++ Redistributable 套件需要安裝，我是在 Visual Studio 2015 編譯的，找 <a href="https://www.microsoft.com/en-us/download/details.aspx?id=48145">Visual C++ Redistributable for Visual Studio 2015</a> 安裝就可以了。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;雖然一些排程或是資訊可以透過系統排程來處理，但是原有的 SQL Server 管理方式幾乎都是透過 SQL Server</summary>
        
      
    
    
    
    
    <category term="MariaDB" scheme="https://sujunmin.github.io/blog/tags/MariaDB/"/>
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
  <entry>
    <title>Ubuntu Kernel 的 Livepatch 機制</title>
    <link href="https://sujunmin.github.io/blog/2016/10/21/Ubuntu%20Kernel%20%E7%9A%84%20Livepatch%20%E6%A9%9F%E5%88%B6/"/>
    <id>https://sujunmin.github.io/blog/2016/10/21/Ubuntu%20Kernel%20%E7%9A%84%20Livepatch%20%E6%A9%9F%E5%88%B6/</id>
    <published>2016-10-21T03:15:29.000Z</published>
    <updated>2026-09-26T03:17:55.350Z</updated>
    
    <content type="html"><![CDATA[<p>最近 Ubuntu 推出一個新的服務，Kernel 的 <a href="https://ubuntu.com/livepatch">Livepatch 服務</a>，還蠻不錯的，不用重開機就可以更新 Kernel，不過還是有些限制，整理與測試如下。</p><ul><li>神人的詳細<a href="http://blog.dustinkirkland.com/2016/10/canonical-livepatch.html">連結</a>。</li><li>需要訂閱服務，免錢的有 3 個，也有付費服務(<a href="https://buy.ubuntu.com/">Ubuntu Advantage</a>)。</li><li>不過我試一下超過 3 個好像也可以。</li><li>只有沒被修改的 Kernel 可以用。</li><li>被修改過的或是想要自己做 Livepatch 很<a href="http://chrisarges.net/2015/09/21/livepatch-on-ubuntu.html">複雜</a>。</li><li>支援 x86_64，amd64。</li><li>支援 16.04 LTS 的 4.4 Kernel。</li><li>要裝 snapd，沒更新到最新就會有錯。<img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/ubuntu_livekernelpatch_04.png /></li><li>對象是 CVE 的 Patch，所以效能或是其他增進的還是要走老路 (update and reboot)。</li></ul><p>首先要有個 Ubuntu SSO 帳號，登入後就可以獲得 Token 了。</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/ubuntu_livekernelpatch_01.png /><p>不過這個畫面沒有說到用了幾個 licenses。</p><p>接下來到想要用的機器上執行指令。</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/ubuntu_livekernelpatch_02.png /><p>可以看到現在正在用的 Kernel Patches。</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/ubuntu_livekernelpatch_03.png /><p>如果不想用就 <code>canonical-livepatch disable</code>。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;最近 Ubuntu 推出一個新的服務，Kernel 的 &lt;a href=&quot;https://ubuntu.com/livepatch&quot;&gt;Livepatch 服務&lt;/a&gt;，還蠻不錯的，不用重開機就可以更新</summary>
        
      
    
    
    
    
    <category term="Ubuntu" scheme="https://sujunmin.github.io/blog/tags/Ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>Trace File 挖寶記</title>
    <link href="https://sujunmin.github.io/blog/2016/10/19/Trace%20File%20%E6%8C%96%E5%AF%B6%E8%A8%98/"/>
    <id>https://sujunmin.github.io/blog/2016/10/19/Trace%20File%20%E6%8C%96%E5%AF%B6%E8%A8%98/</id>
    <published>2016-10-19T06:53:47.000Z</published>
    <updated>2026-09-26T03:17:55.350Z</updated>
    
    <content type="html"><![CDATA[<p>昨天下午收到一封告警訊息，發現到是一個從來沒有在告警訊息中出現的帳號 (datareader&#x2F;datawriter, dbo login)，覺得很奇怪，不知道是不是又被 SQL Injection 攻擊了，於是有了這篇紀錄說明如何從 Trace File 挖寶。</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/eventclass_118_1.png /><p>這篇的結果不是那麼重要，但是怎麼從 Trace File 找到想要的資訊我覺得還蠻重要的。</p><p>基本上 TextData 應該都會有 Query 資訊，大部份都能馬上判定是什麼狀況，但是像這種沒有 TextData 的就要直接去看看 Trace File 裡頭的資訊了。</p><p>Event Class 是 118，<a href="https://msdn.microsoft.com/en-us/library/ms190802.aspx">Audit Object Derived Permission Event</a>。</p><p>裡頭寫到重要的資訊，就是欄位編號，名稱，跟內容。</p><p>從 <a href="https://github.com/sujunmin/SQLServerTraceFileParser">Log Server</a> 拿出所要的 csv 檔，取出需要的部份 (如果原來的太大了的話)。</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/eventclass_118_2.png /><p>一些重要的欄位</p><table><thead><tr><th>欄位號碼</th><th>名稱</th><th>內容</th></tr></thead><tbody><tr><td>8</td><td>HostName</td><td>Name of the computer on which the client is running. This data column is populated if the host name is provided by the client. To determine the host name, use the HOST_NAME function.</td></tr><tr><td>10</td><td>ApplicationName</td><td>Name of the client application that created the connection to an instance of SQL Server. This column is populated with the values passed by the application rather than the displayed name of the program.</td></tr><tr><td>11</td><td>LoginName</td><td>Name of the login of the user (either the SQL Server security login or the Microsoft Windows login credentials in the form of DOMAIN\username).</td></tr><tr><td>14</td><td>StartTime</td><td>Time at which the event started, if available.</td></tr><tr><td>21</td><td>EventSubClass</td><td>Type of event subclass.<br/>1&#x3D;Create<br/>2&#x3D;Alter<br/>3&#x3D;Drop<br/>4&#x3D;Dump<br/>11&#x3D;Load</td></tr><tr><td>23</td><td>Success</td><td>1 &#x3D; success. 0 &#x3D; failure. For example, a value of 1 indicates success of a permissions check and a value of 0 indicates failure of that check.</td></tr><tr><td>26</td><td>ServerName</td><td>Name of the instance of SQL Server being traced.</td></tr><tr><td>27</td><td>EventClass</td><td>Type of event &#x3D; 118.</td></tr><tr><td>34</td><td>ObjectName</td><td>Name of the object that is being created, altered, or dropped.</td></tr><tr><td>35</td><td>DatabaseName</td><td>Name of the database in which the user statement is running.</td></tr><tr><td>37</td><td>OwnerName</td><td>Database username of the object owner of the object being created, altered, or dropped.</td></tr></tbody></table><p>其中除了帳號來源之外，最重要的就是 EventSubClass &#x3D; Create，ObjectName &#x3D; _WA_Sys_…。</p><p>所以我們知道是 Create Statistics。</p><p>看一下統計資料</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/eventclass_118_3.png /><p>正式當時產生的：某個 Query 需要其他類型的統計資料，所以就產生了。</p><p>總結：如果要從 Trace File 挖寶，可以這麼做</p><ol><li>找到該 EventClass 的 msdn。</li><li>找出該 Event 的資料。</li><li>對應 EventClass 的欄位資訊。</li><li>依據結果繼續找下去。</li></ol>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;昨天下午收到一封告警訊息，發現到是一個從來沒有在告警訊息中出現的帳號 (datareader&amp;#x2F;datawriter, dbo login)，覺得很奇怪，不知道是不是又被 SQL Injection 攻擊了，於是有了這篇紀錄說明如何從 Trace File</summary>
        
      
    
    
    
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
  <entry>
    <title>從 SQL Server 到 MariaDB - [7] Linked Server 與 FederatedX Storage Engine</title>
    <link href="https://sujunmin.github.io/blog/2016/10/05/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[7]%20Linked%20Server%20%E8%88%87%20FederatedX%20Storage%20Engine/"/>
    <id>https://sujunmin.github.io/blog/2016/10/05/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[7]%20Linked%20Server%20%E8%88%87%20FederatedX%20Storage%20Engine/</id>
    <published>2016-10-05T02:05:16.000Z</published>
    <updated>2026-09-26T03:17:55.354Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>資料庫有跨地區傳送資料的需求，在 SQL Server 用到 Linked Server 來傳送資料，希望在 MariaDB 也有類似的 Solution 可以使用。</p><p>原來的架構如下圖</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/ls_1.png /><h2 id="採用-FederatedX-Storage-Engine-的原因"><a href="#採用-FederatedX-Storage-Engine-的原因" class="headerlink" title="採用 FederatedX Storage Engine 的原因"></a>採用 FederatedX Storage Engine 的原因</h2><p>這種同步問題應該採用 Replication 來執行的，但是因為下面的原因而不使用</p><ol><li>在 Site B 會有一份 Site A <code>A_DB</code> 的副本，然後透過那個副本來傳送資料到 Site B 的 <code>B_DB</code>，架構如下</li></ol><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/ls_2.png /><ol start="2"><li>這樣 Site B 有一份 <code>A_DB</code> 的副本，蠻浪費空間的，如果把讀寫頭指向 <code>B_DB</code>，雖然可以多個 GTID，但是感覺很混亂，而且出問題就很難修復。</li><li>Replication 不能設定同步時間 (Site A 與 Site B 中間的連線頻寬與其他服務 share，不能一直使用)，雖說可以透過網路管控讓他自己停止 Replication，但是覺得這樣不能完全控制，不太方便。</li><li>在<a href="http://stackoverflow.com/questions/5370970/how-to-create-linked-server-mysql">這篇</a>裡有網友解答可以透過 FEDERATED engine 達成 linked server 的需求，因此就來看看這是什麼。</li></ol><h2 id="MariaDB-FederatedX-Storage-Engine-介紹"><a href="#MariaDB-FederatedX-Storage-Engine-介紹" class="headerlink" title="MariaDB FederatedX Storage Engine 介紹"></a>MariaDB FederatedX Storage Engine 介紹</h2><p>後來去找 MariaDB 上的 <a href="https://mariadb.com/kb/en/mariadb/federated-storage-engine/">FEDERATED Storage Engine</a> 時，發現到它已經不再開發了，取而代之的是 MariaDB 自己 fork 的 <a href="https://mariadb.com/kb/en/mariadb/about-federatedx/">FederatedX Storage Engine</a>，相關比較如此<a href="https://mariadb.com/kb/en/mariadb/differences-between-federatedx-and-federated/">連結</a>。</p><p>FederatedX Storage Engine 的說明文件是我覺得 MariaDB 文件裏面寫的最口語的，很容易了解。</p><p>這個 FederatedX Storage Engine 的產生是原來 Cisco 需要存取各設備的 MySQL 的資料，但是發動端沒有那麼多空間儲存這些資料，於是這個機制就產生了。</p><p>以下是針對 SQL Server Linked Server 與 MariaDB FederatedX Storage Engine 做一個比較</p><table><thead><tr><th>項目</th><th>SQL Server Linked Server</th><th>MariaDB FederatedX Storage Engine</th></tr></thead><tbody><tr><td>實作層級</td><td>Database Level</td><td>Table Level</td></tr><tr><td>異質資料庫連結種類</td><td>多種類</td><td>MariaDB, MySQL, PostgreSQL (其他專案)</td></tr><tr><td>遠端資料庫的 DDL 異動處理</td><td>沒有感覺</td><td>需重建 HANDLER</td></tr><tr><td>執行遠端資料庫的 DDL</td><td>可以 (<code>exec(&#39;ddl&#39;)</code>)</td><td>不可以</td></tr></tbody></table><h2 id="測試"><a href="#測試" class="headerlink" title="測試"></a>測試</h2><h3 id="建立-HANDLER"><a href="#建立-HANDLER" class="headerlink" title="建立  HANDLER"></a>建立  HANDLER</h3><p>首先開一個 Site A，同一台機器的 3308 Port。</p><p>上面開一個 <code>A_DB</code>，裡頭一個 <code>table</code>，一個欄位 <code>col</code> <code>char(50)</code>，一行 <code>&#39;A&#39;</code>。</p><p>在原來的 MariaDB 假設他是 Site B，開一個 DB <code>B_DB</code> 裡頭一個 <code>table</code>，一個欄位 <code>col</code> <code>char(50)</code>，一行 <code>&#39;B&#39;</code>。</p><p>在 Site B 上執行以下指令</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">use B_DB;</span><br><span class="line"></span><br><span class="line"><span class="keyword">create</span> server <span class="string">&#x27;Site_A&#x27;</span> <span class="keyword">foreign</span> data wrapper <span class="string">&#x27;mysql&#x27;</span> options</span><br><span class="line">  (HOST <span class="string">&#x27;127.0.0.1&#x27;</span>,</span><br><span class="line">  DATABASE <span class="string">&#x27;A_DB&#x27;</span>,</span><br><span class="line">  <span class="keyword">USER</span> <span class="string">&#x27;root&#x27;</span>,</span><br><span class="line">  PASSWORD <span class="string">&#x27;passowrd&#x27;</span>,</span><br><span class="line">  PORT <span class="number">3308</span>,</span><br><span class="line">  SOCKET <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">  OWNER <span class="string">&#x27;root&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">CREATE TABLE</span> A_table ENGINE<span class="operator">=</span>FEDERATED CONNECTION<span class="operator">=</span><span class="string">&#x27;Site_A/table&#x27;</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>接著可以 <code>select * from A_table</code> 看看，應該會看到 A。</p><p>這邊還有一些要注意的</p><ol><li>在 <code>CREATE TABLE</code> 的時候可以故意打錯 table 名字，會報錯 (與原來 FEDERATED 的差異第 2 項)。</li><li>在 <code>CREATE TABLE ... CONNECTION=&#39;Site_A/table&#39;</code> 的時候也可以 <code>mysql://username:password@hostname:port/database/tablename</code>，但是我的密碼有特殊字@，所以還好有 <code>create server</code> 這個方法建立。</li><li>建立完成後可以看看資料庫的 <code>B_DB</code> 資料夾，只會有 <code>a_table.frm</code>，不會有 <code>a_table.ibd</code>，因為資料實際不在 Site B。</li><li>Replication Slave 不會有這樣方法產生出來的 Table，如果要切換記得要手動加回去。</li></ol><h3 id="遠端資料庫執行-DDL"><a href="#遠端資料庫執行-DDL" class="headerlink" title="遠端資料庫執行 DDL"></a>遠端資料庫執行 DDL</h3><p>在 Site A 的 <code>A_DB</code> 把 table 多一個欄位 <code>col1</code> <code>int</code>。</p><p>到 Site B <code>select * from A_table</code> 看看。</p><p>你會只看到一個欄位 <code>col</code>，結果是 A。</p><p>這時候需要重建 HANDLER</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">use B_DB;</span><br><span class="line"><span class="keyword">drop</span> <span class="keyword">table</span> A_table;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> A_table ENGINE<span class="operator">=</span>FEDERATED CONNECTION<span class="operator">=</span><span class="string">&#x27;Site_A/table&#x27;</span>;</span><br></pre></td></tr></table></figure><p>重新 Select 看看，應該會看到正確的資料。</p><h3 id="在-FEDERATED-Table-上執行-DDL"><a href="#在-FEDERATED-Table-上執行-DDL" class="headerlink" title="在 FEDERATED Table 上執行 DDL"></a>在 FEDERATED Table 上執行 DDL</h3><p>在 Site B 的 <code>B_DB</code> 對 <code>A_table</code> 新增一個欄位</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER TABLE</span> `A_table` <span class="keyword">ADD</span> <span class="keyword">COLUMN</span> `col3` <span class="type">INT</span> <span class="keyword">NULL</span> AFTER `col2`;</span><br></pre></td></tr></table></figure><p>會有以下的錯誤</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SQL錯誤（<span class="number">1031</span>）：Storage engine FEDERATED of the table b_db.a_table doesn&#x27;t have this option</span><br></pre></td></tr></table></figure><p>如果透過 <code>execute</code> 呢?</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">use B_DB;</span><br><span class="line"><span class="keyword">set</span> <span class="variable">@sql</span> :<span class="operator">=</span> <span class="string">&#x27;ALTER TABLE A_table ADD COLUMN col3 INT NULL AFTER col2&#x27;</span>;</span><br><span class="line"><span class="keyword">prepare</span> stmt <span class="keyword">from</span> <span class="variable">@sql</span>;</span><br><span class="line"><span class="keyword">execute</span> stmt;</span><br></pre></td></tr></table></figure><p>還是跟上面一樣的錯(不能偷渡就是了)。</p><h2 id="一些想法"><a href="#一些想法" class="headerlink" title="一些想法"></a>一些想法</h2><p>這個 FEDERATED Table 有點像 .Net 開發時拉 Web Reference 會產生出 wsdl 檔在本機，然後透過他的定義去使用 Web Services，只是開發時有一些方法可以 auto update wsdl，以確保介面進出的正確性，這裡的 FEDERATED Table 沒有更新的功能。</p><p>不過如果資料表都沒什麼會動的話其實沒什麼差異，這樣就很好用了。</p><p>然後因為是 Table Level 的關係，Linked Server 只要做一個 Database Link 即可，但是這個就要每個 Table 都要建一次。</p><p>最後的架構</p><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/ls_3.png /><h2 id="上線紀錄"><a href="#上線紀錄" class="headerlink" title="上線紀錄"></a>上線紀錄</h2><h3 id="2016-11-14"><a href="#2016-11-14" class="headerlink" title="2016&#x2F;11&#x2F;14"></a>2016&#x2F;11&#x2F;14</h3><p>實際執行的時候，發現到有很大的問題，不管是怎麼調整都會有一樣的錯。</p><ol><li>網路很通暢 (之前 MSSQL 的 Linked Server 的路)</li><li>資料庫 loading 很低 (挑選沒有人使用資料庫的作業時間)</li><li>減少 access Federated Table 的次數 (原來 <code>insert into federated_table select * from local_table ...;</code> -&gt; <code>insert into local_staged_table select * from local_table ...; insert into federated_table select * from local_staged_table;</code>)</li></ol><p>錯誤的訊息 <code>Got error 10000 &#39;Error on remote system: 2006: MySQL server has gone away&#39; from FEDERATED</code>。</p><p>這個也是一個蠻久的 <a href="https://jira.mariadb.org/browse/MDEV-4452">bug</a>，我也發了<a href="https://jira.mariadb.org/browse/MDEV-11276">一個</a>，希望未來有解。</p><p>現在的方法改成是 <code>mysqldump ... DB.table | mysql ... DB ...</code>，透過 <a href="https://sujunmin.github.io/blog/2017/06/08/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[8]%20User-defined%20Functions%20%E8%88%87%E6%93%B4%E5%A2%9E%20Event%20%E5%8A%9F%E8%83%BD/"><code>sys_exec</code></a> 執行。</p><h3 id="2016-11-23"><a href="#2016-11-23" class="headerlink" title="2016&#x2F;11&#x2F;23"></a>2016&#x2F;11&#x2F;23</h3><p>感謝<a href="https://jira.mariadb.org/secure/ViewProfile.jspa?name=elenst">神人</a>幫忙，終於解開疑惑了，他的解釋十分清楚，也不厭其煩的解釋每個動作。</p><p>原因在於遠端連線的問題。</p><p>基本上一個 Federated Table 的存取大概是這樣</p><table><thead><tr><th>動作</th><th>到遠端連線狀態</th><th>結果</th></tr></thead><tbody><tr><td><code>Create Server</code></td><td>N&#x2F;A</td><td>OK</td></tr><tr><td><code>Create Table</code></td><td>會測試連線是否正常，測試完即結束連線</td><td>OK</td></tr><tr><td>1st DMLs for Federate Table</td><td>第一個動作會建起到遠端連線的 session，並做相關紀錄</td><td>OK</td></tr><tr><td>Other DMLs for Federate Table</td><td>順利透過剛剛建立的 session 繼續作業</td><td>OK</td></tr><tr><td>遠端的 session 的 idle 時間大於 session <code>wait_timeout</code> 導致被 Kill 掉 <br />遠端重開機或是一些緣故導致之前建立的 session 斷掉</td><td>N&#x2F;A</td><td>N&#x2F;A</td></tr><tr><td>斷掉後的第一個 DMLs for Federate Table</td><td>N&#x2F;A</td><td>必定失敗(因為原來紀錄的 session 不見了)</td></tr><tr><td>重做 DMLs for Federate Table 或是下一個</td><td>建起到遠端連線的 session，並做相關紀錄</td><td>OK</td></tr></tbody></table><p>我的問題是每天做一次的排程匯入資料從 A 地到 B 地，在預設的情況下遠端的 session 的 idle 時間大於 session <code>wait_timeout</code> (28800 秒，8小時) 導致被 Kill 掉，下一次作業時就會因為必定失敗的問題而無法繼續，所以有以下兩個解法。</p><ol><li>設大一點的 <code>wait_timeout</code>，但我覺得比較不切實際，因為如果重開機了 session 不在還是得失敗一次才能完成。</li><li>每個小時 select federated table 一次，這個我覺得可以把問題縮短到一個小時就能發現，比較實際。</li></ol><h3 id="2018-11-19"><a href="#2018-11-19" class="headerlink" title="2018&#x2F;11&#x2F;19"></a>2018&#x2F;11&#x2F;19</h3><p>在建立完排程機制後，還是會發現錯誤，類似像這樣，規律的 “Got an error reading communication packets”。<br><img src=https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/ls_4.png /></p><p>原來因為發生只在每個小時維持 Federated Table 連線的 Job，最近發現到連正常的 Job 也會拋錯，因此想來好好解決了。</p><p>兩個 Site 之前的網路重新調整後，有比較少一點，但是還是會發生。</p><p>調整資料庫 *timeout, allowed packets 也沒用。</p><p>有天找到<a href="https://bugs.mysql.com/bug.php?id=67861">這個</a>，發現到有人透過 <code>flush table</code> 方式來避免這個錯誤，來試試看以後，竟然就不會有錯了，解了多年來的問題。</p><p>後來發個 <a href="https://jira.mariadb.org/browse/MDEV-17651">issue</a>，被 merge 到其他 issue 上，雖然覺得不是一樣的問題。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;資料庫有跨地區傳送資料的需求，在 SQL Server 用到 Linked Server 來傳送資料，希望在</summary>
        
      
    
    
    
    
    <category term="MariaDB" scheme="https://sujunmin.github.io/blog/tags/MariaDB/"/>
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
  <entry>
    <title>從 SQL Server 到 MariaDB - [6] Audit 的實作</title>
    <link href="https://sujunmin.github.io/blog/2016/10/03/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[6]%20Audit%20%E7%9A%84%E5%AF%A6%E4%BD%9C/"/>
    <id>https://sujunmin.github.io/blog/2016/10/03/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[6]%20Audit%20%E7%9A%84%E5%AF%A6%E4%BD%9C/</id>
    <published>2016-10-03T05:52:23.000Z</published>
    <updated>2026-09-26T03:17:55.353Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>在資料庫的 Audit (稽核) 部份，需要有一套如同 SQL Server 的方式來執行。</p><h2 id="MariaDB-Server-Audit-Plugin"><a href="#MariaDB-Server-Audit-Plugin" class="headerlink" title="MariaDB Server Audit Plugin"></a>MariaDB Server Audit Plugin</h2><p>好在 MariaDB 有 Server Audit Plugin，相關文件<a href="https://mariadb.com/kb/en/mariadb/about-the-mariadb-audit-plugin/">在此</a>。</p><p>開起來很簡單，設定也很容易。</p><h2 id="MariaDB-Server-Audit-Plugin-與-SQL-Sever-Trace-File-Audit-的比較"><a href="#MariaDB-Server-Audit-Plugin-與-SQL-Sever-Trace-File-Audit-的比較" class="headerlink" title="MariaDB Server Audit Plugin 與 SQL Sever Trace File Audit 的比較"></a>MariaDB Server Audit Plugin 與 SQL Sever Trace File Audit 的比較</h2><p>SQL Server 的 Audit 是用 Trace File 來儲存 Audit 資料，以下用一個表格做一個比較</p><table><thead><tr><th>項目</th><th>SQL Server Trace File Audit</th><th>MariaDB Server Audit Plugin</th></tr></thead><tbody><tr><td>儲存方式</td><td>Binary File 自有格式</td><td>Text File (逗號隔開)， syslog</td></tr><tr><td>取用方式</td><td><code>fn_trace_gettable()</code> 取得資料</td><td>打開檔案或是 syslog client</td></tr><tr><td>紀錄分類方式與數量</td><td>20 種 Class，每個 Class 數個 Events (<a href="https://msdn.microsoft.com/en-us/library/ms175481(v=sql.110).aspx">2012</a>)</td><td>8 種 (<code>CONNECT</code>, <code>QUERY</code>, <code>READ</code>, <code>WRITE</code>, <code>CREATE</code>, <code>ALTER</code>, <code>RENAME</code>, <code>DROP</code>)</td></tr><tr><td>資料的控制</td><td><code>sp_trace_*</code> 設定人事時地物，檔案需要自己處理</td><td><code>server_audit_*</code> 設定人事時地物，有 rotate 設定</td></tr></tbody></table><h2 id="設定說明"><a href="#設定說明" class="headerlink" title="設定說明"></a>設定說明</h2><p>在文件裡 Logging events 一節提到的可設定的 Type，說明如下</p><ol><li><code>CONNECT</code> 就是連線訊息</li><li><code>QUERY</code> 執行 SQL 指令與結果代碼</li><li><code>TABLE</code> 哪些 Tables 因此 <code>QUERY</code> 而受影響</li><li><code>QUERY_DDL</code> DDL 的 Query，是 <code>QUERY</code>的子集合</li><li><code>QUERY_DML</code> DML 的 Query，是 <code>QUERY</code>的子集合</li><li><code>QUERY_DCL</code> DCL 的 Query，是 <code>QUERY</code>的子集合</li></ol><p>什麼是預設的 DDL, DML, DCL 的 Query 呢? 這邊要看 Code 才知道，因為後續會有改程式碼的動作，所以到時一併說明之。</p><p>另外一個部份是 Output 的 Operation 欄位，有 <code>CONNECT</code>, <code>QUERY</code>, <code>READ</code>, <code>WRITE</code>, <code>CREATE</code>, <code>ALTER</code>, <code>RENAME</code>, <code>DROP</code>，這部份的詳細定義也是要看 Code。</p><h2 id="客製化-Server-Audit-Plugin"><a href="#客製化-Server-Audit-Plugin" class="headerlink" title="客製化 Server Audit Plugin"></a>客製化 Server Audit Plugin</h2><p>原來的 Server Audit Plugin 其實大部份的需求都可以達成了，如果有<a href="https://github.com/sujunmin/SQLServerTraceFileParser">自己</a>做 Log Server 的需求，需要更細緻的分類，因為有些資訊可以延遲送出(如日報表)，但是特權帳號的使用要馬上通報給其他長官，光只有這 8 種情形無法滿足需求。</p><h3 id="針對-DDL-與-DCL-加入更細緻的分類"><a href="#針對-DDL-與-DCL-加入更細緻的分類" class="headerlink" title="針對 DDL 與 DCL 加入更細緻的分類"></a>針對 DDL 與 DCL 加入更細緻的分類</h3><p>這邊先來看看 MariaDB 對於 DDL 與 DCL 是怎麼判斷的，首先到官網下載原始碼 (如 10.1.17)，不要到 github 上抓，因為那太新了。</p><h4 id="Code-分析與修改"><a href="#Code-分析與修改" class="headerlink" title="Code 分析與修改"></a>Code 分析與修改</h4><p>到 <code>plugin\server_audit\server_audit.c</code> 找到以下程式碼</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">sa_keywords</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  SQLCOM_NOTHING=<span class="number">0</span>,</span><br><span class="line">  SQLCOM_DDL,</span><br><span class="line">  SQLCOM_DML,</span><br><span class="line">  SQLCOM_GRANT,</span><br><span class="line">  SQLCOM_CREATE_USER,</span><br><span class="line">  SQLCOM_CHANGE_MASTER,</span><br><span class="line">  SQLCOM_CREATE_SERVER,</span><br><span class="line">  SQLCOM_SET_OPTION,</span><br><span class="line">  SQLCOM_ALTER_SERVER,</span><br><span class="line">  SQLCOM_TRUNCATE,</span><br><span class="line">  SQLCOM_QUERY_ADMIN,</span><br><span class="line">  SQLCOM_DCL,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="type">int</span> length;</span><br><span class="line">  <span class="type">const</span> <span class="type">char</span> *wd;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> *<span class="title">next</span>;</span></span><br><span class="line">  <span class="class"><span class="keyword">enum</span> <span class="title">sa_keywords</span> <span class="title">type</span>;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">xml_word</span>=</span>   &#123;<span class="number">3</span>, <span class="string">&quot;XML&quot;</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">user_word</span>=</span>   &#123;<span class="number">4</span>, <span class="string">&quot;USER&quot;</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">data_word</span>=</span>   &#123;<span class="number">4</span>, <span class="string">&quot;DATA&quot;</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">server_word</span>=</span> &#123;<span class="number">6</span>, <span class="string">&quot;SERVER&quot;</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">master_word</span>=</span> &#123;<span class="number">6</span>, <span class="string">&quot;MASTER&quot;</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">password_word</span>=</span> &#123;<span class="number">8</span>, <span class="string">&quot;PASSWORD&quot;</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">function_word</span>=</span> &#123;<span class="number">8</span>, <span class="string">&quot;FUNCTION&quot;</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">statement_word</span>=</span> &#123;<span class="number">9</span>, <span class="string">&quot;STATEMENT&quot;</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">procedure_word</span>=</span> &#123;<span class="number">9</span>, <span class="string">&quot;PROCEDURE&quot;</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">keywords_to_skip</span>[]=</span></span><br><span class="line">&#123;</span><br><span class="line">  &#123;<span class="number">3</span>, <span class="string">&quot;SET&quot;</span>, &amp;statement_word, SQLCOM_QUERY_ADMIN&#125;,</span><br><span class="line">  &#123;<span class="number">0</span>, <span class="literal">NULL</span>, <span class="number">0</span>, SQLCOM_DDL&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">not_ddl_keywords</span>[]=</span></span><br><span class="line">&#123;</span><br><span class="line">  &#123;<span class="number">4</span>, <span class="string">&quot;DROP&quot;</span>, &amp;function_word, SQLCOM_QUERY_ADMIN&#125;,</span><br><span class="line">  &#123;<span class="number">4</span>, <span class="string">&quot;DROP&quot;</span>, &amp;procedure_word, SQLCOM_QUERY_ADMIN&#125;,</span><br><span class="line">  &#123;<span class="number">4</span>, <span class="string">&quot;DROP&quot;</span>, &amp;user_word, SQLCOM_DCL&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;CREATE&quot;</span>, &amp;user_word, SQLCOM_DCL&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;CREATE&quot;</span>, &amp;function_word, SQLCOM_QUERY_ADMIN&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;CREATE&quot;</span>, &amp;procedure_word, SQLCOM_QUERY_ADMIN&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;RENAME&quot;</span>, &amp;user_word, SQLCOM_DCL&#125;,</span><br><span class="line">  &#123;<span class="number">0</span>, <span class="literal">NULL</span>, <span class="number">0</span>, SQLCOM_DDL&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">ddl_keywords</span>[]=</span></span><br><span class="line">&#123;</span><br><span class="line">  &#123;<span class="number">4</span>, <span class="string">&quot;DROP&quot;</span>, <span class="number">0</span>, SQLCOM_DDL&#125;,</span><br><span class="line">  &#123;<span class="number">5</span>, <span class="string">&quot;ALTER&quot;</span>, <span class="number">0</span>, SQLCOM_DDL&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;CREATE&quot;</span>, <span class="number">0</span>, SQLCOM_DDL&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;RENAME&quot;</span>, <span class="number">0</span>, SQLCOM_DDL&#125;,</span><br><span class="line">  &#123;<span class="number">8</span>, <span class="string">&quot;TRUNCATE&quot;</span>, <span class="number">0</span>, SQLCOM_DDL&#125;,</span><br><span class="line">  &#123;<span class="number">0</span>, <span class="literal">NULL</span>, <span class="number">0</span>, SQLCOM_DDL&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>以上可以看的的出來 DDL 是哪些會算，哪些不會算，譬如說 <code>DROP</code>, <code>DROP User</code> 算是 DDL，但是 <code>DROP Function</code> 不算 DDL。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">dml_keywords</span>[]=</span></span><br><span class="line">&#123;</span><br><span class="line">  &#123;<span class="number">2</span>, <span class="string">&quot;DO&quot;</span>, <span class="number">0</span>, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">4</span>, <span class="string">&quot;CALL&quot;</span>, <span class="number">0</span>, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">4</span>, <span class="string">&quot;LOAD&quot;</span>, &amp;data_word, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">4</span>, <span class="string">&quot;LOAD&quot;</span>, &amp;xml_word, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;DELETE&quot;</span>, <span class="number">0</span>, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;INSERT&quot;</span>, <span class="number">0</span>, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;SELECT&quot;</span>, <span class="number">0</span>, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;UPDATE&quot;</span>, <span class="number">0</span>, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">7</span>, <span class="string">&quot;HANDLER&quot;</span>, <span class="number">0</span>, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">7</span>, <span class="string">&quot;REPLACE&quot;</span>, <span class="number">0</span>, SQLCOM_DML&#125;,</span><br><span class="line">  &#123;<span class="number">0</span>, <span class="literal">NULL</span>, <span class="number">0</span>, SQLCOM_DML&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>這邊可以看得出來 DML。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">dcl_keywords</span>[]=</span></span><br><span class="line">&#123;</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;CREATE&quot;</span>, &amp;user_word, SQLCOM_DCL&#125;,</span><br><span class="line">  &#123;<span class="number">4</span>, <span class="string">&quot;DROP&quot;</span>, &amp;user_word, SQLCOM_DCL&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;RENAME&quot;</span>, &amp;user_word, SQLCOM_DCL&#125;,</span><br><span class="line">  &#123;<span class="number">5</span>, <span class="string">&quot;GRANT&quot;</span>, <span class="number">0</span>, SQLCOM_DCL&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;REVOKE&quot;</span>, <span class="number">0</span>, SQLCOM_DCL&#125;,</span><br><span class="line">  &#123;<span class="number">3</span>, <span class="string">&quot;SET&quot;</span>, &amp;password_word, SQLCOM_DCL&#125;,</span><br><span class="line">  &#123;<span class="number">0</span>, <span class="literal">NULL</span>, <span class="number">0</span>, SQLCOM_DDL&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>這邊可以看得出來 DCL。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sa_keyword</span> <span class="title">passwd_keywords</span>[]=</span></span><br><span class="line">&#123;</span><br><span class="line">  &#123;<span class="number">3</span>, <span class="string">&quot;SET&quot;</span>, &amp;password_word, SQLCOM_SET_OPTION&#125;,</span><br><span class="line">  &#123;<span class="number">5</span>, <span class="string">&quot;ALTER&quot;</span>, &amp;server_word, SQLCOM_ALTER_SERVER&#125;,</span><br><span class="line">  &#123;<span class="number">5</span>, <span class="string">&quot;GRANT&quot;</span>, <span class="number">0</span>, SQLCOM_GRANT&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;CREATE&quot;</span>, &amp;user_word, SQLCOM_CREATE_USER&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;CREATE&quot;</span>, &amp;server_word, SQLCOM_CREATE_SERVER&#125;,</span><br><span class="line">  &#123;<span class="number">6</span>, <span class="string">&quot;CHANGE&quot;</span>, &amp;master_word, SQLCOM_CHANGE_MASTER&#125;,</span><br><span class="line">  &#123;<span class="number">0</span>, <span class="literal">NULL</span>, <span class="number">0</span>, SQLCOM_NOTHING&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>這邊說明什麼時候會碰到密碼 (在 log 裡會紀錄動作，然後把密碼用星號表示)。</p><p>接下來找到以下的程式碼</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">log_statement_ex</span><span class="params">(<span class="type">const</span> <span class="keyword">struct</span> connection_info *cn,</span></span><br><span class="line"><span class="params">                            <span class="type">time_t</span> ev_time, <span class="type">unsigned</span> <span class="type">long</span> thd_id,</span></span><br><span class="line"><span class="params">                            <span class="type">const</span> <span class="type">char</span> *query, <span class="type">unsigned</span> <span class="type">int</span> query_len,</span></span><br><span class="line"><span class="params">                            <span class="type">int</span> error_code, <span class="type">const</span> <span class="type">char</span> *type)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">size_t</span> csize;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">if</span> (query == <span class="number">0</span>)</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="comment">/* Can happen after the error in mysqld_prepare_stmt() */</span></span><br><span class="line">    query= cn-&gt;query;</span><br><span class="line">    query_len= cn-&gt;query_length;</span><br><span class="line">    <span class="keyword">if</span> (query == <span class="number">0</span> || query_len == <span class="number">0</span>)</span><br><span class="line">      <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">if</span> (query &amp;&amp; !(events &amp; EVENT_QUERY_ALL) &amp;&amp;</span><br><span class="line">      (events &amp; EVENT_QUERY))</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="type">const</span> <span class="type">char</span> *orig_query= query;</span><br><span class="line">    ...</span><br><span class="line">    ...</span><br><span class="line">    ...</span><br><span class="line">csize= log_header(message, message_size<span class="number">-1</span>, &amp;ev_time,</span><br><span class="line">                    servhost, servhost_len,</span><br><span class="line">                    cn-&gt;user, cn-&gt;user_length,cn-&gt;host, cn-&gt;host_length,</span><br><span class="line">                    cn-&gt;ip, cn-&gt;ip_length, thd_id, query_id, type);</span><br><span class="line">    ...    </span><br></pre></td></tr></table></figure><p>這邊的 Code 說明有許多的紀錄後來會被標記成 <code>QUERY</code>，如果要利用原來已經分類的 DCL 與 DDL 架構，這邊就要先來利用一下。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">log_statement_ex</span><span class="params">(<span class="type">const</span> <span class="keyword">struct</span> connection_info *cn,</span></span><br><span class="line"><span class="params">                            <span class="type">time_t</span> ev_time, <span class="type">unsigned</span> <span class="type">long</span> thd_id,</span></span><br><span class="line"><span class="params">                            <span class="type">const</span> <span class="type">char</span> *query, <span class="type">unsigned</span> <span class="type">int</span> query_len,</span></span><br><span class="line"><span class="params">                            <span class="type">int</span> error_code, <span class="type">const</span> <span class="type">char</span> *type)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">size_t</span> csize;</span><br><span class="line">  ...</span><br><span class="line">  <span class="type">char</span> *modtype = type;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (query == <span class="number">0</span>)</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="comment">/* Can happen after the error in mysqld_prepare_stmt() */</span></span><br><span class="line">    query= cn-&gt;query;</span><br><span class="line">    query_len= cn-&gt;query_length;</span><br><span class="line">    <span class="keyword">if</span> (query == <span class="number">0</span> || query_len == <span class="number">0</span>)</span><br><span class="line">      <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (!filter_query_type(query, not_ddl_keywords) &amp;&amp;</span><br><span class="line">  filter_query_type(query, ddl_keywords))</span><br><span class="line">  modtype = <span class="string">&quot;QUERY_DDL&quot;</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (filter_query_type(query, dml_keywords))</span><br><span class="line">  modtype = <span class="string">&quot;QUERY_DML&quot;</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (filter_query_type(query, dcl_keywords))</span><br><span class="line">  modtype = <span class="string">&quot;QUERY_DCL&quot;</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">if</span> (query &amp;&amp; !(events &amp; EVENT_QUERY_ALL) &amp;&amp;</span><br><span class="line">      (events &amp; EVENT_QUERY))</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="type">const</span> <span class="type">char</span> *orig_query= query;</span><br><span class="line">    ...</span><br><span class="line">    ...</span><br><span class="line">    ...</span><br><span class="line">  csize= log_header(message, message_size<span class="number">-1</span>, &amp;ev_time,</span><br><span class="line">                    servhost, servhost_len,</span><br><span class="line">                    cn-&gt;user, cn-&gt;user_length,cn-&gt;host, cn-&gt;host_length,</span><br><span class="line">                    cn-&gt;ip, cn-&gt;ip_length, thd_id, query_id, modtype);</span><br><span class="line">    ...  </span><br></pre></td></tr></table></figure><h4 id="Build-Plugin"><a href="#Build-Plugin" class="headerlink" title="Build Plugin"></a>Build Plugin</h4><p>接下來要來 Build Plugin，根據這個<a href="https://mariadb.com/kb/en/mariadb/Building_MariaDB_on_Windows/">文件</a>，安裝該安裝的函式庫與工具，以下紀錄一下</p><ol><li>Visual Studio 2015 Community 版</li><li>Bison (記得路徑不要有空白)</li><li>cmake</li><li>Windows SDK 8.1 (這個現在沒裝沒關係，等會可以裝)</li></ol><p>Build 方式，在 Source 底下 </p><ol><li><code>mkdir bld</code></li><li><code>cd bld</code></li><li><code>cmake .. -G &quot;Visual Studio 14 2015 Win64&quot;</code> (64 位元版)</li><li><code>cmake --build . --config Relwithdebinfo</code></li></ol><p>會碰到的錯 (然後會有一大堆的 warning)</p><ol><li><p>Windows SDK 8.1 沒裝</p><p>這個要去 <code>bld</code> 底下用 IDE 開 <code>MySQL.sln</code>，打開的時候他會說你的 SDK 缺少，要不要下載安裝，讓他自己跑完就可以了。</p></li><li><p><code>sql\sql_locale.cc</code> 有怪字無法編譯</p><p>我查到的結果都是開 notepad++ 去改 utf-8，不過我做完還是失敗，後來是開 notepad 然後用 unicode 儲存 (不是 utf-8 喔) 就可以過了。</p></li></ol><h3 id="測試"><a href="#測試" class="headerlink" title="測試"></a>測試</h3><p>把 MariaDB 停掉，從 <code>bld\plugin\server_audit\RelWithDebInfo</code> 把 dll 跟 pdb 複製到 <code>MariaDB\lib</code> 下，重開看看。</p><p>原來的 log 大概像這樣</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">20161003 13:15:58,WSTest,sujunmin,localhost,98,783,QUERY,test1,&#x27;REVOKE RELOAD  ON *.* FROM \&#x27;lsuser\&#x27;@\&#x27;%\&#x27;&#x27;,0</span><br></pre></td></tr></table></figure><p>應該會變成這樣</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">20161003 16:31:14,WSTest,sujunmin,localhost,98,783,QUERY_DCL,test1,&#x27;REVOKE RELOAD  ON *.* FROM \&#x27;lsuser\&#x27;@\&#x27;%\&#x27;&#x27;,0</span><br></pre></td></tr></table></figure><p>後續的 Log Parser 就可以根據這些關鍵字來分析使用了。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;在資料庫的 Audit (稽核) 部份，需要有一套如同 SQL Server 的方式來執行。&lt;/p&gt;
&lt;h2</summary>
        
      
    
    
    
    
    <category term="MariaDB" scheme="https://sujunmin.github.io/blog/tags/MariaDB/"/>
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
  <entry>
    <title>用指令開啟或關閉 SD 卡合併到內存</title>
    <link href="https://sujunmin.github.io/blog/2016/09/27/%E7%94%A8%E6%8C%87%E4%BB%A4%E9%96%8B%E5%95%9F%E6%88%96%E9%97%9C%E9%96%89%20SD%20%E5%8D%A1%E5%90%88%E4%BD%B5%E5%88%B0%E5%85%A7%E5%AD%98/"/>
    <id>https://sujunmin.github.io/blog/2016/09/27/%E7%94%A8%E6%8C%87%E4%BB%A4%E9%96%8B%E5%95%9F%E6%88%96%E9%97%9C%E9%96%89%20SD%20%E5%8D%A1%E5%90%88%E4%BD%B5%E5%88%B0%E5%85%A7%E5%AD%98/</id>
    <published>2016-09-27T12:55:22.000Z</published>
    <updated>2026-09-26T03:17:55.356Z</updated>
    
    <content type="html"><![CDATA[<p>今天老婆說剛買沒多久的手機又滿了，不能裝東西，想說颱風天來幫她合併 SD 卡內存，這樣她又更能盡情裝 APP 了(?)，結果系統一直顯示無法合併 (因為速度太慢)，但是其實測速以後還好阿，因此用手動的方式來設定合併內存。</p><p>這篇有用的對象如下</p><ol><li>Android 6.0 以上設備</li><li>會開啟 adb 跟 debug 模式</li><li>不管 SD 卡的速度 (因為合併內存一定會降速度，很差的卡降更多)</li><li>合併內存功能被鎖起來，或是因為速度偵測而無法合併的都可以使用</li></ol><h1 id="合併內存"><a href="#合併內存" class="headerlink" title="合併內存"></a>合併內存</h1><ol><li>備份 SD 卡</li><li><code>adb start-server</code></li><li><code>adb shell sm list-volumes all</code> [應該類似 public:179,32 mounted … 這樣，後面的 179,32 等會會用到]</li><li><code>adb shell sm set-force-adoptable true</code></li><li><code>adb shell sm partition disk:179,32 private</code> [這步將會把 SD 卡合併到內存，包含格式化，等了很久才完成，這時候看手機應該看不到 SD 卡了]</li><li><code>adb shell sm set-force-adoptable false</code></li><li><code>adb shell sm list-volumes all</code> [應該會變成 private:179,32 mounted … 這樣，原來的 public 不見了]</li><li>重開機</li><li>檢查一下，到內存那邊移轉資料，還原 SD 卡資料，完成</li></ol><h1 id="恢復成可攜式-SD-卡"><a href="#恢復成可攜式-SD-卡" class="headerlink" title="恢復成可攜式 SD 卡"></a>恢復成可攜式 SD 卡</h1><ol><li>備份 SD 卡</li><li><code>adb start-server</code></li><li><code>adb shell sm list-volumes all</code> [看 ID，179,32]</li><li><code>adb shell sm set-force-adoptable true</code></li><li><code>adb shell sm partition disk:179,32 public</code> [這步會把 SD 設為可攜式 SD 卡，包含格式化]</li><li><code>adb shell sm set-force-adoptable false</code></li><li><code>adb shell sm list-volumes all</code> [應該會變到 public 裡]</li><li>重開機</li><li>檢查一下，還原 SD 卡資料，完成</li></ol>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;今天老婆說剛買沒多久的手機又滿了，不能裝東西，想說颱風天來幫她合併 SD 卡內存，這樣她又更能盡情裝 APP 了(?)，結果系統一直顯示無法合併</summary>
        
      
    
    
    
    
    <category term="Android" scheme="https://sujunmin.github.io/blog/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>從 SQL Server 到 MariaDB - [5] Log-shipping 與 MariaDB Replication</title>
    <link href="https://sujunmin.github.io/blog/2016/09/19/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[5]%20Log-shipping%20%E8%88%87%20MariaDB%20Replication/"/>
    <id>https://sujunmin.github.io/blog/2016/09/19/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[5]%20Log-shipping%20%E8%88%87%20MariaDB%20Replication/</id>
    <published>2016-09-19T03:50:18.000Z</published>
    <updated>2026-09-26T03:17:55.353Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>在資料庫的備援部份，採用 SQL Server Log-shipping 方式來處理，希望在移轉至 MariaDB 時也有不錯的類似 Log-shipping 方式執行。</p><h2 id="MariaDB-Replication-uses-GTID"><a href="#MariaDB-Replication-uses-GTID" class="headerlink" title="MariaDB Replication uses GTID"></a>MariaDB Replication uses GTID</h2><p>接下來的文章來自於許多的文件，從<a href="https://mariadb.com/kb/en/mariadb/gtid/">官方文件</a>，<a href="http://mysql.taobao.org/monthly/2016/02/08/">MariaDB · 版本特性 · MariaDB 的 GTID 介绍</a>，還有<a href="https://segmentfault.com/a/1190000000598062">mariadb10 GTID 研究笔记. md</a>與<a href="https://segmentfault.com/a/1190000000601849">實作</a>，整理了一下。</p><h3 id="Master-與-Slave"><a href="#Master-與-Slave" class="headerlink" title="Master 與 Slave"></a>Master 與 Slave</h3><p>與 SQL Server Log-shipping 不一樣的地方如下</p><ol><li>不管什麼樣的 Replication (樹狀或是環型串聯)，發動端皆是 <b>Slave</b>，也就是說 Master 不存放任何有關 Replication 的資料，所有資料皆在 Slave 裡。</li><li>在 Primary Database (Master) 會是 Backup Trasnsaction 的動作，而在 Secondary Database (Slave) 是 Copy 與 Restore 的動作，在 MariaDB Replication 只會在 Slave 連到 Master 時看要複製哪一段的 binlog 加以重現。</li><li>因為 GTID 的設計，一個 Slave 的 Master 可以是多個，當然一個 Master 可以有多個 Slave。</li><li>Slave 重現完資料以後就會繼續跟 Master 要進一步的 binlog 資料，所以基本上是沒有落差可言的，在 SQL Server Log-shipping 是有發動時間設定的(預設為 15 分鐘)。</li><li>系統的資料庫 (mysql 那些)也可以做 Replication。</li></ol><p>在 4 的部份，其實對於現在的維運有些麻煩。因為這 15 分鐘的落差，可以拿來利用成正式環境的 buffer，下錯指令時如果還在 15 分鐘的時間分隔下，還可以趕快去備援區拿資料蓋回去，現在沒有時間設定的話就比較麻煩。有人發個 <a href="https://jira.mariadb.org/browse/MDEV-7145">issue</a> 希望能 merge MySQL 的 <code>MASTER_DELAY</code> 功能，不過表定是 10.2.x 的功能，希望能實作在 10.1.x。</p><p>在 5 的部份我覺得還蠻方便的就是帳號密碼的同步，在 SQL Server Log-shipping 是要分開想辦法同步的，管理就很方便了。</p><h3 id="Slave-如何透過-GTID-做-Replication"><a href="#Slave-如何透過-GTID-做-Replication" class="headerlink" title="Slave 如何透過 GTID 做 Replication"></a>Slave 如何透過 GTID 做 Replication</h3><p>GTID 的規格部份就不解釋了，上面的文件寫的很清楚。</p><p>在 MariaDB 有一些 Server Variables 紀錄 GTID 的資訊</p><table><thead><tr><th>Server Variables Names</th><th>意義</th></tr></thead><tbody><tr><td><code>gtid_binlog_pos</code></td><td>binlog 裡最後一筆紀錄的  GTID</td></tr><tr><td><code>gtid_slave_pos</code></td><td>Slave 上紀錄的最後一個 binlog GTID</td></tr><tr><td><code>gtid_current_pos</code></td><td>Database 上紀錄的最後一個 binlog GTID</td></tr></tbody></table><p>什麼叫作 “紀錄的最後一個 binlog GTID” 呢?</p><p>對 <code>gtid_slave_pos</code> 來說，就是 master 的最後一筆 GTID。</p><p>對 <code>gtid_current_pos</code> 來說，不管當下的 Instance 之前是 Master 還是 Slave</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">if gtid_binlog_pos.ServerID = Instance.ServerID and gtid_binlog_pos.DomainID = Instance.DomainID and gtid_binlog_pos.SequenceID &gt; gtid_slave_pos.SequenceID (同 ServerID 與 DomainID 狀態下) then</span><br><span class="line">  gtid_current_pos = gtid_binlog_pos</span><br><span class="line">else </span><br><span class="line">  gtid_current_pos = gtid_slave_pos (同 ServerID 與 DomainID 狀態下)</span><br><span class="line">end if</span><br></pre></td></tr></table></figure><p>官方文件有說到一個例子，假設 A 為 Master，B 為 Slave，參數為 <code>slave_pos</code>，把 A 先關一會兒，讓 B 成為 Master，最後回復 A 並成為 B 的 Slave。</p><img src = "https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/MariaDB_Replication_1.png" /><ul><li>一開始 A 的 <code>gtid_binlog_pos</code> 與 <code>gtid_current_pos</code> 相同（<code>1-1-123</code>, <code>1-1-137</code>, <code>1-1-145</code>），<code>gtid_slave_pos</code> 是空值。</li><li>B 的 <code>gtid_slave_pos</code> 記著 A 的最後一個 GTID。</li><li>在 A 的 <code>1-1-145</code> 的時候把 A 先關掉，中間 B 的修改先忽略不看(假設沒發生)，現在資料庫停在 <code>1-1-145</code>。</li><li>在 A 恢復以後，設定 A 為 B 的 Slave，但是這個時候的 <code>gtid_slave_pos</code> 是空值，所以設成 <code>current_pos</code> (就是 <code>1-1-145</code>)。</li><li>到 B 的 <code>1-1-147</code> 的時候，最新的就是 <code>1-1-147</code>，B 就知道怎麼恢復了 (<code>145</code>-<code>147</code>)，正式加成 A 的 Slave。</li></ul><p>這個的假設前提是在那個中斷時間內沒 B 沒有任何的 binlog 紀錄交易，這樣能確保 <code>1-1-145</code> 到 <code>1-1-147</code> 都只有在有 Slave 的狀態下 Master 產生的交易，假設 B 中間原來忽略的的 <code>2-2-138</code> 被產生了，那麼 binlog 的順序就會在 <code>1-1-145</code> 與 <code>1-1-147</code> 中間夾一個 <code>2-2-138</code>，奇怪的交易紀錄，會導致 A 的加入失敗。</p><p>所以官方建議在 Slave 上不要有任何的 binlog 紀錄 (Session 中 <code>@@sql_log_bin=0</code>)，避免當這個 Slave 有機會變成 Master 或是再 Slave 到其他地方時有交易紀錄異常的問題。</p><p>如果還是要修改 Slave 的資料造成 binlog 會有變動，還是要把參數設為 <code>slave_pos</code>，確保 Slave 會從 Master 看的 binlog 資料不會因為 Slave 的變動而異常，不能像 <code>current_pos</code> 那樣兩邊都有可能取值，不管是什麼角色。</p><p>在 B 當 Slave 的時候如果沒有開 binlog，那麼 <code>current_pos</code> 與 <code>slave_pos</code> 是同一件事，當然後續的切換是不會發生的 (B 沒 binlog 不能當 Master)。</p><h2 id="最後的設計"><a href="#最後的設計" class="headerlink" title="最後的設計"></a>最後的設計</h2><p>剛剛最後有說到在 Slave 沒有開 binlog 的方式，我原來是想要用這個的，因為這蠻像現在的維運模式。</p><ol><li>當 Master 壞了就停掉 Slave 的 Service</li><li>等到 Master 好的時候停機備份到 Master 上</li><li>在 Slave 重新建 Replication</li></ol><p>如果 Slave 有開 binlog，第2步的停機備份似乎可以把時間縮短</p><ol><li>當 Master 壞了就停掉 Slave 的 Service</li><li>等到 Master 好的時候倒回 Slave 的備份</li><li>同步完成後重新建立 Master &#x2F; Slave 順序</li></ol><h2 id="測試"><a href="#測試" class="headerlink" title="測試"></a>測試</h2><h3 id="在同一個機器上開新的-Instance"><a href="#在同一個機器上開新的-Instance" class="headerlink" title="在同一個機器上開新的 Instance"></a>在同一個機器上開新的 Instance</h3><ul><li><p>有一個不錯用的工具 <a href="https://mariadb.com/kb/en/mariadb/mysql_install_dbexe/">mysql_install_db.exe</a> 可以直接開新的 Instance。</p><p>我們用這個先開一個 Instance 在 Port 3307。</p></li><li><p>設定原來在 3306 的為 Master (<code>server-id=1</code>)，3307 的為 Slave (<code>server-id=2</code>)，都開啟 binlog (<code>log-bin</code>)。 </p></li><li><p>Event 記得設成 Disabled on Slave。</p></li><li><p>接下來備份 Master</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">E:\<span class="title">backup</span>&gt;<span class="title">mysqldump</span> -<span class="title">u</span> <span class="title">sujunmin</span> -<span class="title">p</span> --<span class="title">master</span>-<span class="title">data</span> --<span class="title">all</span>-<span class="title">databases</span> --<span class="title">events</span> --<span class="title">routines</span> --<span class="title">gtid</span> &gt; <span class="title">all_db.sql</span></span></span><br><span class="line"><span class="function"><span class="title">Enter</span> <span class="title">password</span>: ************</span></span><br></pre></td></tr></table></figure><p>其中幾個重要參數</p><table><thead><tr><th>參數</th><th>意義</th></tr></thead><tbody><tr><td><code>master-data</code></td><td>dump 出來的掛 master ID</td></tr><tr><td><code>events</code></td><td>包含 events</td></tr><tr><td><code>routines</code></td><td>包含 functions 與 store procedures</td></tr><tr><td><code>gtid</code></td><td>產生 <code>CHANGE MASTER TO master_use_gtid</code> 語法</td></tr></tbody></table><p>觀察一下 <code>all_db.sql</code> 的內容</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- MySQL dump 10.16  Distrib 10.1.17-MariaDB, for Win64 (AMD64)</span></span><br><span class="line">(中間省略)</span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- Position to start replication or point-in-time recovery from</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- CHANGE MASTER TO MASTER_LOG_FILE=&#x27;WSTest-bin.000002&#x27;, MASTER_LOG_POS=3732;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- GTID to start replication from</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"></span><br><span class="line">CHANGE MASTER <span class="keyword">TO</span> MASTER_USE_GTID<span class="operator">=</span>slave_pos;</span><br><span class="line"><span class="keyword">SET</span> <span class="keyword">GLOBAL</span> gtid_slave_pos<span class="operator">=</span><span class="string">&#x27;0-1-770&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- Current Database: &#x27;master&#x27;</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line">(後面省略)</span><br></pre></td></tr></table></figure><p>可以看到如果要用舊的方式(兼容 MySQL Replication) 與新的方式。</p></li><li><p>啟動 Slave 的 Instance。  </p></li><li><p>把 <code>all_db.sql</code> 倒到 Slave (3307) 上面。</p></li><li><p>在 Master 上開一個 User <code>lsuser</code> 作為 Replication 用，權限是 <code>REPLICATION SLAVE</code> 與 <code>SUPER</code> (這個我不設定會沒權限登入，但是官方網站沒有這個權限)。</p></li><li><p>在 Slave 上設定 <code>CHANGE MASTER</code> </p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MariaDB [(none)]&gt; change master to master_host=&#x27;localhost&#x27;, master_port=<span class="number">3306</span>, master_user=&#x27;lsuser&#x27;, master_password=&#x27;password&#x27;;</span><br><span class="line">Query OK, <span class="number">0</span> rows affected (<span class="number">0</span>.<span class="number">05</span> sec)</span><br></pre></td></tr></table></figure></li><li><p><code>START SLAVE;</code></p></li><li><p><code>SHOW SLAVE STATUS;</code></p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">MariaDB [(none)]&gt; show slave status\G</span><br><span class="line">*************************** <span class="number">1</span>. row ***************************</span><br><span class="line"><span class="function">            Slave_IO_State: <span class="title">Waiting</span> <span class="title">for</span> <span class="title">master</span> <span class="title">to</span> <span class="title">send</span> <span class="title">event</span></span></span><br><span class="line"><span class="function">               <span class="title">Master_Host</span>: <span class="title">localhost</span></span></span><br><span class="line"><span class="function">               <span class="title">Master_User</span>: <span class="title">lsuser</span></span></span><br><span class="line"><span class="function">               <span class="title">Master_Port</span>: 3306</span></span><br><span class="line"><span class="function">             <span class="title">Connect_Retry</span>: 60</span></span><br><span class="line"><span class="function">           <span class="title">Master_Log_File</span>: <span class="title">WSTest</span>-<span class="title">bin</span>.000002</span></span><br><span class="line"><span class="function">       <span class="title">Read_Master_Log_Pos</span>: 5429</span></span><br><span class="line"><span class="function">            <span class="title">Relay_Log_File</span>: <span class="title">WSTest</span>-<span class="title">relay</span>-<span class="title">bin</span>.000002</span></span><br><span class="line"><span class="function">             <span class="title">Relay_Log_Pos</span>: 5757</span></span><br><span class="line"><span class="function">     <span class="title">Relay_Master_Log_File</span>: <span class="title">WSTest</span>-<span class="title">bin</span>.000002</span></span><br><span class="line"><span class="function">          <span class="title">Slave_IO_Running</span>: <span class="title">Yes</span></span></span><br><span class="line"><span class="function">         <span class="title">Slave_SQL_Running</span>: <span class="title">Yes</span></span></span><br><span class="line"><span class="function">           <span class="title">Replicate_Do_DB</span>:</span></span><br><span class="line"><span class="function">                        (省略)</span></span><br><span class="line"><span class="function">                <span class="title">Using_Gtid</span>: <span class="title">Slave_Pos</span></span></span><br><span class="line"><span class="function">               <span class="title">Gtid_IO_Pos</span>: 0-1-775</span></span><br><span class="line"><span class="function">   <span class="title">Replicate_Do_Domain_Ids</span>:</span></span><br><span class="line"><span class="function"> <span class="title">Replicate_Ignore_Domain_Ids</span>:</span></span><br><span class="line"><span class="function">             <span class="title">Parallel_Mode</span>: <span class="title">conservative</span></span></span><br><span class="line"><span class="function"> 1 <span class="title">row</span> <span class="title">in</span> <span class="title">set</span> (0.00 <span class="title">sec</span>)</span></span><br></pre></td></tr></table></figure></li><li><p>看一下同步狀況</p></li><li><p>移除 Master 模擬 Master 壞掉了狀態</p><ul><li><code>sc stop mariadb</code></li><li><code>sc delete mariadb</code></li><li>移除資料</li></ul></li><li><p>Slave 沒法連到 Master 了</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">MariaDB [(none)]&gt; show slave status\G</span><br><span class="line">*************************** <span class="number">1</span>. row ***************************</span><br><span class="line"><span class="function">             Slave_IO_State: <span class="title">Reconnecting</span> <span class="title">after</span> <span class="title">a</span> <span class="title">failed</span> <span class="title">master</span> <span class="title">event</span> <span class="title">read</span></span></span><br><span class="line"><span class="function">                <span class="title">Master_Host</span>: <span class="title">localhost</span></span></span><br><span class="line"><span class="function">                <span class="title">Master_User</span>: <span class="title">lsuser</span></span></span><br><span class="line"><span class="function">                <span class="title">Master_Port</span>: 3306</span></span><br><span class="line"><span class="function">              <span class="title">Connect_Retry</span>: 60</span></span><br><span class="line"><span class="function">            <span class="title">Master_Log_File</span>: <span class="title">WSTest</span>-<span class="title">bin</span>.000002</span></span><br><span class="line"><span class="function">        <span class="title">Read_Master_Log_Pos</span>: 8223</span></span><br><span class="line"><span class="function">             <span class="title">Relay_Log_File</span>: <span class="title">WSTest</span>-<span class="title">relay</span>-<span class="title">bin</span>.000002</span></span><br><span class="line"><span class="function">              <span class="title">Relay_Log_Pos</span>: 8551</span></span><br><span class="line"><span class="function">      <span class="title">Relay_Master_Log_File</span>: <span class="title">WSTest</span>-<span class="title">bin</span>.000002</span></span><br><span class="line"><span class="function">           <span class="title">Slave_IO_Running</span>: <span class="title">Connecting</span></span></span><br><span class="line"><span class="function">          <span class="title">Slave_SQL_Running</span>: <span class="title">Yes</span></span></span><br><span class="line"><span class="function">                         (省略)</span></span><br><span class="line"><span class="function">              <span class="title">Last_IO_Errno</span>: 2003</span></span><br><span class="line"><span class="function">              <span class="title">Last_IO_Error</span>: <span class="title">error</span> <span class="title">reconnecting</span> <span class="title">to</span> <span class="title">master</span> &#x27;<span class="title">lsuser</span>@<span class="title">localhost</span>:3306&#x27; - <span class="title">retry</span>-<span class="title">time</span>: 60  <span class="title">retries</span>: 86400  <span class="title">message</span>: <span class="title">Can</span>&#x27;<span class="title">t</span> <span class="title">connect</span> <span class="title">to</span> <span class="title">MySQL</span> <span class="title">server</span> <span class="title">on</span>&#x27;<span class="title">localhost</span>&#x27; (10061 &quot;<span class="title">Unknown</span> <span class="title">error</span>&quot;)</span></span><br><span class="line"><span class="function">             <span class="title">Last_SQL_Errno</span>: 0</span></span><br><span class="line"><span class="function">             <span class="title">Last_SQL_Error</span>:</span></span><br><span class="line"><span class="function"> <span class="title">Replicate_Ignore_Server_Ids</span>:</span></span><br><span class="line"><span class="function">           <span class="title">Master_Server_Id</span>: 1</span></span><br><span class="line"><span class="function">             <span class="title">Master_SSL_Crl</span>:</span></span><br><span class="line"><span class="function">         <span class="title">Master_SSL_Crlpath</span>:</span></span><br><span class="line"><span class="function">                 <span class="title">Using_Gtid</span>: <span class="title">Slave_Pos</span></span></span><br><span class="line"><span class="function">                <span class="title">Gtid_IO_Pos</span>: 0-1-784</span></span><br><span class="line"><span class="function">    <span class="title">Replicate_Do_Domain_Ids</span>:</span></span><br><span class="line"><span class="function"> <span class="title">Replicate_Ignore_Domain_Ids</span>:</span></span><br><span class="line"><span class="function">              <span class="title">Parallel_Mode</span>: <span class="title">conservative</span></span></span><br><span class="line"><span class="function"> 1 <span class="title">row</span> <span class="title">in</span> <span class="title">set</span> (0.00 <span class="title">sec</span>)</span></span><br></pre></td></tr></table></figure></li><li><p><code>STOP SLAVE</code></p></li><li><p>打開 Slave 的 Event 服務</p></li><li><p>繼續服務</p></li><li><p>重建 Master</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">E:\<span class="title">backup</span>&gt;<span class="title">mysqldump</span> -<span class="title">u</span> <span class="title">sujunmin</span> -<span class="title">p</span> --<span class="title">port</span> 3307 --<span class="title">master</span>-<span class="title">data</span> --<span class="title">all</span>-<span class="title">databases</span> --<span class="title">events</span> --<span class="title">routines</span> --<span class="title">gtid</span> &gt; <span class="title">all_db.sql</span></span></span><br><span class="line"><span class="function"><span class="title">Enter</span> <span class="title">password</span>: ************</span></span><br></pre></td></tr></table></figure><p>觀察一下 <code>all_db.sql</code> 的內容</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- MySQL dump 10.16  Distrib 10.1.17-MariaDB, for Win64 (AMD64)</span></span><br><span class="line">(中間省略)</span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- Position to start replication or point-in-time recovery from</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- CHANGE MASTER TO MASTER_LOG_FILE=&#x27;WSTest-bin.000003&#x27;, MASTER_LOG_POS=12441;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- GTID to start replication from</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"></span><br><span class="line">CHANGE MASTER <span class="keyword">TO</span> MASTER_USE_GTID<span class="operator">=</span>slave_pos;</span><br><span class="line"><span class="keyword">SET</span> <span class="keyword">GLOBAL</span> gtid_slave_pos<span class="operator">=</span><span class="string">&#x27;0-2-976&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- Current Database: &#x27;master&#x27;</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line">(後面省略)</span><br></pre></td></tr></table></figure></li><li><p>啟動 Master 的 Instance。  </p></li><li><p>把 <code>all_db.sql</code> 倒到 Master (3307) 上面。</p></li><li><p>在 Slave 上設定 <code>CHANGE MASTER</code> 為 <code>current_pos</code> </p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MariaDB [(none)]&gt; change master to master_host=&#x27;localhost&#x27;, master_port=<span class="number">3306</span>, master_user=&#x27;lsuser&#x27;, master_password=&#x27;password&#x27;, master_use_gtid=current_pos;</span><br><span class="line">Query OK, <span class="number">0</span> rows affected (<span class="number">0</span>.<span class="number">05</span> sec)</span><br></pre></td></tr></table></figure></li><li><p><code>START SLAVE;</code></p></li><li><p><code>SHOW SLAVE STATUS;</code></p> <figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"> MariaDB [(none)]&gt; show slave status\G</span><br><span class="line">*************************** <span class="number">1</span>. row ***************************</span><br><span class="line">                           (省略)</span><br><span class="line"><span class="function">              Last_IO_Errno: 1236</span></span><br><span class="line"><span class="function">              <span class="title">Last_IO_Error</span>: <span class="title">Got</span> <span class="title">fatal</span> <span class="title">error</span> 1236 <span class="title">from</span> <span class="title">master</span> <span class="title">when</span> <span class="title">reading</span> <span class="title">data</span> <span class="title">from</span> <span class="title">binary</span> <span class="title">log</span>: &#x27;<span class="title">Error</span>: <span class="title">connecting</span> <span class="title">slave</span> <span class="title">requested</span> <span class="title">to</span> <span class="title">start</span> <span class="title">from</span> <span class="title">GTID</span> 0-2-980, <span class="title">which</span> <span class="title">is</span> <span class="title">not</span> <span class="title">in</span> <span class="title">the</span> <span class="title">master</span>&#x27;<span class="title">s</span> <span class="title">binlog</span>. <span class="title">Since</span> <span class="title">the</span> <span class="title">master</span>&#x27;<span class="title">s</span> <span class="title">binlog</span> <span class="title">contains</span> <span class="title">GTIDs</span> <span class="title">with</span> <span class="title">higher</span> <span class="title">sequence</span> <span class="title">numbers</span>, <span class="title">it</span> <span class="title">probably</span> <span class="title">means</span> <span class="title">that</span> <span class="title">the</span> <span class="title">slave</span> <span class="title">has</span> <span class="title">diverged</span> <span class="title">due</span> <span class="title">to</span> <span class="title">executing</span> <span class="title">extra</span> <span class="title">erroneous</span> <span class="title">transactions</span>&#x27;</span></span><br><span class="line"><span class="function">             <span class="title">Last_SQL_Errno</span>: 0</span></span><br><span class="line"><span class="function">             <span class="title">Last_SQL_Error</span>:</span></span><br><span class="line"><span class="function"> <span class="title">Replicate_Ignore_Server_Ids</span>:</span></span><br><span class="line"><span class="function">           <span class="title">Master_Server_Id</span>: 1</span></span><br><span class="line"><span class="function">             <span class="title">Master_SSL_Crl</span>:</span></span><br><span class="line"><span class="function">         <span class="title">Master_SSL_Crlpath</span>:</span></span><br><span class="line"><span class="function">                 <span class="title">Using_Gtid</span>: <span class="title">Current_Pos</span></span></span><br><span class="line"><span class="function">                <span class="title">Gtid_IO_Pos</span>: 0-2-980</span></span><br><span class="line"><span class="function">    <span class="title">Replicate_Do_Domain_Ids</span>:</span></span><br><span class="line"><span class="function"> <span class="title">Replicate_Ignore_Domain_Ids</span>:</span></span><br><span class="line"><span class="function">               <span class="title">Parallel_Mode</span>: <span class="title">conservative</span></span></span><br><span class="line"><span class="function">1 <span class="title">row</span> <span class="title">in</span> <span class="title">set</span> (0.00 <span class="title">sec</span>)</span></span><br></pre></td></tr></table></figure></li><li><p><code>STOP SLAVE;</code></p></li><li><p>在 Slave 上設定 <code>CHANGE MASTER</code> 為 <code>slave_pos</code>   </p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MariaDB [(none)]&gt; change master to master_host=&#x27;localhost&#x27;, master_port=<span class="number">3306</span>, master_user=&#x27;lsuser&#x27;, master_password=&#x27;password&#x27;, master_use_gtid=slave_pos;</span><br><span class="line">Query OK, <span class="number">0</span> rows affected (<span class="number">0</span>.<span class="number">05</span> sec)</span><br></pre></td></tr></table></figure></li><li><p><code>START SLAVE;</code></p></li><li><p><code>SHOW SLAVE STATUS;</code></p> <figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">MariaDB [(none)]&gt; show slave status\G</span><br><span class="line">*************************** <span class="number">1</span>. row ***************************</span><br><span class="line"><span class="function">            Slave_IO_State: <span class="title">Waiting</span> <span class="title">for</span> <span class="title">master</span> <span class="title">to</span> <span class="title">send</span> <span class="title">event</span></span></span><br><span class="line"><span class="function">               <span class="title">Master_Host</span>: <span class="title">localhost</span></span></span><br><span class="line"><span class="function">               <span class="title">Master_User</span>: <span class="title">lsuser</span></span></span><br><span class="line"><span class="function">               <span class="title">Master_Port</span>: 3306</span></span><br><span class="line"><span class="function">             <span class="title">Connect_Retry</span>: 60</span></span><br><span class="line"><span class="function">           <span class="title">Master_Log_File</span>: <span class="title">WSTest</span>-<span class="title">bin</span>.000003</span></span><br><span class="line"><span class="function">       <span class="title">Read_Master_Log_Pos</span>: 7177</span></span><br><span class="line"><span class="function">            <span class="title">Relay_Log_File</span>: <span class="title">WSTest</span>-<span class="title">relay</span>-<span class="title">bin</span>.000003</span></span><br><span class="line"><span class="function">             <span class="title">Relay_Log_Pos</span>: 7466</span></span><br><span class="line"><span class="function">     <span class="title">Relay_Master_Log_File</span>: <span class="title">WSTest</span>-<span class="title">bin</span>.000003</span></span><br><span class="line"><span class="function">          <span class="title">Slave_IO_Running</span>: <span class="title">Yes</span></span></span><br><span class="line"><span class="function">         <span class="title">Slave_SQL_Running</span>: <span class="title">Yes</span></span></span><br><span class="line"><span class="function">                        (省略)</span></span><br><span class="line"><span class="function">                <span class="title">Using_Gtid</span>: <span class="title">Slave_Pos</span></span></span><br><span class="line"><span class="function">               <span class="title">Gtid_IO_Pos</span>: 0-1-1167</span></span><br><span class="line"><span class="function">   <span class="title">Replicate_Do_Domain_Ids</span>:</span></span><br><span class="line"><span class="function"> <span class="title">Replicate_Ignore_Domain_Ids</span>:</span></span><br><span class="line"><span class="function">               <span class="title">Parallel_Mode</span>: <span class="title">conservative</span></span></span><br><span class="line"><span class="function">1 <span class="title">row</span> <span class="title">in</span> <span class="title">set</span> (0.00 <span class="title">sec</span>)</span></span><br></pre></td></tr></table></figure><ul><li>這兩個對應 (<code>0-2-976</code>, <code>0-1-1167</code>) 都幫你做好了</li><li>完成</li></ul><h2 id="一些想法"><a href="#一些想法" class="headerlink" title="一些想法"></a>一些想法</h2><ul><li>如果是 Multi-Master 的這個系統會很好做 (<code>CREATE MASTER to ..., master_use_gtid=slave_pos;</code>)，因為 <code>slave_pos</code> 可以是多個的，例如 <code>gtid_slave_pos=&#39;0-1-123,1-2-456,...&#39;</code></li><li>Slave 如果要快速完復，不需要備份還原的話，那麼就只能當 Snapshot 來用 (不能動他讓他產生自己的 binlog)</li><li>對於原來的需求 (單一 Master 單一 Slave) 基本上設定一下就完成了</li></ul><h2 id="上線紀錄"><a href="#上線紀錄" class="headerlink" title="上線紀錄"></a>上線紀錄</h2><h3 id="2016-11-8"><a href="#2016-11-8" class="headerlink" title="2016&#x2F;11&#x2F;8"></a>2016&#x2F;11&#x2F;8</h3><p>有時候會碰到有些指令沒法傳過去的問題(如建立 FederatedX Table)，這時候 Slave 會卡在 Error 的地方不能過去，所以要手動 bypass 它。</p><p>但是不太清楚要 skip 幾個的狀態下，就一個一個看了。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">stop slave;</span><br><span class="line"><span class="keyword">set</span> <span class="keyword">global</span> sql_slave_skip_counter<span class="operator">=</span><span class="number">1</span>;</span><br><span class="line"><span class="keyword">start</span> slave;</span><br><span class="line"></span><br><span class="line"><span class="keyword">select</span> sleep(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">show</span> slave status;</span><br></pre></td></tr></table></figure></li></ul>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;在資料庫的備援部份，採用 SQL Server Log-shipping 方式來處理，希望在移轉至 MariaDB</summary>
        
      
    
    
    
    
    <category term="MariaDB" scheme="https://sujunmin.github.io/blog/tags/MariaDB/"/>
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
  <entry>
    <title>從 SQL Server 到 MariaDB - [4] SQL Agent 與 MariaDB Event</title>
    <link href="https://sujunmin.github.io/blog/2016/09/10/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[4]%20SQL%20Agent%20%E8%88%87%20MariaDB%20Event/"/>
    <id>https://sujunmin.github.io/blog/2016/09/10/%E5%BE%9E%20SQL%20Server%20%E5%88%B0%20MariaDB%20-%20[4]%20SQL%20Agent%20%E8%88%87%20MariaDB%20Event/</id>
    <published>2016-09-10T06:11:42.000Z</published>
    <updated>2026-09-26T03:17:55.353Z</updated>
    
    <content type="html"><![CDATA[<h2 id="需求說明"><a href="#需求說明" class="headerlink" title="需求說明"></a>需求說明</h2><p>回到維運面的處理，在 SQL Server 有 SQL Server Agent Service 作為 Job System，說實在真的好用，在 msdb 裡儲存了很多 Job 的資料，不管是統計還是錯誤時的資料收集都做的非常好，因此系統每天會出資料庫系統狀態報表，其中有兩個項目有很多東西能看</p><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/SQLServerAgentLog_1.png" /><img src="https://raw.githubusercontent.com/sujunmin/sujunmin.github.com/master/test/SQLServerAgentLog_2.png" /> <p>因此每天的檢查大部份都是看這些 Log 的狀態，希望在 MariaDB 上面希望也有類似的方法實作。</p><h2 id="Job-System-的選擇"><a href="#Job-System-的選擇" class="headerlink" title="Job System 的選擇"></a>Job System 的選擇</h2><p>在 MariaDB 上有 <a href="https://mariadb.com/kb/en/mariadb/events/"><code>Event</code></a> 可以用，在 Windows 裡有 Windows Tasks 可以用，但他們跟 SQL Server Agent Service 真的是差太多了，這兩個服務 MariaDB 的 <code>Event</code> 幾乎是射後不理，Windows 的 Tasks 也只有少少的 Log 可以用，基本上直接用是沒法達到原來維運的要求的，最後仍然是選擇 MariaDB 的 <code>Event</code> 配合一點紀錄來實作 Job System。</p><h2 id="MariaDB-的-Event"><a href="#MariaDB-的-Event" class="headerlink" title="MariaDB 的 Event"></a>MariaDB 的 <code>Event</code></h2><p>詳細資料可以參考<a href="https://mariadb.com/kb/en/mariadb/events/">這個</a>，基本上很簡單，就是設定某個 Schema 的什麼時候做什麼事情，用 <code>Show events;</code> 可以看到 <code>Event</code> 的內容，不過這邊要來說明的是 <code>mysql.event</code> 這個<a href="https://mariadb.com/kb/en/mariadb/mysqlevent-table/">資料表</a>，因為所有的 <code>Event</code> 資料都是從這個地方加工的。</p><p>其中有一些時間戳記，經過一些實驗結果 (因為真的寫的好少，不太清楚真正的意思) 有一些認識</p><table><thead><tr><th>欄位名</th><th>意義</th></tr></thead><tbody><tr><td><code>created</code></td><td><code>Event</code> 產生的時間</td></tr><tr><td><code>modified</code></td><td><code>Event</code> 被修改的時間</td></tr><tr><td><code>last_executed</code></td><td>上次執行結束時間 (不論成功或失敗)</td></tr><tr><td><code>starts</code></td><td><code>Event</code> 開始時間 (第一次開始執行的時間)</td></tr><tr><td><code>ends</code></td><td><code>Event</code> 結束時間 (如果沒有結束日期就是 Null)</td></tr></tbody></table><p>要注意的是 <code>last_executed</code> 是不管成功或是失敗執行完的時間，<code>starts</code> 是第一次執行的時間，如果他是週期性執行，沒有結束的時間，只會記住第一次的</p><p>這個時間系統很重要的是因為它會關係到後續報表怎麼出，所以要先了解一下</p><p>另外的是處理情形的資訊，在 <code>mysql.event</code> 沒有任何紀錄最後一次狀態的資訊 (所以才說它幾乎是射後不理的系統)，這邊就需要自己紀錄了。 </p><h2 id="紀錄-Event-執行狀態的-table"><a href="#紀錄-Event-執行狀態的-table" class="headerlink" title="紀錄 Event 執行狀態的 table"></a>紀錄 <code>Event</code> 執行狀態的 table</h2><p>根據需求，我在 <code>master</code> Create 了 <code>event_history</code> 資料表</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE TABLE</span> event_history (</span><br><span class="line">    db <span class="type">CHAR</span>(<span class="number">64</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">    name <span class="type">CHAR</span>(<span class="number">64</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">    <span class="keyword">start</span> DATETIME <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">    <span class="keyword">end</span> DATETIME <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">    <span class="keyword">sqlstate</span> <span class="type">CHAR</span>(<span class="number">64</span>) <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">    errno <span class="type">CHAR</span>(<span class="number">64</span>) <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">    message_text <span class="type">VARCHAR</span>(<span class="number">500</span>) <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">    record_time DATETIME <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span>,</span><br><span class="line">    INDEX idx1 (db, name)</span><br><span class="line">)</span><br><span class="line"><span class="keyword">COLLATE</span><span class="operator">=</span><span class="string">&#x27;utf8_general_ci&#x27;</span></span><br><span class="line">ENGINE<span class="operator">=</span>InnoDB</span><br><span class="line">;</span><br></pre></td></tr></table></figure><table><thead><tr><th>欄位名</th><th>意義</th></tr></thead><tbody><tr><td><code>db</code></td><td><code>mysql.event.db</code></td></tr><tr><td><code>name</code></td><td><code>mysql.event.name</code></td></tr><tr><td><code>start</code></td><td>這次執行開始時間</td></tr><tr><td><code>end</code></td><td>這次執行結束時間</td></tr><tr><td><code>sqlstate</code></td><td>錯誤時紀錄 <code>sqlstate</code></td></tr><tr><td><code>errno</code></td><td>錯誤代碼</td></tr><tr><td><code>message_text</code></td><td>訊息</td></tr><tr><td><code>record_time</code></td><td>紀錄時間</td></tr></tbody></table><h3 id="攔截錯誤時的訊息"><a href="#攔截錯誤時的訊息" class="headerlink" title="攔截錯誤時的訊息"></a>攔截錯誤時的訊息</h3><p>MariaDB 有個 <a href="https://mariadb.com/kb/en/mariadb/declare-handler/">Handler</a> 作為錯誤處理 try-catch 的機制，可以從這邊下手。</p><p><a href="https://mariadb.com/kb/en/mariadb/get-diagnostics/">Diagnostics</a> 可以在錯誤時拿到相關的資訊，有 <code>sqlstate</code> ，<code>errno</code> ，<code>message_text</code> 可以拿來用。</p><h3 id="Prepare-Statements"><a href="#Prepare-Statements" class="headerlink" title="Prepare Statements"></a>Prepare Statements</h3><p>由上面的準備，幾乎是可以完成表裡需要的欄位項目，但是如果要每個 <code>Event</code>都要前面加 <code>Handler</code> 與 <code>Diagnostics</code> 來塞資料，這樣太麻煩也有可能會忘記，所以要做個 Store Procedure 來達成這個需求。</p><p>其中需要讓 Store Procedure 執行傳進來的 SQL Statements，有 <a href="https://mariadb.com/kb/en/mariadb/prepare-statement/">Prepare Statement</a> 可以用，這樣就差不多了</p><p>不過這個 Prepare Statement 有限制使用的語法，而且只能執行一行。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">PROCEDURE</span> proc_for_event_history(<span class="keyword">IN</span> idb <span class="type">char</span>(<span class="number">64</span>), <span class="keyword">IN</span> iname <span class="type">char</span>(<span class="number">64</span>), <span class="keyword">IN</span> <span class="keyword">sql</span> TEXT)</span><br><span class="line">    <span class="keyword">LANGUAGE</span> <span class="keyword">SQL</span></span><br><span class="line">    <span class="keyword">NOT</span> <span class="keyword">DETERMINISTIC</span></span><br><span class="line">    <span class="keyword">CONTAINS</span> <span class="keyword">SQL</span></span><br><span class="line">    <span class="keyword">SQL</span> SECURITY DEFINER</span><br><span class="line">    COMMENT <span class="string">&#x27;&#x27;</span></span><br><span class="line"><span class="keyword">BEGIN</span></span><br><span class="line">      <span class="keyword">DECLARE</span> EXIT HANDLER <span class="keyword">FOR</span> <span class="keyword">SQLEXCEPTION</span></span><br><span class="line">      <span class="keyword">BEGIN</span></span><br><span class="line">        <span class="keyword">GET</span> DIAGNOSTICS <span class="keyword">CONDITION</span> <span class="number">1</span> <span class="variable">@sqlstate</span> <span class="operator">=</span> RETURNED_SQLSTATE,  <span class="variable">@errno</span> <span class="operator">=</span> MYSQL_ERRNO, <span class="variable">@text</span> <span class="operator">=</span> MESSAGE_TEXT;</span><br><span class="line">        <span class="keyword">select</span> now() <span class="keyword">into</span> <span class="variable">@start</span> <span class="keyword">from</span> dual;</span><br><span class="line">        <span class="keyword">insert into</span> master.event_history (db, name, <span class="keyword">start</span>, <span class="keyword">end</span>, <span class="keyword">sqlstate</span>, errno, message_text) <span class="keyword">values</span> (idb, iname, <span class="variable">@start</span>, now(), <span class="variable">@sqlstate</span>, <span class="variable">@errno</span>, <span class="variable">@text</span>);</span><br><span class="line">      <span class="keyword">END</span>;</span><br><span class="line">     </span><br><span class="line">     <span class="keyword">set</span> <span class="variable">@sql</span> :<span class="operator">=</span> <span class="keyword">sql</span>;</span><br><span class="line">     <span class="keyword">prepare</span> stmt <span class="keyword">from</span> <span class="variable">@sql</span>;</span><br><span class="line">    </span><br><span class="line">     <span class="keyword">select</span> now() <span class="keyword">into</span> <span class="variable">@start</span> <span class="keyword">from</span> dual;</span><br><span class="line">     <span class="keyword">set</span> <span class="variable">@outs</span> :<span class="operator">=</span> <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">  <span class="keyword">execute</span> stmt;</span><br><span class="line">      <span class="keyword">select</span> sleep(<span class="number">10</span>);</span><br><span class="line">  if instr(<span class="variable">@sql</span>, <span class="string">&#x27;@outv&#x27;</span>) <span class="operator">&lt;&gt;</span> <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line">      <span class="keyword">select</span> concat(<span class="string">&#x27;OK &#x27;</span>, <span class="variable">@outv</span>) <span class="keyword">into</span> <span class="variable">@outs</span>;</span><br><span class="line">     <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">select</span> <span class="string">&#x27;OK&#x27;</span> <span class="keyword">into</span> <span class="variable">@outs</span>;</span><br><span class="line">     <span class="keyword">end</span> if;</span><br><span class="line">    <span class="keyword">insert into</span> master.event_history (`db`, `name`, `<span class="keyword">start</span>`, `<span class="keyword">end</span>`, `<span class="keyword">sqlstate</span>`, `errno`, `message_text`) <span class="keyword">values</span> (idb, iname, <span class="variable">@start</span>, now(), <span class="keyword">NULL</span>, <span class="keyword">NULL</span>, <span class="variable">@outs</span>); </span><br><span class="line"><span class="keyword">END</span></span><br></pre></td></tr></table></figure><p>錯誤的時候有錯誤訊息，但正確的時候不知道那邊可以找到訊息，只好先打個 OK 了。</p><p>有一個 <code>select sleep(10);</code> 是拿來做實驗用的，平時可以拿掉。</p><h2 id="實驗"><a href="#實驗" class="headerlink" title="實驗"></a>實驗</h2><ol><li><p><code>Event</code> 系統要打開</p><p><code>SET Global event_schedular=1</code></p><p>或是在 <code>my.cnf</code> 加上 <code>events_scheduler=1</code></p></li><li><p>一個簡單 table，然後做一個 <code>Event</code> 是一直加一的</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> Database test1;</span><br><span class="line"></span><br><span class="line"><span class="keyword">CREATE TABLE</span> test2 (</span><br><span class="line"> idtest2 <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line"> <span class="keyword">PRIMARY KEY</span> (idtest2)</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8;</span><br><span class="line"></span><br><span class="line"><span class="keyword">CREATE</span> EVENT test_event</span><br><span class="line"> <span class="keyword">ON</span> SCHEDULE</span><br><span class="line">     <span class="keyword">EVERY</span> <span class="number">1</span> <span class="keyword">MINUTE</span> STARTS <span class="string">&#x27;2016-09-07 16:00:58&#x27;</span></span><br><span class="line"> <span class="keyword">ON</span> COMPLETION <span class="keyword">NOT</span> PRESERVE</span><br><span class="line"> ENABLE</span><br><span class="line"> COMMENT <span class="string">&#x27;&#x27;</span></span><br><span class="line"> DO <span class="keyword">begin</span></span><br><span class="line">  <span class="keyword">call</span> master.proc_for_event_history(<span class="string">&#x27;test1&#x27;</span>, <span class="string">&#x27;test_event&#x27;</span>, <span class="string">&#x27;UPDATE test1.test22 SET idtest2 = idtest2 + 1&#x27;</span>);</span><br><span class="line"><span class="keyword">end</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>這裡的 <code>test22</code> 是故意打錯的，看看結果如何</p></li><li><p>看一下結果</p></li></ol><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">MariaDB [master]&gt; select * from event_history order by record_time desc limit <span class="number">1</span>\G</span><br><span class="line">*************************** <span class="number">1</span>. row ***************************</span><br><span class="line"><span class="function">          db: <span class="title">test1</span></span></span><br><span class="line"><span class="function">        <span class="title">name</span>: <span class="title">test_event</span></span></span><br><span class="line"><span class="function">       <span class="title">start</span>: 2016-09-10 15:57:58</span></span><br><span class="line"><span class="function">         <span class="title">end</span>: 2016-09-10 15:57:58</span></span><br><span class="line"><span class="function">    <span class="title">sqlstate</span>: 42<span class="title">S02</span></span></span><br><span class="line"><span class="function">       <span class="title">errno</span>: 1146</span></span><br><span class="line"><span class="function"><span class="title">message_text</span>: <span class="title">Table</span> &#x27;<span class="title">test1.test22</span>&#x27; <span class="title">doesn</span>&#x27;<span class="title">t</span> <span class="title">exist</span></span></span><br><span class="line"><span class="function"> <span class="title">record_time</span>: 2016-09-10 15:57:58</span></span><br><span class="line"><span class="function">1 <span class="title">row</span> <span class="title">in</span> <span class="title">set</span> (0.00 <span class="title">sec</span>)</span></span><br></pre></td></tr></table></figure><ol start="4"><li>調回來正確的，再看一下結果</li></ol><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">MariaDB [master]&gt; select * from event_history order by record_time desc limit <span class="number">1</span>\G</span><br><span class="line">*************************** <span class="number">1</span>. row ***************************</span><br><span class="line"><span class="function">          db: <span class="title">test1</span></span></span><br><span class="line"><span class="function">        <span class="title">name</span>: <span class="title">test_event</span></span></span><br><span class="line"><span class="function">       <span class="title">start</span>: 2016-09-10 16:00:58</span></span><br><span class="line"><span class="function">         <span class="title">end</span>: 2016-09-10 16:01:08</span></span><br><span class="line"><span class="function">    <span class="title">sqlstate</span>: <span class="title">NULL</span></span></span><br><span class="line"><span class="function">       <span class="title">errno</span>: <span class="title">NULL</span></span></span><br><span class="line"><span class="function"><span class="title">message_text</span>: <span class="title">OK</span></span></span><br><span class="line"><span class="function"> <span class="title">record_time</span>: 2016-09-10 16:01:08</span></span><br><span class="line"><span class="function">1 <span class="title">row</span> <span class="title">in</span> <span class="title">set</span> (0.00 <span class="title">sec</span>)</span></span><br></pre></td></tr></table></figure><p>開始跟結束差 10 秒鐘是之前加的 <code>select sleep(10)</code> 的關係。</p><h2 id="還需要解決的項目"><a href="#還需要解決的項目" class="headerlink" title="還需要解決的項目"></a>還需要解決的項目</h2><ol><li>Query 正確時的回傳要能抓到</li><li>該次真正開始執行時間</li><li>下一次開始執行時間</li></ol><h2 id="修改"><a href="#修改" class="headerlink" title="修改"></a>修改</h2><h3 id="2016-11-7"><a href="#2016-11-7" class="headerlink" title="2016&#x2F;11&#x2F;7"></a>2016&#x2F;11&#x2F;7</h3><p>透過 <code>@outv</code> 收 SQL 訊息，傳到 <code>@outs</code> 給 Event 紀錄系統。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;需求說明&quot;&gt;&lt;a href=&quot;#需求說明&quot; class=&quot;headerlink&quot; title=&quot;需求說明&quot;&gt;&lt;/a&gt;需求說明&lt;/h2&gt;&lt;p&gt;回到維運面的處理，在 SQL Server 有 SQL Server Agent Service 作為 Job</summary>
        
      
    
    
    
    
    <category term="MariaDB" scheme="https://sujunmin.github.io/blog/tags/MariaDB/"/>
    
    <category term="MSSQL" scheme="https://sujunmin.github.io/blog/tags/MSSQL/"/>
    
  </entry>
  
</feed>
