<?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>kafka0102的边城客栈 &#187; mysql</title>
	<atom:link href="http://www.kafka0102.com/tag/mysql/feed" rel="self" type="application/rss+xml" />
	<link>http://www.kafka0102.com</link>
	<description>要有最朴素的生活与最遥远的梦想，即使明日天寒地冻、路远马亡。</description>
	<lastBuildDate>Sat, 18 Jun 2011 04:20:42 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>oracle提升mysql售价，误解与无视</title>
		<link>http://www.kafka0102.com/2010/11/397.html</link>
		<comments>http://www.kafka0102.com/2010/11/397.html#comments</comments>
		<pubDate>Sat, 06 Nov 2010 18:13:38 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[mysql]]></category>
		<category><![CDATA[oracle]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=397</guid>
		<description><![CDATA[前几天就在twitter上看到oracle提升mysql售价的消息，推上的英文圈对oracle的这一举动议论纷纷，但中文圈很是平静，倒是像 csdn、javaeye这样的媒体做头条报道：Oracle提高了MySQL的售价。这一新闻难免又引起大家对mysql前景的悲观，对迁移存储系统到postgre、nosql等蠢蠢欲动。其实多数mysql使用者可以轻松的忽略这一新闻，因为人家 oracle提价mysql影响的是买收费版本mysql的用户，并不会影响我等使用免费版本的用户。
]]></description>
			<content:encoded><![CDATA[<p>前几天就在twitter上看到oracle提升mysql售价的消息，推上的英文圈对oracle的这一举动议论纷纷，但中文圈很是平静，倒是像csdn、javaeye这样的媒体做头条报道：<a href="http://www.javaeye.com/news/18425" target="_blank">Oracle提高了MySQL的售价</a>。这一新闻难免又引起大家对mysql前景的悲观，对迁移存储系统到postgre、nosql等蠢蠢欲动。其实多数mysql使用者可以轻松的忽略这一新闻，因为人家oracle提价mysql影响的是买收费版本mysql的用户，并不会影响我等使用免费版本的用户。</p>
<p>国内媒体在转载这一新闻时也没有深究其信息的准确性，对于一些概念解释上的偏差，容易引起误解。这一新闻的源头就是<a href="http://www.mysql.com/products/" target="_blank">http://www.mysql.com/products/</a>的价格表上的变化。从该网页可看到，mysql版本有5个，免费的版本有Community Edition和Classic Edition（现有页面没有这个称呼，它就是MySQL as an Embedded Database），收费的版本有Enterprise Edition、Standard Edition、Cluster Carrier Grade Edition。oracle对mysql售价的调整有两方面：</p>
<p>1、提高三种收费版本的价格，比如Standard Edition现在2000$,而原来的价格只有599$，这对付费用户确实有些影响。这里插段说明，可以将数据库使用者分两种，一种是使用免费的，出了问题自己搞定或搞不定，比如广大的互联网从业人员;另一种是花钱买保障的，产品使用中出了问题直接找提供商解决，比如使用IT产品的企业用户，它们买的不单单是个软件，更是其提供的服务，这后者才是oracle的目标用户（你facebook纵有那么庞大的mysql集群，也没有为oracle贡献几毛钱的利润，就不是oracle的目标用户）。对于数据库产品来说，像oracle数据库高昂的价格是中小企业无力承担的，转而使用价格低廉得多的mysql就很划算。oracle收购mysql（sun）后，oracle数据库面向大型企业，mysql面向中小企业，高低端都覆盖到了。oracle显然认为sun时期的mysql售价过于保守了，要通过提高价格赚更多的钱。这一举动是否会使其原有用户放弃mysql转用其他产品也未可知，但就数据库产品来说，像oracle、db2、sql server都太贵，便宜的也就mysql、postgre了。这次价格调整也给了其他mysql商业支持企业的一些机会，比如由一些mysql专家组成的SkySQL公司，就借此宣传了<a href="http://www.skysql.com/en/letter-to-mysql-customers" target="_blank">其更低廉的mysql商业支持</a>，Percona也站出来表明其<a href="http://www.mysqlperformanceblog.com/2010/11/05/perconas-commitments-to-mysql-users/" target="_blank">拥抱用户的立场</a> 。</p>
<p>2、oracle对Classic Edition不再提供对innodb SE的支持，使得免费的嵌入式mysql只支持myisam引擎，oracle此举是要毁掉免费的嵌入式mysql，驱使使用嵌入式mysql的ISV、OEMs、VAR们都使用收费版本。这一变化对普通用户没什么影响，不过有人错以为Classic Edition是Community Edition，引发不必要的恐慌。</p>
<p>比较mysql的免费和收费版本的功能，收费版本多了些如Enterprise Backup、Enterprise Monitor等高级功能，可我们不都从来没用上innodb的backup工具吗？也就不值一说了。只是，像oracle只顾谋取暴利的做法总是会让热爱开源事业的人反感。最近oracle宣布在openjdk之外推出收费版本的消息，也是勾起大家的纷纷议论，但mysql、java的气候显然不是oracle一家能阻挡的了的。话又说回来，mysql这些年在sun的温柔乡里发展得明显有些迟缓，如果mysql卖得好，也许oracle会投入更多资源。就算oracle手中的mysql凋零，如maria、drizzle等分支够争气，也会是用户平滑迁移的好选择。而java这块，随着jrocket和hotspot的融合，也会进一步增强jvm，即便是免费的版本。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/11/397.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>mysql的多线程复制</title>
		<link>http://www.kafka0102.com/2010/10/380.html</link>
		<comments>http://www.kafka0102.com/2010/10/380.html#comments</comments>
		<pubDate>Sun, 24 Oct 2010 08:39:17 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysql 复制]]></category>
		<category><![CDATA[slave master]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=380</guid>
		<description><![CDATA[对于使用主从复制的mysql用户来说，经常会遇到的问题是，当主库的写压力增大时，基于单线程复制的从库跟不上主库的写速度，造成应用从从库读到脏数据。如果在数据库前面使用了cache，那么这种脏数据的影响就更恶劣。更糟糕的是，如果在主库上执行了耗时很长的sql，那么从库就会被完全阻塞，这也限制了程序员们不要在主库执行耗时长的语句，更不要提那些alter schema的语句。对于mysql主从的这个缺陷，可以使用一些其他方式规避它，比如做sharding处理，控制每个master的写压力在合适的范围内，也可以去主从，通过应用来分发请求到各个db（应用相当于主库），但这些做法相比复制无疑会复杂的多。]]></description>
			<content:encoded><![CDATA[<p>对于使用主从复制的mysql用户来说，经常会遇到的问题是，当主库的写压力增大时，基于单线程复制的从库跟不上主库的写速度，造成应用从从库读到脏数据。如果在数据库前面使用了cache，那么这种脏数据的影响就更恶劣。更糟糕的是，如果在主库上执行了耗时很长的sql，那么从库就会被完全阻塞，这也限制了程序员们不要在主库执行耗时长的语句，更不要提那些alter schema的语句。对于mysql主从的这个缺陷，可以使用一些其他方式规避它，比如做sharding处理，控制每个master的写压力在合适的范围内，也可以去主从，通过应用来分发请求到各个db（应用相当于主库），但这些做法相比复制无疑会复杂的多。</p>
<p>我很早就有的一个想法是，为什么不做一个mysql的多线程复制（并行复制），使得从库的写速度能跟上主库，而是一直使用这种低效的单线程复制？那时我没有从mysql官方看到这方面的消息。最近，看到Baron Schwartz的<a href="http://www.mysqlperformanceblog.com/2010/10/20/mysql-limitations-part-1-single-threaded-replication/ " target="_blank">MySQL Limitations Part 1: Single-Threaded Replication</a>，值得分享和讨论一下这个问题。</p>
<p>Baron Schwartz在文章中提到了多线程复制的3种思路，列举如下:</p>
<p>1）每个数据库一个线程。这样做的前提是各个数据库之间是独立的，主库不需要做什么修改，从库上对每个数据库起一个线程执行复制过来的binary log。这种方式的优点在于，基于现有的复制机制修改起来较为简单，缺点就是它不具有通用性。但我想，大多数应用的事务都不是跨数据库的，另一方面，是否可以做成，通过配置，配置哪些独立的数据库可以起独立线程，而关联的数据库还是使用原来的复制方式？</p>
<p>2）在从库上由一个协调线程（coordinator）负责分发任务给worker线程池。协调线程读relay log到一个事务段（而不是一个语句），将整个事务交由某个工作线程执行，工作线程执行到COMMIT，但不是真正的commit，然后回报给协调线程。协调线程将确保所有的事务以相同的顺序开始和提交。如果出现死锁或者锁等待超时等错误，协调线程要使工作线程回滚，并且重试或者串行化执行有问题的事务。相比于1），该方法具有通用性，缺点是实现起来会更复杂，并且当出现死锁等问题时，是否能正确的处理好也是个问题。</p>
<p>3）主库上有多个binlog，每个数据库一个，这样从库的复制单位就是单个数据库的binlog。这也使得，从库可以从多个主库复制，这对于一些场景会很有用。该方法的缺点是，对现有的复制机制改动很大，包括现有的复制配置、管理命令等都需要做很大的改变。</p>
<p>说完上面的实现思路，接下来的问题就是，mysql是否会做多线程复制（且不说它使用哪种方式）？在<a href="http://www.mysqlperformanceblog.com/2010/10/20/mysql-limitations-part-1-single-threaded-replication/ " target="_blank">MySQL Limitations Part 1: Single-Threaded Replication</a>的评论中，有人给出了这个<a href="http://forge.mysql.com/wiki/ReplicationFeatures/ParallelSlave" target="_blank">http://forge.mysql.com/wiki/ReplicationFeatures/ParallelSlave</a>链接，就是说mysql从5.1开始做这个事情，只是没想到其如此低调，连Baron Schwartz似乎都不知晓。在<a href="http://forge.mysql.com/worklog/task.php?id=4648" target="_blank">http://forge.mysql.com/worklog/task.php?id=4648</a>可以看到mysql对多线程复制的一些思路，它是基于5.1的行复制（估计现在多数应用还是基于语句复制），实现思路似乎和上面的2）有些相像。不知道以mysql的办事效率，该功能何年何月才能成熟起来。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/10/380.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Facebook的Online Schema Change for MySQL</title>
		<link>http://www.kafka0102.com/2010/09/338.html</link>
		<comments>http://www.kafka0102.com/2010/09/338.html#comments</comments>
		<pubDate>Wed, 29 Sep 2010 08:43:16 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[mysql]]></category>
		<category><![CDATA[Facebook]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=338</guid>
		<description><![CDATA[Feng的 Facebook 针对 MySQL 开源 Online Schema Change 代码 介绍了 Facebook最新开源的在线修改mysql schema的工具，并对该工具极尽赞扬之辞。于是乎，我这个有大半年没动过mysql的人翻看起该工具的文档Online Schema Change for MySQL，并简单的浏览了其代码OnlineSchemaChange.php。总的来说，尽管其实现思想很朴素，这个工具真的很实用，它借鉴的openark-kit似乎发布了很长时间，只是名头没有Facebook的guys响亮，知道的人似乎不多。openark上的其他工具也很不错，比如ycheckpoint，就是个很不错的监控mysql运行状态的工具。]]></description>
			<content:encoded><![CDATA[<p>Feng的 <a href="http://www.dbanotes.net/opensource/facebook_mysql_online_schema_change.html" target="_blank">Facebook 针对 MySQL 开源 Online Schema Change 代码 </a>介绍了Facebook最新开源的在线修改mysql schema的工具，并对该工具极尽赞扬之辞。于是乎，我这个有大半年没动过mysql的人翻看起该工具的文档<a href="http://www.facebook.com/notes/mysql-at-facebook/online-schema-change-for-mysql/430801045932" target="_blank">Online Schema Change for MySQL</a>，并简单的浏览了其代码<a href="http://bazaar.launchpad.net/~mysqlatfacebook/mysqlatfacebook/tools/annotate/head:/osc/OnlineSchemaChange.php " target="_blank">OnlineSchemaChange.php</a>。总的来说，尽管其实现思想很朴素，这个工具真的很实用，它借鉴的<a href="http://code.openark.org/forge/openark-kit" target="_blank">openark-kit</a>似乎发布了很长时间，只是名头没有Facebook的guys响亮，知道的人似乎不多。openark上的其他工具也很不错，比如<a href="http://code.openark.org/forge/mycheckpoint/" target="_blank">ycheckpoint</a>，就是个很不错的监控mysql运行状态的工具。</p>
<p>对于修改mysql schema的操作，我之前的经验是：在主从模式下，先对从库逐台做修改操作，操作期间该从库不用于提供服务（应用端排除对该从库的读操作，如果脏读在一定程度上可以接受，该从库也可以对外提供服务）;所有从库做完后，将主库切到一个从库，对主库进行修改，修改完成后再切换回去。这个过程看起来有些繁杂，但只要有清晰的操作步骤和熟练操作工种，对于大多数一般规模的主从来说，这个过程是可以接受的，毕竟不是经常修改schema。不过，对于像Facebook动辄上千台数据库的情况，这个过程就会耗时很长很折腾，而人家专业的DBA人数又比不上国内的大公司们，所以很有必要搞出个更高效的解决方案。</p>
<p>对于修改schema，mysql的执行过程主要就是建个copy表，把原表数据拷过去，然后将copy表重命名为原表（对一些特定index的修改操作，InnoDB支持<a href="http://dev.mysql.com/doc/innodb-plugin/1.0/en/innodb-create-index.html" target="_blank">Fast Index Creation</a>），而整个过程是需要加互斥锁的，数据量越大，这个数据拷贝过程就越久，这对在线应用往往是不可接受的。Facebook的改进思路是，在将原表数据拷贝到copy表过程中，通过触发器把外部同时进行的写提交记录到deltas表中，在数据拷贝完成后，再回放deltas表中的数据到copy表，保证copy表不会丢失操作期间的写提交，最后再rename copy表为原表。而在实现细节上，其实要考虑的问题还是很多的，而原文不但阐述了他们怎么做的还阐述了他们为什么这样做，其思考过程本身就很值得学习。</p>
<p>翻看OnlineSchemaChange.php，可以在代码的后面清晰的看到该脚本主体的执行过程，下面做些概述：</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">init</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">createCopyTable</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">alterCopyTable</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">createDeltasTable</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">createTriggers</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">startSnapshotXact</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">selectTableIntoOutfile</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">dropNCIndexes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">loadCopyTable</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">replayChanges</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// false means not in single xact</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">recreateNCIndexes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">replayChanges</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// false means not in single xact</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">swapTables</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cleanup</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>1、init：初始化阶段会做些验证操作，也是该脚本需要的约束，比如检查mysql版本（目前只支持5.0.84和5.1.47，应该是facebook只用到这两个版本）、修改的表不能有触发器和外键等。在初始化之前（构造函数中），通过sql_log_bin = 0会将关闭该seesion的sql复制，所以在主库执行该脚本不会影响从库。<br />
2、createCopyTable：直接创建一个和原表一样结构的copy表。<br />
3、alterCopyTable：对copy表执行本次的目标修改操作”alter table&#8230;“。<br />
4、createDeltasTable：创建deltas表，该表保存修改schema期间外部对原表提交的写操作，所以该表除了包含原表所有列，还额外有两列，一列是自增主键，一列是提交的写操作类型（insert、update、delete），以便后面回放。<br />
5、createTriggers：对原表创建insert、update、delete的触发器，触发器的内容就是将产生的更新写到deltas表中。<br />
6、startSnapshotXact：开启事务，同时创建并初始化临时表TEMP_TABLE_IDS_TO_EXCLUDE，这个表保存从deltas表得到的在回放期间需要放弃的记录id（避免重复回放），比如在上面createTriggers后，deltas表可能就会有记录了，初始化时就要将它们从deltas表的主键select到TEMP_TABLE_IDS_TO_EXCLUDE。<br />
7、selectTableIntoOutfile：考虑到减轻mysql的负载，将原表数据拷贝到copy表不是直接进行的，它先是分片（chunked）地将数据输出到多个外部文件（outfile），然后将外部文件的数据导入到copy表中，分片的大小可以指定（默认是500000）。该步骤就是分片导出原表数据到外部文件中。<br />
8、dropNCIndexes：在导入数据到copy表前，先把copy表的非聚簇索引去掉，以加快导入。<br />
9、loadCopyTable：将上面得到的文件数据导入到copy表。<br />
10、replayChanges：这里又用到另一个临时表TEMP_TABLE_IDS_TO_INCLUDE，该表从deltas表得到有效的记录id（排除掉TEMP_TABLE_IDS_TO_EXCLUDE的），用于对copy表回放deltas表的有效记录，回放完成后会将TEMP_TABLE_IDS_TO_INCLUDE的id们扔给TEMP_TABLE_IDS_TO_EXCLUDE。<br />
11、recreateNCIndexes：重建非聚簇索引。<br />
12、replayChanges：再次回放。<br />
13、swapTables：将原表和copy表交换名称，整个过程要锁表并停止对从库的复制，会有短暂的停提交。<br />
14、cleanup：清理现场。</p>
<p>上面只是简单的罗列其执行过程，OnlineSchemaChange.php还要处理不同版本mysql的区别，并需要对各种异常做处理。如果该功能能集成到mysql（innodb），就不会再费nosql们指责mysql修改schema麻烦的口舌了。而现在，至少Facebook提供了一个不错的工具来解决这个问题。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/09/338.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>分享Poppen.de架构经验</title>
		<link>http://www.kafka0102.com/2010/04/96.html</link>
		<comments>http://www.kafka0102.com/2010/04/96.html#comments</comments>
		<pubDate>Sun, 25 Apr 2010 07:49:17 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[architecture]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[php-fpm]]></category>
		<category><![CDATA[Poppen.de]]></category>
		<category><![CDATA[RabbitMQ]]></category>
		<category><![CDATA[架构]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=96</guid>
		<description><![CDATA[Poppen.de是德国的一家婚姻中介网站，对于该网站的统计数字有：1）2.000.000的用户数，2）20.000的并发用户数，3）每天产生300.000的私信，4）250.000的日登录用户数。这样的网站也就是个中型规模的网站，下面看看这个网站在技术应用及经验方面带来的东西。]]></description>
			<content:encoded><![CDATA[<p>Poppen.de是德国的一家婚姻中介网站，对于该网站的统计数字有：1）2.000.000的用户数，2）20.000的并发用户数，3）每天产生300.000的私信，4）250.000的日登录用户数。这样的网站也就是个中型规模的网站，下面看看这个网站在技术应用及经验方面带来的东西。</p>
<p>原文链接：<a href="http://highscalability.com/blog/2010/4/12/poppende-architecture.html" target="_blank">http://highscalability.com/blog/2010/4/12/poppende-architecture.html</a></p>
<h2>Nginx</h2>
<p>Poppen.de使用nginx作为web server，包括静态请求和动态请求。特别的，Poppen.de通过nginx来直接从memcache中获取缓存的动态页面内容而不需要经过PHP，一个例子就是请求用户信息页。对于缓存整个动态请求页面内容，这是一种方法，更常见的是使用squid、varnish来做。在请求用户上传图片的处理方面，Poppen.de是将图片上传到统一的文件服务器，而前端的nginx在请求时会做本机的cache处理（不知道它的请求是如何落到nginx上的，如果是随机的，多台nginx本机cache的数据就重复了，有些浪费，但也最简单），对于图片量不大的情况倒是简单的处理方法。</p>
<h2>PHP-FPM</h2>
<p>Poppen.de使用了PHP 5.3.x、APC和PHP-FPM，PHP-FPM是启用了100个进程，也是个标配。使用APC使得PHP能减少30%的CPU和内存消耗。Poppen.de竟然使用了symfony作为开发框架，出发点自然是希望快速开发，但从我的使用经验来说，symfony还是太重的一个框架，无论是使用上还是性能上都不是最优的PHP框架。当然，它也提到，框架是有性能损失的，但还可以接受。就PHP框架来说，尽管开源的框架一筐一筐，但没一个真正流行的，而重复的轮子却不停的出现，此种现象很耐人寻味。Poppen.de还使用了Facebook出品的XHProf来分析PHP程序的性能问题。</p>
<h2>Mysql</h2>
<p>Poppen.de的数据存在Mysql中，主要就是主从模式。由于现在的数据量并不大，并没有做数据分区处理，当数据量上来时，像垂直和水平分区自然是扩展性能的简单方式。稍显意外的是，Poppen.de使用由4台机器构成的NDB cluster来存储写频繁的数据，比如某个用户页都被谁访问过这样的统计信息。如果NDB cluster能被一些大型应用证明其稳定性，对于解决写频繁的应用（比如Social game？）来说是个不错的选择。Poppen.de的表大多是MyISAM，自然不是很好的选择，不过他们竟然有计划转向XtraDB（自然是经过测试比较后的结果），真是够有个性的。尽管XtraDB很优秀，但官方的innodb显然会更成熟，并且新版也在吸收如XtraDB等存储引擎的优点，选择一个不明前途的第三方引擎还是有些风险的。</p>
<h2>Memcached</h2>
<p>cache为王，Poppen.de有45GB、51个结点的cache数据。并且还做了一个系统，当一个表数据变化时，自动作废cache数据，好处自然是减少了应用处理。当然，也可以做一个穿透的数据访问中间件来做cache填充与失效的事情（就像DAL）。Poppen.de似乎也在考虑使用Redis或MongoDB等来将cache操作和数据操作自然的绑在一起。</p>
<h2>RabbitMQ</h2>
<p>Poppen.de使用了RabbitMQ来做一些异步job处理。特别有趣的是，它使用了PHP-FPM的一个专有特性，即fastcgi_finish_request() 函数，能够在请求结束前，在把结果返回到客户端后，异步的做诸如连接关闭、发送消息的处理。在消费消息方面，Poppen.de使用了最简单的山寨方法，就是同时运行多个PHP脚本，每个脚本在消费250个job后退出，然后由监控脚本再新起一个消费脚本。当处理不过来时，或者多加脚本或者多加机器来解决。</p>
<h2>其他</h2>
<p>Poppen.de使用CouchDB来存储log而是代替人工直接分析日志文件，这个对于单机的日志分析或许会更快更方便些，并且他们有计划将日志可视化，但他们想必现在还没打算做分布式的日志存储与分析的工作。Poppen.de使用了叫做Red5的东西来存储视频（我是不了解了）。Poppen.de还使用Graphite来做数据监控，包括如Memcached的命中率、RabbitMQ状态监控、各机器的负载监控等。当一个网站有一定规模后，这种监控是必不可少的，而通常也是拿现成的工具使用，在必要时做些功能上的扩展。Poppen.de还使用Tsung来做基准测试，比如HTTP请求及各Mysql存储引擎方面的测试。</p>
<h2>经验</h2>
<p>1、背靠大树好乘凉，选择技术和工具时要挑成熟、活跃的社区，这样有问题也有途径及时解决。<br />
2、了解你所使用的技术的优缺点，发挥它的优点，避开它的缺点。<br />
3、扩展工具，让工具更好的满足你。<br />
4、敢于尝试。可以看到，越是大公司，在技术选型时越是缩手缩脚，生怕外面的东西不成熟有问题，总相信自己能造成更好的自己能掌控的轮子。而看看Poppen.de上面提到的东西，有多少是你不知道没用过的？再感慨一下，每天都有大量的开源项目出现，一些因为被人关注而众人拾柴，东西就存活下去，一些尽管东西很好但因为无人问津，也就悄无声息的死去。<br />
5、度量一切，对网站的各个模块、系统、流量等数字有清晰的认识。<br />
6、经验积累、全面把握。不要等到上线时发现新模块的功能不是需要的，不要等到上线时发现模块性能不满足需求。</p>
<h2>总结</h2>
<p>Poppen.de还做了些展望，比如将更多的使用erlang产品，这要是个国内的公司，估计很多人会膜拜的。而总结来说，Poppen.de分享的东西真是很实在很实用，是个很开放也很技术流的公司，对中小规模的网站来说是可充分借鉴的。</p>
<p><a href="http://www.poppen.de/"> </a></p>
<p><a href="http://www.poppen.de/"> </a></p>
<p><a href="http://www.poppen.de/"> </a></p>
<p><a href="http://www.poppen.de/"> </a></p>
<p><a href="http://www.poppen.de/"> </a></p>
<p><span style="font-family: 'AR PL UMing CN', serif;"><a href="http://www.poppen.de/"> </a></span></p>
<p><span style="font-family: 'AR PL UMing CN', serif;"><a href="http://www.poppen.de/"> </a></span></p>
<p><span style="font-family: 'AR PL UMing CN', serif;"><a href="http://www.poppen.de/"> </a></span></p>
<p><span style="font-family: 'AR PL UMing CN', serif;"><a href="http://www.poppen.de/"> </a></span></p>
<p><span style="font-family: 'AR PL UMing CN', serif;"><a href="http://www.poppen.de/"> </a></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/04/96.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>一周技术文档分享</title>
		<link>http://www.kafka0102.com/2010/02/46.html</link>
		<comments>http://www.kafka0102.com/2010/02/46.html#comments</comments>
		<pubDate>Thu, 11 Feb 2010 09:14:40 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[other]]></category>
		<category><![CDATA[HipHop]]></category>
		<category><![CDATA[LinkedIn]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[social game]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=46</guid>
		<description><![CDATA[1、SYNCHRONIZING DATABASES IN DIFFERENT GEOGRAPHIC LOCATIONS

http://highscalability.com/blog/2007/12/7/synchronizing-databases-in-different-geographic-locations.html

翻出很久之前的一篇文章，该文提出了跨地域同步数据的问题。就Mysql来说，如果数据需要在两个地域传输，使用双主复制模式是较为简单的方法。如果mysql能支持多主复制模式，多地域的数据复制或许就解决了。但多主复制很难搞，这种多点并发复制很难自动化的处理提交冲突的问题。]]></description>
			<content:encoded><![CDATA[<p>1、SYNCHRONIZING DATABASES IN DIFFERENT GEOGRAPHIC LOCATIONS</p>
<p><a href="http://highscalability.com/blog/2007/12/7/synchronizing-databases-in-different-geographic-locations.html">http://highscalability.com/blog/2007/12/7/synchronizing-databases-in-different-geographic-locations.html</a></p>
<p>翻出很久之前的一篇文章，该文提出了跨地域同步数据的问题。就Mysql来说，如果数据需要在两个地域传输，使用双主复制模式是较为简单的方法。如果mysql能支持多主复制模式，多地域的数据复制或许就解决了。但多主复制很难搞，这种多点并发复制很难自动化的处理提交冲突的问题。</p>
<p>2、LinkedIn Search: A Look Beneath the Hood</p>
<p><a href="http://thenoisychannel.com/2010/01/31/linkedin-search-a-look-beneath-the-hood/">http://thenoisychannel.com/2010/01/31/linkedin-search-a-look-beneath-the-hood/#</a></p>
<p>这篇揭示LinkedIn的检索系统PPT很不错，尽管我对检索了解不多。对检索结果，LinkedIn没有采用通常的cache方式，而是重新计算。Cache检索结果的好处是提高性能，但无法保证cache数据的真实性，而LinkedIn期望每一次的检索结果都是准确的数据。LinkedIn采用数据库分区、大内存缓存DB数据的方式提高DB查询效率，以至于像双向好友关系也是实时查询的。LinkedIn对Lucene打的实时检索补丁，也很值得研究。</p>
<p>3、<a href="http://developers.facebook.com/news.php?blog=1&amp;story=358">HipHop for PHP: Move Fast</a></p>
<p><a href="http://developers.facebook.com/news.php?blog=1&amp;story=358">http://developers.facebook.com/news.php?blog=1&amp;story=358</a></p>
<p>Facebook爆出的HipHop对PHP社区绝对是爆炸性的消息，如果它开源的话，估计会被众多公司研究和采用。HipHop原理是将PHP代码翻译成C++代码，再编译成二进制程序执行。这一方式和LiteXml类似，都是为了克服脚本语言解释过程的低性能，而在生产环境迂回成编译性程序执行。不过，HipHop会因为C++语言的限制而失掉一些PHP语法功能，尽管不是很重要的说。我倒是很期望PHP能做成虚拟机，不失开发效率，也一样能提高解释执行的性能。</p>
<p>4、<a href="http://highscalability.com/blog/2010/2/8/how-farmville-scales-to-harvest-75-million-players-a-month.html">HOW FARMVILLE SCALES TO HARVEST 75 MILLION PLAYERS A MONTH</a></p>
<p><a href="http://highscalability.com/blog/2010/2/8/how-farmville-scales-to-harvest-75-million-players-a-month.html?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed:+HighScalability+(High+Scalability)&amp;utm_content=Google+Reader">http://highscalability.com/blog/2010/2/8/how-farmville-scales-to-harvest-75-million-players-a-month.html?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed:+HighScalability+(High+Scalability)&amp;utm_content=Google+Reader</a></p>
<p>该文分享了一个social game的技术经验，但没有什么太细节的东西。只有最后大众化的LESSONS LEARNED可以汇总下：</p>
<p>1）对于写多读少的social game，采用in-memory架构，异步提交数据是王道。</p>
<p>2）系统内的组件间低耦合，即便某个组件挂掉，也不会完全影响整个系统的使用。当系统出现性能等问题时，可以关闭一些不必要的功能来缓解问题。</p>
<p>3）缓存Facebook的数据。</p>
<p>4）对新功能做好性能预估及应对方案。</p>
<p>5）抽样部分数据来做分析。</p>
<p>5、When should you store serialized objects in the database?</p>
<p><a href="http://www.mysqlperformanceblog.com/2010/01/21/when-should-you-store-serialized-objects-in-the-database/">http://www.mysqlperformanceblog.com/2010/01/21/when-should-you-store-serialized-objects-in-the-database/</a></p>
<p>Peter的文章总会跟来一堆人的争论。Peter反对在mysql中使用无模式的blob字段，它给出的理由主要有：</p>
<p>1）查询时需要获取整个数据，即便只需要其中部分。</p>
<p>3）blob字段在更新时需要复制的数据更大，并且非定长会造成数据碎片。</p>
<p>3）如MIN, MAX, AVG函数不能使用。</p>
<p>4）blob字段内容没有类型约束检查，需要程序保证，这可能带来潜在的问题。</p>
<p>以用户信息这样的数据类型来说，它的典型特点是：1）一个id对应着一堆属性信息。2）需要针对属性字段做查询条件。这种情况的最简单的做法是，建立一张多属性的schema表和一堆属性索引，但这会带来属性查询的低性能。所以，像FriendFeed、Twitter等，是将属性数据和索引分开。在此基础上，问题只是属性数据的存储是要还是不要schema。如果要schema，peter提到的问题都不是问题，但也带来新的问题：1）属性增加需要修改表结构，这是很折腾人的事情。2）如果属性很多，表结构需要的列数会很多，一个解决方法是，可以将属性分成几类分别存储。实际上，如果在mysql前面有cache（或者Mysql自己的cache够大）来缓存主用户信息，而更新又不是很频繁，blob存储是没什么太大问题的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/02/46.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

