余晟以为

Original: 余晟 余晟以为
余晟以为

yurii-says

我是这么以为的,当然你也可以那么以为

Today

上周在介绍极客时间课程时提了点数据库的旧事,不想还有许多朋友认为有意思,希望多听点。所以我厚着脸皮,回忆了几个与数据库打交道的细节,记录在这里。

学校开数据库课程的时候,用的是高等教育出版社的教材。大概还有些朋友有印象,当时高教社出了一整套《面向21世纪课程教材》,正方形开本,压膜封面,依照专业不同,颜色分别是绿色、红色、蓝色等等。数据库的教材感觉中规中矩,就是从小到大都熟悉的模式。不过,当时有朋友介绍了一本新的教材,给了我很大影响。

这本《数据库:原理、性能与编程》也是高教社出的,当时国内刚开始有影印版的计算机教材,高教社也不甘人后,影印了《操作系统概念》在内的一大批经典教材,而且高教社的定价很良心,唯一问题是纸张太薄,但与价格比起来已经不算什么了。

那个年代“学英语”还是一个沉重的任务,更别说看英文教材了,所以几乎没有朋友交流,全靠自己啃。好在我前一年已经啃过一本C++的影印教材,并没有感觉特别困难。

这本书与“官方教材”最大的不同就是编排节奏,印象里开头很大篇幅都没有涉及数据库和SQL,反倒是和离散数学之类关系更近,作者还要不厌其烦告诉你:我说的是关系,不是表;我说的是元组,不是行;我说的是属性,不是列…… 当时觉得特别麻烦,表、行、列,多么清楚直观,看得见摸得着的东西,而关系、元组、属性都是抽象概念,理解起来麻烦很多,我们需要费那么大劲去绕远路吗?

而且还有大量的习题。计算机专业很多练习都是上机操作,但这本书开头的习题基本都只能在纸上演算,倒是和上机还很不方便的时代很般配。我记得有个题目我在自习室琢磨了一下午,绞尽脑汁也想不出来,最后无奈才看的答案。

题目简单复述是这样的(我使用了表、列、行等等直观说法):如果一张表中有若干行,除了一列的值不同,其它列的值完全相同,如何用SQL把这些列找出来?

如果用编程语言当然很容易,但用SQL就想破头也不知道怎么做了,尤其是在不借助其它临时表、变量的情况下怎么做,更是没有头绪。其实答案也很简单,这张表自己和自己做JOIN,条件是“其它列相同唯有一列不同”,就可以找出来。

大概是因为之前想破头也解不出来,看到答案却如此简单,这个题目我印象特别深刻。当时我瞬间就明白了,笛卡尔积原来可以“一口咬住自己的尾巴”。等到了工作中,也确实触类旁通,靠这个思路解决了不少诡异的问题。

在后来的面试中,我也曾经问过不少候选人这个问题,90%的人都答不上来。而剩下10%能答上来的,基本对数据库操作、数据建模都相当熟练。所以我经常在想:或许学数据库的时候不要一上来就SQL,而是从关系代数讲起,反而会学得更好?

如今数据库的教材丰富了很多,这本书的评价也算不上特别好,不过我时常还会想起它,感谢它。它也提示我:若你真的打算学一门知识,不要一开始就太过纠结教材、方法、老师。哪怕你的教材、方法、老师都不是最顶尖的,只要不是太离谱,都不是大问题,重要的是一门心思扎进去学,然后总会有收获。

我刚刚工作不到一个月,就遇上了封闭开发。

如今“封闭开发”似乎是个很久远的名词了,但是联系到大家叫苦连天的996、007,我忽然感觉“封闭开发”还挺幸福。通常的做法是把程序员拉到与世隔绝的度假村或者山庄,给好吃好喝伺候,每天早上9点干到晚上七八点,周末不休息或只休一天。如此看起来,以前的老板们似乎更加人性化一些。

那个时代的开发很简单粗暴,根本没有“敏捷”这一说,纯粹瀑布式,项目经理往下派任务,按期完成。编程语言是我在学校没怎么用过的Java,不但要用Java,还要用Java操作数据库——这时候我才知道还有个东西叫JDBC,因为之前只在RDBMS里写过简单的SQL而已。在这样巨大的落差面前,即便不怀疑人生,起码也要怀疑自己的大学:我之前学的那些方法是对的吗?做的那些题目都白搭了吗?……

这还不算,还有连接池、线程池、异常、单元测试等等一大堆新鲜概念扑面而来,让我这种在学校只写过“学生程序”的人目不暇接、捉襟见肘。原来,招聘启事上说的“提供丰富的成长和学习空间”,根本不是给你循序渐进、步步为营的机会,而是以铺天盖地、雷霆万钧的方式现身。

回想起来,那时候最大的煎熬不是白天,而是晚上。白天再怎么压力大,毕竟硬着头皮做就可以,晚上面对的却是激烈的思想斗争:这么难,明天要做的事情还没有一点头绪,要不要继续干下去?会不会被开?是不是明天就干脆不干回家更好点?再找工作的话,多久能找到,生活费从哪来? 

每天晚上都在矛盾中辗转睡去,每天早上都没有下定决心,只能继续编程。

慢慢的,似乎从绝境中找到一点出路:不会的可以用Google去搜(那时候还可以直接访问),我英文还算可以,搜出来的东西基本能看懂,这样写程序无非慢一点,毕竟有进展;每找到一点新的想法都去和项目经理沟通(那时候的项目经理大多是技术出身,不只盯进度),听听他的意见;每次因为没做好被项目经理骂“不动脑筋”,都厚着脸皮不多顾及面子,赶紧改……

那时候每两三天都要开早会,每个人汇报自己的进展。有个同事是研究生,学历一直让我仰慕,结果在连续几次汇报“不会”导致任务延期之后,项目经理火了:“不会?什么叫不会?不会应该是你们说的理由吗?不会赶紧学!

说完他还专门看了我一眼。那一瞬间我发现,与平时的“挑三拣四”、“吹毛求疵”不同,那是满意的眼神。

“不会不是理由,不会赶紧学”,这是我刚工作第一个月听到的。后来想起来,能这么早听到这样的话,也算是种幸运。

有趣的是,这次封闭开发完,技术总监考虑到大家在数据库方面能力的不足,专门从搜狐请了DBA来给大家培训(那还是三大门户的年代)。连续三个周末的培训下来,除了知道之前从没听过的“行迁移”等等问题,收获最大的是DBA给我们提供了他多年的精华:脚本包日常的任务基本都能从里面找到对应的脚本,改改参数就可以用。从那以后,我也很注意收集整理自己的各种脚本,并且收获颇丰。

存储过程、触发器这种东西,在学校的时候确实接触过,但也只是“玩过”,真正该怎么用,我很长时间里一直都不知道。

刚工作那几年,听到做项目的朋友抱怨其他人把存储过程当编程语言来用:复杂的处理流程不用程序代码来写,而是用PL/SQL写成了三百多行的存储过程,使用的时候直接调用,然后就拿到结果了。至于其中的逻辑,许多人尝试去梳理,都无功而返。在这种设计里,速度慢还不是最大的问题,源代码管理、更改处理逻辑、处理能力复制才是更加令人生畏的噩梦。所以我得出了一个结论:存储过程、触发器这种东西,尽量少用。

大概是在2006年左右,一个问题让我希望尝试使用触发器。当时要做的是多张简单表的数据联动,没有想到用消息队列,只能以程序代码不断循环扫描各张表,找出变化量,再更新其它表。其实每次的数据变更都不大,但不断循环扫描的程序总是出各种问题,而且不方便调试。

考虑到数据变更的量很小,计算量也很小,我想触发器应当很合适。在测试环境写好之后验证,确实没有问题,不需要依赖外部程序,只要DBMS在跑着,内部的数据就会自动更新。看上去,再也没有比这更好的方案了。

第二天找了一个不忙的时间,神不知鬼不觉的,我就把触发器部署到生产环境,接着停掉外部程序,然后紧张观察,发现一切正常。我估摸着,下个礼拜的技术分享,我可算有的说了。

谁知道还不到5分钟,我的希望就化为了泡影。楼下陆续有人喊:数据库怎么不转了?谁把数据库锁住了? 我赶紧登上数据库,才发现已经有几百条一模一样的SQL语句都在等待执行,它们全都来自触发器……

后来我才知道,在做这个设计的时候,我只考虑了数据正确性和计算成本,没有考虑到系统压力触发器所在的表每天写入超过一百万次,峰值每秒写入行数超过50(那是在2006年)。这种压力下,触发器确实很容易卡死。

还好我迅速就撤回了修改,谁也没有发现真正的原因,只是给自己留下了深刻的教训。

不过我认为,要想把技术练好,总要有些不甘现状,勇于“折腾”的念头,哪怕要因此吃点亏。也得亏有这次教训,我终于知道触发器这类东西要如何玩了。七八年之后一个偶然的机会,我用触发器设计了个“妖娆”的方案,解决了问题。

当时是帮朋友的忙,遇到的是个MySQL+PHP的遗留系统,原本设计容量大概只有几万的表,使用多年以后已经有了接近三百万的数据。这样一来,再用 select count + where 来进行各项统计就已经很吃力了。

可是这些数据统计偏生又是主管们关心,并且希望实时看到的。一开始是系统响应慢,显示不出来,结果越显示不出来就越刷新页面,越刷页面系统的压力就越大,如此陷入恶性循环。

如果有充分的时间和资源,当然可以把这个系统全面重构,彻底杜绝此类问题。但业务需求常常要和技术改造打架,难得有那么多的时间和资源。眼下这个问题,就只有一两天的时间。

在评估了数据的更新频率、统计数据的计算量之后,我设计了一张统计表,把需要统计的数据都存在表格里,同时写了多个触发器,按照源数据更新的方式不同,以不同的触发器去更新统计表里的统计数据然后再把统计数据的取数逻辑稍作修改——只是改改SQL语句,对接到统计表而已。因为原有系统里,PHP没有任何“状态”也没有任何“后台程序”,相比搭建中间件,用触发器的思路虽然妖娆奇特,但确实成本低。

在仔细测试过之后上线,迅速解决了统计数据更新的问题,准确、及时,而且整个改进只用了不到半天时间。

如今时常有人问我架构怎么做,有什么独门秘籍。其实我想来想去,架构这东西真没那么神秘,大部分问题还是在时间、安全、稳定、性能等方面做权衡,同时注意控制复杂度。权衡需要宽阔的视野、良好的沟通能力、多样化的价值观,不能钻牛角尖,要能妥协而且懂得如何妥协;控制复杂度的很大一部分工作则是合理划分责任:事情还是那些事情,切成多少个部分,每个部分放在哪里,怎么实现,怎么组合。这工作有时候看起来像拼图一样简单,其实需要之前修炼的经验和眼光才能决策,而架构设计的水平高下,往往就存在于那些拼图方案之中。 

如今有许多人总给我戴上“牛人”的帽子,这总是让我无比为难。第一世间高人很多,见过越多就越谦卑,万万不敢夜郎自大;第二我讲的很多东西很普通,甚至有很多如今看来很“愚蠢”的经历,“牛人”的人设反而会因为这些经历减分,两相比较,还是不要虚名才最安心。

如果您认为本文有意思,欢迎长按识别上面二维码订阅。

“余晟以为”虽是个人号,但只用心做原创,不虚张声势,不故弄玄虚,不带节奏,力求定期更新,只为和你一同探索世界,分享致中平和的观点。