Monthly Archive for 2月, 2009

运维的误区

要问DBA,SA最怕什么,很多人的第一反应就是宕机,因为宕机就意味着系统不可用,意味着故障。所以,我们在很多时候把硬件的可用性和整个系统的可用性等同起来,要想提高系统的可用性,那就得买最好的设备。但是有时候过分追求硬件的可用性,而忽视了软件层面的可用性。我并不否认设备的可靠性很重要,但是仅仅依赖于强大的硬件设备来保障整个系统,我觉得和指望人品没多大差别,太过依赖于硬件,也从另外一个侧面说明我们的系统很脆弱。

今年我们要大规模使用MySQL数据库,肯定会抛弃大型的主机和设备,而选择pc server+本地磁盘的方案,很多人觉得无法保证系统的可用性,甚至建议买一台大型的存储,集中存放数据,这种思路还是局限在Oracle这种集中式数据库架构,由强大的主机,存储和数据库来保证整个系统的可用性。我们现在要做的就是用可靠性相对较低的设备搭建一个可靠性相对较高的系统,通过数据分拆或功能分拆,将压力分布到多个数据库上,通过提高系统软件层面的可用性,实现负载均衡,故障切换等。可以实现某个机器发生故障时,系统几乎不受影响,或者只影响部分功能。

让宕机成为一种习惯,让宕机后我们不再手忙脚乱,甚至宕机后系统可以自动恢复,这才是我们运维人员要追求的目标。

买一堆EMC,IBM的设备,其实没什么好炫耀的。

–EOF–

外连接与连接顺序

在Oracle 9i中,外连接决定了连接的顺序,比如TEST1.id=TEST2.id(+),那么表TEST1一定是驱动表(即从表TEST1去join表TEST2),不管采用nest loop join或者hash join。因为外连接中,表TEST1的所有记录都要返回,所以必须用它来作为驱动表,如果反过来用表TEST2作为驱动表,实现比较困难,这个其实很好理解。

select a.* from TEST1 a,TEST2 b where a.id=b.id(+);

------------------------------------------
| Id  | Operation                | Name  |
------------------------------------------
|   0 | SELECT STATEMENT         |       |
|*  1 |  HASH JOIN OUTER         |       |
|   2 |   TABLE ACCESS FULL      | TEST1 |
|   3 |   TABLE ACCESS FULL      | TEST2 |
------------------------------------------

在10g版本中,Oracle引入了一个HASH JOIN RIGHT OUTER,可以改变外连接的顺序。Oracle会根据COST来决定hash join的顺序,比如Oracle发现表TEST2比TEST1小很多时,Oracle将会改变hash join的顺序,因为根据hash join的原理,选择比较小的表build hash table效率比较高。看看下面的执行计划:

------------------------------------------
| Id  | Operation                | Name  |
------------------------------------------
|   0 | SELECT STATEMENT         |       |
|*  1 |  HASH JOIN RIGHT OUTER   |       |
|   2 |   TABLE ACCESS FULL      | TEST2 |
|   3 |   TABLE ACCESS FULL      | TEST1 |
------------------------------------------

因为是外连接,TEST1表的记录需要全部返回,如果用TEST2表作为驱动表,Oracle一定做了特殊的处理。我们猜测一下Oracle如何处理,首先是采用普通的inner join的方法,将TEST2表在内存中build hash table(hash table A),然后扫描TEST1表,并进行hash join,如果TEST1表的记录未在hash table A中找到对应的记录,则在另外的内存区域记录(hash table B),然后将两个hash table合并,返回。这个特性仅适用于hash join,不适用于nest loop join,也没有hash join left outer.

我们再来看看anti join(反连接)的情况:

select a.* from TEST1 a WHERE not exists(select null from TEST2 b where a.id=b.id);

可以等价转换为外连接的形式

select a.* from TEST1 a,TEST2 b where a.id=b.id(+) and b.id is null;(只取TEST1表的内容)

------------------------------------------
| Id  | Operation                | Name  |
------------------------------------------
|   0 | SELECT STATEMENT         |       |
|*  1 |  HASH JOIN ANTI          |       |
|   2 |   TABLE ACCESS FULL      | TEST1 |
|   3 |   TABLE ACCESS FULL      | TEST2 |
------------------------------------------

select a.id,b.name from TEST1 a,TEST2 b where a.id=b.id(+) and b.id is null;(取两个表的内容)

------------------------------------------
| Id  | Operation                | Name  |
------------------------------------------
|   0 | SELECT STATEMENT         |       |
|*  1 |  FILTER                  |       |
|*  2 |   HASH JOIN OUTER        |       |
|   3 |    TABLE ACCESS FULL     | TEST1 |
|   4 |    TABLE ACCESS FULL     | TEST2 |
------------------------------------------

第一个SQL使用了反连接,因为它只需要返回TEST1表并且不在TEST2表中的记录,所以反连接在join的过程中,如果发现没有匹配的记录,只需要在TEST1表的hash table中作标记,然后返回即可。而第二个SQL需要返回两个表的记录,所以必须将TEST2表也build到hash table中,并且多了一个filter的工作(用来过滤b.id is null),所以反连接就是一种特殊的外连接,而且连接的顺序也是固定的,anti nest loop join的原理也是一样的。

再让我们看看HASH JOIN RIGHT OUTER用在反连接的环境中,我们又有新的发现,那就是HASH JOIN RIGHT ANTI.

select a.* from TEST1 a,TEST2 b where a.id=b.id(+) and b.id is null;(只取TEST1表中的内容)

------------------------------------------
| Id  | Operation                | Name  |
------------------------------------------
|   0 | SELECT STATEMENT         |       |
|*  1 |  HASH JOIN RIGHT ANTI    |       |
|   2 |   TABLE ACCESS FULL      | TEST2 |
|   3 |   TABLE ACCESS FULL      | TEST1 |
------------------------------------------

select a.id,b.name from TEST1 a,TEST2 b where a.id=b.id(+) and b.id is null;(取两个表的内容)

------------------------------------------
| Id  | Operation                | Name  |
------------------------------------------
|   0 | SELECT STATEMENT         |       |
|*  1 |  FILTER                  |       |
|*  2 |   HASH JOIN RIGHT OUTER  |       |
|   3 |    TABLE ACCESS FULL     | TEST2 |
|   4 |    TABLE ACCESS FULL     | TEST1 |
------------------------------------------

可以看到,虽然join的顺序发生了变化,但是如果我们只取inner table中的字段,就可以利用到反连接的特性。小小执行计划的改变,蕴含了更深层次的原理。

–EOF–

Know Unknown Through Known

在学习Oracle的过程中,我们不光要知道某个功能如何使用,而且要经常思考Oracle为什么要这样设计。假如我们是Oracle的开发人员,我们会怎么设计这个功能?通过这样的思考,能够帮助我们更深层次的了解其设计原理。就算一个你从来没有用过的功能,花几分钟查阅一下文档,你就可以迅速上手,完全不需要死记命令。

学习的过程是由点到面,由浅到深。最初是学习每个知识点,然后每个点不断深入,再融汇贯通后,就可以形成连成一个网状的知识面。学习的最高境界是Know Unknown Through Known(通过已知知未知)。当你到达一定的高度时,你会发现很多设计原理都是相通的,比如Oracle启动和OS的启动过程,表面虽然不同,但原理很相似。甚至我们可以通过已知的知识来推断出一个未知的东西是如何设计的。

昨天和一个同事聊到了flashback这个东西,问题是在flashback table的时候,Oracle要求必须打开enable row movement,文档上只是说明这个是前提条件,但是为什么却没有说明。我从来没有用过flashback,但是我基本了解实现原理,flashback有两种实现方式:一类是通过undo来实现的,比如flashback query,其实就是利用Oracle的一致性读实现的,因为undo不记录ddl操作,所以ddl操作比如truncate table,是不能flashback的。另一类是10g中引入的flashback log,这个log定期记录了每个block变化的前镜像,其原理实际上是找到变化的block在需要恢复时间点之前的前镜像,然后再用redo将数据库前滚到一致的状态,所以不管什么操作都可以恢复,所以flashback database就是利用它来实现的。

flashback table和flashback query一样,都是通过undo去实现的,所以它同样不支持DDL操作,但是flashback table会自动同步索引,也就是说索引在flashback后,不会变成invalid状态。有两种可能的实现方案:第一种是根据undo生成了一系列的递归的undo sql,然后在表上应用这些SQL,达到flashback的目的(很明显,这个方案很复杂)。第二种是直接利用flashback query生成那个时间点的一致读状态,然后删除原有的数据(delete或truncate),再根据flashback query的结果插入数据。

以上都是我们的猜测,通过10046 trace可以验证Oracle实际上是采用了第二个方案(flashback query+delete+insert),为什么使用delete而不是truncate,因为truncate会生成新的data_object_id.

step1:先利用flashback query生成一个临时表,这个表上有一个字段表明操作的类型(D代表删除,I代表插入):

INSERT /*+ APPEND */into SYS_TEMP_FBT
SELECT /*+ FBTSCAN FULL  PARALLEL(S, DEFAULT) */:1, :2, :3, rowid, SYS_FBT_INSDEL
FROM "SYSTEM"."TEST" as of SCN :4 S

step2:然后关联两个表,删除D类型的操作:

DELETE /*+ BYPASS_UJVC */
FROM (SELECT /*+ ORDERED USE_NL  PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */ S.rowid
FROM SYS_TEMP_FBT T, "SYSTEM"."TEST" S
WHERE T.rid = S.rowid
and T.action = 'D'
and T.object# = :1) V

step3:插入flashback query的结果,完成flashback table:

INSERT /*+ PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */
INTO "SYSTEM"."TEST_1"
SELECT /*+ USE_NL  ORDERED PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */ S.*
FROM SYS_TEMP_FBT T, "SYSTEM"."TEST" as of SCN :1 S
WHERE T.rid = S.rowid
and T.action = 'I'
and T.object# = :2

这下全明白了,因为都是普通的DML操作,这样就可以理解为什么Oracle要求必须enable row movement,而且flashback table后索引也处于一个一致的状态。

Flashback Table happens in place by rolling back only the changes made to the table or tables and their dependent objects, such as indexes. Note that Flashback Table is different from Flashback Drop: Flashback Table undoes recent transactions to an existing table whereas Flashback Drop recovers a dropped table; Flashback Table uses data in the undo tablespace whereas Flashback Drop uses the recycle bin.

The FLASHBACK TABLE command brings one or more tables back to a point in time before any number of logical corruptions have occurred on the tables. To be able to flashback a table, you must enable row movement for the table; because DML operations are used to bring the table back to its former state, the ROWIDs in the table change. As a result, Flashback Table is not a viable option for applications that depend on the table’s ROWIDs to remain constant.

多谢棉花糖兄的trace结果,大胆猜测,小心论证,就完成了整个结论。

–EOF–

后记:这个事情还没完,有人问我:flashback table到底和enable row movement有什么关系?
回答:flashback table虽然可以保证数据不变,但是实际上里面的行的rowid已经发生了变化,也就是说样子虽然没变,但是内部已经发生了好大的变化(比如:一个update操作,flashback是通过delete,insert来实现的,所以行的位置已经变化了)。enable row movement常用于分区表,但是其实际的含义是允许行的rowid发生变化,因为普通的DML操作是不会导致rowid发生变化的(不要拿delete,再insert说事,那是两个行,不是一个行,rowid当然会变)。因为Oracle有这个承诺,所以当rowid会变化时,要求必须打开enable row movement这个开关。

漫漫装修路(之一)

“装修,装修,边装边修”。

“如果你爱他,让他去装修,如果你恨他,让他去装修”。

“装修是对人意志品质的重大考验”。

”心比天高,命比纸薄,形容装修是一个逐步降低期望值的过程“。

最近要开始考虑装修了,我和LP都没有太多的时间和精力,所以只能找装修公司了。现在的装修公司多如牛毛,每家都说自己如何好,在经过几个星期的考察之后,周六终于选定了一家,选他们并不是感觉他们有多好,而是因为我们明白了一个道理,天下乌鸦一般黑,根本就不存在所谓厚道的装修公司,所以也就无所谓了,只是感觉这个设计师和我们的想法比较一致,项目经理又说的很诚恳,就定下来了。LP跟我说,她有些累了,我也很累,不想折腾了。

定了装修公司,马上要去买材料,周日马不停蹄参加了一个团购会,由于是第一次参加,感觉真是疯狂啊。尤其是那个叫“校长”的首席砍价师,实在是太NB了,我都怀疑是不是他们和商家在演双簧。反正定了一大堆东西:西蒙的开关,索玛丽的移门,诺克斯的橱柜,窗帘,木门,门锁,拉篮,容声的吊顶……

漫漫装修路开始了,希望我们的运气好一点,早点住进我们自己的家。

–EOF–

今天折腾上了twitter

Twitter就是mini blog,或者称为一句话博客,挺好玩的,侧边栏的我的废话

就是啦。

twitter.com注册一个帐号,大家都来follow me!

–EOF–

败家也能出书

.!.

俺们公司的名人,三钻卖家很平常,三钻买家你见过吗?据说三年在淘宝败了100万,最近出书了,实在是太牛了。我算是搞明白了,就算是花钱败家也可以搞得很专业的。

孩子他娘的书-《happy女人happy购》

以下内容为转载:

当我写出“孩子他娘的真败家史”这几个字的时候,我就想到了几个组合,所排列出来的不同意思。他娘的真败家–这个组合,就映射出我05年在淘宝混到现在的历程,那就是真的败家,很多淘友会问我,败了多少钱啊。。其实我真的不太算的清楚了,只记得,我基本上家里所消耗的,必要的没必要的,都在淘宝上解决了。。自己的衣服,配饰,收藏品,常用的不太常用的,都在淘宝上解决了。。阿宝的吃的,玩的,穿的,都在淘宝上解决了。。这时候觉得,把淘宝做出来的人真伟大,偶尔也会觉得,把淘宝做出来的人,真的太可怕,竟然能把我这个优秀的良妇变得这么的败家。。无论如何,淘宝的发展还是功大大的大于过,试想没淘宝,就没孩子他娘这个人物的今天。。那我的生活将黯淡太多了。

“孩子他娘的败家史”这个文字组合,就是从孩子他娘一个普普通通的网购者,到现在的一个由少许粉丝的网络写手,这就是一个发展史。当初,我只是在我购买的购物评价里去写点关于刚购买到的宝贝的详解。比如,买了一个玻璃碗,会写到这个碗是剔透的,价廉的,实用的等说明,为了就是方便其他的淘友能更客观的了解到这个商品的一五一十的好处于不足。而后来,有点为难了,一口气给阿宝买了5条内裤的时候,那我还围绕着小裤裤写5点内容嘛,很雷人的。渐渐的,就演化到,我写的购物评价,跟买到的宝贝没半毛钱关系。。然后就有了现在的82集的“孩子他娘的一句话心情”的文字。。这些文字,就是反映了自己的心情,想说点啥,就写点啥,当时的文字对于我来说,那纯粹的就是心情的一种宣泄。哪个村姑不八卦的原理,我把能说的想说的会说的敢说的,都写了一遍。随之而来的,是另外一群“村民”,他们从我的文字里读出了共鸣,读出了嬉皮,读出了生活,读出了自己的一份明亮心情。一个午后,一个叫“楠楠”经纪人找到了我,说,把你的文字,变成铅字,出书吧。。然后我就完全不知道要做什么的情况下,签约成了他们公司的公司写手,随后就认识了“胖兔子粥粥”,一个能通杀20岁到60岁女性的阳光型少男。。弱弱的说,他只比我小一岁,但是我还是用少男来形容他,没办法,随叫他是一个标准的白净江南才子,完全把实际年龄藏到了最角落,任他人怎么翻都翻不到他最真实的“年轮”。以前连做梦都不赶去想的,一个我的偶像兔子,竟然会给我的书做插画,用他那马良的神笔,画出来每句话的灵魂之处。几经辗转,书《HAPPY女人HAPPY购》出版了,看到书的同时,我是那种怎么也没办法去压不住的激动。一个从来不敢当真的梦想,一点点的变成了期盼,再一点点的实现。感动,为带铅字纸张的那种味道,为每一页内容把我思绪的牵引,为每一个读者读完这本书之后心情的明亮。

“真他娘的孩子”再一个文字组合,是要写给阿宝的,有这么个儿子相伴左右,为我的人生狠狠的带来了艳丽的色彩。红色的血肉相连,绿色的步步成长,黄色的欢乐嬉戏,蓝色的点点滴滴。我的文字,就像冲洗出来的相片,一张张的记录阿宝不同时期的不同记忆。这是我留给阿宝的最大的财富,遐想多少年之后都做了爷爷的阿宝,躺在摇椅,戴着老花眼镜看着我的文字,时而把书扣在胸前,品了口淡淡的绿茶,缓缓的点了点头。

“孩子他娘的家”又是另外一个文字组合,他爹常挂在嘴边的就是那句话,他是两个孩子的爹,一个是我,吃钱的,一个是阿宝,吃奶的。他爹的兴趣爱好,导致了我们这家的无比和谐,因为他只对赚钱有兴趣,而我只对花钱有兴趣。。近几年,好不容易才培养出这个爱写字的兴趣,他爹终于松了一口气。。觉得,他娘有出息了,学别人写书养家糊口了。即使有一天,他娘的书卖不动了,他还有一条生财之道,就是他爹写一本书,叫做《被我睡过的女作家》。

–EOF–

如何判断undo空间是否足够

自从Oracle提供了undo自动管理的功能后,管理undo就变成了一个简单的工作,只需要设置一个足够大的undo表空间和undo_retention参数。但是我们如何衡量undo的空间是否足够了呢?首先我们了解Oracle申请undo空间的过程。

当一个事物需要undo空间时,执行以下步骤:

1.在没有active transaction的undo segment上分配一个extent,oracle尽可能将事物分布在所有的undo segment上。

2.如果没有找到online的segment,则尝试去找offline的segment,并online它。

3.如果没有找到,则创建一个新的undo segment.

4.如果空间不允许,在当前的undo segment上尝试重用expired状态的extent.

如果一个已经运行的事物,需要更多的undo空间时,执行以下步骤:

1.如果当前的extent有free block,则使用它们。

2.如果当前的extent没有free block,并且下一个exent是expired状态,则使用下一个extent.

3.如果下一个extent不是expired状态,那么尝试从undo tablespace获取空间,如果有空闲空间,则为当前的segment分配新的extent.

4.如果没有空闲空间,需要从offline segment上偷extent,先从offline segment上deallocate extent,然后加到当前的undo segment上。

5.如果没有offline segment,则从online segment上偷extent,同样从online segment 上deallocate extent,然后加到当前的undo segment上。

6.尝试扩展数据文件。

7.尝试重用当前undo segment上unexpired状态的extent,如果所有的exetent都繁忙(包含未提交的事务),则下一步。

8.尝试从offline的segment上偷取unexpired状态的extent.

9尝试从online的segment上偷取unexpired状态的extent.

10.报错ORA-30036:unable to extend segment by %s in undo tablespace ‘%s’

从10g开始,Oracle增加了新的特性,根据undo tablespace的大小和undo的使用状况自动计算undo retention,目的是为了保留尽可能多的undo,v$UNDOSTAT.TUNED_UNDORETENTION字段就反映了这个信息,通过dba_undo_extents表中的status字段,我们可以看到大部分extent处于unexpired状态。所以在10g中,就算我们将undo tablespace设置的足够大,undo_retention设置的足够小,观察dba_free_space和dba_undo_extents,free space和expired extent都是非常小的,但这并不代表undo空间不够用了,根据上面提到的Oracle申请undo空间的过程来看,这些空间都是可以重用的。但是如果设置了Guaranteed UNDO Retention,那情况就不同了,因为Oracle要保证unexpired状态的extent不被覆盖。这个特性可以用_undo_autotune这个隐含参数关闭。

可以通过v$undostat这个视图来判断undo空间是否足够,下面的这个SQL可以计算每秒需要的UNDO大小:

SELECT (UR * (UPS * DBS)) + (DBS * 24) AS “Bytes”
FROM (SELECT value AS UR FROM v$parameter WHERE name = ‘undo_retention’),
(SELECT (SUM(undoblks)/SUM(((end_time - begin_time)*86400))) AS UPS FROM v$undostat),
(select block_size as DBS from dba_tablespaces where tablespace_name=
(select value from v$parameter where name = ‘undo_tablespace’));

一般可以通过以下几个因素来确定undo tablespace的大小:数据库类型(OLTP or DSS),每秒消耗的undo,SQL查询时间(ORA-1555)等等。

–EOF–

招聘MySQL DBA

我们现在Oracle方面的人已经招得差不多了,但是MySQL DBA还一个都没有招到。相比Oracle而言,MySQL DBA实在是稀缺资源,未来MySQL DBA可能比Oracle还要牛。

如果水平不够,但是有意向这个方向发展,我们也可以培养,简历发邮件到freezr@gmail.com.

–EOF–

淘宝牛人真多

眼看不是情人节到了嘛,想想LP也挺辛苦的,买个礼物给她一个惊喜吧。就去淘宝逛逛,找了一个卖家,才问了几句,就被卖家狂扁:

“你的问题我不解释”

“你去别家吧,我不卖给你了”

“你这种人,别来网上买东西了”

“没见过你这么磨叽的人”

“我是淘宝培养的全球购卖家”

本来想发火骂人,后来想想算了,不值得。看来这个东西不适合我们这种阶层,估计都是人傻钱多的大款买的吧,不知道为什么女人就喜欢这个。这个卖家估计也是谁家的大小姐吧,惹不起躲得起,我放弃了。(买的什么东西,大家就别猜了,反正女人喜欢的)。

最后还是决定买实际点的东西,立马杀到淘宝家庭主妇的频道,东西真多啊,榨汁机,豆浆机,菜刀,炒锅,拖把……

–EOF–

Canon其实也没机身马达

原来一直认为只有Nikon的D40,D40X,D60这种低端的单反机没有机身对焦马达,而佳能的都有机身对焦马达。昨天和别人争论这个问题,结果我错了,原来佳能才是真正的机身无马达,全部由镜头马达驱动。相反,由于Nikon一直都采用机身马达驱动,所以很多镜头都不带马达,必须由机身马达驱动才能实现自动对焦,只有AF-S的镜头才内置了马达。

由于Canon是始作俑者,所以Canon现在的镜头(包括副厂头)都带有马达,而Nikon的很多经典镜头都依赖于机身马达驱动,用在D40上必须手动对焦,现在Nikon也在逐步推出AF-S的镜头,比如最近推出的50mm F1.4就内置了马达,不过价格也是高高在上。

很多人总拿机身马达说事,因为这个原因说Nikon不厚道,让别人误以为Canon的机身都带有机身马达,其实是冤枉了Nikon。

我现在的D40还行,就是用标头手动对焦有些困难,看了朋友的D60,虽然也是手动对焦,但是就容易很多,因为D60有一个合焦的刻度表(不知道这样表达是否准确),可以方便的知道对焦的状态,而D40只有一个合焦的小绿点,对焦就困难了许多,基本上照三张糊两张,这是D40的一个显著缺点。

–EOF–