山羊博客 Just go for fun http://blog.fungo.me/ Sat, 02 Dec 2023 19:02:29 +0800 Sat, 02 Dec 2023 19:02:29 +0800 Jekyll v4.3.2 用 Valgrind 剖析 MySQL 内存使用 <h2 id="前言">前言</h2> <p>最近在线上遇到一个实例经常 OOM(Out Of Memory),通过监控看,内存使用在短时间内快速上涨,最终超过 CGroup 上限。从以往经验来看,这种一般是连接数短时间内爆涨(几百~上千)导致的,因为每个连接对应 MySQL 内的一个用户线程,每个用户线程都挂了一 THD 对象用来维护上下文,以及各种 thread local cache,可能在几百 KB或者 MB 级别,但是从监控数据看最多也就几十个连接;还有一种可能是 Memory Leak,但是这种通常内存是慢慢涨上去的,不是爆涨,但是不排除这种可能。</p> <h2 id="问题分析">问题分析</h2> <p>对于这种陌生的问题,第一个想法是能否复现(可以复现的问题都不是问题),观察了下用户的语句和表结构:</p> <ol> <li>就一张表,用的是分区表,有 149 个分区,引擎是 TokuDB;</li> <li>语句非常简单,都是 SELECT + ORDER BY + LIMIT,但是 Where 条件里没有指定分区字段,因此所有分区都会被访问。</li> </ol> <p>根据这2个特征,线下很容易就复现出来了。</p> <p>用 64 个并发线程来压测,可以看到物理内存(RSS)从 500M 左右迅速涨到 20G 左右,<code class="language-plaintext highlighter-rouge">tokudb_cache_size</code> 配置为 2.4G 左右。</p> <pre><code class="language-txt">04:14:38 PM PID minflt/s majflt/s VSZ RSS %MEM Command 04:14:39 PM 32552 0.00 0.00 5255548 293956 0.06 mysqld 04:14:40 PM 32552 0.00 0.00 5255548 293956 0.06 mysqld 04:14:41 PM 32552 70980.00 0.00 6537852 568712 0.11 mysqld 04:14:42 PM 32552 2369116.00 0.00 17990268 10008148 1.89 mysqld 04:14:43 PM 32552 1347716.00 0.00 23970428 8793920 1.66 mysqld 04:14:44 PM 32552 669286.00 0.00 24920700 11250280 2.13 mysqld 04:14:45 PM 32552 1378265.00 0.00 29225596 14673244 2.77 mysqld 04:14:46 PM 32552 1164854.00 0.00 29225596 18549660 3.50 mysqld 04:14:47 PM 32552 117668.00 0.00 29225596 19010792 3.59 mysqld 04:14:48 PM 32552 30837.00 0.00 29225596 19133736 3.61 mysqld 04:14:49 PM 32552 74417.00 0.00 29225596 19422264 3.67 mysqld 04:14:50 PM 32552 32878.00 0.00 29225596 19553356 3.69 mysqld 04:14:51 PM 32552 73059.00 0.00 29225596 19836520 3.75 mysqld 04:14:52 PM 32552 32665.00 0.00 29229692 19966784 3.77 mysqld 04:14:53 PM 32552 68573.00 0.00 29545084 20232064 3.82 mysqld 04:14:54 PM 32552 33559.00 0.00 29749884 20365884 3.85 mysqld 04:14:55 PM 32552 781718.00 0.00 30765692 16992452 3.21 mysqld 04:14:56 PM 32552 1052088.00 0.00 31748732 15055824 2.84 mysqld 04:14:57 PM 32552 1137534.00 0.00 32707196 17160700 3.24 mysqld 04:14:58 PM 32552 797537.00 0.00 32707196 20223936 3.82 mysqld 04:14:59 PM 32552 10240.00 0.00 32707196 20160464 3.81 mysqld 04:15:00 PM 32552 34351.00 0.00 32707196 20116904 3.80 mysqld </code></pre> <p><a href="http://valgrind.org/">Valgrind</a> 是一个非常好用的内存检测工具,通过它我们可以快速来定位 Memory Leak 问题,我们用 <code class="language-plaintext highlighter-rouge">valgrind --tool=memcheck</code> 来重新启动 mysqld,然后跑复现压测,但是并没有检测到内存泄漏。</p> <p>那么究竟是哪里把内存吃掉了呢?这就该本文的主角了——<a href="http://valgrind.org/docs/manual/ms-manual.html">Massif</a> 登场了 ,和 <code class="language-plaintext highlighter-rouge">memcheck</code> 类似,<code class="language-plaintext highlighter-rouge">massif</code> 也是 Valgrind 套装中一个工具,主要用来做 memory profiling:</p> <blockquote> <p>Massif is a heap profiler. It measures how much heap memory your program uses.</p> </blockquote> <p>用起来也比较简单 <code class="language-plaintext highlighter-rouge">valgrind --tool=massif prog</code> 即可,压测完后,会生成一个 <code class="language-plaintext highlighter-rouge">massif.out.&lt;pid&gt;</code> 的文件,这是一个 txt 文件,虽然可以直接打开看,但是为了更好的看结果,valgrind 提供了另一个工具 <code class="language-plaintext highlighter-rouge">ms_print</code> 来展示,官方认为将数据采集和展示分开,是为了将来更好更方便的对展示工具做扩展 :)</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> GB 22.80^ : | :@@:::::@::::::#:: | @@::::@ :::::@: ::::#:: ::::::@:::: | @@: ::@ :::::@: ::::#:: :::@:::::@:::: | @@: ::@ :::::@: ::::#:: @::::::@:::::@:::: | @@@: ::@ :::::@: ::::#:::: ::::@::::::@:::::@:::: | @@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | @@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | :@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | ::::::::::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | :::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | :::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | :::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | :::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | :::::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | :: :::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | @: :::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | @: :::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | @: :::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: | @: :::::: :::::@@@: ::@ :::::@: ::::#::: :::@::::::::@::::::@:::::@:::: 0 +-----------------------------------------------------------------------&gt;Gi 0 281.7 Number of snapshots: 82 Detailed snapshots: [1, 15, 16, 17, 22, 28, 34 (peak), 41, 54, 64, 74] </code></pre></div></div> <p>massif 会对程序运行过程中的内存使用做 snapshot,上图中纵坐标代表内存使用量,横坐标可以看作是一种时间单位,默认是 CPU 指令条数;图中每一列代表一次内存 snapshot,不同的字符有不同的含义,其中 <code class="language-plaintext highlighter-rouge">:</code> 表示一次正常的 snapshot,<code class="language-plaintext highlighter-rouge">@</code> 表示一次 detailed snapshot,<code class="language-plaintext highlighter-rouge">#</code> 表示本次采集的峰值(peak 一定是 detailed)。</p> <p>我们这里只关心 detailed snapshot,因为它包含内存使用的 stack trace 信息,通过这个我们就知道当前的内存使用,是哪些代码路径引起的。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-------------------------------------------------------------------------------- n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B) -------------------------------------------------------------------------------- 2 8,139,138,982 7,192,367,296 7,068,742,047 123,625,249 0 3 12,518,919,899 7,674,937,104 7,519,173,315 155,763,789 0 4 18,630,130,824 14,528,934,960 14,433,389,357 95,545,603 0 5 23,053,916,332 14,528,940,728 14,433,394,482 95,546,246 0 6 28,667,255,552 14,530,496,688 14,434,920,681 95,576,007 0 7 31,442,322,447 14,530,513,504 14,434,933,623 95,579,881 0 8 36,910,366,437 14,530,583,456 14,434,985,999 95,597,457 0 9 40,736,846,313 14,530,640,472 14,435,028,200 95,612,272 0 10 46,739,261,418 14,530,667,992 14,435,049,267 95,618,725 0 11 50,685,776,389 14,530,668,784 14,435,049,971 95,618,813 0 12 57,194,337,509 14,530,670,152 14,435,051,187 95,618,965 0 13 60,902,677,929 14,551,221,560 14,456,544,294 94,677,266 0 14 65,578,137,351 15,263,985,032 15,014,333,631 249,651,401 0 15 69,870,684,301 19,339,915,904 17,864,686,175 1,475,229,729 0 92.37% (17,864,686,175B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.92.37% (17,864,686,175B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc. -&gt;39.91% (7,718,060,098B) 0x973463: my_malloc (my_malloc.c:xxx) | -&gt;28.51% (5,513,971,756B) 0xBE989B: ha_tokudb::unpack_blobs(unsigned char*, unsigned char const*, unsigned int, bool) (hatoku_defines.h:xxx) | | -&gt;19.31% (3,734,605,089B) 0xCAE6BD: c_getf_set_callback(unsigned int, void const*, unsigned int, void const*, void*, bool) (ydb_cursor.cc:xxx) | | | -&gt;19.31% (3,734,605,089B) 0xC8B1B5: ft_cursor_search_eq_k_x_getf(unsigned int, void const*, unsigned int, void const*, void*, bool) (cursor.cc:38 2) | | | -&gt;19.31% (3,734,605,089B) 0xC4A000: ft_search_node(ft_handle*, ftnode*, ft_search*, int, int (*)(unsigned int, void const*, unsigned int, void const*, void*, bool), void*, bool*, ft_cursor*, unlockers*, ancestors*, pivot_bounds const&amp;, bool) (ft-ops.cc:xxx) | | | -&gt;19.30% (3,732,546,926B) 0xC494AA: ft_search_node(ft_handle*, ftnode*, ft_search*, int, int (*)(unsigned int, void const*, unsigned int, voi d const*, void*, bool), void*, bool*, ft_cursor*, unlockers*, ancestors*, pivot_bounds const&amp;, bool) (ft-ops.cc:xxx) | | | | -&gt;19.30% (3,732,546,926B) 0xC494AA: ft_search_node(ft_handle*, ftnode*, ft_search*, int, int (*)(unsigned int, void const*, unsigned int, void const*, void*, bool), void*, bool*, ft_cursor*, unlockers*, ancestors*, pivot_bounds const&amp;, bool) (ft-ops.cc:xxx) -&gt;30.41% (5,882,175,012B) 0x9735BC: my_realloc (my_malloc.c:xxx) | -&gt;26.92% (5,206,093,356B) 0xBE989B: ha_tokudb::unpack_blobs(unsigned char*, unsigned char const*, unsigned int, bool) (hatoku_defines.h:xxx) | | -&gt;24.27% (4,693,513,693B) 0xCAE6BD: c_getf_set_callback(unsigned int, void const*, unsigned int, void const*, void*, bool) (ydb_cursor.cc:xxx) | | | -&gt;24.27% (4,693,513,693B) 0xC8B1B5: ft_cursor_search_eq_k_x_getf(unsigned int, void const*, unsigned int, void const*, void*, bool) (cursor.cc:xxx) | | | -&gt;24.27% (4,693,513,693B) 0xC4A000: ft_search_node(ft_handle*, ftnode*, ft_search*, int, int (*)(unsigned int, void const*, unsigned int, void const*, void*, bool), void*, bool*, ft_cursor*, unlockers*, ancestors*, pivot_bounds const&amp;, bool) (ft-ops.cc:xxx) | | | -&gt;24.27% (4,693,513,693B) 0xC494AA: ft_search_node(ft_handle*, ftnode*, ft_search*, int, int (*)(unsigned int, void const*, unsigned int, void const*, void*, bool), void*, bool*, ft_cursor*, unlockers*, ancestors*, pivot_bounds const&amp;, bool) (ft-ops.cc:xxx) </code></pre></div></div> <p>从上面的 stack trace 中可以看到,在第15个 snapshot中, 有70%+(39.91% + 30.41%) 的内存是通过 <code class="language-plaintext highlighter-rouge">ha_tokudb::unpack_blobs</code> 这里占用的。</p> <p>这个栈的逻辑是将 TokuDB engine 返回的数据,转换成 MySQL server 层的数据格式,而在 blob 字段转换过程中,会用到临时的 buffer(<code class="language-plaintext highlighter-rouge">blob_buf</code>),问题就出在这个 buffer 使用上。这个 buffer 是弹性的,但是只能往上弹,并且只有在表 close 时才会释放。</p> <p>加了调试代码后,我们发现 <code class="language-plaintext highlighter-rouge">blob_buff</code> 非常大,平均在 2M 左右,最大可以到20M左右,每一个 TokuDB handler 都有这样的一个buffer,而用户的 sql 又会打开所有的分区表(149个),这样一个用户连接就会占 149*2M = 298M 左右的内存,64 个连接差不多用掉 18G 左右的内存。</p> <p>至此问题已经非常清楚了,一个简单的解法就是尽快释放 <code class="language-plaintext highlighter-rouge">blob_buffer</code>,而不是等到 close 表的时候才释放。</p> <h2 id="总结">总结</h2> <p>从上面的分析过程可以看出,massif 给出内存使用信息是关键,帮助我们快速定位到内存使用大头的stack。如果没有 massif,我们可能就要在内存 malloc 的地方埋点打印信息,然后人肉汇总分析。</p> <p>valgrind massif 是一个通用的工具,本文是以 MySQL 为例分享一个 case,如果有其它程序也出现内存使用异常的,也可以拿来 profiling 一把。</p> <p>Tips:</p> <ol> <li>massif 默认是统计 <code class="language-plaintext highlighter-rouge">*alloc/new</code> 这些相对来说 high level 的函数的,如果程序里用了 <code class="language-plaintext highlighter-rouge">mmap/brk</code> 这些函数,是不是会统计的,需要使用 <code class="language-plaintext highlighter-rouge">--pages-as-heap=yes </code> 这个参数开关;</li> <li>stack 使用默认不会统计,<code class="language-plaintext highlighter-rouge">--stacks=yes</code> 可以打开,如果用了 <code class="language-plaintext highlighter-rouge">--pages-as-heap=yes</code>,就不需要 <code class="language-plaintext highlighter-rouge">--stack=yes</code>,因为已经包含了;</li> <li>程序 gcc 编译参数中应该包括 <code class="language-plaintext highlighter-rouge">-g</code> 选项。</li> </ol> <p>官方文档有详细的关于<a href="http://valgrind.org/docs/manual/ms-manual.html#ms-manual.options">各个参数的介绍</a>,参数并不多,在使用前建议看一看。</p> <p>Have fun!</p> Sun, 23 Jul 2017 00:00:00 +0800 http:/blog.fungo.me/2017/07/profile-mysql-memory-usage-with-valgrind-massif/ http:/blog.fungo.me/2017/07/profile-mysql-memory-usage-with-valgrind-massif/ MySQL Memory valgrind technology MySQL AliSQL 源码编译 <h2 id="前言">前言</h2> <p>AliSQL 在 2016 云栖大会宣布开放源代码之后,迅速就获得了广泛的关注,目前(2016-10-23) star 数目已达 1187,欢迎访问 <a href="https://github.com/alibaba/AliSQL">AliSQL GitHub</a> 项目关注。社区反应也非常活跃,在 <a href="https://github.com/alibaba/AliSQL/issues">Issue</a> 中提了不少反馈建议,其中有一部分是和编译安装相关的,因为官方目前并没有提供 binary,有同学可能没有 GNU/Linux 环境下编译代码的经验,导致不能直接使用。针对这个问题,本文提供一个非官方 (unofficial) 的编译指导,希望对大家有所帮助。</p> <p>目前只提供使用最普遍的 GNU/Linux 发行版 Ubuntu 和 CentOS 的编译指导。</p> <h2 id="ubuntu-1604">Ubuntu 16.04</h2> <ol> <li> <p>编译环境准备</p> <p>编译需要 <code class="language-plaintext highlighter-rouge">gcc &gt;= 4.7, cmake &gt;= 2.8</code>,16.04 apt 源里的版本 (gcc = 5.4, cmake = 3.5) 已经够用了,直接安装使用。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> apt-get update -y apt-get install git gcc g++ cmake -y apt-get install bison libncurses5-dev zlib1g-dev libssl-dev -y </code></pre></div> </div> </li> <li> <p>从 GitHub clone 代码</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> git clone https://github.com/alibaba/AliSQL.git </code></pre></div> </div> </li> <li> <p>cmake 配置</p> <p>MySQL 有非常多的 cmake 参数,大家可以参考<a href="https://dev.mysql.com/doc/mysql-sourcebuild-excerpt/5.6/en/source-configuration-options.html">官方这个页面</a>,这里的配置只是个参考,大家根据需要修改:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> cmake . \ -DCMAKE_BUILD_TYPE="Release" \ -DCMAKE_INSTALL_PREFIX="/opt/alisql" \ -DWITH_EMBEDDED_SERVER=0 \ -DWITH_EXTRA_CHARSETS=all \ -DWITH_MYISAM_STORAGE_ENGINE=1 \ -DWITH_INNOBASE_STORAGE_ENGINE=1 \ -DWITH_PARTITION_STORAGE_ENGINE=1 \ -DWITH_CSV_STORAGE_ENGINE=1 \ -DWITH_ARCHIVE_STORAGE_ENGINE=1 \ -DWITH_BLACKHOLE_STORAGE_ENGINE=1 \ -DWITH_FEDERATED_STORAGE_ENGINE=1 \ -DWITH_PERFSCHEMA_STORAGE_ENGINE=1 \ -DWITH_TOKUDB_STORAGE_ENGINE=1 </code></pre></div> </div> </li> <li> <p>编译安装</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> make -j4 &amp;&amp; make install </code></pre></div> </div> <p><code class="language-plaintext highlighter-rouge">-j4</code> 表示开 4 个并发编译进程,加速编译,根据机器 CPU 核数调整,一般是 CPU 核数 + 1,最终二进制安装在 <code class="language-plaintext highlighter-rouge">/opt/alisql</code> 目录下。</p> </li> </ol> <h2 id="centos-68">CentOS 6.8</h2> <ol> <li> <p>编译环境准备</p> <p>CentOS 和 Ubuntu 环境的区别就在这一步,CentOS yum 源里的 gcc 版本是 4.4 的,不满足需求,可以通过<a href="http://blog.fungo.me/2016/03/centos-development-env/">我之前介绍过</a>的 devtoolset 来安装高版本 gcc,devtoolset 目前最新套装是 <a href="https://www.softwarecollections.org/en/scls/rhscl/devtoolset-4/">devtoolset-4</a>,包含 gcc 5.2。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> yum install centos-release-scl -y yum install devtoolset-4-gcc-c++ devtoolset-4-gcc -y yum install cmake git -y yum install ncurses-devel openssl-devel bison -y </code></pre></div> </div> </li> <li> <p>从 GitHub clone 代码</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> git clone https://github.com/alibaba/AliSQL.git </code></pre></div> </div> </li> <li> <p>cmake 配置</p> <p>在配置前,要先设置下环境变量,这样才能用到 devtoolset-4 套装里的gcc。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> scl enable devtoolset-4 bash cmake . \ -DCMAKE_BUILD_TYPE="Release" \ -DCMAKE_INSTALL_PREFIX="/opt/alisql" \ -DWITH_EMBEDDED_SERVER=0 \ -DWITH_EXTRA_CHARSETS=all \ -DWITH_MYISAM_STORAGE_ENGINE=1 \ -DWITH_INNOBASE_STORAGE_ENGINE=1 \ -DWITH_PARTITION_STORAGE_ENGINE=1 \ -DWITH_CSV_STORAGE_ENGINE=1 \ -DWITH_ARCHIVE_STORAGE_ENGINE=1 \ -DWITH_BLACKHOLE_STORAGE_ENGINE=1 \ -DWITH_FEDERATED_STORAGE_ENGINE=1 \ -DWITH_PERFSCHEMA_STORAGE_ENGINE=1 \ -DWITH_TOKUDB_STORAGE_ENGINE=1 </code></pre></div> </div> </li> <li> <p>编译安装</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> make -j4 &amp;&amp; make install </code></pre></div> </div> </li> </ol> <p>玩的开心 ^_^ !</p> Sun, 23 Oct 2016 00:00:00 +0800 http:/blog.fungo.me/2016/10/compile-alisql-from-source/ http:/blog.fungo.me/2016/10/compile-alisql-from-source/ AliSQL MySQL CentOS/RHEL 开发环境之 devtoolset <h2 id="前言">前言</h2> <p>CentOS/RHEL Linux 发行版以稳定性著称,所有的软件都要尽可能 stable,导致的一个结果就是基础软件的版本非常的低,比如 CentOS 6.7(15年发布) 中 gcc 版本还是 4.4.7(12年的版本)。这对开发来说就不是很友好,比如我们想用 C++ 11 中的某个特性,就必须自己编译一个高版本的 gcc 出来,但是这会有另外一个问题,开发环境不好维护,如果自己有多台电脑或者多个人合作的项目,每台机器上都要自己编一份,维护起来就比较麻烦。</p> <p>那么有没有法子像正常的安装 rpm 包一样,<code class="language-plaintext highlighter-rouge">yum install</code> 一个呢?有:</p> <ol> <li>自己打个 rpm 包,维护个 yum 源,这个代价太大 ==!;</li> <li>用别人提供的 rpm 包。</li> </ol> <p>本文介绍的就是第二种,推荐 <a href="http://linux.web.cern.ch/linux/devtoolset/" title="Developer Toolset">devtoolset</a> + <a href="https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Developer_Guide/scl-utils.html" title="Software Collections and scl-utils">scl</a>,也是绝配。</p> <p>devtoolset 是由 <a href="http://linux.web.cern.ch/linux/centos.shtml" title="Linux @ CERN">Linux @ CERN</a> 维护的,scl 是方便 RedHat <a href="https://www.softwarecollections.org" title="Software Collections">Software Collections</a> 软件包使用的工具。</p> <p>关于 Software Collections RedHat 官方是这样介绍的:</p> <blockquote> <p>For certain applications, more recent versions of some software components are often needed in order to use their latest new features. Red Hat Software Collections is a Red Hat offering that provides a set of dynamic programming languages, database servers, and various related packages that are either more recent than their equivalent versions included in the base Red Hat Enterprise Linux system, or are available for this system for the first time.</p> </blockquote> <p>RedHat 推出 <a href="https://www.softwarecollections.org" title="Software Collections">Software Collections</a> 的目的就是为了解决前面说的问题,想在 RedHat 系统下能使用新版本的工具,让同一个工具(如gcc)的不同版本能在系统中共存,在需要的时候切换到对应的版本中,是不是有点像 <a href="https://rvm.io/" title="Ruby Version Manager (RVM)">rvm</a>(ruby) 或者 <a href="https://github.com/creationix/nvm" title="Node Version Manager">nvm</a>(node)呢,不过这个可是系统级别的哦,对所有软件都适用。</p> <p>而 devtoolset 就是按照 Software Collections 的规范打出来的一套 rpm 包,目前的最新版本是 <a href="https://www.softwarecollections.org/repos/rhscl/devtoolset-3/epel-6-x86_64/" title="Developer Toolset 3.0">Developer Toolset 3.0</a>。</p> <p><img src="/assets/pic/201603/devtoolset-3.png" alt="devtoolset 3.0" title="devtoolset-3 软件列表" /></p> <p>看到 gcc 4.9 是不是很激动,别急,下面我们就介绍下怎么来安装和使用。</p> <h2 id="安装方法">安装方法</h2> <p>关于 devtoolset 的安装,Software Collections 官方有一个<a href="https://www.softwarecollections.org/en/scls/rhscl/devtoolset-3/" title="Devtoolset-3 by Software Collections">指导</a>,这里介绍的也差不多,只不过会更详细一些。</p> <ol> <li> <p>安装 scl-utils,<code class="language-plaintext highlighter-rouge">yum install scl-utils</code>,如果你的 yum 源里找不到这个包的话,可以这样</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> rpm -ivh "http://vault.centos.org/6.6/updates/x86_64/Packages/scl-utils-20120927-27.el6_6.x86_64.rpm" </code></pre></div> </div> </li> <li> <p>安装 devtoolset-3 yum 源</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> rpm -ivh "https://www.softwarecollections.org/repos/rhscl/devtoolset-3/epel-6-x86_64/noarch/rhscl-devtoolset-3-epel-6-x86_64-1-2.noarch.rpm" </code></pre></div> </div> </li> <li> <p>安装需要的 rpm 包,官方给的是 <code class="language-plaintext highlighter-rouge">yum install devtoolset-3</code>,这样会安装 devtoolset-3 源里的所有 rpm 包,完全没必要,如果我们需要 gcc 的话,只需要这样:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> yum install devtoolset-3-gcc devtoolset-3-gcc-c++ devtoolset-3-gdb </code></pre></div> </div> </li> <li> <p>激活 devtoolset-3</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> scl enable devtoolset-3 bash </code></pre></div> </div> <p>然后 <code class="language-plaintext highlighter-rouge">gcc --version</code> 就会看到已经变成 4.9 啦,或者可以这样</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> scl enable devtoolset-3 "gcc --version" </code></pre></div> </div> </li> </ol> <p>关于使用,这里多说一点,scl-utils 只是方便 Software Collections 使用,比如要查看当前安装了哪些 Software Collections,可以 <code class="language-plaintext highlighter-rouge">scl --list</code>,我们其实可以安全不用这个工具。devtoolset-3 中的 gcc 安装在 <code class="language-plaintext highlighter-rouge">/opt/rh/devtoolset-3/root/usr/bin/gcc</code>,我们可以</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export CC=/opt/rh/devtoolset-3/root/usr/bin/gcc </code></pre></div></div> <p>或者直接 source 环境变量</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>source /opt/rh/devtoolset-3/enable </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">scl enable</code> 命令也是 source 这个 enable 文件,只不是临时的,只对对当前命令有效。</p> <p>Software Collections 官网除了 devtoolset-3 外,还有大把其它的 <a href="https://www.softwarecollections.org/en/scls/" title="browse collections">collections</a>,如 ruby2.2、python3.4 等,有需要的自己安装,方法和 devtoolset-3 差不多。</p> <p>玩的开心 ^_^ !</p> Sun, 13 Mar 2016 00:00:00 +0800 http:/blog.fungo.me/2016/03/centos-development-env/ http:/blog.fungo.me/2016/03/centos-development-env/ 神兵利器 env dev linux Optimize MyISAM 表导致索引文件损坏 <h2 id="前言">前言</h2> <p>最近线上遇到一个问题,用户对表执行了 Optimize 后,发现数据都不见了,<code class="language-plaintext highlighter-rouge">select count(*) xxx</code> 返回值为 0。在了解了用户的场景后,本地复现了下,确实是这样,如下:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql&gt; select count(*) from t1; +----------+ | count(*) | +----------+ | 10230 | +----------+ 1 row in set (0.00 sec) mysql&gt; optimize table t1; +---------+----------+----------+--------------------------------------+ | Table | Op | Msg_type | Msg_text | +---------+----------+----------+--------------------------------------+ | test.t1 | optimize | error | myisam_sort_buffer_size is too small | | test.t1 | optimize | status | OK | +---------+----------+----------+--------------------------------------+ 2 rows in set (0.00 sec) mysql&gt; select count(*) from t1; +----------+ | count(*) | +----------+ | 0 | +----------+ 1 row in set (0.00 sec) </code></pre></div></div> <p>通过观察文件变化,可以快速确认的是索引文件坏掉了。在 Optimize 之前,文件大小和 MD5 如下:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$du -s test/t1.* 12 test/t1.frm 1924 test/t1.MYD 216 test/t1.MYI $md5sum test/t1.* b1da352c2ba355f940e26bd57e1a9f48 test/t1.frm 46785a243dec91f4df9601921d6d801d test/t1.MYD 313d82d2960dae4a0e58594d2f1c97e1 test/t1.MYI </code></pre></div></div> <p>Optimize 之后:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$du -s test/t1.* 12 test/t1.frm 1924 test/t1.MYD 4 test/t1.MYI $md5sum test/t1.* b1da352c2ba355f940e26bd57e1a9f48 test/t1.frm 46785a243dec91f4df9601921d6d801d test/t1.MYD e739452f3313de04af12e5d38d1b4661 test/t1.MYI </code></pre></div></div> <h2 id="bug-分析">bug 分析</h2> <p>有了复现 case 后,查问题就不难了,整个代码中报 “myisam_sort_buffer_size is too smal” 这个错的只有 2 处,掏出 gdb 全都加上断点。</p> <p>代码用的是目前最新的 5.6.29 版本,堆栈如下:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#0 _create_index_by_sort() at mysql-server/storage/myisam/sort.c:156 #1 mi_repair_by_sort() at mysql-server/storage/myisam/mi_check.c:2436 #2 ha_myisam::repair() at mysql-server/storage/myisam/ha_myisam.cc:1101 #3 ha_myisam::optimize() at mysql-server/storage/myisam/ha_myisam.cc:1015 #4 handler::ha_optimize() at mysql-server/sql/handler.cc:4270 #5 mysql_admin_table() at mysql-server/sql/sql_admin.cc:648 #6 Sql_cmd_optimize_table::execute() at mysql-server/sql/sql_admin.cc:1126 #7 mysql_execute_command() at mysql-server/sql/sql_parse.cc:4975 #8 mysql_parse() at mysql-server/sql/sql_parse.cc:6385 #9 dispatch_command() at mysql-server/sql/sql_parse.cc:1339 ... </code></pre></div></div> <p>Optimize 实际是通过 repair 表来实现的,这里 MySQL 判断需要通过排序的方式来做 repair,但是在 <code class="language-plaintext highlighter-rouge">_craete_index_by_sort()</code> 中因为 <code class="language-plaintext highlighter-rouge">myisam_sort_buffer_size</code> 设置的 buffer 空间太小,就返回错误了,然后 <code class="language-plaintext highlighter-rouge">mi_repair_by_sort</code> 设置 <code class="language-plaintext highlighter-rouge">param-&gt;retry_repair=1</code>,表示再做一次 repair。重试逻辑在 ha_myisam.cc:1015:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1015 if ((error= repair(thd,param,1)) &amp;&amp; param.retry_repair) 1016 { 1017 sql_print_warning("Warning: Optimize table got errno %d on %s.%s, retrying", 1018 my_errno, param.db_name, param.table_name); 1019 param.testflag&amp;= ~T_REP_BY_SORT; 1020 error= repair(thd,param,1); 1021 } </code></pre></div></div> <p>索引文件损坏就在第二次 repair 过程中,stack 如下:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#0 mi_sort_index () at mysql-server/storage/myisam/mi_check.c:1986 #1 ha_myisam::repair () at mysql-server/storage/myisam/ha_myisam.cc:1125 #2 ha_myisam::optimize () at mysql-server/storage/myisam/ha_myisam.cc:1020 #3 handler::ha_optimize () at mysql-server/sql/handler.cc:4270 #4 mysql_admin_table () at mysql-server/sql/sql_admin.cc:648 #5 Sql_cmd_optimize_table::execute () at mysql-server/sql/sql_admin.cc:1126 #6 mysql_execute_command () at mysql-server/sql/sql_parse.cc:4975 #7 mysql_parse () at mysql-server/sql/sql_parse.cc:6385 #8 dispatch_command () at mysql-server/sql/sql_parse.cc:1339 ... </code></pre></div></div> <p>在 <code class="language-plaintext highlighter-rouge">mi_sort_index()</code> 中会重建索引,但是在第一次 repairt 时,会调用 <code class="language-plaintext highlighter-rouge">mi_drop_all_indexes()</code> 将所有的删掉,<code class="language-plaintext highlighter-rouge">state-&gt;key_del[i]= HA_OFFSET_ERROR;</code>,所以 <code class="language-plaintext highlighter-rouge">mi_sort_index()</code> 中重建索引时,会跳过所有的 index,导致新生成的索引文件是空的,代码在 mi_check.cc:1986:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1986 if (share-&gt;state.key_root[key] != HA_OFFSET_ERROR) 1987 { 1988 index_pos[key]=param-&gt;new_file_pos; /* Write first block here */ 1989 if (sort_one_index(param,info,keyinfo,share-&gt;state.key_root[key], 1990 new_file)) 1991 goto err; 1992 } </code></pre></div></div> <h2 id="总结">总结</h2> <p>这个 bug 已经提给官方了,见 <a href="https://bugs.mysql.com/bug.php?id=80503" title="optimize MyISAM leads to corrupted index">#80503</a>。在分析 bug 之前,我先 Google 了一把,发现 12 年就有人发现了这个 bug,还是 5.1 版本的,不过有意思的是作为系统的 bug 提给了 <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=665013" title="Debian bug: OPTIMIZE TABLE on a big MyIsam-Table with to small myisam_sort_buffer_size leads to table corruption">Debian</a> 社区,而不是给 MySQL 社区 。</p> <p>官方对 MyISAM 引擎已经基本放弃冶疗了,所以这个 bug 估计也不太会修,即使修的话也比较慢。</p> <p>在 bug 修复之前,如果遇到这个问题,可以采用下面的 workaround:</p> <ol> <li>optimize 出现问题后,通过 <code class="language-plaintext highlighter-rouge">repair table xxx</code> 先把表索引修好;</li> <li>调大 <code class="language-plaintext highlighter-rouge">myisam_sort_buffer_size</code> 的值,确保后续的 optimize 不会出问题。</li> </ol> <p>MyISAM 引擎注定是要被时代抛弃的,不安全、不能并发插入(一个例外是 <a href="https://dev.mysql.com/doc/refman/5.6/en/concurrent-inserts.html" title="MyISAM Concurrent Inserts">Concurrent Inserts</a>)、表级锁等等,另外 InnoDB 的读性能早就超过了 MyISAM,引用2张<a href="http://dimitrik.free.fr/blog/archives/2012/11/mysql-performance-innodb-vs-myisam-in-56.html" title="MySQL Performance: InnoDB vs MyISAM in 5.6">官方</a>性能对比的图来看吧:</p> <p><img src="/assets/pic/201602/innodb-oltp-ro.png" alt="InnoDB OLTP RO" title="InnoDB OLTP RO 测试" /> <img src="/assets/pic/201602/myisam-oltp-ro.png" alt="MyISAM OLTP RO" title="MyISAM OLTP RO 测试" /></p> <p>InnoDB 比 MyISAM 快了 3-6 倍!</p> <p>快点抛弃 MyISAM 吧 ^_^</p> Thu, 25 Feb 2016 00:00:00 +0800 http:/blog.fungo.me/2016/02/optimize-lead-to-myisam-index-corruption/ http:/blog.fungo.me/2016/02/optimize-lead-to-myisam-index-corruption/ bugs MySQL Percona XtraBackup 介绍 <h2 id="前言">前言</h2> <p><a href="https://www.percona.com/software/mysql-database/percona-xtrabackup" title="Percona XtraBackup 官网">Percona XtraBackup</a> 是 Percona 公司开发的一个用于 MySQL 数据库<strong>物理热备</strong>的备份工具,支持 MySQl(Oracle)、Percona Server 和 MariaDB,并且全部开源,真可谓是业界良心。</p> <p>项目的 blueprint 和 bug 讨论放在 <a href="https://launchpad.net/percona-xtrabackup" title="Launchpad 地址">Launchpad</a>,之前代码也放在 Launchpad 用 Bazzar 管理,现在已经迁移到 <a href="https://github.com/percona/percona-xtrabackup" title="Github 地址">Github</a> 啦,项目更新发布非常快,感兴趣的可以关注 :-)</p> <p><img src="/assets/pic/201511/percona-xtrabackup-logo.png" alt="Percona XtraBackup" title="Percona XtraBackup" /></p> <p>真是一脸傲骄的 Lion。。。</p> <h2 id="功能支持">功能支持</h2> <p>Percona XtraBackup 可以说是业内最成熟流行的、开源的 MySQL 物理热备工具,提供了一套完备的备份方案。</p> <h3 id="对引擎的支持">对引擎的支持</h3> <p>首先明确下热备的概念,最基本的要求就是不需要 shutdown mysqld 进程,再进一步的是不能影响数据库的正常使用。</p> <ol> <li>InnoDB/XtraDB 引擎,完全的热备,不影响用户对引擎的使用,备份过程中可读写;</li> <li>非 InnoDB 引擎,如 MyISAM、Merge、Archive 和 CSV等,不完全的热备,虽然备份不需要关闭 mysqld 进程,但是会加全局读锁,备份过程中用户连接只能读不能写;</li> <li>TokuDB 引擎,相对于 MyISAM 和 InnoDB,这个引擎可以说是后起之秀,数据文件高压缩,节省空间,并且支持事务,目前 XtraBackup 并不支持对 TokuDB 表的备份。在今年4月份的时候,Percona <a href="https://www.percona.com/percona-acquires-tokutek" title="Percona acquires tokutek">收购了 Tokutex 公司</a>,现在 TokuDB 已归 Percona 所有,最近 Percona 又将 TokuDB 的热备工具<a href="https://github.com/percona/Percona-TokuBackup" title="Percoan TokuBackup">开源</a>,再一次称赞下业界良心,相信下一步就是集成进 Percona-XtraBackup,大家可以期待下 :-)</li> </ol> <h3 id="备份类型的支持">备份类型的支持</h3> <p>XtraBackup 提供了不同的备份方式:</p> <ol> <li>全备,全库全量备份,这种备份相当于对数据库做一个快照,这种方式比较常用;</li> <li>增量备份,也是全库备份,但备的是增量数据。增量备份不能单独使用,要和全量结合使用,可以有多个增量。增量备份的一个明显优势是节省空间,用户可以制定适合自己的备份策略,如全增增(1天全量、1天增量和1天增量)周期;</li> <li>部分备份,和全库备份对应,只备部分库或者部分表。如果觉得全库备份代价较大或者没必要,用户可以只备自己比较关心的表。</li> </ol> <h3 id="备份速度">备份速度</h3> <p>物理备份是直接拷贝文件,相对于逻辑备份已经很快了,XtraBackup 还提供了对备份速度的控制功能。</p> <ol> <li>加速,默认是单线程备份的,可以开并发来加快拷贝速度,并发数由用户指定;</li> <li>限速,虽然备份不会影响 mysqld 进程,但是备份依然耗系统的资源,如果备份压力太大,会间接影响 mysqld。拷贝文件最耗的资源 IO,XtraBackup 提供了 IOPS 限制功能。</li> </ol> <p>加速和限速只对 InnoDB 表的备份起作用,MyISAM 表备份都是单线程不限速的。</p> <h3 id="其它功能">其它功能</h3> <p>还有一些其它比较好用的功能,如压缩、流式输出、加密等,关于 XtraBackup 的详细功能支持,可以参考<a href="https://www.percona.com/doc/percona-xtrabackup/2.2/intro.html" title="About Percoan TokuBackup">官方介绍</a>。</p> <h2 id="工具使用">工具使用</h2> <p>是不是迫不及待的想试试了,下面看看怎么玩。</p> <h3 id="安装">安装</h3> <p>XtraBackup 目前只支持 GNU/Linux 平台,所以得先有一个 Linux 机器。</p> <p>官方发行版提供了 deb 包、rpm 包和全 Linux 平台通用的二进制压缩包,可以直接从<a href="https://www.percona.com/downloads/XtraBackup/LATEST/" title="Download Percona XtraBackup">官网下载</a>最新的进行安装。</p> <p>官方还提供了 yum 源和 apt 源,Debian 系和 Red Hat 系可以配置下源,直接用包管理命令安装,参见<a href="https://www.percona.com/doc/percona-xtrabackup/2.2/installation.html" title="Installing Percona XtraBackup">官方指导</a>;</p> <p>因为 XtraBackup 是开源的,所以我们可以自己编译,这对于需要自己增加功能的同学来说是必须的,还是看<a href="https://www.percona.com/doc/percona-xtrabackup/2.2/installation.html" title="Installing Percona XtraBackup">官方指导</a></p> <p>我后面会写一篇关于 XtraBackup 开发的文章 :-)</p> <h3 id="命令说明">命令说明</h3> <p>安装完后一共有4个二进行文件,如下:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>usr ├── bin │   ├── innobackupex │   ├── xbcrypt │   ├── xbstream │   └── xtrabackup </code></pre></div></div> <p>最主要的是 innobackupex 和 xtrabackup,前者是一个 perl 脚本,后者是 C 编译出的二进制。</p> <p>xtrabackup 是用来备份 InnoDB 表的,不能备份非 InnoDB 表,和 mysqld server 没有交互;innobackupex 脚本用来备份非 InnoDB 表,同时会调用 xtrabackup 命令来备份 InnoDB 表,还会和 mysqld server 发送命令进行交互,如加读锁(FTWRL)、获取位点(SHOW SLAVE STATUS)等。</p> <p>一般来说,我们是希望能备份 MyISAM 表的,虽然我们可能自己不用 MyISAM 表,但是 mysql 库下的系统表是 MyISAM 的,因此备份基本都通过 innobackupex 命令进行;另外一个原因是我们可能需要保存位点信息。</p> <p>另外2个命令相对小众些,xbcrypt 是加解密用的;xbstream 类似于tar,是 Percona 自己实现的一种支持并发写的流文件格式。两都在备份和解压时都会用到(如果备份用了加密和并发)。</p> <h3 id="常用命令姿势">常用命令姿势</h3> <p>XtraBackup 的使用,简单来说就是三板斧——备份、还原、恢复,简单好用。</p> <blockquote> <p>在使用前要注意下权限问题,这里有2个权限,一个是操作系统用户的权限,能写文件就行了;另一个是 mysql 用户的权限,具体需要哪些权限<a href="https://www.percona.com/doc/percona-xtrabackup/2.2/innobackupex/privileges.html" title="Connection and Privileges Needed">参考官网</a>。</p> </blockquote> <ol> <li> <p>备份 默认情况下备份是在指定的 backup-dir 目录下创建一个时间戳目录,然后备份集写入到时间目录下:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> innobackupex --host=127.0.0.1 --port=3306 --user=root --password=123456 --defaults-file=/etc/my.cnf backup-dir </code></pre></div> </div> <p>也可以通过流方式输出到标准输出,然后就可以在 Linux 下通过管道任意发挥了,如:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> innobackupex --host=127.0.0.1 --port=3306 --user=root --password=123456 --defaults-file=/etc/my.cnf --stream=tar backup-dir | gzip &gt; backup.tar.gz </code></pre></div> </div> <p>上面的命令备出来是一个 tar.gz 压缩包,解压时要用 tar izxvf 参数(千万不能少了i)。 也可以管道到 nc 命令,将备份通过网络传走。 Imagination is the only boundary!</p> </li> <li> <p>还原 备份集是不能直接用的,要先用工具还原才能使用。如果备份到压缩包的话,要先解压到目录,才能还原。 恢复是用 <code class="language-plaintext highlighter-rouge">--apply-log</code> 参数,另外恢复时强烈推荐加 <code class="language-plaintext highlighter-rouge">--use-memory</code> ,因为恢复会启动一个 innodb 引擎,这个参数指定的就是 <code class="language-plaintext highlighter-rouge">innodb_buffer_pool_size</code> 的大小, 默认是100M,如果备份集中 redo log 比较大的话,设置较大的 use-memory 可以显著提高恢复速度。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> innobackupex --defaults-file=/etc/my.cnf --use-memory=4G --apply-log backup-dir </code></pre></div> </div> </li> <li> <p>恢复 恢复就是把还原好的备份集拷贝到对应 mysqld 的数据目录,首先要用一个空的 mysqld 目标目录,并且提供相应的 my.cnf。innobackupex 提供了2个好用的参数,可以根据 my.cnf 中的目录配置,把备份集目录里的数据拷贝/移动到对应目标目录。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> innobackupex --defaults-file=/etc/my.cnf --copy-back backup-dir </code></pre></div> </div> <p>或者</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> innobackupex --defaults-file=/etc/my.cnf --move-back backup-dir </code></pre></div> </div> </li> </ol> <h2 id="延伸阅读">延伸阅读</h2> <p>想要用好 XtraBackup,<a href="https://www.percona.com/doc/percona-xtrabackup/2.2/index.html" title="Percona XtraBackup - Online Documentation">官方文档</a>是一定要看的,看完后基本可以解决 90% 以上备份遇到的问题。</p> <ol> <li><a href="https://www.percona.com/doc/percona-xtrabackup/2.2/how-tos.html" title="How-tos and Recipes">How-tos and Recipes</a>,各种使用姿势指导;</li> <li><a href="https://www.percona.com/doc/percona-xtrabackup/2.2/innobackupex/innobackupex_option_reference.html" title="The innobackupex Option Reference">innobackupex 参数</a> 和 <a href="https://www.percona.com/doc/percona-xtrabackup/2.2/xtrabackup_bin/xbk_option_reference.html" title="The xtrabackup Option Reference">xtrabackup 参数</a>,工具的详细参数列表,翻翻看,可能会发现意想不到的功能哦;如果安装工具的话,可以直接 <code class="language-plaintext highlighter-rouge">innobackupex --help</code> 或者 <code class="language-plaintext highlighter-rouge">xtrabackup --help</code> 查看;</li> <li><a href="https://www.percona.com/doc/percona-xtrabackup/2.2/manual.html" title="Percona XtraBackup User Manual">User Manual</a>,每个命令详细介绍。</li> <li><a href="https://www.percona.com/doc/percona-xtrabackup/2.2/xtrabackup-files.html" title="Index of files created by Percona XtraBackup">备份生成的辅助文件</a>,为了保证备份集的正确使用,XtraBackup 会生成一些额外的文件,如记录备份集对应的 binlog 位点。</li> </ol> Sat, 14 Nov 2015 00:00:00 +0800 http:/blog.fungo.me/2015/11/introduce-to-percona-xtrabackup/ http:/blog.fungo.me/2015/11/introduce-to-percona-xtrabackup/ 备份 神兵利器 Percona xtrabackup technology MySQL OH Mac!!! <p>哪个程序猿不想要一台Mac呢。</p> <p>最近把 Miss Lou 的 Mac Air 霸占了,又回到 *nix 的世界了 ^_^ !!!</p> <p>上一遍文章还是去年10月份,一年没更新了,真是被自己的懒折服了。。。</p> <p>现在有了 Mac,再也不能因为<em>环境的原因</em>不去更新了。</p> <p>折腾了一年的 MySQL,也算小小地入门,后面应该会更新很多 MySQL 相关的文章。</p> <p>Let’s play!</p> Mon, 25 May 2015 00:00:00 +0800 http:/blog.fungo.me/2015/05/oh-Mac/ http:/blog.fungo.me/2015/05/oh-Mac/ Mac life Hello World, Jekyll! <p>终于把博客换成 Jekyll 了,新的记录,新的开始。</p> <p>旧的博客放在 <a href="http://wp.fungo.me/">老山羊博客</a>,不在更新。</p> <p>Markdown, Gentoo, Emacs, MySQL, Let’s PLAY!</p> Sat, 25 Oct 2014 00:00:00 +0800 http:/blog.fungo.me/2014/10/hello-world-jekyll/ http:/blog.fungo.me/2014/10/hello-world-jekyll/ jekyll life