把服务包部署到多台设备
多台机器一起向外提供服务,合并在一起的软件: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文件中, 设置堆栈信息
代码语言:javascript复制JAVA_OPTS="-server -Xms256m -Xmx512m -Xss256k -XX:PermSize=128m"
-server:第一个参数,指定为服务,多核时使用
-Xms:启动时,初始堆大小;没有配置时,从最小逐步增加到最大值
-Xmx:运行时分配的最大堆大小;默认64M
-Xmn:新生代堆大小
-Xss:每个线程栈大小
-XX:PermSize:初始化非内存大小
-XX:MaxPermSize:永久代(非堆)最大内存大小
-XX:MaxNewSize:新生代最大大小
catalina文件中, 设置GC
代码语言:javascript复制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.59去掉了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就会关闭不活动的线程
连接数不够
修改如下内容:
代码语言:javascript复制<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/>
<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" acceptCount="1000"/>
- 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文件夹下
代码语言:javascript复制---
lowercaseOutputLabelNames: true
lowercaseOutputName: true
rules:
- pattern: 'Catalina<type=GlobalRequestProcessor, name="(w -w )-(d )"><>(w ):'
name: tomcat_$3_total
labels:
port: "$2"
protocol: "$1"
help: Tomcat global $3
type: COUNTER
- pattern: 'Catalina<j2eeType=Servlet, WebModule=//([-a-zA-Z0-9 &@#/%?=~_|!:.,;]*[-a-zA-Z0-9 &@#/%=~_|]), name=([-a-zA-Z0-9 /$%~_-|!.]*), J2EEApplication=none, J2EEServer=none><>(requestCount|maxTime|processingTime|errorCount):'
name: tomcat_servlet_$3_total
labels:
module: "$1"
servlet: "$2"
help: Tomcat servlet $3 total
type: COUNTER
- pattern: 'Catalina<type=ThreadPool, name="(w -w )-(d )"><>(currentThreadCount|currentThreadsBusy|keepAliveCount|pollerThreadCount|connectionCount):'
name: tomcat_threadpool_$3
labels:
port: "$2"
protocol: "$1"
help: Tomcat threadpool $3
type: GAUGE
- pattern: 'Catalina<type=Manager, host=([-a-zA-Z0-9 &@#/%?=~_|!:.,;]*[-a-zA-Z0-9 &@#/%=~_|]), context=([-a-zA-Z0-9 /$%~_-|!.]*)><>(processingTime|sessionCounter|rejectedSessions|expiredSessions):'
name: tomcat_session_$3_total
labels:
context: "$2"
host: "$1"
help: Tomcat session $3 total
type: COUNTER
- pattern: ".*"
配置catalina.sh
代码语言:javascript复制JAVA_OPTS="-javaagent:./jmx_prometheus_javaagent-0.14.0.jar=3088:./tomcat.yml"
启动tomcat
配置prometheus
配置prometheus.yml文件
代码语言:javascript复制- job_name:"tomcat_export"
static_configs:
- targets:["被监控机器IP:3088"]
配置grafana
- 添加数据源:URL
http://prometheus_ip:9000
- 引入模版:8563或3457
- 名称:自定义
- job修改为prometheus.yml文件中的job_name
GC分析
https://gceasy.io/
nginx
是一个用C语言编写的高性能HTTP服务器,反向代理web服务器
- 占用内存小
- 并发能力强
image-20210628215410866
代码语言:javascript复制worker_processes 1;
events {
use epoll;
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream web_app {
server 192.168.114.139:8080 weight=1 max_fails=2 fail_timeout=30;
server 192.168.114.139:8880 weight=1 max_fails=2 fail_timeout=30;
}
server {
listen 80;
server_name 192.168.114.139;
location / {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://web_app;
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
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常用配置
对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次数更少
- 每次查询的结果都是在叶子节点,查询性能稳定
- 叶子节点形成链表,范围查询更方便
索引
是一种数据结构,用于帮助我们在大量数据中快速定位我们要查找的数据
建索引:使用空间换时间,索引有一定大小,占磁盘、内存空间,以此来换取时间更少。是为了提升查询数据的速度,它会降低修改速度
主键索引:有且仅有一个
唯一索引:不可重复,但是可以存储NULL
代码语言:javascript复制create unique index 索引名 on 表名(字段);
- 复合索引:由表的多列按照顺序组合成为索引,使用时,按照组合顺序使用索引,也可以使用组合索引中部分索引字段
create index 索引名 on 表名(字段1,字段2....)
代码语言:javascript复制create index index_name on name(id,co3,co2);
select * from tb_name where id=?,co3=? 使用了索引
select * from tb_name where id=?,co2=?
select * from tb_name where co3=?,co2=?
select * from tb_name where co2=?,co3=?
select * from tb_name where id=?,co3=?,co2=? 使用了索引
select * from tb_name where id=?,co2=?,co3=?
select * from tb_name where id=? 使用了索引
索引的弊端
- 索引本身很大,通常存放在磁盘或内存
- 不是索引情况都可以用索引,数据库很少,列值比频繁变更,列很少使用
- 索引会降低增删改的效率,但是一般会提升查的效率
索引的优势
- 降低IO、CPU使用率
- 索引列,可以保证行的唯一性
- 可以有效缩短数据检索时间
- 加快表与表之间的连接
Select
语法
代码语言:javascript复制SELECT {*|字段列名} 查询要显示的列名
FROM <表名1>,<表名2> join, on 数据来源表
WHERE <表达式> 限制条件
GROUP BY<字段> 查询的结果,按照条件字段分组
HAVING <expression> 过滤分组
ORDER BY <字段> 按照字段排序
LIMIT <offset>[<row count>] 显示数据条数
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;
SHOW VARIABLES LIKE '%slow_query_log%' 查看慢查询的开关与日志路径 默认10秒为慢查询
SHOW VARIABLES LIKE 'long_query_time' 慢查询的阈值默认10秒
代码语言:javascript复制SHOW VARIABLES LIKE 'max_connections%' 查看系统配置的最大连接数
SHOW VARIABLES LIKE 'Max_used_connections' 查看当前用户已经建立的连接数
当出现ERROR 1040:Too many connections
可以通过修改连接数来解决
定位慢查询日志
- 开启慢查询的开关
因为,数据中慢查询日志,一般情况下都是关闭的,因为慢查询的开启,就要写日志,会消耗IO。 所以在生产数据库中,建议千万不要去开启
我们用jmeter做性能测试,设计了一个性能场景,运行,发现在一定量的并发用户时,平均响应时间,已经超过了1秒钟,那么,我们可以说,可能存在了慢查询日志。
响应时间都没有超过1秒(阈值),那么肯定没有慢查询日志。
SQL优化
代码语言:javascript复制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
- 主从同步
- 分表分区
主从同步
- 数据同步
- 读写分离
在主数据库中做任何操作,在从数据库中,都会重复一次
在从数据库中修改,主数据库是不会变化的
所以主数据库进行写操作,从数据库进行读操作
可以手动设置同步时间间隔
分表分区
分表
- 拆列:一张表多列,被拆到多张表「垂直分表」
表字段变少,行数不变
- 拆行:一张表某些行,被拆到另外行「水平分表」
表字段不变,行数变少
分区
把数据存到不同地方