wenfengsun

Archive for the ‘Uncategorized’ Category

Posted on: April 9, 2012

Highly Scalable Blog

NoSQL databases are often compared by various non-functional criteria, such as scalability, performance, and consistency. This aspect of NoSQL is well-studied both in practice and theory because specific non-functional properties are often the main justification for NoSQL usage and fundamental results on distributed systems like the CAP theorem apply well to NoSQL systems.  At the same time, NoSQL data modeling is not so well studied and lacks the systematic theory found in relational databases. In this article I provide a short comparison of NoSQL system families from the data modeling point of view and digest several common modeling techniques.

I would like to thank Daniel Kirkdorffer who reviewed the article and cleaned up the grammar.

To  explore data modeling techniques, we have to start with a more or less systematic view of NoSQL data models that preferably reveals trends and interconnections. The following figure depicts imaginary “evolution” of the major NoSQL…

View original post 3,581 more words

Advertisements

用户故事是从用户的角度来描述用户渴望得到的功能。一个好的用户故事包括三个要素:
1.角色:谁要使用这个功能。
2.活动:需要完成什么样的功能。
3.商业价值:为什么需要这个功能,这个功能带来什么样的价值。
用户故事通常按照如下的格式来表达:
英文:
As a , I want to , so that .
中文:
作为一个, 我想要, 以便于
举例:
作为一个“网站管理员”,我想要“统计每天有多少人访问了我的网站”,以便于“我的赞助商了解我的网站会给他们带来什么收益。”
需要注意的是用户故事不能够使用技术语言来描述,要使用用户可以理解的业务语言来描述。
Ron Jeffries的3个C
关于用户故事,Ron Jeffries用3个C来描述它:
卡片(Card) – 用户故事一般写在小的记事卡片上。卡片上可能会写上故事的简短描述,工作量估算等。
交谈(Conversation)- 用户故事背后的细节来源于和客户或者产品负责人的交流沟通。
确认(Confirmation)- 通过验收测试确认用户故事被正确完成。
用户故事的六个特性- INVEST
INVEST = Independent, Negotiable, Valuable, Estimable, Small, Testable
一个好的用户故事应该遵循INVEST原则。
独立性(Independent)— 要尽可能的让一个用户故事独立于其他的用户故事。用户故事之间的依赖使得制定计划,确定优先级,工作量估算都变得很困难。通常我们可以通过组合用户故事和分解用户故事来减少依赖性。
可协商性(Negotiable)— 一个用户故事的内容要是可以协商的,用户故事不是合同。一个用户故事卡片上只是对用户故事的一个简短的描述,不包括太多的细节。具体的细节在沟通阶段产出。一个用户故事卡带有了太多的细节,实际上限制了和用户的沟通。
有价值(Valuable)— 每个故事必须对客户具有价值(无论是用户还是购买方)。一个让用户故事有价值的好方法是让客户来写下它们。一旦一个客户意识到这是一个用户故事并不是一个契约而且可以进行协商的时候,他们将非常乐意写下故事。
可以估算性(Estimable)—开发团队需要去估计一个用户故事以便确定优先级,工作量,安排计划。但是让开发者难以估计故事的问题来自:对于领域知识的缺乏(这种情况下需要更多的沟通),或者故事太大了(这时需要把故事切分成小些的)。
短小(Small)— 一个好的故事在工作量上要尽量短小,最好不要超过10个理想人/天的工作量,至少要确保的是在一个迭代或Sprint中能够完成。用户故事越大,在安排计划,工作量估算等方面的风险就会越大。
可测试性(Testable)—一个用户故事要是可以测试的,以便于确认它是可以完成的。如果一个用户故事不能够测试,那么你就无法知道它什么时候可以完成。一个不可测试的用户故事例子:软件应该是易于使用的。

[代码] [SQL]代码
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
3.应尽量避免在 where 子句中使用!=或操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.下面的查询也将导致全表扫描:
select id from t where name like ‘%abc%’
若要提高效率,可以考虑全文检索。
7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=’abc’ // oracle总有的是substr函数。
select id from t where datediff(day,createdate,’2005-11-30′)=0 //查过了确实没有datediff函数。
应改为:
select id from t where name like ‘abc%’
select id from t where createdate>=’2005-11-30′ and createdate= to_date(‘2005-11-30′,’yyyy-mm-dd’)
10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
12.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(…)
13.很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
21.避免频繁创建和删除临时表,以减少系统表资源的消耗。
22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。
29.尽量避免大事务操作,提高系统并发能力。
30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

一,tcpdump
tcpdump是linux下网络分析工具,在linux下查看tcpdump命令参数如下:
[@yd_81_103 ~]# tcpdump -h
tcpdump version 3.9.4
libpcap version 0.9.4
Usage: tcpdump [-aAdDeflLnNOpqRStuUvxX] [-c count] [ -C file_size ]
[ -E algo:secret ] [ -F file ] [ -i interface ] [ -M secret ]
[ -r file ] [ -s snaplen ] [ -T type ] [ -w file ]
[ -W filecount ] [ -y datalinktype ] [ -Z user ]
[ expression ]
-a    将网络地址和广播地址转变成名字;
   -d    将匹配信息包的代码以人们能够理解的汇编格式给出;
   -dd    将匹配信息包的代码以c语言程序段的格式给出;
   -ddd   将匹配信息包的代码以十进制的形式给出;
   -e    在输出行打印出数据链路层的头部信息;
   -f    将外部的Internet地址以数字的形式打印出来;
   -l    使标准输出变为缓冲行形式;
   -n    不把网络地址转换成名字;
   -t    在输出的每一行不打印时间戳;
   -v    输出一个稍微详细的信息,例如在ip包中可以包括ttl和服务类型的信息;
   -vv    输出详细的报文信息;
-x 打印出数据包的内容
-xx 更加详细的打印出数据包的内容
-X 用ASCII码的形式打印出数据包的内容
-XX 同上,不过打印出来的信息内容更加详尽
   -c    在收到指定的包的数目后,tcpdump 就会停止;
   -F    从指定的文件中读取表达式,忽略其它的表达式;
   -i    指定监听的网络接口;
   -r    从指定的文件中读取包(这些包一般通过-w选项产生);
   -w    直接将包写入文件中,并不分析和打印出来;
   -T    将监听到的包直接解释为指定的类型的报文,常见的类型有rpc (远程过程调用)和snmp(简单网络管理协议;)
常用的几个命令:
1. 监控某个网卡下的指定协议指定端口的数据包情况:
[@yd_81_103 ~]# tcpdump -i eth1 tcp and port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
2. 监控目的地址为指定地址的,指定网卡下,指定协议指定端口的数据包情况:
[@yd_81_103 ~]# tcpdump -i eth1 dst net 221.179.173.215 and tcp and port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
3. 打印数据包的内容,并指定数据包长度(指定长度 -s 长度, 打印数据包内容 -X)
[@yd_81_103 ~]# tcpdump -f -i eth1 tcp and port 80 -vv -s 1000 -X
tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 1000 bytes
二,netstat
Netstat的一些常用选项
netstat -s——本选项能够按照各个协议分别显示其统计数据。如果你的应用程序(如Web浏览器)运行速度比较慢,或者不能显示Web页之类的数据,那么你就可以用本选项来查看一下所显示的信息。你需要仔细查看统计数据的各行,找到出错的关键字,进而确定问题所在。

netstat -e——本选项用于显示关于以太网的统计数据。它列出的项目包括传送的数据报的总字节数、错误数、删除数、数据报的数量和广播的数量。这些统计数据既有发送的数据报数量,也有接收的数据报数量。这个选项可以用来统计一些基本的网络流量。

netstat -r——本选项可以显示关于路由表的信息,类似于后面所讲使用route print命令时看到的 信息。除了显示有效路由外,还显示当前有效的连接。

netstat -a——本选项显示一个所有的有效连接信息列表,包括已建立的连接(ESTABLISHED),也包括监听连接请求(LISTENING)的那些连接,断开连接(CLOSE_WAIT)或者处于联机等待状态的(TIME_WAIT)等

netstat -n——显示所有已建立的有效连接。

memcache在高并发的情况下,可能会出现一个key多个线程去操作的情况;

典型的场景是:

1. API限制频率的场合,当某个用户的请求同时发过来的时候,如何更新缓存;

2. 当某缓存失效,要去数据库读取的时候,此时大量并发请求会造成数据库压力过大;

此时要考虑使用mutex模式,即为读取和设置增加一把锁。

方式1.增加锁,利用memcached的add操作的原子性保证同时只有一个进程获得锁。

例如:

String mutexKey = key+"_mutex";
MemcacheUtil client = MemcacheUtil.getInstance();
try{
if(client.getValue(mutexKey)==null){
if(client.add(mutexKey, "true", 10)){
int timer = (int)(rate.getReset_time_in_seconds()-(System.currentTimeMillis()/1000));
client.putObjValue(key, rate, timer);//set remain time.
client.removeValue(mutexKey);
}else{
Thread.sleep(10);
updateRateCache(rate, key);
}
}else{
Thread.sleep(10);
updateRateCache(rate, key);
}
}catch(Exception e){
log.error("Update ratelimit error-->"+e);
e.printStackTrace();
}

方式2.为了避免大量的请求阻塞,获取不到数据,而在value写入的时候加入timeout,在过期之后,用锁机制去db获取数据,而其他的未获取到db获取权限的,则还是从缓存读取数据。

v = memcache.get(key);
if (v == null) {
if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {
value = db.get(key);
memcache.set(key, value);
memcache.delete(key_mutex);
} else {
sleep(50);
retry();
}
} else {
if (v.timeout <= now()) {
if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {
// extend the timeout for other threads
v.timeout += 3 * 60 * 1000;
memcache.set(key, v, KEY_TIMEOUT * 2);
// load the latest value from db
v = db.get(key);
v.timeout = KEY_TIMEOUT;
memcache.set(key, value, KEY_TIMEOUT * 2);
memcache.delete(key_mutex);
} else {
sleep(50);
retry();
}
}
}

linux随机启动原理分析。

首先,linux随机启动有七个级别,分别是0-6,它们代表的意义是:

#0:停机

#1:单用户模式

#2:多用户模式,没有NFS

#3:完全多用户模式(标准模式)

#4:not used

#5:X11(xWindow)

#6: 重新启动

备注:千万不能设置initdefault为0和6.

这些级别的配置在/etc/inittab文件中配置,可以查看当前机器配置的是哪个级别。

那么Linux究竟是如何启动那些服务的呢?答案是通过脚本启动。

在/etc/rc(n) 文件夹下指明那些脚本是随机启动的【注意,这里的n的取值是0-6】。

这些脚本会统一配置到:/etc/init.d,而rc(n)目录下只是到这些脚本的软连接。

建立一个软连接的命令是:ln -s /etc/rc.d/init.d/apached /etc/rc3.d/S90apache

这里为软连接命名是有规范的:凡是以K开头的,表示随机不启动;凡是以S开头的,表示随机启动;

启动脚本:

你可能需要去网上搜罗有用的启动脚本了,因为脚本至少要提供 start命令才能够在启动的时候顺利启动。

当然,start|stop|restart 作为最常用的三个命令是必要的。

特别说明,如果你打算随机启动一个resin的web服务,那么你要做的事情会不少:

1. 去你的$RESIN_HOME/contrib下,拷贝init.resin到/etc/init.d下并重命名为resin,cp $RESIN_HOME/contrib/init.resin /etc/init.d/resin【备注:如果你的resin下没有contrib目录,说明你是拷贝过来的,并非以configure/make/make install的方式完成安装的。你需要重新编译安装resin即可得到这个目录】

2. 增加resin文件的权限:chmod 755 resin

3. 修改文件中的JAVA_HOME, RESIN_HOME为真实的目录地址。修改你的启动脚本,如果你使用了其他的配置文件,请在$EXE -p $PID -conf $RESIN_HOME/conf/xxx.conf -server xxx start $JARGS, 同时也要修改stop的设置。

4. 配置到启动目录,在/etc/rc3目录下增加软连接 ln -s /etc/init.d/resin S20resin

reboot后就会发现,这个服务随机启动了。

而且,你可以通过service resin start|stop|restart来操作这个服务。