三年前,本来我只是个不学无术的数据小码农,空有一腔热情;而当时公司也处在艰难的转型期,旧产品不见转机,新产品前途未卜。想见着也不可能用这么小的数据玩出花来,而新产品的数据也不是一时半会能成规模。仍是本着最大限度学习的心思,鼓足勇气和老板提换岗,要去扛后台开发的大旗,最大程度参与到产品的一线去。一个小决定,换来的是整整半年的不眠之夜,眼见着第1个用户到第500万个用户,眼见着1台到4台再到10台服务器,眼见着后台业务由单一的播放到能播放能上传再到有完整的社交交互。从刚开始三天两头崩溃出事故,到最终一点不怕市场的同事搞拉新的活动,什么情况都能作到心中有数、遇事不慌。回头一想吓一大跳:本身并非后台工程师科班出身,历来对语言和框架的争论无感无力,网络编程的基础知识更是差强人意,可是凭着小米步枪,凭着奇技淫巧,凭着持续思考和不断尝试,竟然也能搭建起一个支撑千万级别用户的后台框架。总结那半年,留下了5条事关生死的建议,在这里泣血奉上。前端
一个完整的后台服务,组件其实就只分3种:接入、逻辑和数据。这比如一家饭店,后台工程师就是开店的老板,客人数量小于1万,服务流程是第一位的,老板们吭哧吭哧忙着写逻辑;1万到10万之间,接入组件的设计会是重中之重:一个店的服务能力有限,老板们忙着多开几个分店,让客人分流,而决定客人到哪个分店的,就是接入组件;可是用户一旦大于10万,数据的读写能力就决定了这家超级饭店的服务容量,无论开多少个分店,都要保证数据是一致的,读起来又快又准,而写数据不会影响到读的性能。表结构怎么设计,数据库怎么分布(主从、读写分离、分库、分表),缓存怎么选怎么分布,就是老板们最重要的工做(让老板高兴的是,名片也能够改印个高大上的抬头:架构师)。数据库
一旦用户量过了十万,要再想光靠数据库一部卡车打天下就不太现实了,而缓存(物理存储地在内存,天生比数据库读写性能强)这匹野马的出现就知足了咱们对于速度的极致需求。缓存对服务器的架构带来了两个深远的影响:一是热数据和冷数据的分离:热数据访问的人多,缓存挡在前面,为数据库分担巨大的读压力;而热数据从产品的角度也更应得到快速的响应。二是数据一致性的门槛提升,更新数据库的同时必须更新缓存,一旦缓存更新失败,数据库也必定要回滚而保证数据的一致性,不能闹给客人上冷菜的笑话。固然缓存存什么、怎么存,也是大有一番学问,容我下一小节再讲。但缓存的重要性总结一句话:没有缓存是万万不能的。不管你是选老马Memcached仍是火热的头马Redis,必定要在数据库感觉到压力以前上马,而且作好缓存备份和恢复的预案。固然,平安无事你是没办法感觉到缓存的好处的,它就像一个平时提醒你吃饭睡觉多喝热水的备胎,只有当她弃你而去之时,你看着服务器哗哗成百倍上涨的响应时间,巴不得找块豆腐一头撞死。编程
Web时代,因为翻页先后用户出现了界面的切换,用户对于列表自己的变化并不敏感(假如翻页的同时列表新加入了内容,只要保证用户浏览的这个片断没有重复就能够),可是移动端这种滚动列表的设计简直就是全部后台工程师的梦魇(加入用户上拉列表获取更多的同时新加入了内容,那用户会看到相邻两个重复的内容,而后就气炸了,什么破APP!),应对「列表重复」这个难题的方法出一本书都够了。由于这个需求,咱们只能放弃了原有的自增ID,采用时间戳做为获取列表片断的方式:简单来说,就是客户端每次都上报一个当前页最后一个内容的时间戳,服务器再去取比这个时间更旧的若干个内容。这里必需要感谢Redis的做者提供了如此丰富的缓存使用的API,我以为Redis最出色的一点就是把列表的全部使用场景都设想得很通透。缓存
实体就是热数据,热数据的缓存有两问:一是存什么?有人会说简单,把整个结构体转化为一个JSON存进去不就得了?但这实际上是有问题的,当你的服务器要面对数十万同时到来的用户,可能短短一瞬就要作数以千万计的JSON到结构体之间的来回切换,而这个过程的效率其实是很不理想的,那么也许你要想一些更快的方案(此处买个关子)。二是怎么存?雪崩效应并不罕见,一旦源数据改变,一时间许多个线程同时去访问更新缓存的API,服务器瞬间堵死,想到后台工程师会所以而失业,我默默加了一个锁。服务器
小张是端菜的服务员,此次上菜,他要先去凉菜区取个土豆丝、再去荤菜区取个东坡肉、顺到素菜区取个手撕包菜、最后到饮料区再拎两瓶果汁,听起来很低效,对不?这和数据获取的过程是相似的,数据库的表设计首要考虑的是归类,好比用户的信息存一张表,用户和小组的关系再存一张表,那么若是有一个场景须要读用户以及他最后访问过的小组,就得作两次的数据表读取,一旦这个场景频繁出现,适当的数据冗余(把用户最后访问的小组ID加入到用户表的字段中)就可以下降数据库的读取压力。因此表设计必定必定必定(重要的事情说三遍)要考虑业务场景。微信
有的小盆友跑来问我,我这个服务器框架选的牛啊,异步多线程的,单进程并发一万多垂手可得,怎么仍是慢啊?我说,「异步」这个词可不要说得过轻松,底层异步了,流程里的每一个步骤是否是异步的呢?数据库读写、缓存读写、外部接口的访问,这些都不能异步吧?既然不是异步,卡在哪里你还不知道呢,还不赶忙打日志。仍是说说最令我崩溃的一个案例:某次服务器炸了,打多少第二天志都没办法定位到卡住的缘由。最后猜是怎么着?居然是日志组件(Log4j)就不是异步的,打日志这个步骤就卡住了,欲哭无泪。网络
一个高级饭店要有厨师,要有大堂经理,要有端盘子的,要有收银的,但千万别忘了还要有保安。他虽然不是饭店成功与否的核心因素,可是若是缺了他,危机时刻就会应付不来。下面这三位哥们就是服务器的保安:日志、监控和有损服务。多线程
先说日志,日志是很微妙的,打多了不行,影响性能、占据空间,打少了,关键问题排查不出缘由。那么哪些是必打的呢?我认为有三点:一是行为的基本属性,无非是什么时候何地何人,时间、用户ID、IP、版本(存下来除了排错,还能够用来作数据分析);二是往返的参数,尤为是客户端上报的参数,服务器返回的数据也许会很大,不建议全部都打印,能够打印统计数据,好比返回了多少个小组之类;三是报错信息,底层必定要catch全部的出错信息,并把它打到单独的日志里。架构
再说监控,日志是一旦发现了问题帮助咱们找出问题的缘由的工具,那么什么能帮咱们发现问题呢?答案是监控和告警。监控与日志不一样,要抓核心的数据,不能多,我建议取三个数据:用户的并发访问数、读取的人均响应时间、写入的人均响应时间,告警的话再加上服务器的崩溃、重启的次数,以及主机性能相关的指标(CPU、内存、硬盘等)。并发
「发生这种事,你们都不想的。饿不饿,我给你煮碗面?」,服务器运气很差崩溃了,我便经常用这句TVB的经典台词与小伙伴们调侃。其实不管事前机关算尽,成长期的APP总会遇到服务器出情况的。可是,以我有限的经验,服务器的问题每每不出在自身,而是它所依赖组件致使的问题,好比Memcached机器dump、转码服务队列阻塞、或者图片存储空间爆满等等。那么在问题被解决以前,总不能干瞪眼,看着用户投诉一波波来吧?咱们会想,对于如今的业务来讲,最不能崩溃的场景是什么?好比播放是咱们的最基础服务,那咱们死也要保证任何外部组件的崩溃都不能影响热门内容的播放,所以咱们要把这部分少而重要的热数据加载到内存,以防止外部存储出了什么问题,服务器本身还有碗面吃。真正是,本身的事情本身干,靠天靠地靠祖宗,不算是好汉。
服务器体系越长越大,咱们首要作的事情是分封,儿子长大了,总要给他一块地盘,当个小王,今后本身打拼去。因而数据读写被抽象成服务了,同时对APP和前端负责,作最大的一个王;编码解码抽象成服务了,反正编码解码是给UGC用户提供的,想当明星的人总要等得起;日志存储和解析也抽象成服务了,反正有少量的丢失咱们也不介意。表面看来服务器被拆得支离破碎,增长了网络时延,是一笔不划算的生意,但实际上对服务器的稳定性大有助益。为何?一是大王国被拆成小王国了,定位问题更容易,迁移和复制也更简单,数据读写有压力?没问题!再给两块地盘。二是在整个链条上,任何一个环节都是多点,俗话说,不把鸡蛋都放在一个篮子,任何一台服务器dump都不会要了咱们的命。
细枝末节且不提,总结当时半年内服务器高速发展期留下来的经验,我认为最重要的就是这五点,业务场景不一样,服务器的架构和侧重点也确定会略有差别;不过这五点基本等同于锦囊,等同于基石,等同于保命符,作好了,这饭店生意必定蒸蒸日上。恭喜你,老板!
更多精彩内容,欢迎关注微信公众号「码农咖啡馆」