帝都的一栋高档写字楼MoScow中,来了一个长相普通的青年,逸飞26岁,某211院校计算机专业研究生毕业。是SCI公司新来的初级后端工程师。
逸飞开始了互联网行业的1095的节奏。刚工作的前2周在简单的适应阶段,到了第3周。逸飞接到了他职业生涯的第一个任务。
任务很简单,就是用他熟悉的语言从数据库中读取数据,然后对外提供RESTful接口。对于这种专业和时髦名词,一知半解的小飞在百度上找到了阮一峰介绍RESTful相关的文章,真的是简单且易懂。小飞在学校动手能力很强,成绩也不错,这种任务哪能放在心上。对于职业生涯的第一个任务就这样的完成了。
项目描述:基础数据读取平台
任务描述:利用编程语言从数据库中读取数据,对外提供HTTP接口用JSON的形式返回给客户端
技术栈:Node.js + MongoDB
工作顺利的完成了,不过几周后公司得到了一笔融资,开始做了广告推广。小飞的项目从10 r/m 提升到了1k r/m系统很快就撑不住了。
小飞在学校从来没有想过自己的项目有朝一日还会遇到性能的问题,小飞找到了他的Leader,公明老师。公明老师跟小飞讲了这个系统的来龙去脉,其实最早期用的是Python + MySQL架构的,里面有很多关系映射。直接用MongoDB重构,里面的数据模型并不合理。其实归根结底还是Schema的问题,不过小飞应该把注意力放到当前已经宕机的服务上,可以去了解下MongoDB的索引。
数据库索引小飞当然知道,不过换到MongoDB上居然有些知识对应不上。或者说根本就对应不上!从头到尾读了一遍《MongoDB权威指南》一书后,对MongoDB的索引有了一定的了解。回过头在看现在的问题,好像有了一些头绪。当小飞想尝试的去给数据库添加索引的时候,发现他想添加的索引已经存在了!想想也是,从开始读这本书到现在已经两个星期过去了。如果问题,自己估计早就被老板开除了!是公明老师帮小飞解决了这个问题,还指导小飞去学习指定的知识。不过小飞这时并不是很领公明老师的情。
转眼就过去了半年,随着小飞的能力的提升。对MongoDB数据模型以及业务设计模式的理解也不断加强。期间Schema也改了几次。小飞也迎来了第二个挑战,就是给移动端开发团队提供接口。
因为MongoDB中本来就适合做这种异构数据的,而Python和Node.js这类动态类型检测语言也没有什么JSON结构的要求。已经习惯了这种玩法的小飞和移动端团队开始了不愉快的首次配合。
移动端团队希望小飞把所有数据都组合成他们希望的样子
小飞希望对所有团队提供的对外数据都一样
移动端要求所有结构必须固定,但是小飞之前的接口都是那种递归型的。层数不一致
当哪个结构空的时候,小飞给的JSON根本就没有那个字段,但是移动端要求即使空也得有这个字段
双方对HTTP协议的掌握程度上差距很大
起初小飞觉得,移动端都是在难为自己:
数据本来就长这个样子的,树形结构本来就是层数不一定。
结构固定,原始数据都不一致,又怎么让我对外提供的一致?
空数据你判断一下不就行了吗?
这里还有一个小故事导致小飞对移动团队一直有偏见
少师:飞哥,这个接口有点问题。没有数据返回
小飞:大佬,咱们联调一下,你给我发一次请求。
...
小飞:大佬,这不是有数据吗?
少师:第一次有数据,第二次就没有了
小飞:再来
...
小飞:这返回的不是304吗?
少师:所以没数据啊
小飞:304就是没数据的
少师:我要200
小飞:你HTTP请求头里面有if-none-match所以返回的是304
少师:其他的接口也有if-none-match,也是304。怎么都能返回数据,就你这个接口不能
小飞:因为这个接口数据量大,你缓存不了这么大数据。不想要304你就把这个字段去掉
少师:iOS封装好的我们改不了
小飞:不可能,你看看Android都能正常运行,就是你们iOS运行不了
少师:是你接口没返回数据。别的接口都有数据,就这个接口不行
小飞:…...
为了能让系统顺利上线,小飞委曲求全,在接口中加了个中间件把if-none-match字段去掉了。虽然问题解决了,但是彼此心里埋下了仇恨的种子……
冬去春来,一年的时间就过去了。小飞已经有了3个小弟,2男1女,女前男后。这时候公明老师找到小飞。告知小飞的MVP服务已经没有响应很长一段时间了,重启服务啥的都不好使。最终找到问题,是因为其中一个后端小伙伴给数据库建了一条索引,在线建的。这个小飞也不知道原来建索引的时候要记得加参数background: true否则会卡死线上服务的。为此不懂管理的小飞还冲这个小弟发了顿脾气。其实这个点连小飞都不知道,根本没有理由怪别人。
第二年的第一个季度,公明老师又构建了一个AI团队。从此小飞的噩梦开始了由于系统主要是数据工作,而这个团队的工作模式是从数据中抽取数据,然后进行计算。最后又写回到数据库中。不过这个MapReduce的流量那叫一个大,每分钟5M级别的数据库读操作。害的小飞的项目那叫一个不稳定。每小时都有10分钟的时间服务是不起作用的。顺便在这里在吐槽一下MongoDB资源隔离的也真是要命。最后是发现,这些做工程的和写数据算法的没法沟通。
第二年的第二个季度,经过一段时间的努力。小飞的项目已经很稳定了,不过却总是存在着一定的慢查询。而且服务也时不时的崩溃一下。小飞求助了他们的运维大佬胖子,胖子给小飞专门搭建了kibana来帮助小飞发现问题。通过小飞连蒙带唬的算是找到了问题。就是当MongoDB的某集合当中出现了慢查询,那么在这一刻发生在这个集合上的所有查询都会变得很慢的。为了专门解决这个问题,胖子把小飞的MongoDB改成了复制集模式。从此小飞开始了读写分离的生活。小飞把所有慢接口,能优化的优化,不能优化的就打到指定的从库中去。快的留在了主库。
第二年第三个季度,此时的小飞已经拥有了10人的团队,4后端6前端。而且小飞也已经可以熟练的使用Redis作为缓存,数据库读写分离的利用。所以的优化以及数据模型的设计了。这时候团队迎来了第一个产品经理的加入。产品经理也不知道怎么想的,查看资源列表的时候,非得只要一共多少页,还得要求可以一页一页的翻页。起初还没觉得怎么样。不过流量大了才发现,这个COUNT的操作真TM是性能杀手啊,还有那个分页功能。而且查询条件也越发的变态起来,从早期的2个筛选条件+1个排序条件。发展到了7个筛选+3个排序功能,而且还要求可以分页,还要求有共XX页,共XX条的提示。如此一来,索引怎么建都不合理,而且查询条件变态COUNT还不能提前计算出来。然后小飞有开启了和产品经理斗智斗勇的生活。
第二年第四个季度,SCI公司已经是一家行业知名的公司。有一些小公司就盯上了SCI的劳动成果,爬虫窃取小飞的数据。
-
第一回合
起初小飞的数据ID是连续的整数,爬虫就是直接循环递增ID来获取数据的。小飞发现了,便对ID进行了编码和解码,组织了爬虫几天。不过很快小飞发现这种编码除了给内部员工带来一些调试的麻烦以外,对爬虫暴力的爬取,根本就没起到任何作用。
-
第二回合
小飞利用Redis给一个用户构建3个键,分钟,小时,天。而且每个键的过期时间也都是1分钟,1小时,1天。每一个时间范围内都有一个阈值。一旦用户请求数超过这个阈值就视为爬虫进行封号操作。不过这样问题就又浮出水面了,因为很多用户的账号都这么被误伤了。
-
第三回合
在之前做的基础上,将Redis之前的STRING类型换成了Hash类型,每一个子键使用了接口名。只对需要保护的数据进行监控而其他的接口就不用太去关注了。不过好景不长,因为有些给客服使用的账号。好几个客服都使用同样的账号,在给用户展示的时候,一到关键时刻就掉链子。
-
第四回合
没办法,小飞又给一些用户添加了白名单和黑名单的功能。白名单下访问的数据量可以比正常用户多很多。当前边发现用户是爬虫的话,就污染他的数据。将当前ID随机替换成另外2个ID,并且将其他两条数据进行随机重组,再将组合完的数据随机增删改无意义的字符!
一转眼小飞迎来了工作的第三个年头
第三年第一季度
由于去年年底,小飞解决爬虫问题大量的使用Redis尝到了甜头。面对之前一直定期跑脚本,把最新和最热的数据排行版都刷到数据库中的行为,改成所有的最新和最热都要写到Redis,而且有效时间是为一天,每日凌晨计算。虽然没能有什么根本上的改进,不过这个行为却让小飞越来越对Redis产生好感。
第三年第二季度
对于之前一直没法解决的变态的查询条件。这一天小飞翻看阮一峰的ElasticSearch的文章,忽然来了灵感。ElasticSearch根本就不是什么搜索引擎,它就是一个数据库。那么为什么不能用ElasticSearch代替MongoDB成为项目的主数据库呢。但是有很多项目都已经围绕着MongoDB来做的,于是乎小飞决定使用一个他刚听来不久的名词CQRS。同时利用RabbitMQ来完成多个数据库的读写操作。
流量高峰
域名统一入口,无法从监控中找到问题服务
流量忽然间的抖动,会比持续压力造成够大的危害
外部攻击
内部压力
兄弟团队疯狂的调用
公司内部文档不统一,其他端的用法也很诡异
移动端的队友对数据监测比较严格
在线建索引要记得不能影响到服务器
交付给其他团队的接口不合格
数据库
新数据在线批量入库
数据挖掘引起的刷库问题
AI团队用数据库直接聚合
复合索引,建索引的顺序
PK的类型对数据库查询也有很大的影响
业务
极其变态的查询条件
分页和count性能问题
只拿Redis当缓存
最新最热的排行榜问题
反爬
通过分时天来检测爬虫
设置黑白名单
混淆的实现