Loading...
墨滴

遇见0和1

2021/12/30  阅读:56  主题:全栈蓝

项目性能调优

项目性能调优

除了采用分库分表、分布式系统之外,个人所熟知的还有以下一些基本的项目调优方案:

1 数据库调优

以下主要是针对MySQL的调优方案,Oracle有些许区别

1.1 合理使用主键自增

对于频繁涉及到插入、更新或删除数据的表,避免使用主键。MySQL的主键相当于一个不允许存在NULL值的唯一索引,在执行以上操作同时还需要耗费时间来处理主键约束。这种情况可考虑在程序中使用时间戳+随机数、UUIDSnowFlake(雪花算法)等方式生成一个唯一ID。

1.2 合理创建索引和表分区

对于大数据量的清单表,若除了查询外该表基本没有其他操作,可考虑创建表分区或以条件字段创建索引来提升查询效率,尤其是在查询语句当中包含有max()min()order by这些命令的时候,性能提高更为明显。

1.3 使用连接查询(JOIN)来代替子查询

子查询是使用select语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在他的父查询中。 连接(JOIN)查询MySQL不需要在内存中创建临时表来完成这个逻辑上两个步骤的查询工作。

1.4 不使用NOT IN和<>操作

not in和<>操作都不会使用索引,将进行全表扫描。not in可以用not exists代替,id<>3则可使用id>3 or id<3来代替 Tip:!= , <> , not in 这三种语法都会忽略掉null值

1.5 MySQL参数调优

主要调整MySQL的innodb_buffer_pool_instancesinnodb_buffer_pool_size这两个参数分别代表 缓冲池的数量缓冲池的大小 。缓冲池的数量默认为1,若MySQL存在高并发和高负载访问,设置为1则会造成大量线程对BUFFER_POOL的单实例互斥锁竞争,该参数大小可设置为CPU核心数。缓冲池的大小通常配置为物理内存的50%左右,并且需要为缓冲池数量的整数倍,可以被pool_instance整除,为每个buffer_pool实例平均分配内存。

innodb_buffer_pool_size=256G
innodb_buffer_pool_instances=32

1.6 程序中使用批量插入代替循环插入

要插入多条数据时,避免使用循环来一条一条的插入,因为每一次遍历执行都需要取得一次数据库连接再去执行插入语句,可将list数据全部传入mapper层执行数据批量插入。

<insert id="insertTargetValue">
    insert into tb_rpt_target_value_temp (
        user_id,
        stat_month,
        report_name,
        branch_company,
        grid_name,
        target_month,
        target_day,
        create_time
    )
    values
    <foreach collection="targetList" item="item" open="" close="" separator="," index="index">
        (
            #{item.userId},
            #{item.statMonth},
            #{item.reportName},
            #{item.branchCompany},
            #{item.gridName},
            #{item.targetMonth},
            #{item.targetDay},
            now()
        )
    </foreach>
</insert>

2 JVM参数调优

在进行JVM调优时主要是调整以下五个JVM参数:

-vmargs -Xms256m -Xmx512m -XX:MaxNewSize=512m -XX:PermSize=256M -XX:MaxPermSize=512M
  • -Xms256m JVM初始分配的堆内存
  • -Xmx512m JVM最大允许分配的堆内存
  • -XX:MaxNewSize=512m JVM堆区域新生代内存的最大可分配大小
  • -XX:PermSize=256M JVM初始分配的非堆内存
  • -XX:MaxPermSize=512M JVM最大允许分配的非堆内存

JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;

IDEA开发时可在运行按钮旁边的Edit configurations,在VM option上配置JVM参数。生产环境中,可在jar包的启动脚本中配置JVM参数。

//导致异常的原因:JVM堆内存不足
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

在启动脚本中配置示例:

#!/bin/bash
nohup java -jar -Xms1024m -Xmx9120m registerTest.jar &

tail -f nohup.out

启动脚本中nohup 表示不挂断运行,&表示以后台方式运行

在Linux中,可按如下步骤定位内存资源占用高的应用进行优化.

# 查看内存占用情况
free -h

# 查看各个应用的内存占用情况,找到内存占用过高的pid进程
top

# 根据pid查询进程以及项目路径
ps -aux |grep -v grep|grep 23370

3 Tomcat调优

Tomcat的参数调整,在项目优化中自我感觉效果不大这里不再详细介绍,就以一个yml中的配置示例说明一下

server:
  tomcat:
    # 等待队列长度,默认100
    accept-count: 1000
    # 最大连接数默认8192(在同一时间,tomcat能够接受的最大连接数)
    max-connections: 8192
    threads:
      # 最大线程数,默认200(最大线程数决定了Web服务容器可以同时处理多少个请求)
      max: 200
      # 最小工作空闲线程数,默认10, 适当增大一些,以便应对突然增长的访问量
      min-spare: 100
    # 默认2MB,-1
    max-http-form-post-size: 2MB+

4 NGINX负载均衡

nginx负载均衡是应对高并发,提升单体应用响应效率最常用的解决方式之一。其提供的负载均衡策略有内置策略和扩展策略两种,内置策略分为轮询权重iphash。第三方的扩展策略主要有fair,urlhash最少连接等,这里主要介绍官方的内置策略

4.1 轮询

轮询是Nginx负载默认的方式,所有请求都按照时间顺序分配到不同的服务上,如果某台服务Down掉,会自动剔除

如下示例在172.20.20.66主机的9001和9002端口部署了两套服务,login-register下的请求会根据register-server-test这个服务名将请求分发到这两套服务中

http {

  ******

  # 两台服务-负载均衡
  upstream register-server-test {
      server    172.20.20.66:9001;
      server    172.20.20.66:9002;
  }

  server {
      listen       4645;
      server_name  localhost;
      access_log  logs/host.access.log  main;

      location / {
          root   html;
          index  index.html index.htm;
      }

      location /login-register {
          # register-server-test:负载均衡服务名
          proxy_pass http://register-server-test/;
          proxy_set_header Host $host;  
          proxy_set_header X-Real-IP $remote_addr;  
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
          #该参数解决nginx 504错误 (连接超时)
          proxy_connect_timeout   3000; 
          proxy_send_timeout      3000; 
          proxy_read_timeout      3000;
          # Tip:以timeout结尾配置项时间要配置大点
          proxy_buffer_size       16k; 
          proxy_buffers           464k; 
          proxy_busy_buffers_size 128k; 
          proxy_temp_file_write_size 128k;
          proxy_ignore_client_abort on;
      }

  }

}

4.2 权重

weight:指定每个服务的权重比例,weight和访问比率成正比,通常用于后端服务器性能不均的情况,将性能好的分配权重高来发挥服务器最大性能,如下配置9002服务的访问比率会是9001服务的二倍。

upstream  register-server-test {
   server    172.20.20.66:9001 weight=1;
   server    172.20.20.66:9002 weight=2;
}

4.3 iphash

ip_hash:每个请求都根据访问ip的hash结果分配,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。

upstream  register-server-test {
   ip_hash; 
   server  172.20.20.66:9001;
   server  172.20.20.66:9002;
}

4.4 fair

fair:按照服务器响应时间的长短来进行分发,服务器响应时间越短的,优先分发。

upstream  dalaoyang-server {
    server  172.20.20.66:9001;
    server  172.20.20.66:9002;
    fair;
}

4.5 最少连接

least_conn:根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求 ​

upstream  dalaoyang-server {
    least_conn;
    server  172.20.20.66:9001;
    server  172.20.20.66:9002;
}

注意:nginx有自己的容错机制,nginx的超时重试就是容错的一种,默认情况下,当请求服务器发生错误或超时的时候,请求会尝试发到下一台服务器。

这种机制在查询操作还是挺好的,如果是插入操作,那就有问题了,如果这条插入的请求特别耗时,并且时间超过Nginx的proxy_connect_timeout时间设置,Nginx会自动将该请求转发到另外一台服务器,这样容易重复插入同一条数据。

nginx这种机制可以配置proxy_next_upstream off;关闭,但是关闭后会影响Nginx的效率,最好的解决方案就是专门针对耗时长的插入操作接口做一下过滤,即在Nginx的server配置标签中,专门对几个特定的URL过滤,关闭Nginx的重试机制,配置如下

server {
       location ~ /test-api/insertData {
           proxy_connect_timeout 60;
           proxy_send_timeout 60;
           proxy_read_timeout 60;
           proxy_next_upstream off;
        }
 }

遇见0和1

2021/12/30  阅读:56  主题:全栈蓝

作者介绍

遇见0和1