《大型网站技术架构:核心原理与案例分析》读书笔记

大型网站技术架构:核心原理与案例分析 李智慧 著

大型网站架构的演化发展

  • 一台服务器
  • 三台服务器:应用服务器、文件服务器、数据库服务器
  • 把经常访问的数据放到缓存(内存)
  • 服务器集群
  • 数据库读写分离
  • 反向代理、内容分发网络
  • 分布式文件系统、分布式数据库系统(业务分库、数据库拆分)
  • NoSQL、搜索引擎
  • 业务拆分

Web前端性能优化

浏览器访问优化

  • 减少http请求,每次http请求都需要建立连接,服务器要启动独立线程去处理。可以通过合并css、JavaScript、图片答到目的,这样只需要一次请求就能获取全部资源。
  • 使用缓存。http状态码的304就是这么来的。
  • 对文本使用压缩。
  • 浏览器在下载完全部css之后才对页面进行渲染,所以最好讲css放在页面最上面。而JavaScript是立即执行,可能会阻塞页面显示,所以最好放在最下面。
  • 减少cookie传输

CDN

Content Delivery Network。不同地区用户访问网络时,速度差别很大。CDN就是当用户请求网站时,可以从距离自己最近的网络提供商机房获取数据。也就是说采用各种缓存服务器,并把这些缓存服务器分布到用户访问相对集中的地区或网络中,使用户尽量少跳的访问的内容。

反向代理

感觉有点像重定向。比如我访问某个资源,但实际上那个服务器是从其他服务器取出资源给我。而正向代理就是我不能访问服务器A,但可以通过服务器B去访问。反向代理可以用来实现负载均衡,使不同服务器只有一个对外接口。


应用服务器性能优化

分布式缓存

原理

缓存是奖数据存储在相对较高访问速度的存储介质中,可减少数据访问的时间。同时如果数据是计算后得出的,则无需重复计算。
本质是一个内存哈希表。

用途

缓存主要用于读写比很高、很少变化的数据。如商品的类目信息、热门商品信息等。

应用 JBoss Cache

当某台服务器的缓存数据更新时,会通知集群的其他机器更新缓存。
当集群规模较大时,同步信息的代价太高,因而很少在大型网站使用。

应用 Memcached

使用TCP协议通信(UDP也支持),其序列化协议是一套基于文本的自定义协议,非常简单。
其服务端通信模块基于Libevent,一个支持事件触发的网络通信程序库。
客户端路由算法是一致性Hash,使得集群内服务器互不通信也能打到目的同时可以做到几乎无限制的线性伸缩。
一致性Hash就是让哈希值分布在一个环里,就像有符号整数的环一样,最大值再加一就变为最小值。而缓存服务器就分布在这个换上,当计算出在换上的哈希值时,顺时针找到最近的缓存服务器,要找的缓存就在那里。当添加、删除缓存服务器,只需要迁移部分数据,不需要像一般的Hash重新计算所有缓存应该存放的位置。当然,一致性Hash还有不少拓展和优化,这里不赘述。

异步操作

如果在大型网站,还是使用同步,发送请求,等待数据库写入成功再返回结果,那会对数据库造成巨大压力,用户体验也非常不好。
所以这里引入消息队列,请求发送给消息队列后立即返回,再由消费者进程从消息队列获取数据写入数据库(生产者消费者模式),由于消息队列服务器响应时间远快于数据库,所以用户体验提升,数据库负担也没这么大。
但对用户来说,信息的修改可能不是立即有效,所以需要适当调整业务流程。

使用集群

使用负载均衡技术构建服务器集群。讲并发访问请求分发到多台服务器上处理。

代码优化

多线程

启动线程数 = ( 任务执行时间 / ( 任务执行时间 - IO等待时间 ) ) * CPU内核数

这对于都是CPU计算的,没有IO的任务,线程数就是CPU内核数。对于需要等待磁盘操作、网络相应的任务,多启动线程能提高系统吞吐能力。

资源复用

系统运行时,要减少那些开销很大的资源的创建和销毁,比如数据库连接、网络连接、线程、复杂对象等。
复用的方法主要有,单例模式和对象池。
其实对象池就是单例模式的拓展(多例),不过加入了各种机制来保证每个对象都能充分利用。对象池有:数据库连接池、线程池等。

数据结构

使用合适的数据结构。

垃圾回收

如在Java开发中,通过JVM调优尽量减少Full GC。

存储性能优化

使用固态硬盘

B+树 LSM树

B+树,多叉搜索树,通过机械硬盘的预读性质快速查询,这里不细说。
LSM树,NoSQL产品主要的数据结构。可以看作是一个N阶合并树,数据写操作都在内存进行,并会创建一个新纪录,在内存中仍是排序树,当数据量超过设定的值后,将其与磁盘上最新的排序树合并,当这棵树再超,继续与磁盘上下一级的排序树合并。
这里顺便记录下NoSQL,不是NO SQL,而是Not only SQL。只是为了噱头,但其实有很多分类,比如基于文档的MongoDB,刚才说过的基于key-value存储的Memcached、Redis,基于列存储的Hbase、Cassandra等,各有特色,各有适用场景。

RAID and HDFS

Redundant Arrays of Independent Disks, 直译似乎是独立磁盘构成的冗余阵列,简称磁盘阵列。主要是为了改善磁盘访问速度,增强磁盘容错能力(容错:有块磁盘突然崩掉了怎么办)。
假如服务器有N块磁盘。

  • RAID0:根据磁盘数量将数据分成N份分别放到N块磁盘,这样数据读取、访问速度是N倍,但是容错能力非常糟糕,一旦有一块磁盘出了问题,所有磁盘的数据都没用了。
  • RAID1:数据写入时,一份数据同时写入两块磁盘,一块磁盘受损就插入一块新的,复制一下。但这样写入数据的时候有点慢。
  • RAID10:将所有磁盘平均分成两部分,数据同时写入两份磁盘,相当于RAID1,对于每部分磁盘,将数据分到N/2块磁盘上,相当于RAID0。这样既保证可靠性,又提高写入、访问速度,不过磁盘利用率只有50%。
  • RAID3:将数据分成N-1份,写到N-1块磁盘,最后一块磁盘记录校验数据,利用(一般情况下,一台服务器上不会同时出现两块损坏磁盘)的现象,任一磁盘损害,都可以用其他N-1块磁盘的数据恢复。(也不知道这个校验数据是如何做到这样的)但频繁写入可能会导致记录校验数据的磁盘更容损坏,需要经常更换,因此这个方案一般不用。
  • RAID5:没看懂
  • RAID6:没懂,之后再补充吧
    RAID在关系型数据库经常用,但在NoSQL很少用,其实不是说不用,只是软件帮我们做了相应的功能。
    比如HDFS,默认一份数据有三个副本。

你可能感兴趣的:(读书笔记,大型网站技术架构)