Learning MySQL Notes I 八月 26th, 2010
Summary
列函数:
count() min(),max(),sum(),avg(), distinct
聚集查询:
group by column1,column2... having ...
Sequence
AUTO_INCREMENT:
- 生成连续正整数(unsigned int,tinyint...),通常从1开始,不能为NULL;
- 索引上的要求,primary key 或者 unique;
- 删除顶端数值后,BDB从当前最大值+1开始,MyISAM或者InnoDB则是过去没有使用的最小整数;
id int unsigned not null auto_increment primary key, OR id int unsigned not null auto_increment unique,
查询序列值:
针对每一个mysql连接
select last_insert_id();
根据顶端值使用:
alter table tbl_name auto_increment = 1;
多列序列 :
- 把auto_increment列和某列链接在一起,使其构成一个索引,且auto列需放在最后;
- primary key不能包含NULL值;
id int unsigned not null auto_increment, name varchar(20) not null, primary key(name,id)
计数器:
last_insert_id(expr)句法;
insert into tbl_name (key1,key2) values('xxx',last_insert_id(1))
on duplicate key update key2 = last_insert_id(key2+2);
select last_insert_id(); //返回此更新后的key2值
循环序列
- 表间相同行信息匹配;
- 连接列需要索引;
select * from tbl_name1,tbl_name2 where tb1_name1.column1 = tb2_name.column2; 等价于 select * from tbl_name1 inner join tbl_name2 on tb1_name1.column1 = tb2_name.column2; 其中若tb1_name1.column1和tb2_name.column2相同,那么可以简写 select * from tbl_name1 inner join tbl_name2 using column1; where 一般的用于限定连接行,而on和using用于连接表
select * from tbl_name1 left join tbl_name2 on tb1_name1.column1 = tb2_name.column2 where tbl_name2.column2 IS NULL;
select * from tbl_name as tb1 left join tbl_name as tb2 on tb1.column = tb2.column;
select tb1.id as seq1,tb2.id as seq2, tb2.column - tb1.column from tbl_name as tb1 left join tbl_name as tb2 on tb1.column+1 = tb2.column;
select tb1.id as seq1,tb2.id as seq2, sum(tb2.column) from tbl_name as tb1 left join tbl_name as tb2 on tb1.column >= tb2.column group by tb1.id;
STDDEV_SAMP() N-1自由度的抽样标准差; VAR_SAMP() N-1自由度的抽样方差; STDDEV() 总体标准差; VARIANCE() 总体方差;
select @n := count(id), @sumxy := sum(x*y),//xy的和 @sumxx := sum(x*x),//x的平方和 @sumx := sum(x),//x的和 @sumy := sum(y), //y的和 @meany := avg(y),//y的平均数 @meanx := avg(x) from tbl_name \G; select @b := (@n*@sumxy - @sumx*@sumy)/(@n*@sumxx - @sumx*@sumx); select @a := (@meany - @b*@meanx);
select @n := count(id), @sumxy := sum(x*y),//xy的和 @sumxx := sum(x*x),//x的平方和 @sumx := sum(x),//x的和 @sumy := sum(y), //y的和 @meany := avg(y),//y的平均数 @meanx := avg(x) from tbl_name \G; select (@n*@sumxy - @sumx*@sumy)/sqrt((@n*@sumxx - @sumx*@sumx)*(@n*@sumyy - @sumy*@sumy));
order by rand(); rand(x);
import itertools,random
face = ['A','B','C','D']
sult = [str(i) for i in range(1,13)]
def shuff():
card = map(lambda x:(x[0]+x[1],random.random()),itertools.product(face,sult))
return map(lambda x:x[0],sorted(card,key = lambda x:x[1]))
>>> shuff()
['C8', 'D6', 'A11', 'B10', 'B3', 'D9', 'C4', 'A4', 'A1', 'C9', 'B2', 'B5', 'D8', 'B12', 'B1', 'D5',
'B6', 'C11', 'A3', 'C3', 'D7', 'A7', 'C7', 'B7', 'A6', 'A5', 'C5', 'D11', 'A10', 'D4', 'A12', 'A2',
'C2', 'D12', 'A9', 'B4', 'C6', 'D3', 'D2', 'D1', 'B9', 'C1', 'D10', 'B8', 'C12', 'A8', 'C10', 'B11']
>>> shuff()
['D1', 'A6', 'C2', 'B8', 'B6', 'D2', 'D10', 'A2', 'A9', 'B2', 'D3', 'C1', 'C4', 'B12', 'B9', 'A10',
'D8', 'C3', 'D9', 'C8', 'C5', 'A3', 'A5', 'B5', 'B3', 'D7', 'C12', 'A1', 'B11', 'B4', 'A11', 'C7',
'D11', 'D4', 'B1', 'C9', 'A7', 'B10', 'D12', 'D5', 'C6', 'A4', 'B7', 'A12', 'D6', 'C11', 'C10', 'A8']
unique,primary key
insert ignore into ... ;//判定是否存在一个拷贝 replace into ...;//非键列需要被替换 insert ... on duplicate update ... ;//需要写入,索引列发生重复
在线即时通讯服务的消息存取设计方案 八月 20th, 2010
在线即时通讯服务,就是通常所说的WEBIM.当前在Facebook等SNS中比较常见.本文主要探讨当前产品中WEBIM用户聊天消息的存取方案.
首先,我们看看WEBIM产生的消息特征:
- 频繁发生
- 时序上下文关联
- 多维标识
- 有限长度文本
除此以外,我们的WEBIM产品还具有"多写少读"和"短写长读"的特点,因此这里把读和写分开设计.
"巧妇难为无米之炊"首先来看看,消息是怎么写入的.按照I/O流从前往后依次涉及:
- Chat Server 这个嘛,自然就是接入系统的聊天服务器了
- Message Unit 部署在Chat Server端的 系统客户端 (这个句子貌似有点怪 -_-#)
- Message Stream Server流式消息服务
- Context Intergradation Cache 上下文聚合缓存
- Persistence Layer 持久化存储
好吧,上图

- Message Unit 被设计成采用UDP 协议的异步通讯客户端.消息文本可以被编译成二进制格式, 已节省传输带宽, 参考Google Protocol Buffer.
- Message Stream Server 将包含一个Stream UDP Server. 其采用流式服务器设计, 可以高速的将消息交给后续流程, 而不被阻塞; 另一个重要的部分是Dispatch 服务器, 它将流服务器传递的消息传输至上下文聚集堆.
- Context Intergradation Cache 接受Dispatch 传递的消息(如果为二进制格式,此处还原为文本), 根据其中的上下文流水号, 压入相应的会话消息缓存栈栈顶; 伴随着预定义的栈生命周期结束或者显式的会话结束信号,将整个会话栈的内容(context) 传输至持久化层.
- Persistence Layer 接收前述context, 并根据context 的内容和属性, 构建查询索引(会话流水, 发起人, 接受人...)随后完成context 实体存储.由于大量context 的频繁写硬盘, 可以预见磁盘IOPS 将成为瓶颈. 针对context 的结构类型不同看法, 该层可以通过以下几种方式实现:
- 关系数据库:context 在粗粒度上具有结构化特征. 因此可以采用MySQL Archive 引擎, 相对于MyISAM 等引擎, 其大幅减少物理存储开销, 也提供了高效的读写效率, 并满足''Read Consistency''.
- 文档数据库: 采用JSON,XML 或者YAML 组织文档类型的半结构化数据库. 参考BaseX,MongoDB.
- 分布式文件系统
既然文本已经写入,那我们就来关注下如何读出这些聊天记录.这里需要交代的是,我们WEBIM的产品需求是允许用户查阅历史聊天记录,但并不允许用户删除这写记录,恩,给了我的就别想拿走:)
按照读出的I/O流,基本上涉及:
- Context Cache,主要包括持久化层Index的Cache 和 具体记录的Cache.
- Persistence Storage,就是前文所述的持久化层
上图,上图,我是图片控

当浏览器请求具体的历史消息数据时, 逻辑层会首先在Context Index Cache 中寻找. 命中即返回Context Cache 中相应的数据; 反之, 在持久化存储中查找, 并更新内容缓存和索引缓存.
根据实际业务,Context Cache 的更新策略需要满足Results Consistency.
有些地方"不能说太细",敬请谅解.好了,就到这里,休息休息~~
一种面向搜索引擎的服务设计 七月 27th, 2010
当搜索引擎引导我们访问一个目标站点的时候,有些时候我们会看到,当前站点会提供一个包含高亮搜索关键词的浮层.这个浮层显示了站内其他含有这个关键词的文章信息.方便的聚合了信息的同时,也延长了用户在网站的停留时间.
根据这个思路,我设计了一种面向搜索引擎的服务(SeoS),旨在提升搜索引擎优化效果,并提供一种效果检测的数据方法.

图中字母含义:
- S 百度 谷歌等搜索引擎们
- U 伟大的客户端
- W 吐页面的站点服务器
- Sv SeoS服务
- KeyDB 搜索关键字存储
- IndexDB 站内全文索引
- KeyFS 以关键词为核心的列表页面
按照数字顺序和箭头指向表明了一个基本的访问服务流程.这里简要说明一下:
Q:站点如何得到搜索引擎关键词呢?
A:可以参考HTTP REFER信息
前端服务器将关键词(以下简写KW)传给SeoS服务,Seos通过查询和KW相关的全文索引或者标签索引.返回前端服务器相关的信息列表结果;并异步地将该关键词持久化保存.
那么用户在页面看到的就不仅仅是正文本身,还可以得到为本次浏览而定制的推荐列表.
再来看看记录了关键词信息的KeyDB,通过记录,我们可以准确的知道站点在搜索引擎关键字命中的情况;更重要的是,针对这些关键字,我们可以生成相应的聚合列表页.从而提升站点在该KW上的权重,人为的创造"犹大效应":)
在此基础之上,通过合理的构建KeyDB,我们完全可以建立起搜索引擎爬虫的访问行为规律,动态改变网站的关键词和整体更新频率,保障网站的人气和内容价值.也对搜索引擎更加友好.
[Speed]围绕Subversion的多服务器代码同步 七月 27th, 2010
在实际生产中,一套分布式的程序可能需要被部署在很多环境相同的服务器上.
如何实现程序的自动部署不仅可以降低因版本冲突而导致的服务异常,也可以未将来的持续集成做基础准备.
这里采用的代码版本控制程序为Subversion,由中央版本库向多台服务器同步代码的方法有很多,基本上都是通过使用hook(post-commit)来实现的.
这里记录下我的post-commit:
use Net::SSH::Expect;
#此处添本代码仓库需要同步的服务器组
my @cluster = ('192.168.1.191',
'192.168.1.190',
'192.168.1.193'
);
foreach $svr (@cluster) {
my $ssh = Net::SSH::Expect->new(
host =$svr,
password ='xxx',
user ='xxx',
raw_pty =1
);
my $logins = $ssh->login();
my $command = $ssh->exec('svn up /path');
$ssh->close();
}
Web服务日志分析[上] 七月 27th, 2010
对于一个程序来说,Loging是一件非常有利的武器,其可以帮助程序员快速的找到BUG,分析性能瓶颈等等...甚至还可以在技术社区彰显一番代码的华 丽,实乃死coder居家旅行必备之宝.而网站访问日志除了对开发者有修改缺陷,提升功力之良效以外,对待运营人员,也是分析用户行为的第一手宝贵资料.
我们还是站在程序员的角度来审视程序的运行状态;通过数字手段来分析用户访问状况
既然一切都从访问日志开始,那么首先就来parser weblog吧.让数据变得更加可读些,嘿嘿,自然也更方便处理些.
124.205.30.210 - - [14/Dec/2009:16:02:46 +0800] "GET /HLRelationLog/ent.hunantv.com/y/l/20090306/225172.html HTTP/1.1" 404 252 124.205.30.210 - - [14/Dec/2009:16:04:51 +0800] "GET /HLRelationLog/ent.hunantv.com/y/l/20090901/410652.html HTTP/1.1" 404 252 211.152.32.122 - - [14/Dec/2009:16:15:10 +0800] "GET /manager/html HTTP/1.1" 404 210 124.205.30.210 - - [14/Dec/2009:17:02:18 +0800] "GET / HTTP/1.1" 200 44 124.205.30.210 - - [14/Dec/2009:17:02:19 +0800] "GET /favicon.ico HTTP/1.1" 404 209 124.205.30.210 - - [14/Dec/2009:17:24:59 +0800] "GET /HLRelationLog/ent.hunantv.com/x/20090616/331092.html"
格式很简单:来路IP,本地时间,以及访问的路径.实际情况下往往还有一个User-Agent.但是在本篇我们无视这个参数.先将这些数据"取"出来存 入数据库再说.
awk -F ' ' '/HLRelationLog/ {printf("insert into logs (url,ip,time) values (\"%s\",\"%s\",\"%s\");",$7,$1,substr($4,2))}' access.log > target.sql
然后将数据导入
mysql -u root -p -h 8.8.8.8 < target.sql
这里简单再来看看大致的访问情况:
awk -F ' ' '/HLRelationLog/ {print $7}' access.log |sort|uniq -c|sort -nr > sort.txt
结果摘抄如下:
13102 /HLRelationLog/ent.hunantv.com/e/h/20080917/49514.html 7112 /HLRelationLog/ent.hunantv.com/e/h/20080719/23308.html 6539 /HLRelationLog/ent.hunantv.com/m/20091231/536520.html 6451 /HLRelationLog/ent.hunantv.com/m/20090427/281233.html 5739 /HLRelationLog/ent.hunantv.com/m/20091222/527487.html 5248 /HLRelationLog/ent.hunantv.com/x/20091224/529517.html 4863 /HLRelationLog/ent.hunantv.com/y/20091219/524512.html 4597 /HLRelationLog/ent.hunantv.com/m/20091221/526802.html 4168 /HLRelationLog/ent.hunantv.com/m/20091221/526782.html 4162 /HLRelationLog/ent.hunantv.com/y/20081028/87956.html 3951 /HLRelationLog/ent.hunantv.com/m/20091222/527342.html 3729 /HLRelationLog/ent.hunantv.com/e/20080716/22250.html 3631 /HLRelationLog/ent.hunantv.com/e/h/20080728/26581.html 3561 /HLRelationLog/ent.hunantv.com/m/20091222/527785.html 3372 /HLRelationLog/ent.hunantv.com/e/h/20080903/42076.html 3339 /HLRelationLog/ent.hunantv.com/y/20091216/522056.html 3163 /HLRelationLog/ent.hunantv.com/y/l/20091221/525879.html 2998 /HLRelationLog/ent.hunantv.com/y/20091216/521082.html 2914 /HLRelationLog/ent.hunantv.com/x/20091221/526583.html 2889 /HLRelationLog/ent.hunantv.com/y/20081231/154998.html 2851 /HLRelationLog/ent.hunantv.com/z/20091225/530766.html
下面来处理下频道的访问分布,每个link的倒数第三段[x,y,e...]:首先通过awk得到每个link的频道的识别符和访问数量,丢进一个文件
map(lambda x:[x,sum(map(lambda y:int(y[1]),filter(lambda z:z[0]==x,t)))],set(map(lambda x:x[0],t))) >>> [['x\n', 48173], ['y\n', 266146], ['d\n', 4035], ['z\n', 40631], ['e\n', 98009], ['ent\n', 558], ['m\n', 174542], ['t\n', 59660]]
来看看并发的情况:
tail -10000 access.log | awk '{print $4;}' | sort | uniq -c | sort -nr | head
=====我是分割线====
33 [31/Dec/2009:22:03:18
16 [25/Dec/2009:18:16:07
14 [25/Dec/2009:18:16:06
9 [31/Dec/2009:22:03:19
8 [25/Dec/2009:18:13:35
7 [31/Dec/2009:22:06:38
7 [31/Dec/2009:19:22:40
7 [25/Dec/2009:19:55:54
7 [25/Dec/2009:14:39:06
7 [02/Jan/2010:18:37:54
