《大型网站系统与JAVA中间件实践学习笔记》-1

第一章:分布式系统介绍

  定义:分布式系统是一组分布在网络上通过消息传递进行协作的计算机组成系统。

分布式系统的意义

  • 升级单机处理能力的性价比越来越低
  • 单机处理器能力存在瓶颈
  • 处于稳定性和可用性考虑

阿姆达尔定律:s(P)=1/((1-p)+p/N)

  其中P指的是程序中可并行的部分的程序在单核上执行的时间的占比,N表示处理器的个数(核心数)。S(N)是指程序在N个处理器相对单个处理器的提升速度比。

单进程多线程和多进程的区别

  线程是属于进程的,一个进程内的多个线程共享进程的内存空间;而多个进程之间的内存空间是相对独立的,因此多个进程间通过内存共享、交换数据的方式与多个线程间的方式就有所不同。多进程相对于单进程多线程的方式来说,资源控制更容易实现,此外多进程中单个进程出现问题不会造成整体不可用。

分布式系统的难点

  1. 缺乏全局时钟
  2. 面对故障的独立性。在分布式系统,整个系统的一部分有问题而其它部分正常是经常出现的情况,我们称之为故障的独立性。
  3. 单点故障。在整个分布式系统中,如果某个角色或者功能只有单台机器在支撑,那个这个节点称为单点,发生的故障称为单点故障。在分布式系统中要尽量避免出现单点。如果不能把单机实现变为集群实现,那么一般还有两种选择:
    • 给这个单点做好备份,能够在出现问题是进行恢复,并且尽量做到自动恢复,降低恢复所需要使用的时间。
    • 降低单点故障的影响范围。

  4.事务的挑战。

第二章:大型网站及架构的演进过程

1.从一个单机交易网站说起

  所有的功能模块和数据在单台服务器上,通过各个模块之间通过JVM内部的方法调用来进行交互,而应用和数据库之间是通过JDBC进行访问的。

2.单机负载告警,数据库与应用分离

  随着访问量的增加,服务器负载持续升高,考虑将应用服务器和数据库服务器分离。

3.应用服务器负载告警,如何让应用服务器走向集群

  应用服务器压力变大时,根据对应用服务器的监测结果,可以考虑将服务器从一台变为两台,增加服务器后急需解决如下连个问题:

  1. 用户对于应用服务器的选择问题,可以通过在应用服务器前增加负载均衡设备来解决。
  2. Session问题。

3.1引入负载均衡设备

  引入负载均衡设备后的架构

3.2解决应用服务器的Session问题

  HTTP协议本生是无状态协议,需要基于HTTP协议支持回话(Session State)状态机制。具体的实现方式为:在回话开始时,分配一个唯一的回话标识(SessionID),通过Cookie把这个标识告诉浏览器,以后每次请求的时候,浏览器会带上这个会话标识告诉服务器请求数据那个会话。在Web服务器上,各个会话有独立的存储,保存不同的回话信息。如果遇到禁用Cookie的情况,一般的做法就是把这个回话标识放到URL的参数中。

  如上图所示,如果第一次网站请求在左边的服务器,那么Session保存在左边的服务器上,如果不做处理,就不能保证每次请求都落在同一台服务器上,这就是Session问题。

Session Stickey

  保证同一个回话的请求都落在同意Web服务器上,称为Session Stickey。

  这种方案可以让同样的Session请求每次都发送到同一个服务器进行处理,非常利于对Session进行服务器端本地缓存。不过带来以下问题:

  1. 如果服务器宕机或者重启,那个这台服务器上的回话数据会丢失。
  2. 回话是应用层信息,那么负载均衡要将同一个回话请求都保存到同一个Web服务器上的话,就需要进行应用层负载均衡,这个开销比第四层的交换要大。
  3. 负载均衡器会变为一个有状态节点,要将会话到具体Web服务器的映射保存。和无状态的几点相比,内存消耗更大,容灾方面会更麻烦。
Session Replication

  Session Replication在Web 服务器之间增加了会话数据同步机制,通过保证不同Web服务之间的Session数据的一致,来解决Session问题。一般的应用容器都支持Session Replication。和Session Replication相比,它对负载均衡设备没有要求,但是其本生也存在一些缺点。

  1. 同步Session数据造成了网络带宽的开销。
  2. 每台服务器都要保存保存所有的Session数据,如果整个集群的Session数很多,每台机器用户保存Session的数据占据内存严重。

Session 集中存储

  该方案的问题:

  1. 读写Session数据引入了网络操作,这相对于本地数据读取来说,问题就在于存在时延和不稳定性。
  2. 如果集中的Session服务器或者集群有问题,会对应用产生严重影响。
Cookie Based

  该方案通过Cookie来传递Session数据,将Session数据存放在Cookie中,然后在Web服务器上从Cookie中生成对应的Session数据。相对于Session 集中存储,这个方案不会依赖一个外部存储系统,也就不存在从外部系统获取、写入Session数据的网络时延。

  该方案存在的不足:

  1. Cookie长度的限制。
  2. 安全性。
  3. 带宽消耗。
  4. 性能影响。每次Http请求和响应都带有Session数据,对于Web服务器来说,在同样的处理情况下,响应的结果会减少,支持的并发数就越多。

4.数据库读压力变大,读写分离

采用数据库作为读库

  读写分离导致的问题:

  1. 数据复制问题。
  2. 数据源选择问题

  数据库系统一般都提供了数据复制功能,但是对于数据复制还需要考虑数据复制的时延问题。数据复制延迟会带来数据短期不一致问题。于此同时,对于写操作主要走主库,事务中的读也要走主库,也要考虑到备库相对于主库的延迟。

搜索引擎其实是一个读库

  搜索引擎要工作,首要的一点是需要根据被搜索的数据来构建索引。

  搜索集群的使用方式和读库的使用方式是一样的。可以从两个维度对搜索系统构建索引的方式进行划分:一种是按照全量/增量划分,另一种是按照实时/非实时划分。搜索引擎的技术解决了站内搜索时某些场景下的读的问题,提供更好的查询效率。

加速数据读取的利器-缓存

数据缓存

  大型系统中的数据缓存主要用于分担数据库的读的压力。一般在缓存中存放的是“热”数据而不是全部数据。应用访问缓存,如果缓存不存在,则从数据读出数据后放入缓存。

  使用缓存来加速数据的读取情况,一个很关键的指标是缓存命中率,因此缓存命中率较低,意味着还有不少的请求回到数据库中。同时数据的分布于更新策略也要结合具体的场景来考虑。在分布上,要考虑的问题是需要避免局部热点,并且缓存服务器扩展或者缩容要尽量平滑。而在缓存的更新上,后有定时失效、数据变更时失效和数据变更时更新等策略。

5. 引入分布式存储系统

  分布式存储系统起到存储的作用,也就是提供读写支持。相对于读写分离中“读”源,分布式存储系统更多是直接替代主库。分布式存储系统通过集群提供了一个高容量、高并发访问、数据冗余容灾的支持。

6. 读写分离后数据库又遇到瓶颈

专库专用,数据垂直拆分

  垂直拆分的意思就是把数据库中不同的业务数据拆分到不同的数据库中。

  不同业务的数据从原来的一个数据库中拆分到多个数据库中,就需要考虑如何处理原来单机中跨业务的事务。一种办法是使用分布式事务,其性能明显要低于单机事务;另一种办法就是去掉事务或者不去追求强事务支持。对数据库进行垂直拆分之后,解决了把所有业务数据放在一个数据库的压力问题。并且也可以根据不同的业务特点进行优化。

垂直拆分后的单机遇到瓶颈,数据库水平拆分

  数据库的水平拆分就是把同一个表中的数据拆分到两个数据库中。产生数据水平拆分的原因是某个业务的数据表的数据量或者更新量达到了单个数据库的瓶颈,这是就可以把这个表拆分到两个或者多个数据库中。数据库水平拆分会给业务带来一些影响:首先,要解决SQL路由的问题;其次主键的处理也会变得不同;最后由于同一个业务数据被拆分到了不同的数据库中,因此一些数据查询需要从两个数据库中读取数据,如果数据量较大需要分页就会比较难以处理。

7. 数据库问题解决之后,应用面对的挑战

拆分应用

  随着业务的发展,应用的功能越来越多,应用也会越来越大,这是需要把应用拆开,从一个应用变为两个甚至多个应用。

走服务化的路

  业务之间的访问不仅是单机内部的方法调用了,还引入了远程的服务调用;其次共享的代码不再是散落在不同的应用中了,这些实现被放在各个服务中心。

时间: 2024-12-08 11:47:20

《大型网站系统与JAVA中间件实践学习笔记》-1的相关文章

[c/c++] programming之路(17)、高级指针

一.二级指针 二级指针的作用:1.函数改变外部变量指针2.外挂改变一个指针的值 1 #include<stdio.h> 2 #include<stdlib.h> 3 4 void main(){ 5 int a = 10; 6 int b = 20; 7 int *p1 = &a; 8 int *p2 = &b; 9 int **pp = &p1; 10 printf("%d,", **pp); 11 printf("\n%x,

C++学习笔记----2.4 C++引用在本质上是什么,它和指针到底有什么区别

从概念上讲.指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变. 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量). 在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的: 指针传递参数本质上是值传递的方式,它所传递的是一个地址值.值传递过程中,被调

【转】父类子类指针相互转换问题

1.当自己的类指针指向自己类的对象时,无论调用的是虚函数还是实函数,其调用的都是自己的: 2.当指向父类对象的父类指针被强制转换成子类指针时候,子类指针调用函数时,只有非重写函数是自己的,虚函数是父类的: 3.当指向子类对象的子类指针被强制转换成父类指针的时候,也就是父类指针指向子类对象,此时,父类指针调用的虚函数都是子类的,而非虚函数都是自己的. 将上面三句话总结成一句话就是:当父类子类有同名非虚函数的时候,调用的是转换后的指针类型的函数: 当父类子类有同名虚函数的时候呢,调用的是指针转换前指

C++ Primer 学习笔记与思考_7 void和void*指针的使用方法

(一)void的含义 void的字面意思是"无类型",void差点儿仅仅有"凝视"和限制程序的作用,由于从来没有人会定义一个void变量,让我们试着来定义: void a; 这行语句编译时会出错.提示"illegal use of type 'void'".只是.即使void a的编译不会出错.它也没有不论什么实际意义. void真正发挥的作用在于: (1) 对函数返回的限定: (2) 对函数參数的限定. int f(void); equal t

当this指针成为指向之类的基类指针时,也能形成多态

this指针: 1)对象中没有函数,只有成员变量 2)对象调用函数,通过this指针告诉函数是哪个对象自己谁. 1 #include<iostream> 2 using namespace std; 3 class Shape 4 { 5 public: 6 //void cal_display(Shape* this) 7 void cal_display(){ 8 display(); 9 this->display(); 10 } 11 private: 12 virtual vo

指针x(超简单的x)

指针! 1 #include<cstdio> 2 #include<iostream> 3 4 using namespace std; 5 6 /* 7 相加或者相乘 8 */ 9 10 int main() 11 { 12 int a,b,s,t; 13 cin>>a>>b; 14 int *p; 15 p=&a; 16 int *q; 17 q=&b; 18 s=(*p)+(*q); 19 t=(*p)*(*q); 20 printf(

二重指针实现排序

1 //双指针对十个数排序 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #define N 8 6 //2017.3.5 7 int main() 8 { 9 //初始化数组 赋值N个数 用指针数组指向 10 int num[N]; 11 int *pNum[N]; 12 int **p = NULL; 13 for (int i = 0; i < N; i++) 14 { 15 num[i] = rand() % 100;/

c指针-专题

六---指针 内存和地址怎么理解呢? 机器中有一些位置,每一个位置被称为[字节]/byte,许多现代机器上,每个字节包含8个位.更大内存单位[字],通常包含2个或4个字节组成. 一个字包含4个字节,它的地址是什么? 他仍然只有一个地址,是最左边还是最右边的那个字节的位置,取决于机器. 机器事实-关于整型的起始位置: 在要求边界对齐(boundaryalignment)的机器上,整型存储的起始位置只能是某些特定的字节,通常是2或4的倍数. 变量名和地址关系? 所有高级语言的特性之一,就是通过名字而

智能指针的原理和简单实现

什么是智能指针? 智能指针实质上是一个类,定义一个类来封装资源的分配和释放.这个类的构造函数中传入一个指针,完成资源的分配和初始化.在析构函数中释放传入的该指针,完成资源的释放. 为什么要用智能指针? 智能指针就是智能,自动化的管理指针所指向的动态资源. 例如以下情况:代码中经常会忘记释放动态开辟的内存资源,导致内存泄露. // case1 void Test2() {  int* p1 = new int(2);  bool isEnd = true;  //...  if (isEnd)  

单继承与多继承中的虚函数表和虚函数指针

首先,我们了解一下何为单继承,何为多继承?? 单继承:一个子类只有一个直接父类. 多继承:一个子类有两个或多个直接父类. 单继承中的虚函数表分析: 示例程序: #include <iostream> using namespace std; typedef void(*FUNC)(); class Base { public: virtual void func1() { cout << "Base::func1()" << endl; } virt