<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>宁静致远</title>
	<atom:link href="http://xiaoy.info/feed/" rel="self" type="application/rss+xml" />
	<link>http://xiaoy.info</link>
	<description>生活 工作 存储 技术研究 分享</description>
	<lastBuildDate>Thu, 02 Feb 2012 06:22:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>进程和线程[翻译]</title>
		<link>http://xiaoy.info/2012/02/02/254/fork_and_thread/</link>
		<comments>http://xiaoy.info/2012/02/02/254/fork_and_thread/#comments</comments>
		<pubDate>Thu, 02 Feb 2012 05:46:41 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://xiaoy.info/?p=254</guid>
		<description><![CDATA[进程和线程是OS基本的概念，也是面试热门题目。以前看过这个文章，介绍进程与线程的区别与选择，讲的较浅显，又有一定覆盖面。前两天在某群中<a href="http://www.laruence.com/">鸟哥</a>又推荐了一遍，顺手翻译如下。原文请<a href="http://www.geekride.com/fork-forking-vs-threading-thread-linux-kernel/">猛击这里</a>。
&#160;
＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝华丽的分割线＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
&#160;
<strong>什么是Fork[......]</strong><p class='read-more'><a href='http://xiaoy.info/2012/02/02/254/fork_and_thread/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>进程和线程是OS基本的概念，也是面试热门题目。以前看过这个文章，介绍进程与线程的区别与选择，讲的较浅显，又有一定覆盖面。前两天在某群中<a href="http://www.laruence.com/">鸟哥</a>又推荐了一遍，顺手翻译如下。原文请<a href="http://www.geekride.com/fork-forking-vs-threading-thread-linux-kernel/">猛击这里</a>。</p>
<p>&#160;</p>
<p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝华丽的分割线＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
<p>&#160;</p>
<p><strong>什么是Fork?</strong></p>
<p>Fork就是产生一个新的进程, 它和原进程看起来完全一样, 除了有一个新的进程ID, 拥有自己的内存地址空间. 新进程(子进程)和老进程(父进程)共享代码段, 各自独立运行.</p>
<p>Fork最常见的例子就是在shell下, 每当你运行一个命令, shell就会fork一个子进程来执行你的命令(严格来说, 是fork以后紧接着exec)</p>
<p>执行fork系统调用时，操作系统将拷贝父进程的所有page，并加载进独立的内存区域。但某些情况不需要这些page拷贝，比如exec系列的系统调用，因为execv将替换掉父进程的地址空间。</p>
<p>fork需要注意:</p>
<ul>
<li>子进程有自己的独立进程id </li>
<li>子进程持有父进程的文件描述符 </li>
<li>子进程不会继承父进程的文件锁 </li>
<li>父进程中打开的信号量，在子进程中一样处于开启状态 </li>
<li>子进程持有父进程的消息队列描述符 </li>
<li>子进程有自己的地址与内存空间 </li>
</ul>
<p>相对而言fork被更普遍的使用，大致原因如下:</p>
<ul>
<li>基于fork的开发实现更容易 </li>
<li>更容易维护 </li>
<li>因为进程在自己独立的虚拟地址空间中运行，所以fork更安全。如果一个进程crash或者缓冲区溢出，不会影响到其他的进程 </li>
<li>基于线程的代码更难于debug </li>
<li>fork的移植性更好 </li>
<li>fork在单核cpu上更快，因为没有锁和上下文切换的开销 </li>
</ul>
<p>使用fork的应用有: telnetd(freebsd), vsftpd, proftpd, Apache13, Apache2, thttpd, PostgreSQL.</p>
<p>fork的陷阱</p>
<ul>
<li>每个新进程拥有独立内存地址空间，它带来了更长的启停时间 </li>
<li>如果使用fork，需要考虑两个进程可能需要交互，而进程间通信的成本很高 </li>
<li>如果父进程在子进程前退出，子进程将变成孤儿进程。而线程模型中可以简单的结束线程、挂起线程和恢复线程。如果程序退出，所有线程也会自动结束 </li>
<li>存储空间不足可能导致fork失败 </li>
</ul>
<p><strong>什么是线程？</strong></p>
<p>线程是轻量级进程(LWPS)。一般认为，线程只是CPU状态(和其他一些minimal state)，而进程包括其他的数据、堆栈、IO和信号等等。线程的overhead比fork要小，因为系统不需要初始化新的虚拟内存空间。在多核系统上，可以将执行流分配到其他的处理器上，通过并行和分布式处理得到速度提升；在单处理器的系统上，由于存在IO延迟和其他挂起执行的功能，使用线程一样可以获得收益。</p>
<p>同一进程内的线程将共享：</p>
<ul>
<li>进程指令 </li>
<li>绝大多数数据 </li>
<li>打开的文件(描述符) </li>
<li>信号与信号处理函数 </li>
<li>当前工作目录 </li>
<li>用户和组id </li>
</ul>
<p>每个线程有独立的:</p>
<ul>
<li>线程id </li>
<li>寄存器、栈指针 </li>
<li>栈(本地变量，返回地址) </li>
<li>信号mask </li>
<li>优先级 </li>
<li>errno返回值(errno是tls存储的) </li>
</ul>
<p>线程需要注意:</p>
<ul>
<li>在多处理器/多核系统中，线程效率最高 </li>
<li>只占用一张进程表和一个schedule </li>
<li>进程中的所有线程共享相同的地址空间 </li>
<li>线程不维护创建的线程列表，也不知道是哪个线程创建的自己 </li>
<li>线程通过共享基本部件来减少overhead </li>
<li>线程在内存管理方面效率更高，因为它们使用相同的内存块而不是创建新的 </li>
</ul>
<p>线程的陷阱</p>
<ul>
<li>Race conditions：多个线程同时读写相同的数据，但却不知道其他线程存在，这可能导致数据混乱。这种情况我们称之为竞态条件(race conditions)。操作系统会调度线程，而不能确定其运行方式。线程可能不会按照创建的顺序运行；它们也可能以不同的速度运行。当线程执行时，可能会给出预期外的结果。在线程模型下，必须使用锁和join来获取可预测的执行顺序和产出。 </li>
<li>线程安全代码：在多线程中运行的代码需要是线程安全的。这意味着使用静态变量和全局变量时，不能假设其他线程不会对它进行访问。如果代码使用了静态变量或全局变量，必须使用锁，或者重写函数，以避免使用这些变量。在C语言中，局部变量在栈上动态分析，因此，不使用静态数据和其他共享资源的函数都是线程安全的。程序中，非线程安全函数同一时间只能被一个线程调用，这点必须得到保证。很多不可重入函数返回了指向静态数据的指针，这可以通过返回动态分配的数据或由调用者提供空间的方式来避免。非线程安全函数的一个例子就是strtok，它也是不可重入的。它的可重入版本strtok_r是线程安全的。 </li>
</ul>
<p>线程的优势：</p>
<ul>
<li>线程共享相同的内存空间，因此线程之间共享数据速度很快。 </li>
<li>如果设计实现较好，使用线程将获得较大的速度提升，因为在多线程程序中没有进程级别的上下文切换。 </li>
<li>线程启动和结束很快 </li>
</ul>
<p>使用线程的程序：MySQL, Firebird, Apache2, MySQL 323</p>
<p><strong></strong></p>
<p><strong>FAQs</strong></p>
<p>1. 我该使用哪个？</p>
<p>回答：取决于很多因素。fork比thread更重量级，有更高的启停成本。进程间通信(IPC)比线程间通信更困难，也更慢。实际上在通信方面，线程优势很大。但另一方面，一个线程crash后，将导致所有其他线程停止；并且只要一个线程出现缓冲区溢出，就会为所有的线程带来安全问题。</p>
<p>2. 哪个更好？</p>
<p>回答：这完全取决于需要。在当前的linux(2.6.x)，进程和线程的切换成本没有太大区别（区别只有thread的MMU)。由于共享地址空间，线程还存在一个问题：一个线程的错误指针，可能导致其他线程的数据错误。</p>
<p>3. 什么时候要用线程或进程？</p>
<p>回答:    <br />如果你想用多线程，那么正确的问题应该是：程序的哪些部分可以/不可以被线程化。以下是一些经验法则：</p>
<ul>
<li>是否存在互不依赖的耗时操作（比如画窗体、打印文档、响应鼠标事件、计算表格的列、信号处理等等）？ </li>
<li>数据锁不多（指共享数据的量小）? </li>
<li>准备好了考虑锁、死锁和竞态条件？ </li>
<li>任务能否划分？比如是否可以一个线程处理信号，另一个处理GUI？ </li>
</ul>
<p><strong>结论</strong></p>
<ul>
<li>用线程或者fork，取决于应用需求 </li>
<li>线程更强大，但并不是万能的 </li>
<li>基于线程比基于fork更难写(也更难维护)，只适用于熟手 </li>
<li>只在极其注重性能的程序中使用线程 </li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2012/02/02/254/fork_and_thread/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HandlerSocket调研</title>
		<link>http://xiaoy.info/2010/12/11/229/handlersocket_study/</link>
		<comments>http://xiaoy.info/2010/12/11/229/handlersocket_study/#comments</comments>
		<pubDate>Fri, 10 Dec 2010 18:21:17 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[存储]]></category>

		<guid isPermaLink="false">http://xiaoy.info/2010/12/11/229/handlersocket%e8%b0%83%e7%a0%94%e4%b8%80-%e6%a6%82%e8%bf%b0/</guid>
		<description><![CDATA[概述
背景

在前MySQL核心开发人员Yoshinori Matsunobu<a href="http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html">宣传handlerSocket</a>之后，最近这个插件颇受关注 。它的核心思想很简单，在profiling后发现对于简单的主键查询，SQL层的消耗很大。在数据集较小，能够在内存中存放的情况下(此时随机的Read IO可以忽略)，[......]<p class='read-more'><a href='http://xiaoy.info/2010/12/11/229/handlersocket_study/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<h2>概述</h2>
<h3>背景</h3>
<h2></h2>
<p>在前MySQL核心开发人员Yoshinori Matsunobu<a href="http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html">宣传handlerSocket</a>之后，最近这个插件颇受关注 。它的核心思想很简单，在profiling后发现对于简单的主键查询，SQL层的消耗很大。在数据集较小，能够在内存中存放的情况下(此时随机的Read IO可以忽略)，SQL层就成了最大的瓶颈。为方便不能爬墙的同学，RT一下原文给出的剖分结果：</p>
<pre class="brush: text; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">samples  %        app name                 symbol name
259130    4.5199  mysqld                   MYSQLparse(void*)
196841    3.4334  mysqld                   my_pthread_fastmutex_lock
106439    1.8566  libc-2.5.so              _int_malloc
94583     1.6498  bnx2                     /bnx2
84550     1.4748  ha_innodb_plugin.so.0.0.0 ut_delay
67945     1.1851  mysqld                   _ZL20make_join_statistics
P4JOINP10TABLE_LISTP4ItemP16st_dynamic_array
63435     1.1065  mysqld                   JOIN::optimize()
55825     0.9737  vmlinux                  wakeup_stack_begin
55054     0.9603  mysqld                   MYSQLlex(void*, void*)
50833     0.8867  libpthread-2.5.so        pthread_mutex_trylock
49602     0.8652  ha_innodb_plugin.so.0.0.0 row_search_for_mysql
47518     0.8288  libc-2.5.so              memcpy
46957     0.8190  vmlinux                  .text.elf_core_dump
46499     0.8111  libc-2.5.so              malloc</pre>
<p>可以看出，简单的SQL查询方式下，有很大比例的时间消耗在SQL解析、Query Plan、表锁等SQL层上。因此，如果跳过SQL层，直接与存储引擎进行交互，就可以获取很大程度的性能提升。</p>
<p>基于这一思想，他们的团队开发了HandlerSocket。测试结果显示，单机查询性能能够到达75W+(100w条数据，做纯内存主键查询)，这个数字意味着已经超过了现在绝大多数KV存储系统、甚至缓存系统的性能。</p>
<p>MySQL的Vadim觉得这玩艺儿挺靠谱(enjoyed)，也对它进行了测试，并在MySQL Performance Blog上<a href="http://www.mysqlperformanceblog.com/2010/11/02/handlersocket-on-ssd/">给出了测试的结果</a>，这个测试关注了数据量大到需要换入换出时handlersocket的性能表现。结论与预期的相符：当数据在内存能装下时，性能稳定在60W+ rps的水平，但当数据大到一定级别时，性能开始下降。此时主要的瓶颈就在于IO，像FusionIO这样强悍的硬件，还可以支撑到40W+，而普通的RAID10就已经惨不忍睹。 也就是说数据量大拼的就是硬件IO性能，此时HandlerSocket在SQL层节省的CPU消耗，在巨大的IO成本前不值一提。</p>
<h3>插件结构</h3>
<p>再RT一下Yoshinori给出的结构图:</p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image.png" width="681" height="378" /></p>
<p align="center">图一：HandlerSocket结构 (来源于<a href="http://www.slideshare.net/akirahiguchi/handlersocket-20100629en-5698215">slidershare的PPT</a>)</p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mysql_handlersocket" border="0" alt="mysql_handlersocket" src="http://xiaoy.info/img/HandlerSocket_B007/mysql_handlersocket.png" width="952" height="578" /></p>
<p align="center">图二: HandlerSocket结构 (来源于原文)</p>
<p>&#160;</p>
<p>这两张图大同小异，但主旨都是在正常的SQL解析层外，HandlerSocket为我们开了一条后门，直接通过MySQL的HandlerInterface与存储引擎打交道。第二张结构图更详细，可明显看出HandlerSocket要做的事情比正常的SQL少很多。</p>
<h3>特性</h3>
<p>在原文作者列出了HandlerSocket的一些特性，整理了一下相对重要的，再加上自己的一些粗浅的理解：</p>
<ul>
<li>高效简洁的网络框架
<ul>
<li>在Linux系统上采用epoll的方式驱动，否则采用poll的方式。 </li>
<li>单客户端连接的成本很低，PHP等可以方便的使用短连接访问，而不用再担心并发连接数问题（ps. fcicq<a href="http://www.fcicq.net/wp/?p=971">怀疑新浪微博就是这么杯具</a>的)。 </li>
</ul>
</li>
<li>高性能。除了直接访问引擎接口外，HS还做了一些工作用来提升性能
<ul>
<li>很简洁的网络接口，协议包的冗余数据很少，减少网络带宽占用。 </li>
<li>自动Group客户端请求
<ul>
<li>用过MySQL的同学应该都知道批量提交/批量查询的技巧，用来提升性能 </li>
<li>HandlerSocket往前走了一步，在Server层面为我们做类似的优化，自动Group尽可能多的请求，一次性提交 </li>
</ul>
</li>
<li>开放了一个“只能”进行读操作的端口(图二中的9998)
<ul>
<li>两个端口被称之为ReadPort和WritePort，但不要被名字迷惑。虽然Read端口只能读，但Write Port一样可以进行Read操作 </li>
<li>读请求虽然也是个事务，但纯读可以节省很多成本，比如Transaction Commit，以最大限度提高性能 </li>
<li>在稍后的测试中，我们将对这两个端口的读性能进行测试 </li>
</ul>
</li>
</ul>
</li>
<li>HandlerSocket支持多种请求
<ul>
<li>支持主键查找、列索引查找、范围查询、LIMIT </li>
<li>支持INSERT （注意:无法返回生成的key） </li>
<li>支持UPDATE </li>
<li>支持DELETE </li>
</ul>
</li>
<li>仍然支持SQL查询 ，对于复杂查询，仍然可以走普通的SQL接口进行访问 </li>
<li>数据由相对成熟的数据库引擎(InnoDB)管理，崩溃安全性良好，也可以快速恢复 </li>
<li>不需要重新编译MySQL </li>
<li>支持Row-Based的主从同步
<ul>
<li>由于它跳过了SQL层，所以只能选择存储引擎的行级数据同步，而不能做Statement级别的同步(行级同步在mysql5.1版本引入的，这就是HandlerSocket要求mysql版本5.1+的原因<img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="微笑" src="http://xiaoy.info/img/HandlerSocket_B007/wlEmoticon-smile.png" />) </li>
<li>binlog_format是session相关的变量，HandlerSocket会调用Interface，设置Row-Based同步。 </li>
</ul>
</li>
<li>运维方面简单，现有大量的MySQL运维工具和经验可以直接使用 </li>
</ul>
<h2>源码分析</h2>
<p>作为一个较新的开源项目，HandlerSocket的文档比较薄弱。幸好它的代码还是很简单的，有什么疑问翻一下代码基本都能解决。这里就不展开很细致的代码分析，主要分析一下代码层面重要的几个点。</p>
<h3>插件实现</h3>
<h2></h2>
<p>图一告诉我们，HandlerSocket和SQL Layer在同一层，但实际上这个地方有点小trick。它以daemon plugin的形式的，在这个意义上说，它和InnoDB/MyISAM等引擎插件在同一层；但在daemon_handlersocket_init里，就自己listen端口、起worker线程、接收请求、直接与存储引擎交互。</p>
<p>没有插件开发经验的同学，理解这个trick可能会稍有些疑惑：它是如何被调度的？它又是如何直接访问其他存储引擎的？</p>
<ul>
<li>插件引擎都会有一套接口规范，具体实现的插件都必须遵守这个接口规范，以函数指针或者类对象继承方式由插件引擎调用。而接口规范一般都有init接口用于插件初始化。HandlerSocket就利用了这一特性，Init时开了一个后门( 代码文件:handlersocket.cpp ) </li>
<li>MySQL有一层Handler层(图1中的HandlerInterface)，它直接与各个存储引擎交互，并负责XA事务的两阶段提交，HandlerSocket调用的就是HandlerInterface的ha_update_row、ha_delete_row、ha_write_row或index_read_map等接口进行CRUD操作。这也是它名字的由来<img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="微笑" src="http://xiaoy.info/img/HandlerSocket_B007/wlEmoticon-smile.png" />（代码文件 database.cpp） </li>
</ul>
<h3>工作流程</h3>
<p>worker thread的流程清晰明了，总体流程如下:</p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_3.png" width="696" height="255" /></p>
<h3>事务模型</h3>
<p>工作流程的图示中可以看出，在一次epoll_wait返回的请求，将一并commit，这也是HandlerSocket的基本事务模型：</p>
<ul>
<li>写线程以一个epoll_wait收到的“所有”读写请求作为一个事务 </li>
<li>事务隔离级别也没有特殊之处，各存储引擎按照配置进行 </li>
<li>由HandlerInterface管理XA事务，对事务表和非事务表的提交，与正常SQL处理也相同 </li>
<li>锁冲突也是由各存储引擎处理，MySQL用行锁，InnoDB用行级锁。这里需要注意：如果在3306端口进行了Lock，HandlerSocket一样会阻塞等待。 </li>
<li>也不会影响到MVCC </li>
</ul>
<p>协议</p>
<p>HandlerSocket使用了自定义协议进行交互。具体协议有文档说明，参考源码目录docs-en/protocol.en.txt。协议这里就不详细展开，只提一下基本语法：</p>
<ul>
<li>一个命令一行，采用\n分隔，行内每项数据用\t分隔 </li>
<li>由于\t\n在协议中有特殊含义，如果数据含有\t、\n，就需要进行转义(转义规则设计的有点奇怪，有哪位同学知晓设计思想，欢迎赐教)
<ul>
<li>[0x10-0xff]不转义 </li>
<li>[0x00-0x0f]表示为两字节: [0x01] [0x40+value] </li>
</ul>
</li>
<li>NULL用\0表示，以区别长度为0的字符串 </li>
</ul>
<h2>调研测试</h2>
<h3>侧重点</h3>
<ul>
<li>插入性能
<ul>
<li>HandlerSocket与SQL性能对比 </li>
<li>Group提交对时延的影响 </li>
</ul>
</li>
<li>读取性能
<ul>
<li>HandlerSocket与SQL性能对比 </li>
<li>Read Port与Write Port性能对比 </li>
</ul>
</li>
<li>主从同步 </li>
</ul>
<h3>测试环境</h3>
<ul>
<li>硬件 </li>
</ul>
<p>两台DELL PowerEdge 2950，4核Intel Xeon 5510 @2.66G, 16G内存</p>
<ul>
<li>软件 </li>
</ul>
<p>Red Hat Enterprise Linux AS release 4 (Nahant Update 3)</p>
<p><a href="http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.53.tar.gz/from/http://mysql.ntu.edu.tw/">mysql 5.1.53 Linux-generic-source</a></p>
<p><a href="https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL/tarball/master">HandlerSocket</a> a485973</p>
<ul>
<li>软件配置 </li>
</ul>
<p>MySQL:</p>
<pre class="brush: text; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">innodb_buffer_pool_size = 8G
innodb_flush_log_at_trx_commit = 2
innodb_thread_concurrency = 16
innodb_log_buffer_size = 8M
innodb_log_file_size = 256M
innodb_max_dirty_pages_pct = 90</pre>
<p>HandlerSocket:</p>
<pre class="brush: text; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">loose_handlersocket_port = 9998
loose_handlersocket_port_wr = 9999
loose_handlersocket_threads = 4
loose_handlersocket_threads_wr = 1
open_files_limit = 65535</pre>
<ul>
<li>表格式 </li>
</ul>
<pre class="brush: sql; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">CREATE TABLE user (
  user_id INT UNSIGNED PRIMARY KEY,
  user_name VARCHAR(50),
  user_email VARCHAR(255),
  created DATETIME
) ENGINE=InnoDB;</pre>
<ul>
<li>采用tcprstat测量响应时间 </li>
</ul>
<h3>测试</h3>
<h4>写入性能</h4>
<h5>SQL</h5>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">seq 1000000 | sed 's/\(.*\)/INSERT INTO user set user_id=\1, user_name=\1, user_email=\1;/' &gt; handlersocket.sql
time mysql -D test &lt; handlersocket.sql</pre>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image.gif" width="578" height="266" /></p>
<h5>HandlerSocket</h5>
<p>扩展hstest程序,增加测试用例，插入与SQL相同数据。</p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_3.gif" width="578" height="266" /></p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_4.gif" width="578" height="266" /></p>
<h4>读性能</h4>
<h5></h5>
<h5>SQL</h5>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">关闭qcache
mysqlslap --query=&quot;select user_name from user where user_id=1&quot; --number-of-queries=10000000 --concurrency=30 --host=HOST --port=3306</pre>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_5.gif" width="578" height="266" /></p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_11.gif" width="578" height="266" /></p>
<h5>HandlerSocket Read Port</h5>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">./hstest test=11 tablesize=1000000  host=10.26.53.34 hsport=9998 num=10000000 num_threads=100 timelimit=10 </pre>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_7.gif" width="578" height="266" /></p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_8.gif" width="578" height="266" /></p>
<h5>HandlerSocket WritePort</h5>
<p>在这里，我们需要修改loose_handlersocket_threads_wr，将WritePort的工作线程数为4，保持与ReadPort一致，之后再运行hstest。</p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_9.gif" width="578" height="266" /></p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_10.gif" width="578" height="266" /></p>
<h3>结论</h3>
<p>简单整理分析一下：</p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://xiaoy.info/img/HandlerSocket_B007/image_5.png" width="454" height="90" /></p>
<ul>
<li>写性能约为SQL的3.74倍 </li>
<li>读性能约为SQL的3.83倍，达到20w左右。测试结果离官方宣称的75w+还有一定距离。应该是测试环境的问题：
<ul>
<li>硬件 </li>
<li>mysql版本使用linux-generic版本，未进行configue优化。这个原因可能性较大，因为profile发现锁开销很大，纯读不应该出现这种情况。另外Yoshinori的测试profile结果也显示他们版本的瓶颈在网络层。 </li>
</ul>
</li>
<li>Group提交方式，造成了一定程度的服务时延，平均时延较SQL方式大了1倍左右 </li>
</ul>
<p>总体来看，HandlerSocket有着很不错的性能表现。在以下case应该有不错的应用前景：</p>
<ul>
<li>缓存系统：性能已经接近甚至超过了memcache，还支持固化、崩溃恢复； </li>
<li>内存数据库：handlersocket直接用存储引擎做后端，当后端使用InnoDB时，可以理解成一个B树组织、支持Adaptive Hash的内存数据库。虽然几十万级别的数字，对于内存数据库来说，可能还有挖掘潜力，但毕竟这些存储引擎久经考验，数据安全性值得依赖，而且还是免费的。在小数据量高性能存储的场景，HandlerSocket是一个不错的代替方案。 </li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2010/12/11/229/handlersocket_study/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Godaddy上dokuwiki安全策略</title>
		<link>http://xiaoy.info/2010/12/04/214/improve_dokuwiki_security_on_godaddy/</link>
		<comments>http://xiaoy.info/2010/12/04/214/improve_dokuwiki_security_on_godaddy/#comments</comments>
		<pubDate>Sat, 04 Dec 2010 14:47:52 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[建站]]></category>
		<category><![CDATA[dokuwiki]]></category>
		<category><![CDATA[godaddy]]></category>
		<category><![CDATA[webhosting]]></category>

		<guid isPermaLink="false">http://xiaoy.info/2010/12/04/214/improve_dokuwiki_security_on_godaddy/</guid>
		<description><![CDATA[在GoDaddy上架了个dokuwiki做简单的pkm。在管理员登录后，dokuwiki总有一个安全提示，说dokuwiki的安全设定有问题。之前没去折腾它，一直忍着大红叉，直到今天无意打开了<a href="http://www.dokuwiki.org/security#web_access_securit">指导文章</a>，按照上面的提示，访问了一下url: http://xiaoy.info/pkm/dokuwiki/[......]<p class='read-more'><a href='http://xiaoy.info/2010/12/04/214/improve_dokuwiki_security_on_godaddy/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>在GoDaddy上架了个dokuwiki做简单的pkm。在管理员登录后，dokuwiki总有一个安全提示，说dokuwiki的安全设定有问题。之前没去折腾它，一直忍着大红叉，直到今天无意打开了<a href="http://www.dokuwiki.org/security#web_access_securit">指导文章</a>，按照上面的提示，访问了一下url: http://xiaoy.info/pkm/dokuwiki/data/pages/wiki/dokuwiki.txt，居然很顺利的就把wiki内容取出来了(别试了，现在已经不行了<img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="微笑" src="http://xiaoy.info/img/1cbbdc61975f_125B2/wlEmoticon-smile.png" />)，按图索骥就可得到所有wiki，就算设定了private也照样可以拉到。赶紧看解决方案，官方给出两个方法:</p>
<ol>
<li>通过WebServer配置，禁用关键目录(data/conf)的访问 </li>
<li>将关键目录从htdocs移走 </li>
</ol>
<p>杯具的是，这两个方法对godaddy免费windows主机都无效。它上面跑的是IIS，而IIS想禁用关键目录访问，必须有IIS控制权限，免费主机当然是没有的。</p>
<p>而第2个方法，godaddy为每个用户只提供了htdocs目录，无法把文件放置于其他地方。</p>
<p>难道就没有别的解决方案了么？分析一下现在的目标是：让用户无法直接访问到关键目录。官方两种方法为了达到这个目的，采用了webserver配置或者htdocs的方法，但达到这个目的只有这两个方法么？显然答案是no.在godaddy上，可以用很简单的基于IIS的方案:</p>
<p>修改web.config，用rule匹配的方式，禁用掉data目录和conf目录的访问:</p>
<pre class="brush: xml; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">&lt;rule name=&quot;secure&quot;&gt;
	&lt;match url=&quot;^pkm/dokuwiki/(data|conf|bin|inc)/?(.*)?$&quot; /&gt;
	&lt;action type=&quot;CustomResponse&quot; statusCode=&quot;403&quot; statusReason=&quot;Forbidded&quot; statusDescription=&quot;Forbidden&quot; /&gt;
&lt;/rule&gt;</pre>
<p>当识别到关键目录的直接访问时，直接返回403错误，这样就很简单明了的解决了问题<img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="微笑" src="http://xiaoy.info/img/1cbbdc61975f_125B2/wlEmoticon-smile.png" />。</p>
<p>&#160;</p>
<p>参考: <a href="http://learn.iis.net/page.aspx/557/translate-htaccess-content-to-iis-webconfig/">http://learn.iis.net/page.aspx/557/translate-htaccess-content-to-iis-webconfig/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2010/12/04/214/improve_dokuwiki_security_on_godaddy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OPROFILE安装试用</title>
		<link>http://xiaoy.info/2010/12/03/206/oprofile_install/</link>
		<comments>http://xiaoy.info/2010/12/03/206/oprofile_install/#comments</comments>
		<pubDate>Fri, 03 Dec 2010 07:40:15 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[oprofile profiling]]></category>

		<guid isPermaLink="false">http://xiaoy.info/2010/12/03/206/oprofile%e5%ae%89%e8%a3%85%e8%af%95%e7%94%a8/</guid>
		<description><![CDATA[<a href="http://oprofile.sourceforge.net/faq/">OPROFILE</a>是一个开源的profiling工具，类似于vtune和gprof。但不需要像gprof一样，必须优雅退出才可以剖分。今天在看<a href="http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html">HandlerSocket</a>文章时，想重复一下作者的oprofile结果，因此，安装试用了一下，遇到一些问题，过程记录如下。
1. 连到服务器开发机上，发现已经安[......]<p class='read-more'><a href='http://xiaoy.info/2010/12/03/206/oprofile_install/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p align="left"><a href="http://oprofile.sourceforge.net/faq/">OPROFILE</a>是一个开源的profiling工具，类似于vtune和gprof。但不需要像gprof一样，必须优雅退出才可以剖分。今天在看<a href="http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html">HandlerSocket</a>文章时，想重复一下作者的oprofile结果，因此，安装试用了一下，遇到一些问题，过程记录如下。</p>
<p align="left">1. 连到服务器开发机上，发现已经安装了oprofile。尝试初始化: sudo opcontrol &#8211;init 出现错误：kernel doesn&#8217;t support oprofile. 看着很吓人，以为需要重新编译内核。但仔细看了看介绍，说2.6的内核已经以module的方式支持oprofile。因此，只需要sudo modprobe oprofile一下，如果正常就可以继续，否则就说明没有安装这个module,需要<a href="http://blog.csdn.net/force_eagle/archive/2009/07/10/4338035.aspx">重编内核</a>，在menuconfig时选择是否添加。。</p>
<p>如果modprobe正确，仍出现这个错误，那说明opcontrol没有对应的kernel driver. 解决方案是重新编译安装oprofile。 </p>
<p>2. 安装</p>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">wget http://prdownloads.sourceforge.net/oprofile/oprofile-0.9.6.tar.gz</pre>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">tar xvfz oprofile.*.gz
./configure --prefix=/home/work/software/output/ --with-kernel-support</pre>
<p>出现</p>
<p>/usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/3.4.4/../../../../lib64/libbfd.a(archures.o): relocation R_X86_64_32 against `a local symbol&#8217; can not be used when making a shared object; recompile with –fPIC</p>
<p>改为：<a name="_GoBack"></a></p>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">./configure --prefix=/home/work/software/output/ --with-kernel-support --enable-shared=no</pre>
<p>再make</p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="wps_clip_image-9659" border="0" alt="wps_clip_image-9659" src="http://xiaoy.info/img/cfca2d43a47e_C840/wps_clip_image-9659.png" width="574" height="68" /></p>
<p>服务器用的是64位的系统，而它默认依赖到32位的库．因此，导出LDFLAGS=-L/usr/lib64后，make clean后再make，就成功通过了．</p>
<p><img style="background-image: none; border-right-width: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="wps_clip_image-16547" border="0" alt="wps_clip_image-16547" src="http://xiaoy.info/img/cfca2d43a47e_C840/wps_clip_image-16547.png" width="565" height="131" /></p>
<p>最后还出个waring，提示要添加用户，如果不调试JIT，直接忽略，不用添加用户。</p>
<p>3. 这就可以开始使用oprofile了，不过需要注意的是，需要有root权限才可以运行，请向系统管理员索要sudo权限。</p>
<p>4. 对mysqld进行profile为例:</p>
<pre class="brush: xml; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">sudo opcontrol --reset
sudo opcontrol --separate=lib --no-vmlinux --start --image=/home/software/output/libexec/mysqld
在其他机器起压力,压力停止后再进行后续操作
sudo opcontrol --dump
sudo opcontrol --shutdown

opreport -l /home/software/output/libexec/mysqld
opannotate -s /home/software/output/libexec/mysqld </pre>
<p>参考：<a href="http://oprofile.sourceforge.net/doc/install.html">http://oprofile.sourceforge.net/doc/install.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2010/12/03/206/oprofile_install/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[翻译]为什么数据库使用B树</title>
		<link>http://xiaoy.info/2010/11/20/200/why_disk_based_database_use_b_tree/</link>
		<comments>http://xiaoy.info/2010/11/20/200/why_disk_based_database_use_b_tree/#comments</comments>
		<pubDate>Sat, 20 Nov 2010 14:27:48 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[存储]]></category>

		<guid isPermaLink="false">http://xiaoy.info/2010/11/20/200/why_disk_based_database_use_b_tree/</guid>
		<description><![CDATA[在B树的<a href="http://en.wikipedia.org/wiki/B-tree">wiki</a>页面上,有一小段内容介绍为什么数据库使用B树，很通俗易懂，推荐。
Time to search a sorted file     Usually, sorting and searching algorithms have been characterized by the num[......]<p class='read-more'><a href='http://xiaoy.info/2010/11/20/200/why_disk_based_database_use_b_tree/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>在B树的<a href="http://en.wikipedia.org/wiki/B-tree">wiki</a>页面上,有一小段内容介绍为什么数据库使用B树，很通俗易懂，推荐。</p>
<blockquote><p>Time to search a sorted file     <br />Usually, sorting and searching algorithms have been characterized by the number of comparison operations that must be performed using order notation. A binary search of a sorted table with N records, for example, can be done in O(log2N) comparisons. If the table had 1,000,000 records, then a specific record could be located with about 20 comparisons: log21,000,000 = 19.931&#8230;.</p>
<p>Large databases have historically been kept on disk drives. The time to read a record on a disk drive can dominate the time needed to compare keys once the record is available. The time to read a record from a disk drive involves a seek time and a rotational delay. The seek time may be 0 to 20 or more milliseconds, and the rotational delay averages about half the rotation period. For a 7200 RPM drive, the rotation period is 8.33 milliseconds. For a drive such as the Seagate ST3500320NS, the track-to-track seek time is 0.8 milliseconds and the average reading seek time is 8.5 milliseconds.[2] For simplicity, assume reading from disk takes about 10 milliseconds.</p>
<p>Naively, then, the time to locate one record out of a million would take 20 disk reads times 10 milliseconds per disk read, which is 0.2 second.</p>
<p>The time won&#8217;t be that bad because individual records are grouped together in a disk block. A disk block might be 16 kilobytes. If each record is 160 bytes, then 100 records could be stored in each block. The disk read time above was actually for an entire block. Once the disk head is in position, one or more disk blocks can be read with little delay. With 100 records per block, the last 6 or so comparisons don&#8217;t need to do any disk reads—the comparisons are all within the last disk block read.</p>
<p>To speed the search further, the first 13 to 14 comparisons (which each required a disk access) must be sped up.</p>
</blockquote>
<h2>有序文件的查找时间</h2>
<p>排序和查找算法通常使用“比较次数”衡量。举例来说，对含N条记录的有序表进行二分查询，需要做O(log2N)次比较。如果表中有100万条记录，那么查找特定记录，需要进行20次比较: log21,000,000 = 19.931。</p>
<p>大型数据库通常将数据保存在磁盘中。比较key时间将主要消耗在磁盘读取。磁盘读取时间包含两部分：磁头寻道时间和盘片旋转延时。寻道时间大约是0~20ms，甚至更多；平均旋转延时是一半的盘片旋转周期。7200RPM的磁盘，旋转周期大约为8.3ms。以Seagate ST3500320NS为例，trace-to-track的寻道时间大约0.8ms，平均读寻道时间为8.5ms。简单起见，假设从磁盘读需要10ms。</p>
<p>再简单假设在100w条记录中查找需要20次磁盘读，那么一次查询需要0.2s。</p>
<p>实际时间不会这么糟糕，因为记录实际上是分组分布在“磁盘块”上。假定磁盘块是16k，每条记录是160字节，那么：在一个磁盘块上可以存储100条记录。而上述分析的磁盘读时间实际上会读入一整个块。一旦磁头的位置适合，再读入数据的延时会变少。当一个块中有100条记录时，那么最后6次的比较不需要进行磁盘读(数据已在最后一次读的块中)。</p>
<p>为了进一步加速查找，我们需要优化前13~14次查询，这些查询都需要一次磁盘访问。</p>
<blockquote><p>An index speeds the search</p>
<p>A significant improvement can be made with an index. In the example above, initial disk reads narrowed the search range by a factor of two. That can be improved substantially by creating an auxiliary index that contains the first record in each disk block (sometimes called a sparse index). This auxiliary index would be 1% of the size of the original database, but it can be searched more quickly. Finding an entry in the auxiliary index would tell us which block to search in the main database; after searching the auxiliary index, we would have to search only that one block of the main database—at a cost of one more disk read. The index would hold 10,000 entries, so it would take at most 14 comparisons. Like the main database, the last 6 or so comparisons in the aux index would be on the same disk block. The index could be searched in about 8 disk reads, and the desired record could be accessed in 9 disk reads.</p>
<p>The trick of creating an auxiliary index can be repeated to make an auxiliary index to the auxiliary index. That would make an aux-aux index that would need only 100 entries and would fit in one disk block.</p>
<p>Instead of reading 14 disk blocks to find the desired record, we only need to read 3 blocks. Reading and searching the first (and only) block of the aux-aux index identifies the relevant block in aux-index. Reading and searching that aux-index block identifies the relevant block in the main database. Instead of 150 milliseconds, we need only 30 milliseconds to get the record.</p>
<p>The auxiliary indices have turned the search problem from a binary search requiring roughly log2N disk reads to one requiring only logbN disk reads where b is the blocking factor (the number of entries per block: b = 100 entries per block; logb1,000,000 = 3 reads).</p>
<p>In practice, if the main database is being frequently searched, the aux-aux index and much of the aux index may reside in a disk cache, so they would not incur a disk read.</p>
</blockquote>
<h2>加速索引</h2>
<p>索引能带来显著地改善。在上面的例子中，第一次磁盘读将搜索范围缩小一半。可以创建索引优化，索引中包含每一个磁盘块的第一个记录(称之为“稀疏索引”)。这个索引只有原数据库1%的大小，但却可以进行很快的查找。在索引找到的记录会告诉我们数据在哪个块，在查找索引完成后，只需要在主数据库找到相应的块，代价是一次磁盘读。索引中有1万个记录，所以它只需要14次比较，与主数据库一样，最后6次比较只需要一次磁盘读，因为它们在同一个磁盘块上。在索引中的查找需要8次磁盘读，查找到需要记录总共需要9次磁盘读。</p>
<p>这种创建索引的技巧可以嵌套使用：为索引创建索引。在我们的例子中，索引的索引只有100个记录，可以放进一个磁盘块。</p>
<p>因此，我们实际上只需要3次磁盘读就可以找到记录，而不是14次。一次磁盘读取到索引的索引，就可以定位到相关的索引块；再通过索引块就可以找到数据块。这样我们查找一条记录的时间只需要30ms。</p>
<p>索引改变了查找问题：原本需要log2N次磁盘读，现在只需要logbN，b是块因子(每个磁盘块含有的记录数，上例中b=100, log100 1000000=3)。</p>
<p>在实际读频繁的数据库中，索引的索引以及大部分索引都会在磁盘cache中，还可以节省下来磁盘读。</p>
<blockquote><p>Insertions and deletions cause trouble</p>
<p>If the database does not change, then compiling the index is simple to do, and the index need never be changed. If there are changes, then managing the database and its index becomes more complicated.</p>
<p>Deleting records from a database doesn&#8217;t cause much trouble. The index can stay the same, and the record can just be marked as deleted. The database stays in sorted order. If there are a lot of deletions, then the searching and storage become less efficient.</p>
<p>Insertions are a disaster in a sorted sequential file because room for the inserted record must be made. Inserting a record before the first record in the file requires shifting all of the records down one. Such an operation is just too expensive to be practical.</p>
<p>A trick is to leave some space lying around to be used for insertions. Instead of densely storing all the records in a block, the block can have some free space to allow for subsequent insertions. Those records would be marked as if they were &quot;deleted&quot; records.</p>
<p>Now, both insertions and deletions are fast as long as space is available on a block. If an insertion won&#8217;t fit on the block, then some free space on some nearby block must be found and the auxiliary indices adjusted. The hope is enough space is nearby that a lot of blocks do not need to be reorganized. Alternatively, some out-of-sequence disk blocks may be used.</p>
</blockquote>
<h2>插入和删除带来的问题</h2>
<p>如果数据库不改变，那么创建索引很简单，也不会被改变。但如果存在更改操作，数据库及索引的维护就会变得复杂的多。</p>
<p>删除记录不会带来太大的问题。索引可以仍然存在，只需要将记录标记为己删除。数据库仍然保持有序。但如果有大量的删除，查找效率和存储空间浪费较多。</p>
<p>在有序文件中插入记录会导致灾难，因为需要空间存放插入的纪录。如果在第一条记录前插入一条，那么整个文件中的记录都需要向后移动。这样的操作在实际中代价过于昂贵。</p>
<p>一个技巧是预留一些空间，不写满磁盘块，而是留一些空白为后续的插入准备空间。这些记录可以标记为“已删除”。</p>
<p>现在如果块中空间足够，插入和删除都很快了。但如果插入的记录空间不够，就需要在附近的块中找到一些空间出来，并且索引需要做相应的调整。我们只能寄希望于附近的块有足够的空间，否则就需要大量的磁盘块。有的地方使用了替换方案，一部分块可以乱序。</p>
<blockquote><p>The B-tree uses all those ideas     <br />The B-tree uses all the above ideas. It keeps the records in sorted order so they may be sequentially traversed. It uses a hierarchical index to minimize the number of disk reads. The index is elegantly adjusted with a recursive algorithm. The B-tree uses partially full blocks to speed insertions and deletions. In addition, a B-tree minimizes waste by making sure the interior nodes are at least 1/2 full. A B-tree can handle an arbitrary number of insertions and deletions.</p>
</blockquote>
<h2>集大成者：B树</h2>
<p>B树包含了上述所有的思想：</p>
<ul>
<li>记录有序组织，方便顺序遍历</li>
<li>采用多层索引来最小化磁盘读</li>
<li>优雅的递归组织索引</li>
<li>数据块预留空间以加速插入和删除，另外，它会最小化空间浪费，内部节点至少1/2满</li>
<li>可以处理任意数量的插入和删除</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2010/11/20/200/why_disk_based_database_use_b_tree/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ProjectEuler解题笔记-Problem 12</title>
		<link>http://xiaoy.info/2010/11/20/187/project_euler_problem_12/</link>
		<comments>http://xiaoy.info/2010/11/20/187/project_euler_problem_12/#comments</comments>
		<pubDate>Sat, 20 Nov 2010 12:49:21 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[未分类]]></category>
		<category><![CDATA[math]]></category>
		<category><![CDATA[problem]]></category>

		<guid isPermaLink="false">http://xiaoy.info/2010/11/20/187/projecteuler%e8%a7%a3%e9%a2%98%e7%ac%94%e8%ae%b0-problem-12/</guid>
		<description><![CDATA[<a href="http://projecteuler.net/">ProjectEuler</a>是与mathchallenge类似的oj系统，提供一些数学和计算机相关的问题。题目难度从易到难都有，工作之余，做一两道题目也是很不错的休闲，还能重拾遗忘的数学知识。另外网站还提供了问题分析，看看别人的优化思路，也是一种提高。
题目
<pre class="brush: text; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">The sequence of triang[......]</pre><p class='read-more'><a href='http://xiaoy.info/2010/11/20/187/project_euler_problem_12/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p><a href="http://projecteuler.net/">ProjectEuler</a>是与mathchallenge类似的oj系统，提供一些数学和计算机相关的问题。题目难度从易到难都有，工作之余，做一两道题目也是很不错的休闲，还能重拾遗忘的数学知识。另外网站还提供了问题分析，看看别人的优化思路，也是一种提高。</p>
<h2>题目</h2>
<pre class="brush: text; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be:

1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...

Let us list the factors of the first seven triangle numbers:

1: 1
3: 1,3
6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28

We can see that 28 is the first triangle number to have over five divisors.

What is the value of the first triangle number to have over five hundred divisors?</pre>
<h2>思路</h2>
<h2></h2>
<p>题目很好理解：首先定义了什么叫triangle number，然后提出问题：找出含有500个因子的第一个triangle number。</p>
<h3>Naive方法</h3>
<p>暴力分解，很快代码就能写出来。</p>
<pre class="brush: cpp; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">// 暴力计算因子个数
int get_factor_count( uint64 num )
{
    uint64 count = 0;
    for ( uint64 i = 1; i&lt; num; i ++)
    {
        if (i*i &gt; num)
        {
            break;
        }
        if (num % i ==0)
        {
            count ++;
            if (i * i != num)
            {
                count ++;
            }
        }
    }
    return count;
}
void problem12()
{
    uint i=2;
    do{
        if( get_factor_count(i) &gt;= 500 )  {
            printf(&quot;%u\n&quot;, i);
            return;
        }
        else if( i % 10000 == 0 )  {
            printf( &quot;%d %u\n&quot;, i, get_factor_count(i) );
        }
    }while( i++ );
}</pre>
<p>但跑到1分钟还没出结果，无奈的cancel掉。</p>
<h3>分析优化</h3>
<p>根据题意，三角数是从1累加的值，根据等差公式，第n个triangle num的值为n(n+1)/2。发现这里面有些文章可以做：</p>
<p>1. n和n+1必然有一个为偶数，也就是说，它们中的一个会被2整除。</p>
<p>2 . 除1外，n和n+1没有公共因子。</p>
<p>因此，我们可以得到这个数的因子个数为</p>
<pre class="brush: text; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">P(Tn)=P(n/2)*P(n+1) n为偶数
P(Tn)=P(n)*P( (n+1)/2 ) n为奇数</pre>
<p>根据这一思路，我们就能得到新的函数。这样我们只需要平方根级别的计算。</p>
<pre class="brush: cpp; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">void problem12()
{
	const static int N = 100000;
	int a[N];
	for( int i = 2; i&lt;N; i++ )
	{
		if( i % 2 ==0 )
			a[i]=get_factor_count(i/2);
		else
			a[i]=get_factor_count(i);
	}
	for( int i = 2; i&lt;N-1; i++ )
	{
		if( a[i]*a[i+1]&gt;500)
		{
			printf(&quot;%lu\n&quot;,i*(i+1)/2 );
			break;
		}
	}
}</pre>
<p>经过优化，在我的机器上500ms就计算出了答案。</p>
<h3>进一步优化</h3>
<p>由于时间已经满足要求，就未进一步优化。</p>
<p>如果需要，下手点应该是get_factor_count函数了。参考<a href="http://mathschallenge.net/index.php?section=faq&amp;ref=number/number_of_divisors">这篇文章</a>。</p>
<h2>其他</h2>
<p><a href="http://www.shyamsundergupta.com/triangle.htm">关于三角数的奇妙性质</a></p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2010/11/20/187/project_euler_problem_12/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>一道bt的面试题</title>
		<link>http://xiaoy.info/2010/10/28/176/a_bt_interview_problem/</link>
		<comments>http://xiaoy.info/2010/10/28/176/a_bt_interview_problem/#comments</comments>
		<pubDate>Thu, 28 Oct 2010 09:02:53 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[其他]]></category>
		<category><![CDATA[interview]]></category>

		<guid isPermaLink="false">http://xiaoy.info/2010/10/28/176/a_bt_interview_problem/</guid>
		<description><![CDATA[外出招聘空闲时，大家都会交流交流自己的一些面试题。昨天听到一个很bt的题目：
不使用+ &#8211; ++ &#8212; +=运算符，如何用C++实现a+b？

我不认为这是一个好的面试题。但据说这题是为了考查思维活跃度，当成脑力练习还不错。各位先不要看答案,看看你能想到几种，不限制bt的程度。[......]<p class='read-more'><a href='http://xiaoy.info/2010/10/28/176/a_bt_interview_problem/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>外出招聘空闲时，大家都会交流交流自己的一些面试题。昨天听到一个很bt的题目：</p>
<blockquote><p><font size="4">不使用+ &#8211; ++ &#8212; +=运算符，如何用C++实现a+b？</font></p>
</blockquote>
<p>我不认为这是一个好的面试题。但据说这题是为了考查思维活跃度，当成脑力练习还不错。各位先不要看答案,看看你能想到几种，不限制bt的程度。</p>
<p>以下是我目前为止知道的答案，欢迎补充。</p>
<p>&#160;</p>
<ol>
<li>相对优雅的答案，采用位运算，每次记进位。 （计算机专业出身的人，二进制计算、组成原理学的还不错）      <br /> 
<pre class="brush: cpp; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">int add( int i, int j )
{
    do{
        register int x = i^j;
        j = (i&amp;j)&lt;&lt;1;
        i = x;
    }while( j!=0);
    return i;
}</pre>
</li>
<li>用地址运算 (对地址运算蛮熟悉)<br />
    </p>
<pre class="brush: cpp; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">char* p = (char*)a;
int sum = &amp;(p[b]);</pre>
</li>
<li>用struct计算 (只能作用于编译期)<br />
    </p>
<pre class="brush: cpp; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">const int a = 10;
const int b = 20;
struct T { char ta[a]; char tb[b]; };
printf(&quot;%d&quot;, sizeof(T));</pre>
</li>
<li>用字符串求长
<pre class="brush: cpp; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">char* p = new char[a];
memset( p, 'a',a);
p[a-1]=0;
char* q=new char[(uint32_t)-100];
memset(q,'b',b);
q[b]=0;
strcat( q, p );
printf( &quot;%u&quot;, strlen(q));</pre>
</li>
<li>log( pow(2*a) * pow(2*b) )&#160; (高中数学学的不错的) </li>
<li>用一个vector，把a,b装进去，然后调partial_sum 求和 （算的上熟悉STL） </li>
<li>用VB或者其他语言写个ActiveX，再用VC调用一把 （做过windows开发的,已经开始显得bt了） </li>
<li>写个.dll，再用C++调用一把 (做过windows下动态链接库开发) </li>
<li>写个.so，再用C++调用一下 (做过linux下动态链接库开发的) </li>
<li>内嵌汇编，调用ADD机器指令 (可能这个家伙是搞嵌入式的) </li>
<li>用原子操作
<pre class="brush: cpp; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">for( int i = 0; i &lt; b; ::LockIncreament( &amp;i ) )
{
  ::LockIncreament( &amp;a );
}</pre>
</li>
<li>写个shellcode，在数据段内嵌机器码 (搞破解的人才会想到这个办法) </li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2010/10/28/176/a_bt_interview_problem/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>西安印象</title>
		<link>http://xiaoy.info/2010/10/23/173/xian_impression/</link>
		<comments>http://xiaoy.info/2010/10/23/173/xian_impression/#comments</comments>
		<pubDate>Fri, 22 Oct 2010 16:30:45 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[生活]]></category>
		<category><![CDATA[city]]></category>

		<guid isPermaLink="false">http://xiaoy.info/2010/10/23/173/%e8%a5%bf%e5%ae%89%e5%8d%b0%e8%b1%a1/</guid>
		<description><![CDATA[公司校园招聘，安排到外地，第一站西安，为期一周。

空气环境
17日，check in了个靠窗的位置。10点多国航准点起飞，飞机上老外的比例很大，除了右手边是窗户，前后左三个方向全是老外。天气不错，外面的景观很清晰：云比平时要少，地面的山脉河流真真切切。经过近两个小时的飞行，飞机即将抵达。也真真切切[......]<p class='read-more'><a href='http://xiaoy.info/2010/10/23/173/xian_impression/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>公司校园招聘，安排到外地，第一站西安，为期一周。</p>
<h1></h1>
<h1>空气环境</h1>
<p>17日，check in了个靠窗的位置。10点多国航准点起飞，飞机上老外的比例很大，除了右手边是窗户，前后左三个方向全是老外。天气不错，外面的景观很清晰：云比平时要少，地面的山脉河流真真切切。经过近两个小时的飞行，飞机即将抵达。也真真切切的看到笼罩整个城市、浓厚的、灰白色的“气团”，这就是对西安的第一印象。这个印象在稍后的时间里，一次又一次的加剧。</p>
<p>到现在为止已经在西安六天，空气一直是灰蒙蒙的，登上大雁塔，南北广场上空空气的淡白色，一阅无余，再往远看，就是浓重的灰白色了。能见度虽不足以让高速封闭，但在去过的城市里面，这几天的空气质量可以算是数一数二的差，甚至觉得比大部分时间的北京还糟糕。</p>
<h1>景观</h1>
<p>抵达机场，听提前到达的同事说因为要打票，出租车司机不愿意拉我们，于是我们直接坐大巴来到了钟楼，打车来到了交大附近的南洋宾馆。一路上不少古香古色的建筑，和二环内的北京一样，给人很浓重的历史文化积淀感，但比起北京翻了又翻的景观，西安更有一种说不清道不明的怀旧。这种感觉很好，一个保存还算不错的古都，十三朝古都。</p>
<p>经过了几天辛苦的工作，23号呆下来了。下午去陕西历史博物馆和大雁塔溜达了一圈，作为中原核心地区，陕西有着悠久的历史，自然有着数量众多的文物。博物馆里展列了从石器时代开始到近代的大量文物，藏品不比首都博物馆差多少，不愧对“地上文物看山西，地下文物看陕西”。</p>
<p>溜达完博物馆，顺道去了大雁塔一次。票价不便宜，入寺50，爬塔30，也就是说爬一次塔，需要８０元人民币。如果空气能好一点，爬上去之后望远，应该能是不错的感受。</p>
<p>明天去看古城墙，中国现存最完整的古代城垣建筑还是很有吸引力的。</p>
<h1>吃</h1>
<p>到西安前，让<a href="http://twitter.com/pduanxd">@pduanxd</a>同学帮忙找了一个靠谱的攻略，但由于之前工作较累，到目前为止吃到的小吃仅仅只有炒凉皮和肉夹馍，实在是愧对生活啊。明天一定要杀向回民街。</p>
<p>照片在别人的机器上，稍后补上相片。</p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2010/10/23/173/xian_impression/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VPS的ssl安全twitter api</title>
		<link>http://xiaoy.info/2010/10/16/170/setting_up_twip_with_ssl/</link>
		<comments>http://xiaoy.info/2010/10/16/170/setting_up_twip_with_ssl/#comments</comments>
		<pubDate>Sat, 16 Oct 2010 13:14:47 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[未分类]]></category>
		<category><![CDATA[twip twitter]]></category>

		<guid isPermaLink="false">http://xiaoy.info/2010/10/16/170/setting_up_twip_with_ssl/</guid>
		<description><![CDATA[之前上twitter主要是用tweetdeck，配合google app engine的gtap，但从这周开始，我的gae代理时好时坏，经常无故被rst。为了方便使用，同时安全起见，决定放弃gae。正好上了newtwitter和echofun，用了几天，还是不能放弃新tweet的通知，也忍受不了ec[......]<p class='read-more'><a href='http://xiaoy.info/2010/10/16/170/setting_up_twip_with_ssl/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>之前上twitter主要是用tweetdeck，配合google app engine的gtap，但从这周开始，我的gae代理时好时坏，经常无故被rst。为了方便使用，同时安全起见，决定放弃gae。正好上了newtwitter和echofun，用了几天，还是不能放弃新tweet的通知，也忍受不了echofun的丑陋字体。折腾了一下，觉得还得用api。正好前一阵时间buyvm特价，顺手买了一个15刀一年的白菜VPS，于是准备自己架一个支持https的twitter api。</p>
<p>系统是ubunutu，开工。</p>
<h2></h2>
<h1>一 安装php+lighttpd</h1>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">sudo apt-get install lighttpd
sudo apt-get install php5-cgi
sudo apt-get install curl libcurl3 libcurl3-dev php5-curl
sudo apt-get install php5-mcrypt # 用于加密本地数据</pre>
<h1>二 开启SSL支持</h1>
<p>openssl req -new -x509 –keyout /PATH/server.pem -out server.pem -days 365 –nodes #生成山寨证书,PATH视情况及个人爱好放置</p>
<p>修改lighttpd.conf(默认在/etc/lighttpd目录下)，添加：</p>
<blockquote>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">$SERVER[&quot;socket&quot;] == &quot;:443&quot; {
#server.document-root = &quot;/var/www/https&quot;
ssl.engine                 = &quot;enable&quot;
ssl.pemfile                = &quot;/etc/lighttpd/server.pem&quot;
}</pre>
</blockquote>
<p>重启lighttpd</p>
<p>sudo /etc/init.d/lighttpd force-reload</p>
<h1>三 将ssl证书设置为可信</h1>
<p>用https打开<a href="https://domain/">https://domain/</a>，浏览器会提示证书不是由可信CA颁发的(当然了，是我们自己山寨的嘛)。为了避免每次使用都确认，我们点将证书添加至可信区域。</p>
<p>（update:据<a href="http://www.naga61.com/ssl-finished-out-share-personal-twitter-api">http://www.naga61.com/ssl-finished-out-share-personal-twitter-api</a>介绍，可以申请免费证书）</p>
<h1>四 测试</h1>
<p>添加info.php文件至htocs目录：</p>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">&lt;?php
phpinfo();
?&gt;</pre>
<p>打开url: <a href="https://domain/info.php">https://domain/info.php</a>，如果不需要点确认，就能看到一个长长的表，再搜索一下有curl，我们的lighttpd和php就OK了。。</p>
<h1>五 twip</h1>
<p>twip的文档写的太烂，所以网上有n多的twip使用手册，大家自行搜索，这里就不再重复了。也可以<a href="http://www.iteeyan.com/2010/10/twip-4-twitter-api-proxy-iphone/">猛击这里</a>看我为你选择的一篇。</p>
<p>注意:</p>
<p>1. 选用o模式</p>
<p>2.注意oauth目录的权限</p>
<p>3.修改lighttpd.conf，并重新load配置</p>
<pre class="brush: bash; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">url.rewrite-if-not-file += ( &quot;^/twip/(.+)$&quot; =&gt; &quot;/twip/index.php/$1&quot; )</pre>
<h1>六 设置为私用</h1>
<p>按文档说明，只需要设置private_api即可，但初看了一下php代码，没有相关的功能，因此，只能先采用修改oauth目录权限的方式限制，等待@yegle后续开发。</p>
<p>注：通过google/baidu参考过大量网页。</p>
<p>有任何问题，可以联系<a href="http://xiaoy.info/xiaoy.info@gmail.com">email</a>或者<a href="http://twitter.com/yzhwang">twitter</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2010/10/16/170/setting_up_twip_with_ssl/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>linux发送带附件html格式邮件</title>
		<link>http://xiaoy.info/2010/08/07/150/send_html_mail_with_attachment_with_mutt/</link>
		<comments>http://xiaoy.info/2010/08/07/150/send_html_mail_with_attachment_with_mutt/#comments</comments>
		<pubDate>Sat, 07 Aug 2010 13:49:33 +0000</pubDate>
		<dc:creator>wangyuanzheng</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://xiaoy.info/2010/08/07/150/linux%e5%8f%91%e9%80%81%e5%b8%a6%e9%99%84%e4%bb%b6html%e6%a0%bc%e5%bc%8f%e9%82%ae%e4%bb%b6/</guid>
		<description><![CDATA[<pre class="brush: shell; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">echo '&#60;html&#62;abc&#60;/html&#62;'&#124;mutt recv@xxx.com -a file attach -s 'titile'  -e 'set content_type=&#34;text/html&#34;' </pre>
<p>注意, mutt版本需要1.5+</p>]]></description>
			<content:encoded><![CDATA[<pre class="brush: shell; auto-links: true; collapse: false; first-line: 1; gutter: false; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;">echo '&lt;html&gt;abc&lt;/html&gt;'|mutt recv@xxx.com -a file attach -s 'titile'  -e 'set content_type=&quot;text/html&quot;' </pre>
<p>注意, mutt版本需要1.5+</p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoy.info/2010/08/07/150/send_html_mail_with_attachment_with_mutt/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

