很多水友都问我,为什么要写文,吃力不讨好,上班都已经够累,回到家瘫在床上等吃的,多好。
除了喜欢之外,写文的出发点,就是以文交友。如果身边找不到人谈技术,对于我这样的狂热技术宅来说,是特别苦闷的一件事。而通过互联网把自己的想法散播出去,认识更多与你同频的人,互相交流,多爽。
所以有了你们,有了很多愿意在我文下留言的朋友,真心感谢你们。一句话,一个表情,我都能体会到背后细微的赞同和热情。
上篇文章发出去后,本来是特别细节的一个设计,但引来不小的一波热议。如果你没看到,可以点下方链接直达。
为什么要在每张表中加 CreateDate 和 UpdateDate , 亮三点!
感谢 suxuhui 大佬在这个议题下的留言,让我深刻认识到自己的偷懒和惯性思维,有多么危险。我特意截图出来,大家切引以为戒。勇敢去质疑一切陈旧的思维,不再掉入“路径依赖” 这个大坑!
别看一个小小的设计,其实都带着哲学。
举一个例子,我们在做数据迁移的时候,会有增量和批量更新的操作。那么一般在原始表中,习惯性加上CreateDate 和 UpdateDate. 这样一来,下次抽数据,不用全量,可以使用增量来减少时间。
在抽的过程中,肯定会有源系统的数据在不停的更新。更新时,我们要注意,不要因为我们的抽取动作,而影响源系统的更新。所以我主张用不加锁的脏读去抽。
这里使用脏读,用的是读取未提交数据的方法。也就是把事务隔离机制降为 READ UNCOMMITTED 的思想。一来可以抓到最新数据,二来可以不在源表上加锁,不影响其他更新操作。
那么很显然,会抽到 一些源系统未正式提交的数据。有可能这些数据,因为各种原因被回滚,而没有写入。这样一部分脏数据就会流到下游系统。那么这种情况怎么办呢?做一致性检查。上下游找个时间,对一下。但毕竟有了成本和时间间隔,不是上上策。
经过 suxuhui 大佬群里一提醒,我立刻就知道了。Oracle 强大的事务管理能力,早就实现了读不影响写的特性。那么其他数据库厂商,是不是也同样做到了呢?
就拿 SQL Server 来说,有没有做到呢?翻了翻记忆,我想起来了。有,真有。SQL Server 从 2005 版本起,就开始用 snapshot 隔离机制了。
这里用例子先解释下,什么是读不影响写。文章题目也取自这个案例:
一个很简单的例子,插入一条数据,花费了 51 分钟,还没执行完。这是为什么?通过查询数据字典,我们知道阻塞发生了。
有这么一条语句,采用了严格的隔离机制,将表和表中的数据都锁起来了。在这样的隔离机制下,表不能丢,数据也不能改了:
代码语言:javascript复制SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
正常情况(普通的隔离机制下),读数据的时候,只会对表进行共享锁,对行数据进行 Row 锁,保证其他查询,不能更新正在读取的行,也不能丢弃表。只有读取表数据特别大的时候,才会升级页级锁和表锁(这里和数据量有关,算法可以参考官网)。
一旦读取数据完毕,锁就丢弃了。但 Serializable 这个隔离机制,甚至在读完数据后,锁依然存在。所以上面的插入,不仅仅耗时 50 分钟,只要开启 Serializable 的查询不提交,Insert 将永远等待。
那么有没有不耽误 insert 的隔离机制呢?有,一是我经常使用的 read uncommitted 隔离,二是 snapshot 隔离。
read uncommitted 隔离刚才分析了,会读到一些未提交的数据,俗称“脏数据”。而 snapshot 隔离则只会去读已经提交的数据,且不会对这些数据上锁,以保证其他的更新查询可以不受影响。
这么好用的机制,是怎么实现的呢?SQL Server 的做法,是把数据存放了一份到 tempdb 去,这样读的查询只要读这份数据,自然不用上锁。但很显然,对磁盘要求就高了。Oracle 的做法,我猜是用 undo log 去实现的。
那么,这么好用的隔离机制,为什么我没想到呢?这就得怪该死的“路径依赖”了。看到大部分项目中都用 with(nolock) 来实现无加锁处理,都忘记还有更好用的 snapshot.
好了,今天就分享到这里。有 suxuhui 这样的大佬在群里坐镇,大家还等什么呢,赶紧来加入我们的群吧。