30-服务器架构演进
把服务包部署到多台设备
多台机器一起向外提供服务,合并在一起的软件:niginx
多台机器合并在一起:集群
nginx负载均衡,反向代理
nginx里面记录不同机器的ip,配置不同的分配策略
nginx:web服务器,并不一定是一般理解的前台服务
前后分离:front+server
负载均衡
- 最典型的软件是nginx
- 也可以是硬件F5
- 阿里云的SLB,也是一个软件负载均衡
tomcat
需要有jdk
目录
- bin:启动、关闭tomcat的文件。startup、shutdown为启动文件,catalina文件为配置文件
- conf:配置文件。server.xml为最重要的配置文件
- logs:项目运行时,默认日志路径。在没有修改默认日志路径时,运行日志记录在该路径下
- webapps:项目包防止路径。把wat包,放在这个路径下,启动tomcat会自动解压
catalina.bat「Windows」、catalina.sh「Linux」
在执行startup文件启动tomcat时,会去执行catalina文件
catalina文件中, 设置堆栈信息
1 | JAVA_OPTS="-server -Xms256m -Xmx512m -Xss256k -XX:PermSize=128m" |
-server:第一个参数,指定为服务,多核时使用
-Xms:启动时,初始堆大小;没有配置时,从最小逐步增加到最大值
-Xmx:运行时分配的最大堆大小;默认64M
-Xmn:新生代堆大小
-Xss:每个线程栈大小
-XX:PermSize:初始化非内存大小
-XX:MaxPermSize:永久代(非堆)最大内存大小
-XX:MaxNewSize:新生代最大大小
catalina文件中, 设置GC
1 | JAVA_OPTS="-verbose:gc -XX+PrintGC -XX:PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:filename" |
-verbose:gc:显示垃圾回收信息
-XX:+UseParNewGC:设置minor收集的时间
-XX:+UseConcMarkSweepGC:设置major收集时间
- 设置并行收集器:
-XX:ParallelGCThreads=n:设置并行收集器收时使用的CPU数,并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比
- 设置并发收集器:
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数,并行收集线程数
写GC日志,是要消耗IO的,所以在生成环境,一般不配置这个
conf/server.xml
Tomcat中最顶层的是server,代表整个服务器,一个server可以包含至少一个service,每个service可以包含多个connector和一个container。Connector用于处理连接相关的事情,并提供Socket与Request和Response相关的转化;Container用于封装和管理Servlet,以及具体Request请求;多个connector就可以配置多种类型连接,如http、https
不同的protocol
Connector在处理HTTP请求时,会使用不同的protocol,典型的有:
- BIO:BlockingIO阻塞(tomcat7支持,8支持NIO2,8.5\9去掉了BIO)默认值为maxThreads值
- NIO:Non-blockingIO非阻塞IO,默认值10000
- APR:ApachePortableRuntime Apache可移植运行库,是高并发的首选模式;默认值8192,需要安装apr、apr_utils、tomcat-native包
Protocol默认是HTTP/1.1,不同版本,会自动选择上面的模式(APR模式需要有相应的包才会自动选择)
Connector重要参数
- connectionsTimeout:连接超时时间,单位毫秒
- acceptCount:能接收的队列长度,队列满了,再有连接就会拒绝,默认100
- maxConnections:任意时刻能接受和处理的最大连接数。达到最大值,就会阻塞accept连接。如果设置为-1,则连接数不受限制
- maxThreads:请求处理线程的最大数量。默认为200
- minSpareThreads:tomcat初始化默认时默认创建的线程数,也是以后线程增加时一次增加的最小数量
- maxSpareThreads:这个参数标识,一旦创建的线程数量超过了这个值,Tomcat就会关闭不活动的线程
连接数不够
修改如下内容:
1 | <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/> |
name:线程池的标记
namePrefix:线程名字前缀
maxThreads:线程池中最大活跃线程数,默认200
minSpareThreads:线程池中保存的最小线程数,也是线程每次增加的最小值,默认25
connectionTimrout:连接超时时间,单位毫秒
accepCount:最大可接受的排队数量
maxSpareThreads:一旦创建的线程数量超过这个数值,Tomcat就会关闭不活动的线程
tomcat监控环境搭建
gragana + prometheus监控tomcat
下载jvm_exporter.jar
下载地址:https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/
将jvm_exporter.jar
放到tomcat的bin文件夹下
配置tomcat.yml到tomcat的bin文件夹下
1 | --- |
配置catalina.sh
1 | JAVA_OPTS="-javaagent:./jmx_prometheus_javaagent-0.14.0.jar=3088:./tomcat.yml" |
启动tomcat
配置prometheus
配置prometheus.yml文件
1 | - job_name:"tomcat_export" |
配置grafana
- 添加数据源:URL
http://prometheus_ip:9000
- 引入模版:8563或3457
- 名称:自定义
- job修改为prometheus.yml文件中的job_name
GC分析
nginx
是一个用C语言编写的高性能HTTP服务器,反向代理web服务器
- 占用内存小
- 并发能力强
1 | worker_processes 1; |
niginx+tomcat负载均衡
轮询
默认策略,如果服务器down掉了,会自动剔除该服务器,此策略适合服务器配置相当,无状态且短平快的服务使用
weight权重
权重越高分配到需要处理的请求越多,此策略可以与least_conn和ip_hash结合使用,此策略比较适合服务器的硬件配置差别比较大的情况
ip_hash依据ip分配
ip_hash不能与backup同时使用,此策略适合有状态服务,比如session,服务器需要剔除,必须手动down掉
least_conn最小连接
此负载均衡策略适合请求处理时间长短不一造成服务器过载的情况
fair响应时间
负载均衡策略的实现需要安装第三方插件,按照服务器端的响应时间来分配请求,响应时间短的优先分配
url_hash依据url分配
按访问url结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用
niginx常用配置
对niginx监控
使用nginx-module-vts
与nginx-vts-exporter
- nginx-module-vts: Nginx virtual host traffic status module,nginx的监控模块,能够提供json、html、prometheus格式的数据产出。
- nginx-vts-exporter: Simple server that scrapes Nginx vts stats and exports them via HTTP for Prometheus consumption。主要用于收集nginx的监控数据,并给Prometheus提供监控接口,默认端口号9913。
如何测试集群的性能
首先,需要有一个集群,就要安装项目的集群环境搭建的标准,搭建一套集群环境,只是此时,集群规模不需要做那么大,自己搭建一套最小的集群(至少是两个相同服务构成的一个集群)
然后,对该集群进行性能测试,得到最小规模的集群的性能指标
然后,再在集群中,添加服务,此时集群有3个服务,然后再对集群进行一次性能测试,此时3个服务构成的集群的性能指标tps增加了多少,并发用户增加了多少,那么理论上,可以计算出增加服务后tps等指标的增加情况
数据库
数据:描述事物的符号记录,符号可以是数字,文字,图片,图像,声音,语言
数据库:存放数据的仓库,这个仓库是计算机存储设备,而且数据是按一定的格式存放的
在企业项目中,数据库的读操作更频繁
dbms
关系型数据库
采用关系模型来组织数据库的数据,以行+列方式存储数据
- 结构化方式存储数据库
- 标准的结构化查询语句「SQL」标准的增删改查
- 事物性,寻找ACID规则「原子性、一致性、隔离性、持久性」
sql有四种
- DCL数据库控制语言「Data Control Language」用来确认或取消数据库用户和角色权限变更
- DDL数据库定义语言「Data Definition Language」用来创建或删除数据库以及表
- DML数据库操作语言「Data Mainpulation Language」用来变更表数据记录
- DQL数据库查询语言「Data Query Language」用来查询表中的记录
非关系型数据库NoSQL「Not only SQL」
选择数据库需要注意
- IO性能比较好的
- 磁盘空间比较大
- 稳定性
数据库缓存
数据库的缓存有两部分
- 数据库本身的缓存
- 专门做缓存的数据库
数据库拆分
- 直接拆数据库
- 分表分区
存储引擎
mysql数据库建表的时候,有一个存储引擎,可以选择,默认为InnoDB
数据库存储引擎是数据库底层软件组件,数据库管理系统使用数据库引擎进行创建,查询,更新和删除数据操作。
存储引擎就是指表的类型。数据库的存储引擎决定了表在计算机中的存储方式。不同的存储引擎提供不同的存储机制,索引技巧,锁定水平等功能,使用不同的存储引擎还可以获得特定的功能
例如,存在两张表:一个配置表,一个用户表
- 配置表,一般在项目启动的时候,读取一次,在项目运行过程中,一般都不会去修改
- 用户信息表:在项目启动时,不会去读取,但是在项目运行过程中,随时都有可能修改
所以,它们的使用场景不一样,创建表时会选择不同的搜索引擎
配置表一般选用MyISM
存储引擎
用户信息表一般选用InnoDB
mysql数据库5.5版本及以前
- 默认存储引擎为
MyISM
- 追求存储数据的速度,存储数据不准
- 锁,表锁,会锁定整张表
- 索引:B树索引
mysql数据库5.5版本及以后
- 默认存储引擎为
InnoDB
- 追求存储数据的准确性,事务一致性
- 锁,行锁,会锁定当前行
- 索引:默认B+树索引
B+树的优点:
- 树的层次更低,IO次数更少
- 每次查询的结果都是在叶子节点,查询性能稳定
- 叶子节点形成链表,范围查询更方便
索引
是一种数据结构,用于帮助我们在大量数据中快速定位我们要查找的数据
建索引:使用空间换时间,索引有一定大小,占磁盘、内存空间,以此来换取时间更少。是为了提升查询数据的速度,它会降低修改速度
主键索引:有且仅有一个
1
2create index 索引名 on 表名(字段);
ALTER TABLE 表名 ADD INDEX 索引名(字段);唯一索引:不可重复,但是可以存储NULL
1 | create unique index 索引名 on 表名(字段); |
- 复合索引:由表的多列按照顺序组合成为索引,使用时,按照组合顺序使用索引,也可以使用组合索引中部分索引字段
1 | create index 索引名 on 表名(字段1,字段2....) |
1 | create index index_name on name(id,co3,co2); |
索引的弊端
- 索引本身很大,通常存放在磁盘或内存
- 不是索引情况都可以用索引,数据库很少,列值比频繁变更,列很少使用
- 索引会降低增删改的效率,但是一般会提升查的效率
索引的优势
- 降低IO、CPU使用率
- 索引列,可以保证行的唯一性
- 可以有效缩短数据检索时间
- 加快表与表之间的连接
Select
语法
1 | SELECT {*|字段列名} 查询要显示的列名 |
sql语句的执行顺序,与编写顺序会不一致
sql执行过程:
- 输入数据库的ip,端口,账号,密码「连接层」
- 提供各种接口,CRUD,对脚本进行优化「服务层」
- 执行你的sql「引擎层」
- 数据交换「存储层」
数据库管理系统dbms,写数据时,把你的数据,转化为日志文件,对日志文件进行解读,还原你的日志过程
select的解析过程
- from table_name 数据源中捞取数据
- where 条件 对捞取的数据进行条件过滤
- group by 分组
- 根据上面的条件字段来分组「建议where条件字段」
- 不按照上面的条件字段来分组「会产生临时表」
- having 分组过滤
- select 字段
- order by 建议使用select 字段来排序
- limit 数据量
数据库性能优化
- 数据库库层面的优化「os」
- sysctl
- ulimit
- 数据库配置文件
- /etc/my.cnf
- SHOW VARIABLES;
1 | SHOW VARIABLES LIKE '%slow_query_log%' 查看慢查询的开关与日志路径 默认10秒为慢查询 |
1 | SHOW VARIABLES LIKE 'max_connections%' 查看系统配置的最大连接数 |
当出现ERROR 1040:Too many connections
可以通过修改连接数来解决
定位慢查询日志
- 开启慢查询的开关
因为,数据中慢查询日志,一般情况下都是关闭的,因为慢查询的开启,就要写日志,会消耗IO。
所以在生产数据库中,建议千万不要去开启
我们用jmeter做性能测试,设计了一个性能场景,运行,发现在一定量的并发用户时,平均响应时间,已经超过了1秒钟,那么,我们可以说,可能存在了慢查询日志。
响应时间都没有超过1秒(阈值),那么肯定没有慢查询日志。
SQL优化
1 | explain sql语句 |
id:编号
- id相同时,从上往下执行
- id不同时,从大到小
select type:查询语句
- SIMPLE:简单的SELECT,不使用union或子查询
- PRIMARY:查询中包含复杂的子查询,最外层的select被标记为PRIMARY
- UNION:union中第二个或后面的select语句
- DEPENDENT UNION:union中的第二个或后面的select语句,取决于外面的查询
- UNION RESULT:union的结果
- SUBQUERY:子查询的第一个select
- DEPENDENT SUBQUERY:子查询中的第一个select,取决于外面的查询
- DERIVD:衍生查询,使用临时表(select from子句的子查询)
- UNCACHEABLE SUBQUERY:一个子查询的结果不能被缓存,必须重新评估外链接的第一行
table:表
type:类型
- All:全表扫描Full table scan
- index:遍历索引数据 Full index scan
- range:使用一个索引来检索给定范围的行
- ref:使用了索引列上值进行查询
- eq_ref:类似ref,只是使用的索引为唯一索引
- const,system:MySQL对查询某部分进行优化,并转化为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转化为一个常量,system上const类型的特例,当查询的表只有一行的情况下,使用system
- Null:MySQl在优化过程中分解语句,执行时甚至不用访问表或索引
性能效率:system > const > eq_ref > ref > range > index > all 左边效率高于右边
possible_keys:预测用到的索引
key:实际用到的索引
key_len:实际使用索引的长度
ref:表之间的匹配条件
rows:通过索引查询到的数据量
filtered:
Extra:额外的信息
- Using where:显示的字段不在索引(select 的字段,有的不再索引中,要从源table表中查询)
- Using index:使用了索引,不用回表查询,能够起到性能提升
- Using temporary:使用了临时表,性能消耗比较大,常见于group by语句
- Using fileSort:使用文件排序,无法利用索引完成排序操作,性能消耗非常大,常见于order by语句
- Using join buffer:mysql引擎使用了链接缓存
- Impossible where:where子句永远为false
- Select tables optimized away:仅通过使用索引,优化器可能仅从聚合函数结果返回一行
- NULL:
using where 使用where条件过滤,但是where条件不在索引,那我们就要考虑用和where后面的字段来建索引
优化方法
- 在写on语句时,将数据量小的表放在左边
- where后面的条件尽可能用索引字段,复合索引时,最好按复合索引顺序写where条件
- where后面有in语句,in字段的索引,最好放复合索引的后面,因为in的字段索引可能会失效
- 模糊查询时,尽量用常量开头,不要用%开头,用%开头查询索引将失效
- 尽量不要使用or,否则索引失效
- 尽量不要使用类型转化(显式、隐式),否则索引失效
- 如果主查询数据量大,则使用in
- 如果子查询数据量大,则使用exists
- 查询哪些列,就根据哪些列group by,不然会产生一个临时表
库优化
- os配置修改
- 数据库的配置参数
- 数据库 <=> 应用程序 <=> 配置文件
表优化
表存储引擎
表结构(拆表)
表建立索引
- 慢sql:根据分析结果调整索引,开发人员修改自己的sql
主从同步
分表分区
主从同步
- 数据同步
- 读写分离
在主数据库中做任何操作,在从数据库中,都会重复一次
在从数据库中修改,主数据库是不会变化的
所以主数据库进行写操作,从数据库进行读操作
可以手动设置同步时间间隔
分表分区
分表
- 拆列:一张表多列,被拆到多张表「垂直分表」
表字段变少,行数不变
- 拆行:一张表某些行,被拆到另外行「水平分表」
表字段不变,行数变少
分区
把数据存到不同地方