转眼之间,来杭州已经就满三年了,在公司热烈庆祝四周年生日的时候,我也收到了公司发给我的一块纪念挂表,作为在taobao工作三周年的一个纪念:

三年前,我从广州来到杭州的时候,心情还是比较复杂的,当飞机从白云机场起飞的时候,望着这个我曾经成长的城市,泪水还是忍不住的要流下来。因为这里有我的小窝,有我的朋友们,有我的爱人,但是,现在,我们离开他们,去一个陌生的城市,开始新的生活,开始新的拼搏。
男人需要的是事业,这个也是我当初离开广州的主要原因,我当时的女朋友,也就是我现在的老婆,知道改变不了我的决定之后,就在开始为我默默的做准备了,收拾衣服,打点行装。我知道她舍不得我离开这里,但是,我们也坚信,我们以后还是可以在一起的。最终,她还是不敢去机场送我,因为她怕忍不住要哭。
飞机到达杭州的时候,我已经不是那么陌生了,因为我面试的时候毕竟来过一趟,下飞机之后,就直接去了biti的住所,也就看到了我们的biti同学与现在的小惠惠同学。吃完午饭,他们俩就开始带我找房子,然后买床上用品,所有的一切,一个下午全部都要搞定,因为明天,我就要正式上班了。
上班的时间总是很忙的,哪个时候的淘宝还处于创业阶段,面临ebay那么大的压力,我们不得不全力而上,不过,努力了总是有回报的,大家看看我们现在的taobao应当就知道了。一个公司是如此,个人何曾也不是如此呢,所以说,你要你付出了,努力了,你总是能收到回报的。但是,路是一步一步走出来的,不要指望你一天就把别人一年的路走了,除非你是天才中的天才,如果你觉得不是,那么,请坐下来慢慢的学习,不要浮躁;如果你觉得你是这个天才,那么还要我告诉你,你也需要坐下来学习,因为知识总是需要积累的。
之后,我搬家了,biti也搬家了,我们两个男人加另外一个女孩,开始了3人合租生活。那个女孩是做外贸的,当时在横店工作,可惜,我当时根本不知道横店是什么,结果被鄙视了。这个期间,小惠惠就成了我们的常客到固定居住者,我老婆也经常选择节假日从广州飞过来团聚。生活还是满艰苦的,吃饭也是经常没有规律,平常就在单位解决,一到周末,则到处找地方改善生活。
这个时候,魏大叔(戏称,并不是大叔啊)也加入我们的生活,从北京过来加入了淘宝,因为是我以前的同事,所以彼此就很熟悉了,来面试的时候,就被我与biti拉着斗地主,一直到现在,他是唯一能坚持下来的一个,不容易啊,另外好几个被杀怕了,不跟我们玩这种高风险性游戏了。
再之后,我们都搬家了,因为我老婆也马上就要过来了,她终于把广州的工作辞了,准备过来做一个专职太太。biti也开始了他的小日子幸福生活,那个女孩去了上海,去追随她的男朋友去了。魏大叔则开始演绎他的泡MM生活,直到终于从变化到固定,不容易啊。据说他练就了一身很厉害的本事,睡的再熟的人,一听到风吹草动,就可以跳起来就逃。记得有一天,他睡在自己家里,晚上听到敲门的声音,习惯的跳了起来,准备逃跑。结果是,把他的昂贵的眼镜踩坏了,心疼了很多天。
这个时候的课余生活,除了吃饭,打牌,玩游戏,偶尔也有高雅性活动,如我们组织了一个羽毛球俱乐部,开始锻炼身体。不是我们的本意,但是,我们发现我们的身体在开始变胖以后,就不得不寻找一些解决方案了。但是解决方案没有达到我们最终的目的,我们的身体还是迅速的膨胀起来。
面包总会有的,牛奶也总会有的,这个是谁说的来着。想当初,04年去参加oracle大会,连个相机也没有,现在,这个已经是小日常用品了,新的三大件——房子,车子,孩子都有了。剩下的事情就是怎么样做的更好,因为,路总是没有头的。
感谢这些年来帮助过我的,或者一起玩过的朋友们,biti,汪海,魏大叔,jane等等,我就不一一列出了。在新的以后的日子里,希望大家过的都很好。
前面我介绍了itl引发的阻塞与死锁,这里有必要再介绍一下位图索引引发的阻塞与死锁,因为这个也是不同于普通死锁的一种死锁方式,在有位图索引存在的表上面,其实很容易就引发阻塞与死锁。这个阻塞不是发生在表上面,而是发生在索引上。因为位图索引锁定的范围远远比普通的b-tree索引锁定的范围大。
假定,一个表,上面有标志字段(flags),分别是(0、1),而我们在这个flag字段上创建了一个位图索引,那么,现在我们执行如下的语句:
- Piner@10gR2>update test set flags = 1 where id = 1;
假定id=1的的值原来是0,现在要更新成1,那么,这个语句在位图索引中,将锁住id=1那个记录所在的整个索引块中的flag=0与1的值,如果这个索引块中有很多记录,这个阻塞将是很严重的。另外注意,如果操作的dml不涉及到索引,则是不会被阻塞的。所以,在oltp环境中,如果一个表更新比较频繁,千万不要使用位图索引,如果数据仓库环境中,使用了位图索引,也最好在加载数据的时候将其删除,等数据加载完成以后重新创建。
我们看一个具体的例子
- Piner@10gR2>select * from test;
-
- ID FLAG
- ---------- ----------
- 1 1
- 2 1
- 3 1
- 4 1
- 5 1
- 6 0
- 7 0
- 8 0
- 9 0
- 10 0
- 10 rows selected.
我们在上面创建一个位图索引
- Piner@10gR2>create bitmap index ind_test on test(flag);
- Index created.
然后更新其中的一条记录,仅仅是一条记录
- Piner@10gR2>update test set flag = 0 where id=1;
- 1 row updated.
这个时候,其实整个位图都被锁定了(因为这些记录都在一个索引块中),我们看如下的例子,在另外的会话中,执行如下的语句可以发现:
- Piner@10gR2>update test set flag = flag where id=6;
- 1 row updated.
-
- Piner@10gR2>update test set flag = 1 where id=6; --blocked
可以发现第二个语句被阻塞
其实也就是说,如果不涉及到更新索引(因为第一次中,修改id=6的记录,flag值没有发生变化),那么就不会有阻塞的。当然,如果不是更新flag字段,而是其它字段,也不会有阻塞的,一旦涉及到修改flag的值,阻塞就发生了。
以上是是把0更新成1阻塞,那么把1更新成0同样阻塞
- Piner@10gR2>update test set flag = flag where id=2;
- 1 row updated.
-
- Piner@10gR2>update test set flag = 0 where id=2; --blocked
同样被阻塞
我们看看这个时候的等待原因,可以发现等待在TX锁上面
- select EVENT from v$session_wait where sid=153
- EVENT
- -----------------------------
- enq: TX - row lock contention
理解了阻塞的原因,那么我们应当就很好理解位图死锁的原因了,数据行死锁差不多,2个进程互相锁住了资源,不同的是,行死锁抢的是同一样的数据,如同一行数据,位图索引死锁抢的是位图值,可以是不同的行。
接上面的表数据,我们模拟一个死锁,在会话1中执行如下命令
会话1
- Piner@10gR2>update test set flag = 2 where id=1;
- 1 row updated.
以上命令一旦发出,位图索引中锁住了所有flag=1(原值)以及flag=2(新值)的记录。
在会话2
- Piner@10gR2>update test set flag = 3 where id=6;
- 1 row updated.
这个命令锁住了所有flag=0(原值)以及flag=3(新值)的记录。
再回会话1,我们更新id=7的记录,注意,以上的操作记录都不相同。
- Piner@10gR2>update test set flag = 2 where id=7; --blocked
结果是阻塞了,会话1等待会话2释放位图索引的锁定资源。
那么,我们在会话2中,再更新一条记录
- Piner@10gR2>update test set flag = 3 where id=2; --blocked
虽然该记录与以上任何记录都不一样,也被阻塞了,等待会话1释放资源,到这里,死锁就形成了,马上可以看到,报了一个错在会话1上:
- Piner@10gR2>update test set flag = 2 where id=7;
- update test set flag = 2 where id=7
- *
- ERROR at line 1:
- ORA-00060: deadlock detected while waiting for resource
我们看跟踪文件,发现这里锁定的是索引,因为object_id=13009的对象就是是位图索引
Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TX-00050027-00001599 16 148 X 19 160 S
TX-00070014-00001579 19 160 X 16 148 S
session 148: DID 0001-0010-00000032 session 160: DID 0001-0013-00000004
session 160: DID 0001-0013-00000004 session 148: DID 0001-0010-00000032
Rows waited on:
Session 160: obj - rowid = 000032D1 - AAADLRAAAAAAAAAAAA
(dictionary objn - 13009, file - 0, block - 0, slot - 0)
Session 148: obj - rowid = 000032D1 - AAADLRAAAAAAAAAAAA
(dictionary objn - 13009, file - 0, block - 0, slot - 0)