1、认识服务的规模
注册用户,独立用户
请求数
繁忙时流量
服务器台数
貌似应该有很多其他的指标,以前都听说过,但是从来没有认真分析过,已经上线的系统也没有对此进行过统计和分析!
2、大规模服务中的问题有以下几点:
可扩展性、负载均衡的必要性
一般来说,当一台服务器无法承担负载时,都会采用横向扩展(scale out)或者纵向扩展(scale up)。横向扩展就是通过服务器的数量来分担负载,纵向扩展是通过提高硬件的性能来处理负载。而我们知道,硬件的性能和价格不是成比例的,所以通常采用横向扩展技术!采用横向扩展也会带来问题,如请求如何分配——负载均衡,数据如何同步,网络通信延迟等。
保证冗余性
服务器多了后,故障率也会上升。要么构建稳定的系统,要么构建发生故障时能自动切换以继续运行的系统!
低成本运维的重要性
采用自动化工具。
开发人数和开发方法的变化
如何标准化开发?
考虑下应用程序实现方案?
统一编程语言?
统一库函数和框架?
统一代码规范?
使用版本管理工具管理源代码?
需要有人负责全局的推行。
团队如何管理
应对大规模数据量
数据的流向:磁盘->内存->缓存->CPU。各层的速度差异巨大。
减小数据大小?
分散到多台服务器上?
把数据读取次数降到最低?
3、系统增长战略——最小化开端、预见变化的管理和设计
服务规模小的时候,使用简单的方法效果会更好。应该考虑某种程度上的容量规划,以及在设计服务时尽量减少不必要的数据等。
4、技术团队体制
服务开发部:负责开发各种服务的团队,负责日常的应用程序改进。内部按照服务分成团队,每队3~4人。服务开发部也会跟踪自己开发的服务的性能,将主要页面的大致响应时间定量化,然后每天改进。
基础设施部:负责运维服务器和基础设施的团队,负责准备服务器、运维数据中心和负载均衡等。
5、沟通方式
工作指示基本通过口头传达
如果口头效率低下或者希望留下记录,则通过工具的组合来进行交流
博客+wiki(工作内、实施维护操作方法)
IRC
服务器管理工具
6、改变系统的流程
各团队用10分钟开个短会:共享前一天进度和当天计划。
会议中决定任务负责人,会议结束,立即开始任务。
实现过程尽量书写测试用例。
测试之后开始实现,实现完成提交版本管理。
实现完成后,请求团队内其他开发工程师进行代码审查。
审查通过后,合并到产品代码。
7、数据规模
表的大小以GB为单位,执行select col from table,如果不加索引,因为数据量差不多有千万级别,因此,该SQL会卡死!所以哪怕是调试,也需要避免这种sql语句,可能会引起过高的负载。
8、处理难点
无法在内存中处理!需要读磁盘!内存要比磁盘的I/O快10W~100W倍。
磁盘的搜索和物理结构有关,搜索时有机械物理操作,需要数毫秒,但是内存搜索与物理结构无关,只需要数微秒。
因为磁盘搜索的慢,所以操作系统做了一些加速处理:
将连续的数据放在同一处,然后读取的时候并不是逐个字节读取,而是一次读取4KB左右。(可以参考着看Linux相关基础,鸟哥私房菜里面也提到过。)这样就可以将旋转次数降到最低。
传输速度和总线速度的差异(上面说的是搜索!)。内存的传输速度大概比磁盘的快100倍。。。。。(可以使用hdparm工具查看传输速度【Timing cached reads=内存传输速度】【Timing buffered disk reads=磁盘传输速度】)。
9、 可扩展性的要点——CPU负载和I/O负载
在web应用中,接受HTTP请求、查询数据库,再把数据库返回的数据加工变成HTML后发送给客户端,基本上只消耗CPU。相反,数据库服务器需要较多I/O资源。
由此,对于web应用程序服务器来说,负载均衡非常简单,只需要相同的主机做相同的工作就可以实现了,因为不需要分散数据。但是I/O负载的负载均衡就没有那么简单了,因为数据分散之后就会有数据一致性的问题。
10、处理大规模数据的三个重点——写程序的技巧
能在内存中完成多少
在最大限度减少磁盘寻道次数的基础上灵活运用内存
充分利用局部性的分布式
使用能应对数据量增加的算法
例如线性搜索->二叉树搜索
O(n)->O(log n)
有时可以利用数据压缩和搜索等技术
11、处理大规模数据之前的三大前提知识——程序开发的底层基础
操作系统的缓存
以分布式为前提应用RDBMS时必须要做的事
大规模环境中算法和数据结构怎样使用
12、在理解操作系统缓存的基础上编写应用程序——页面缓存
linux上有页面缓存(page cache)、文件缓存(file cache)、缓冲区缓存(buffer cache)机制。
虚拟内存机制:将逻辑的线性地址变换成物理的物理地址。这样就可以使进程无需考虑自己使用的内存位于什么地方,可以认为比如从0x000地址开始,这样处理就更方便。在分配内存时,以适当的大小(4KB)分配好,并传递给进程,而不是一个字节一个字节的访问。这样的内存块就称为“页面”。
页面缓存原理:操作系统能够让已分配的页面一直维持在这一状态。
进程不能访问磁盘,只能访问虚拟内存。
操作系统从磁盘中读取4KB的块,并写入到内存中,然后将该地址变换为虚拟地址后再告诉进程,最后进程再访问内存。而进程读完数据后,虽然不再需要这块内存,但是并不会释放,而是保留下来,这样其他进程访问同一块磁盘时,就可以直接使用留下来的页面,无需再访问磁盘。这就是页面缓存。
页面缓存的效果就是,一直运行的操作系统更快些。
VFS
LINUX以页面为单位缓存磁盘
LRU
内存空闲时就缓存——通过sar确认:sar -r 1(每秒输出一次当前的内存状态),kbcached即用于缓存的容量,%memused被使用的内存,包括缓存。
增加内存降低I/O负载
13、降低I/O负载的策略
以缓存为前提的降低I/O负载的策略
如果物理内存比数据规模还大,考虑全部缓存。
与经济成本的平衡性。
扩展到多台服务器——无法全部缓存的情况
CPU负载分散只需要简单的增加
I/O分散要考虑局部性
14、利用局部性的分布式
局部性的分布式就是根据访问模式进行分散。
Partitioning——考虑局部性的分布式
Partitioning就是将一个数据库分割到多台服务器上。分割方法很多:
最简单的就是“以表为单位进行分割”,这种方式需要修改程序。
从数据的中间分割,例如按照ID的起始字母进行Partitioning。问题:当改变分割粒度时,需要将数据合并一次比较麻烦。
根据访问模式分割成“岛”——考虑局部性的分布式
比如一般用户分配到岛1,爬虫等分配到岛2
以页面缓存为基础的运维基本准则
操作系统刚启动时不要将服务器投入生产环境
性能测试要在缓存优化后进行
15、分布式MySQL应用的三大要点
灵活应用操作系统缓存
正确应用索引
以横向扩展为前提设计系统
16、灵活应用操作系统缓存
考虑所有数据的大小,尽量将数据量维持在物理内存量之下。
内存不足时增加内存
考虑表结构设计对数据大小的影响
当记录数上亿之后,即使增加8字节的列,数据量也会增加3GB
规范化虽然可以减少数据量,但是会使查询变得复杂,因此应该在速度和数据量的平衡性前提下,考虑规范化。
17、索引
B树
二叉树和B树:B树可以合理的设置节点大小,比如设置为4KB,则和上面的缓存大小相一致!
B+树
MySQL的索引采用的就是B+树:使得搜索外部设备时能够将寻道次数最小化;搜索复杂度O(n)->O(log n)
MySQL索引的不足
使用所以的有->where、order by、group by条件中指定的列
何时有效->明确的添加的索引;主键、UNIQUE约束。可以通过show index确认
MySQL索引的陷阱->想同时使用多个列上的索引,就必须使用复合索引
确认索引是否有效的方法——explain命令
type和rows:type为ALL,rows很大则索引无效,type为ref,rows比较小则索引有效
Extra列也十分重要,如果出现Using filesort或Using temporary的查询不能说是好查询
18、MySQL的的分布式
MySQL的replication功能,即常见的主从和读写分离
应用程序服务器通过负载均衡去查询slave,这样就可以把查询分散到多台服务器上了。
master/slave的特征——对参照系进行扩展,更新类不扩展
主从的结构确定了master是无法实现分布式的。
通常的应用程序,读占据了90%以上,因此master不会称为瓶颈。
需要对更新/写入类进行扩展——表分割、key-value存储
当master的表负载过高,则需要对表进行分割。通过分割来分散写入操作,如果可以分割表文件,就可以将其分散在同一台机器的多块硬盘或者分散到多台服务器上。
或者考虑不使用RDBMS,而使用key-value,如通常的点赞。
19、MySQL的横向扩展策略
数据能放入内存吗?
->yes:放入内存
->no:增加内存,无法增加内存,则用Partitioning
分割之后,将无法使用join
以Partitioning为前提的设计
如若数据库表之前耦合性非常大,则设计时不要将其分割到不同的服务器上。
避免join——利用where in
Partitioning的好处
降低负载
增加局部性
提高缓存的效果
缺点
运维变得复杂
故障率上升
实现冗余需要几台服务器
答:4台。1master+3slave。如果是1+2,假设slave坏掉,则在恢复数据的时候就必须停掉剩下那台slave!
20、特殊用途索引——处理大规模数据
超过RDBMS的处理能力时,利用批处理操作从RDBMS中提取出数据,建立索引服务器之类的东西,再让Web应用程序通过RPC等访问索引服务器。
特殊用途索引——使用调优后的数据结构
关键字链接的处理
使用巨大的正则表达式,虽然比数据库快,但是仍然非常慢
Trie+Common Prefix Search
21、理论联系实践
探寻必须的技术条件
RDBMS中不使用JOIN,这应该是“最差实践”,教科书是绝对不会说不使用JION的,但这是从实践中得来的经验!
相反,使用一些教科书上的理论解决一些相应的问题,却是十分正确的!
从计算机的角度去思考
后续是有关压缩、算法和搜索的话题
22、以紧凑、简洁的方式保存整数数据
压缩大规模数据可以降低磁盘I/O。
23、可变字节码和速度的感觉
可变字节码——整数的编码方式
对例子进行编码实现
24、算法的实用化
算法和算法的评测
数据规模和复杂化的差异
如果数据很小,则算法的复杂度也区分不出来,但随着数据规模变大,算法选择的差异性就越来越大!
算法的评测->复杂度
时间复杂度(执行时间、操作步骤数)、空间复杂度(内存使用量)
复杂度和常数项——评测很重要
常数项:算法实现中不依赖与输入大小,但却不得不执行的一类处理。
就算实现不复杂,CPU缓存是否容易生效、分支预测是否发生等计算机结构特点也会有影响,因此常数项可能会导致差距。
如同样是O(log n)复杂度的排序算法中,快速排序是最快的,因为它使得CPU缓存容易生效。
25、关键字链接
26、文章分类
27、全文搜索技术的应用范围
28、搜索系统的架构
29、搜索引擎的内部结构
30、创建全文搜索
31、企业软件vs.Web服务
应用范围上的差异
流量、增长度、可靠性、事务两者间差异较大。
Web服务的基础设施
低成本、高效率:不应当追求100%的可靠性
重视可扩展性、响应性方面的设计
Web服务的服务规格经常会发生变化
32、云vs.自行构建基础设施
云计算特点就是价格便宜,可扩展性优秀。缺点:内存有上限和低速I/O,模糊不清的负载均衡器,时常停机
自行构建基础设施:硬件配置可以灵活调整;能够灵活应对服务的要求;可以控制瓶颈。
33、层和扩展性
一台服务器能处理的流量极限
各层的可扩展性
web服务器可扩展比较容易,增加服务器即可
数据库服务器和文件服务器的扩展性相比较而言没有那么容易。
read和write相比较,read的分布式也比较容易实现。
最难的要数write的分布式
34、掌握负载进行调优
可视化的管理界面,服务器管理工具。
测量负载的指标——平均负载、内存和CPU相关信息
经验来说,平均负载的值不超过CPU的核数就没有问题。
根据用途进行调优
面向用户
面向爬虫
35、保证冗余性
应用程序服务器一般采用负载均衡实现失败转移和失败恢复,让故障服务器自动下线,故障恢复之后再上线。
数据库服务器可以采用muti-master的方式。
存储服务器采用分布式文件系统。
36、系统稳定化
留出一定余量!
系统的不稳定性
功能增加+内存泄漏:功能达不到理想的性能,导致整体负载上升,服务停止,使用的编程语言,也很难消除内存泄漏。
地雷:比如一篇文章有一万多个评论,如果没有考虑到这种情况,极有可能将评论数全部读取出来进行展示,会导致服务器性能下降甚至停机。
用户的访问模式:如链接被贴到著名网站,被大量用户访问导致系统停机。通常可以采用缓存服务器。
数据量增加
外部关联程序的增加
内存、硬盘故障
网卡故障
37、系统稳定对策
维持适当余量,可以将70%作为分界线
消灭不稳定因素:降低SQL负载、减少内存泄漏、发生异常时的自律控制等
发生异常时的自律控制如自动DoS判断;自动重启服务器;自动终止耗时查询。
38、提高硬件资源使用率
引入虚拟化技术
通过服务器管理工具在运营上发挥虚拟化的优势
虚拟化的额外开销
CPU大约2%~3%
内存性能10%
网络性能50%
I/O性能降低5%左右
39、网络的分界点
超过1Gbps(从路由来看应该是30万pps)->PC路由器的极限
对策:采用多个PC路由;购买成品路由器
超越500台主机->一个子网的极限
子网、ARP表的极限
网络架构的层次化
1、最小的为访问层
2、上面的是分发层
3、最上方为核心层或OSPF层
全球化->一个数据中心的极限
对策:采用CDN
40、应对大规模服务须知
作业队列系统——TheSchwartz等
存储的选择——RDBMS、key-value等
缓存系统——Squid等
计算集群——Hadoop等