朱赟的技术管理课_13_12_每个工程师都应该了解的数据库知识
你好,我是朱茵。
今天我分享的主题是每个工程师都应该了解的数据库知识,数据库和编程语言一样,同样是软件工程师们的必争之地。
今天我就和大家聊一些数据库相关的知识。
去年,uber发表了一篇文章,宣布他们从post grde circle转到mysql了。
文章的内容很好,同时还科普了一些数据库索引和复制的基本常识。
当时我转给了一个朋友朋友,看了以后说哦,他们二零一三年才发表了一篇文章,说他们从mysql转到post gricycle.我找来朋友提到的那篇旧文,读过之后,大概理解了两篇转型文章背后的原因。
作为两大主流开源数据库,mysql和post gricircle的战争从未停止,虽然硝烟不如编程语言那么浓烈,但也是你来我往、剑影、刀光。
如果去过kora或stack, overflow上搜索mysql对阵postt, r yycl样的关键字会出现一大堆帖子。
大家各执一词,众说纷纭。
我的感觉则是两者各有各的优势和使用场景,并不存在一种数据库对另一种压倒性优势。
对于大部分程序员来说,公司用哪个数据库基本无需你去决定。
加入一个公司的时候,除非是创业公司,或者你是cto VP总监级别别否,否则部部分技技术选型早已应该尘埃落定,尤其是数据库一旦选择在迁移的代价非常大,因此除非有颠覆性的优势或者难以克服的问题,不少有公司会去费时费力。
做这种大的迁移,或论是技术选型还是技术转型,在中不可忽略的因素,是你的工程师更容易驾驭哪种技术或者有话语权的决策者们倾向于哪一种技术?这一点其实和程序语言的选型有异曲同工之处。
类似uber两次高调型型的事情,在曾曾经工作过的square公司也发生过,square最早使用的是mysql.到了二零一二年,由于postgrde ircl的各种优势越来越突出,比如对地理、空间数据和搜索的支持,但是几位资深工程师也开始大力倡定很多新的服务,就尝试性的使用post grysql.那时候公司的架构是mysql和post gr circl并存的。
对于我而言,这就有机会学习掌握和比较两种不同的技术。
在我公司,两者各有特点,有些技术实现在mysql里更方便一点。
另一些则反之,无论哪种数据存储方式,总有方案可以解决问题,并没有觉得非要哪一种才行。
一个公司如果数据库从来不出问题,那一定是因为没有业务量或者流量。
所有技术的选型和设计都有它的应用场景。
除去那些让人开心的案例,剩下的毫无疑问就是坑。
如何尽可能的避免这些坑?如何在出现问题的时候可以用最快的速度去修复,这些都是至关重要的因素。
并部分工程师并不是数据库专家,在square公司两种数据库并存的期间,post gr ccircle的牛人寥寥无几。
但是mysql却有几个专家是极为靠谱的对我们而言,post gr circl和mysql的相对优势都比不过出问题的时候,有人救火和解惑来的重要。
另外一个公司维系两套同类的数据库系统本身就是个负担。
因为这些原因,那些使用postgray circle的服务慢慢就转成了mysql.我们支付类技术工作需要强事物和一致性的支持。
所以no circle类型的数据库用的比较少,我要使用的是mysql或者PSTRE circle.由于在工作中常常与数据库打交道,我也逐渐了解到不少相关的知识和技术。
但是线上真的出了问题,我还是没有把握自己去搞定。
好在每一个公司都会有一些专攻数据库的大牛,这种专门的职位便是DBA.有的中小创业公司没有专职的DBA数据库,便由做运维的人维护,但和几位这样的牛人私交甚好,加之平时自己处理起系统中出现的相关问题,也会常常请教他们。
一来二去就知道了许多有趣的数据库知识对数据库真增加。
我一向是敬佩的态度,他们的价值不可小觑。
公司只要稍具规模,如果数据库这块做不好,基本也就没什么可玩的了。
数据库可以说是互联网公司最宝贵的财持这块不出问题则已一出,问题便是见血封喉,服务直接宕机。
关于数据库最常见的问题都有哪些呢?第一个是选型,因为每个公司的业务不同,数据库系统的应用场景不一样,选型也会不尽相同。
但可以肯定的是,没有哪个系统一定是最好的。
比如做支付业务一定要强,事务性一致性的支持。
很多社交平台更多时候需要的是读可用,有的业务写操作特别重,有的业务更重要的是读操作。
有的业务可能只关心最近几天的数据,于是可以容忍老数据读写的数据,有的却要频频访问历史数据,需要读写的高效。
有的业务可以通过加索引解决查询效率,有的却只能通过加缓存等等。
这就是为什么很多公司会选择多个数据库系统并存,通过不同的技术和架构,给予相关的业务场景最优支持。
如果一旦选型失误,便不会有频频踩坑一说,因为这基本就算直接掉进了坑里。
第二是数据库相关的架构,什么意思呢?这里的架构包括数据库上层的缓存、系统设计、程序语言,对数据库连接的处理、代理层的功能,以及和二进制日志等相关的数据管道的搭建。
当然,其中也包括了数据库系统的分区备份等具体的设计。
很多公司早期所有的表都在一个数据库里面。
因为各种链接池和吞吐量的限制基本没法做,扩展,能够合理的设计数据库表的分离,把数据相关的放在一起,不那么相关或甚至不相关的放在另一个数据库里。
这些看起来很简单的做法,很多时候却可以在很大程度上缓解可扩展的问题。
第三,也是我们平时遇到最多的问题,人为错误。
再好的系统使用姿势不对也是枉然。
更何况并不是所有的工程师都是数据库专家,所以人为的错误是最常出现的。
人为错误分为两种,一种是操作数据库时犯的错误。
第二种是程序员在程序里或者脚本里犯的错误操作,数据库时,犯的错误概率比较低,但危害却最大。
几乎所有的公司都会有类似的传奇时件。
我个人听过的就有三种误操作的版本。
第一个是工程师无意或有意不小心删除了数据库核心表中所有的数据。
这不是段子,在facebook就曾经发生过类似的事情,当时还是我的一个朋友。
好在后来恢复了这事儿,也变成了他工程师历史上光辉的一情。
第二个是工程师在线修改表结构的时候,不小心一步误操作,结果数据库被锁长达几个小时,该公司的网站也就挂了好几个小时。
最后这个版本是听国内一个大公司的朋友说的,细节已经无法还原了。
只记得听说他们的两台服务器在做主,从切换的时候拔错了一个电源插头,然后就没有下文了。
程序员在程序里或者脚本里犯的错误就很常见了。
举一个简单的例子,我们知道ruby on rails对数据库的操作基本是通过active record来完成的。
Active record可以通过一个数据库连接池来限制每个应用到数据库的连接。
如果一个程序或者脚本查询没有索引的数据,导致全表扫描,再加上一些整页服务器的并行访问,经常会有整个数据库的所有连接被占用的情况,连终止查询都没法执行,只能人肉去做一些类似重启的物理操作。
还有更常见的就是程序里的n加一查询问题。
面对这类问题,可以使用数据库的连接查询功能,比如left alter join,避免n加一的问题。
最后一个是数据库访问瓶颈,只要是数据库就有吞吐量的限制,而数据库访问瓶颈便是自然流量增长或者流量突增造成的。
只要你的业务在增长,总有一天数据库访问就会达到一个上限。
在这个预警到来之前,你需要做各种垂直或水平扩展来提升这个上限。
或者你可以通过缓存和其他机制来对访问量进行分流。
这里面可以做的工作就多了,流量的突增,一般是类似分布式拒绝服务或市场活动带来的,也可能是因为某个黑天鹅事件造成的,这些原因都很难预料。
如果是有计划的市场活动,就需要提前做好各种战斗准备。
如果是恶意攻击,那就只能靠各种防御工程,如IP阻塞,或者第三方的高防系统挡掉这些流量来保证数据库的正常工作。
研发过程中有哪些与数据库相关的问题呢?我们以mysql为例,讲讲日常开发中应该注意的与数据库相关的问题。
首先是索引,创建索引通常是为了提高常用查询语句的性能,将某些列以特定的数据结构有序存储起来,维持这样的一个数据结构。
在写数据的时候会有一些系统开销,这如果查询确实是高频的,那么这样的系统开销就很划算。
在建表时,需要考虑所有可能的高频查询。
另一方面,既会过度的为未来设计,也就是加一堆可能根本不常用的索引,反而增加了写数据时候的成本和负担索引。
另一个常见的用途就是保证某一列或者某几列的组合是唯一的这也称为唯一性索引。
在写业务逻辑代码的时候会常常用到。
比如你有一个用户表,你想让所有用户的电子邮件都是唯一的。
这个时候用唯一索引就很方便。
不过唯一索引和可选列组合在一起的时候,也有很多需要注意的地方。
比如你想对列x做唯一索引过了一段时间,也许有些情况,下列x并不唯一。
我们便把索引改成了对列x和列y做唯一索引,但是列y是nowleable的,这个时候会出现什么情况呢?你会有多条记录,有着一样的x值,以及now的y值,很意外,对吧?原因就是now在数据库里常常解释为不确定而不是空。
其次是事务支持。
还有一个比较重要的问题就是数据库的事务支持。
简单来说就是利用数据库本身提供的事务性来封装一系列需要同时完成的动作。
比如在一段事务里面先执行x再执行y如果x和y都是数据库写操作,那要么两个写操作都成功,要么都失败。
也就是说,对数据库的改动会统一把事务所做的修改提交到数据库,而提交前的任何错误都会触发所有更新回滚到开始状态或引发不正常进程的终止。
虽然正确使用事务支持会很方便,但是也常常见到过度使用,让代码变得很脆弱,甚至是出现bug的情况。
常见的几种情况如下,二、事务中封装的代码逻辑太长太复杂,甚至调用了别的函数。
很多时候很难去推理,当执行中抛出异样的话,到底哪些会回滚,哪些会产生遗留影响?二、事务中封装了与数据库改动无关的逻辑。
三、事务中有不可逆的操作,例如发送电子邮件,给用户发布到一个job队列等,这种情况会导致系统的不一致。
比如一个写操作被回滚了,但这条数据相关的job还是被加入到队列了,就会引发错误。
四、事务中包括了在不同数据库里面的事物,也就是分布式事物,这需要单独处理。
五事务中嵌套了事物,不同情况可能会有不同的结果,如果没有搞清楚,就可能会出现意外的行为,更多情况就不一一列举了。
但过度使用事物支持,往往会让逻辑变得不必要的复杂。
再次是数据库锁数据库会出现race condition.我们常常把risk condition叫做竞争条件,如指多个进程或现程并发访问和操作同一数据、五事务中送电子发生的特定顺序序关的现象。
象何何决决争程件件往。
常见的方法是使用各种锁机制来确保行为的预测性。
正确信根据实际情况的不同,加锁的方式会不一样,常见的有乐观锁定和悲观锁定。
总的来说,前者在对性能的要求比较高的系统里更为常见。
在实际应用中,很多系统都会自己实现锁定机制。
最后是缓存和主从机制。
为了提高性能,我们会为数据库增加缓存和主从等机制。
有时候会引起数据的不一致性,常见的情况。
如果系统默认是在从节点读数据,那么一些刚刚更新到主节点的数据,在读的时候就有可能读不到这个情况。
在使用一些数据关联的时候,更容易读不到。
在raws里active record中的数据关联,就很容易出现这一类的问题。
今天跟大家介绍了不少数据库相关的基础知识。
如果你是个软件工程师,想必这些内容都已经耳熟能详。
我们来总结一下,一、文本从mysql和postgrade circle的迁移和选型入手,介绍了数据库的技术特点和选型问题。
在我眼里,没有更好的技术,只有更适合的技术。
二、数据库领域会碰到哪些问题呢?这一部分我为你介绍了数据库选择数据库相关的架构问题,人为失误的问题,还有数据库遭遇流量瓶颈以及相关的应对方式。
三、几乎每个工程师在编程的时候都会和数据库打交道。
那么在研发过程中,我们应该注意什么问题呢?在这个部分,我谈到了索引事物支持数据库锁缓存和主从机制中出现的一系列问题。
这里因为篇幅所限,无法涉及所有的技术细节。
如果你对数据库的知识有兴趣,可以将文章提到的每一个点深入展开,独自去梳理,这样最终可以形成系统的知识储备。
今天讲到的内容都是我在工作中遇到过的问题和实战经验,希望对你有所帮助。
如果你有不同的想法,更系列观点,你对留言中告诉我,我们互通有无,一起成长。