<?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>Tim[后端技术] &#187; mysql</title>
	<atom:link href="http://timyang.net/tag/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://timyang.net</link>
	<description>Tim&#039;s blog, 关于后端架构、互联网技术、分布式、大型网络应用、NoSQL、Key Value等</description>
	<lastBuildDate>Mon, 02 Aug 2010 15:34:40 +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>Twitter停用Cassandra原因分析</title>
		<link>http://timyang.net/data/twitter-cassandra/</link>
		<comments>http://timyang.net/data/twitter-cassandra/#comments</comments>
		<pubDate>Sun, 11 Jul 2010 16:29:41 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[data]]></category>
		<category><![CDATA[cassandra]]></category>
		<category><![CDATA[key value store]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://timyang.net/?p=623</guid>
		<description><![CDATA[Twitter在其7.9一篇官方技术博客Cassandra at Twitter Today提到暂停使用Cassandra来代替MySQL存储feed的计划，这是Twitter一个重要的架构策略调整，因为之前Twitter一直是业界Cassandra方向的领头羊。
For now, we&#8217;re not working on using Cassandra as a store for Tweets. This is a change in strategy. Instead we&#8217;re going to continue to maintain our existing Mysql-based storage. We believe that this isn&#8217;t the time to make large scale migration to a new technology. We will focus our Cassandra work [...]]]></description>
			<content:encoded><![CDATA[<p>Twitter在其7.9一篇官方技术博客<a href="http://engineering.twitter.com/2010/07/cassandra-at-twitter-today.html">Cassandra at Twitter Today</a>提到暂停使用Cassandra来代替MySQL存储feed的计划，这是Twitter一个重要的架构策略调整，因为之前Twitter一直是业界Cassandra方向的领头羊。</p>
<blockquote><p>For now, we&#8217;re not working on using Cassandra as a store for Tweets. This is a change in strategy. Instead we&#8217;re going to continue to maintain our existing Mysql-based storage. We believe that this isn&#8217;t the time to make large scale migration to a new technology. We will focus our Cassandra work on new projects that we wouldn&#8217;t be able to ship without a large-scale data store.</p></blockquote>
<h3>Twitter为什么要停用Cassandra</h3>
<p>我们来分析一下Twitter停止使用Cassandra的原因<br />
1. Cassandra仍然缺少大并发海量数据访问的案例及经验，Cassandra来源自Facebook，但是在Facebook内部Cassandra目前只用在inbox search产品上，容量大约有100-200T。且Inbox Search在Facebook的基础架构中也并非核心应用。并且还传出不少rumors说facebook已经放弃Cassandra。</p>
<p>2. 新产品需要一定稳定期，Cassandra代码或许还存在不少问题，但是Twitter如果投入大量的精力来改进Cassandra和比较优化MySQL的投入来看有点得不偿失。在QCon Beijing上@<a href="http://twitter.com/nk">nk</a>也提到Cassandra在Twitter的内部测试中曾经暴露出不少严重的问题。</p>
<h3>Twitter为什么之前选用Cassandra</h3>
<p>此问题曾经在QCon Beijing 2010做过介绍，在去年的第一期广州技术沙龙也有过交流，类似Twitter这样的网站使用Cassandra的主要原因有<br />
1. 数据增长规模需要不断增加新服务器，传统的切分方案在面临增删硬件时候需要手工维护，当数据规模速度增快，业务又不运行停机维护，手工维护的成本增加造成系统运维不堪重负。<br />
2. 不能简单增加服务器解决请求量增长的问题，需要数据架构师精细的规划。<br />
3. 每一个新的特性都需要重复评估数据拆分及访问优化的问题，架构师需要投入大量精力review几乎相同的业务场景。</p>
<p>Twitter的调整对于MySQL业界来说或许是一大利好，MySQL虽然受近期Oracle收购阴影的影响，但是对于目前大多数拥有海量数据访问的网站依然是他们第一选择。MySQL简单，可靠，安全，配套工具完善，运维成熟。业界碰到的大部分可扩展性方面的问题在MySQL中其实都有清晰明确的解决方法。虽然重复sharding的问题很烦，增删机器相关的运维工作也很繁琐，但是这些工作量还是在可以接受的范围内。</p>
<p>究竟Twitter这次策略改变是NoSQL运动的一次挫折还是前进中的一段小插曲？我们拭目以待。目前另外一大Web 2.0巨头Digg仍然在使用Cassandra。</p>
Similar Posts:<ul><li><a href="http://timyang.net/architecture/2010-tech-predictions/" rel="bookmark" title="December 31, 2009">2010年的技术架构建议</a></li>

<li><a href="http://timyang.net/sns/web20-forum/" rel="bookmark" title="June 6, 2010">Web 2.0技术沙龙设想</a></li>

<li><a href="http://timyang.net/sns/facebook-f8-0/" rel="bookmark" title="July 1, 2009">Facebook平台设计(二)</a></li>

<li><a href="http://timyang.net/architecture/twitter-cache-architecture/" rel="bookmark" title="October 28, 2009">Twitter架构图(cache篇)</a></li>

<li><a href="http://timyang.net/architecture/farmville/" rel="bookmark" title="March 8, 2010">FarmVille(美版开心农场)谈架构:所有模块都是一个可降级的服务</a></li>
</ul><!-- Similar Posts took 10.581 ms -->]]></content:encoded>
			<wfw:commentRss>http://timyang.net/data/twitter-cassandra/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>用Twitter的cursor方式进行Web数据分页</title>
		<link>http://timyang.net/web/pagination/</link>
		<comments>http://timyang.net/web/pagination/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 14:16:58 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://timyang.net/?p=532</guid>
		<description><![CDATA[本文讨论Web应用中实现数据分页功能，不同的技术实现方式的性能方区别。

上图功能的技术实现方法拿MySQL来举例就是
select * from msgs where thread_id = ? limit page * count, count
不过在看Twitter API的时候，我们却发现不少接口使用cursor的方法，而不用page, count这样直观的形式，如 followers ids 接口
URL:
http://twitter.com/followers/ids.format
Returns an array of numeric IDs for every user following the specified user.
Parameters:
* cursor. Required. Breaks the results into pages. Provide a value of -1 to begin paging. Provide values as returned to in the response body&#8217;s next_cursor [...]]]></description>
			<content:encoded><![CDATA[<p>本文讨论Web应用中实现数据分页功能，不同的技术实现方式的性能方区别。<br />
<a href="http://timyang.net/blog/wp-content/uploads/2010/01/pagination.png"><img class="alignnone size-full wp-image-533" title="pagination" src="http://timyang.net/blog/wp-content/uploads/2010/01/pagination.png" alt="" width="376" height="148" /></a><br />
上图功能的技术实现方法拿MySQL来举例就是</p>
<pre>select * from msgs where thread_id = ? limit page * count, count</pre>
<p>不过在看Twitter API的时候，我们却发现不少接口使用cursor的方法，而不用page, count这样直观的形式，如 followers ids 接口</p>
<blockquote><p><strong>URL:</strong></p>
<p>http://twitter.com/followers/ids.format</p>
<p>Returns an array of numeric IDs for every user following the specified user.</p>
<p><strong>Parameters:</strong><br />
* cursor. Required. Breaks the results into pages. Provide a value of -1 to begin paging. Provide values as returned to in the response body&#8217;s next_cursor and previous_cursor attributes to page back and forth in the list.<br />
o Example: http://twitter.com/followers/ids/barackobama.xml?cursor=-1<br />
o Example: http://twitter.com/followers/ids/barackobama.xml?cursor=-1300794057949944903</p></blockquote>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 101px; width: 1px; height: 1px;"><span style="font-family: courier new,monospace;"><span style="font-family: arial,sans-serif;"><span style="font-family: courier new,monospace;"><span style="font-family: arial,sans-serif;"><span style="font-family: courier new,monospace;">http://twitter.com/followers/ids.<em>format</em></span></span></span></span></span></div>
<p>从上面描述可以看到，http://twitter.com/followers/ids.xml 这个调用需要传cursor参数来进行分页，而不是传统的 url?page=n&amp;count=n的形式。这样做有什么优点呢？是否让每个cursor保持一个当时数据集的镜像？防止由于结果集实时改变而产生查询结果有重复内容？<br />
在Google Groups这篇<a href="http://groups.google.com/group/twitter-development-talk/browse_thread/thread/712b4765028d527c/3f444faee8f8d7ef">Cursor Expiration</a>讨论中Twitter的架构师<a href="http://twitter.com/jkalucki">John Kalucki</a>提到</p>
<blockquote><p>A cursor is an opaque deletion-tolerant index into a Btree keyed by source<br />
userid and modification time. It brings you to a point in time in the<br />
reverse chron sorted list. So, since you can&#8217;t change the past, other than<br />
erasing it, it&#8217;s effectively stable. (Modifications bubble to the top.) But<br />
you have to deal with additions at the list head and also block shrinkage<br />
due to deletions, so your blocks begin to overlap quite a bit as the data<br />
ages. (If you cache cursors and read much later, you&#8217;ll see the first few<br />
rows of cursor[n+1]&#8217;s block as duplicates of the last rows of cursor[n]&#8217;s<br />
block. The intersection cardinality is equal to the number of deletions in<br />
cursor[n]&#8217;s block). Still, there may be value in caching these cursors and<br />
then heuristically rebalancing them when the overlap proportion crosses some<br />
threshold.</p></blockquote>
<p>在另外一篇<a href="http://groups.google.com/group/twitter-development-talk/browse_thread/thread/cfccfa4302ff9729/66d6b91f9a6bf96d">new cursor-based pagination not multithread-friendly</a>中John又提到</p>
<blockquote><p>The page based approach does not scale with large sets. We can no<br />
longer support this kind of API without throwing a painful number of<br />
503s.</p>
<p>Working with row-counts forces the data store to recount rows in an O<br />
(n^2) manner. Cursors avoid this issue by allowing practically<br />
constant time access to the next block. The cost becomes O(n/<br />
block_size) which, yes, is O(n), but a graceful one given n &lt; 10^7 and<br />
a block_size of 5000. The cursor approach provides a more complete and<br />
consistent result set.</p>
<p>Proportionally, very few users require multiple page fetches with a<br />
page size of 5,000.</p>
<p>Also, scraping the social graph repeatedly at high speed is could<br />
often be considered a low-value, borderline abusive use of the social<br />
graph API.</p></blockquote>
<p>通过这两段文字我们已经很清楚了，对于大结果集的数据，使用cursor方式的目的主要是为了极大地提高性能。还是拿MySQL为例说明，比如翻页到100,000条时，不用cursor，对应的SQL为</p>
<pre>select * from msgs limit 100000, 100</pre>
<p>在一个百万记录的表上，第一次执行这条SQL需要5秒以上。<br />
假定我们使用表的主键的值作为cursor_id, 使用cursor分页方式对应的SQL可以优化为</p>
<pre>select * from msgs where id &gt; cursor_id limit 100;</pre>
<p>同样的表中，通常只需要100ms以下, 效率会提高几十倍。MySQL limit性能差别也可参看我3年前写的一篇不成熟的文章 <a href="http://hi.baidu.com/jabber/blog/item/67485b43379290119313c6b5.html">MySQL LIMIT 的性能问题</a>。</p>
<h3>结论</h3>
<p>建议Web应用中<strong>大数据集翻页可以采用这种cursor方式</strong>，不过此方法缺点是翻页时必须连续，不能跳页。</p>
Similar Posts:<ul><li><a href="http://timyang.net/architecture/friendfeed-like-aggregator/" rel="bookmark" title="April 3, 2009">Ideas for creating a friendfeed like feed aggregator system</a></li>

<li><a href="http://timyang.net/tech/twitter-whale/" rel="bookmark" title="March 8, 2010">Twitter“鲸鱼”故障技术剖析</a></li>

<li><a href="http://timyang.net/python/python-rest/" rel="bookmark" title="February 12, 2009">用Python实现CRUD功能REST服务</a></li>

<li><a href="http://timyang.net/architecture/twitter-cache-architecture/" rel="bookmark" title="October 28, 2009">Twitter架构图(cache篇)</a></li>

<li><a href="http://timyang.net/sns/twitter-api-changes/" rel="bookmark" title="December 30, 2009">Twitter API最近的一些飞跃</a></li>
</ul><!-- Similar Posts took 9.422 ms -->]]></content:encoded>
			<wfw:commentRss>http://timyang.net/web/pagination/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Friendfeed的MySQL key/value存储</title>
		<link>http://timyang.net/data/friendfeed-mysql-schema-less/</link>
		<comments>http://timyang.net/data/friendfeed-mysql-schema-less/#comments</comments>
		<pubDate>Thu, 29 Oct 2009 05:59:06 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[data]]></category>
		<category><![CDATA[friendfeed]]></category>
		<category><![CDATA[key value store]]></category>
		<category><![CDATA[mysql]]></category>

		<guid isPermaLink="false">http://timyang.net/?p=447</guid>
		<description><![CDATA[这是一篇2009年初的资料How FriendFeed uses MySQL to store schema-less data,相信大部分人已经看过了。如Fenng的中文介绍FriendFeed 使用 MySQL 的经验。本文从不同的角度再补充下。作者几个月前也曾经在广州技术沙龙作过一次Key value store漫谈的演讲，许多参会人员对key value方向存在强烈的使用意愿，但同时也对完全抛弃MySQL存在疑虑，本文介绍的方案也可以给这些人员一些架构参考。
需求
250M entities, entities表共有2.5亿条记录，当然是分库的。
典型解决方案:RDBMS
问题：由于业务需要不定期更改表结构，但是在2.5亿记录的表上增删字段、修改索引需要锁表，最长需要1小时到1天以上。
Key value方案
评估Document类型数据库，如CouchDB
CouchDB问题： Performance？ 广泛使用？ 稳定性？ 抗压性？
MySQL方案
MySQL相比Document store优点：

 不用担心丢数据或数据损坏
 Replication
 非常熟悉它的特性及不足，知道如何解决

结论
综合取舍，使用MySQL来存储key/value(schema-less)数据,value中可以放：
Python dict
JSON object
实际friendfeed存放的是zlib压缩的Python dict数据，当然这种绑定一种语言的做法具有争议性。
表结构及Index设计模式
feed数据基本上都存在entities表中，它的结构为
mysql&#62; desc entities;
+----------+------------+------+-----+-------------------+----------------+
&#124; Field    &#124; Type       &#124; Null &#124; Key &#124; Default           &#124; Extra          &#124;
+----------+------------+------+-----+-------------------+----------------+
&#124; added_id &#124; int(11)    &#124; NO   &#124; PRI &#124; NULL              &#124; auto_increment &#124;
&#124; id       [...]]]></description>
			<content:encoded><![CDATA[<p>这是一篇2009年初的资料<a href="http://bret.appspot.com/entry/how-friendfeed-uses-mysql">How FriendFeed uses MySQL to store schema-less data</a>,相信大部分人已经看过了。如Fenng的中文介绍<a href="http://www.dbanotes.net/arch/friendfeed_mysql.html">FriendFeed 使用 MySQL 的经验</a>。本文从不同的角度再补充下。作者几个月前也曾经在广州技术沙龙作过一次<a href="http://www.slideshare.net/iso1600/key-value-store">Key value store漫谈</a>的演讲，许多参会人员对key value方向存在强烈的使用意愿，但同时也对完全抛弃MySQL存在疑虑，本文介绍的方案也可以给这些人员一些架构参考。</p>
<h3>需求</h3>
<p><strong>250M entities</strong>, entities表共有2.5亿条记录，当然是分库的。</p>
<h3>典型解决方案:RDBMS</h3>
<p>问题：由于业务需要不定期更改表结构，但是在2.5亿记录的表上增删字段、修改索引需要锁表，最长需要1小时到1天以上。</p>
<h3>Key value方案</h3>
<p>评估Document类型数据库，如CouchDB<br />
CouchDB问题： <strong>Performance？ 广泛使用？ 稳定性？ 抗压性？</strong></p>
<h3>MySQL方案</h3>
<p>MySQL相比Document store优点：</p>
<ul>
<li> 不用担心丢数据或数据损坏</li>
<li> Replication</li>
<li> 非常熟悉它的特性及不足，知道如何解决</li>
</ul>
<h3>结论</h3>
<p>综合取舍，使用MySQL来存储key/value(schema-less)数据,value中可以放：<br />
Python dict<br />
JSON object</p>
<p>实际friendfeed存放的是zlib压缩的Python dict数据，当然这种绑定一种语言的做法具有争议性。</p>
<h3>表结构及Index设计模式</h3>
<p>feed数据基本上都存在entities表中，它的结构为</p>
<pre>mysql&gt; desc entities;
+----------+------------+------+-----+-------------------+----------------+
| Field    | Type       | Null | Key | Default           | Extra          |
+----------+------------+------+-----+-------------------+----------------+
| added_id | int(11)    | NO   | PRI | NULL              | auto_increment |
| id       | binary(16) | NO   | UNI |                   |                |
| updated  | timestamp  | YES  | MUL | CURRENT_TIMESTAMP |                |
| body     | mediumblob | YES  |     | NULL              |                |
+----------+------------+------+-----+-------------------+----------------+</pre>
<p>假如里面存的数据如下</p>
<pre>{
"id": "71f0c4d2291844cca2df6f486e96e37c",
"user_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
"feed_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
"title": "We just launched a new backend system for FriendFeed!",
"link": "http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
"published": 1235697046,
"updated": 1235697046,
}</pre>
<p>如果要对link字段进行索引，则用另外一个表来存储。</p>
<pre>mysql&gt; desc index_link;
+-----------+--------------+------+-----+---------+-------+
| Field     | Type         | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| link      | varchar(255) | NO   | PRI |         |       |
| entity_id | binary(16)   | NO   | PRI |         |       |
+-----------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)</pre>
<p>优点是</p>
<ul>
<li> 增加索引时候只需要 1. CREATE TABLE，2.更新程序</li>
<li> 删除索引时候只需要 1. 程序停止写索引表(实际就是一个普通表)，2. DROP TABLE 索引表</li>
</ul>
<p>这种索引方式也是一种值得借鉴的设计模式，特别是key value类型的数据需要索引其中的内容时。</p>
Similar Posts:<ul><li><a href="http://timyang.net/architecture/friendfeed-like-aggregator/" rel="bookmark" title="April 3, 2009">Ideas for creating a friendfeed like feed aggregator system</a></li>

<li><a href="http://timyang.net/tech/key-value-store-draft/" rel="bookmark" title="July 27, 2009">分布式key/value store演讲草稿(一)</a></li>

<li><a href="http://timyang.net/tech/gz-salon-slide/" rel="bookmark" title="August 10, 2009">第一期广州技术沙龙演讲稿及视频</a></li>

<li><a href="http://timyang.net/programming/memcache-mutex/" rel="bookmark" title="July 26, 2010">Memcache mutex设计模式</a></li>

<li><a href="http://timyang.net/web/nginx-module/" rel="bookmark" title="March 17, 2009">如何写nginx module</a></li>
</ul><!-- Similar Posts took 11.906 ms -->]]></content:encoded>
			<wfw:commentRss>http://timyang.net/data/friendfeed-mysql-schema-less/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
