Week 10

发布时间 2023-11-27 09:59:40作者: 鹏懿如斯

week 10

高级篇

性能优化

减少锁粒度

在多线程编程中,如果多个线程需要访问共享资源,通常需要使用锁来保证资源的安全访问。但是,如果锁的粒度太粗,会导致线程之间的竞争过于激烈,从而影响程序的性能。

以下是使用减少锁粒度进行性能优化的主要方式:

  1. 细粒度锁:将共享资源的访问权限限制在一个尽可能小的范围内,从而减少多个线程同时访问共享资源的概率。例如,可以将一个共享变量拆分成多个独立的变量,并分别上锁,从而减少锁的粒度。
  2. 分段锁:将共享资源分成多个段,并对每个段分别上锁。这种方式可以减少多个线程同时访问同一份资源的概率,从而降低锁的竞争。
  3. 动态锁:根据实际情况动态地调整锁的粒度。例如,在某些情况下,可以将多个操作组合成一个原子操作,从而减少锁的使用。
  4. 无锁算法:使用无锁算法来避免使用锁。无锁算法通常使用乐观锁、CAS操作等技术来实现并发控制,从而避免使用锁带来的开销。

需要注意的是,减少锁粒度虽然可以提高程序的并发性能,但是也可能会增加程序的复杂性和代码量。因此,在进行性能优化时,需要根据实际情况进行适当的权衡和选择。同时,还需要注意避免过度优化导致代码可读性和维护性下降的问题。

数据压缩

可以减少数据的存储空间和传输时间,从而提高程序的性能。在某些情况下,数据压缩还可以减少CPU的计算量,从而提高程序的响应速度。

以下是使用数据压缩进行性能优化的主要方式:

  1. 压缩数据存储:将数据按照一定的算法进行压缩,然后存储到磁盘或内存中。这样可以在不损失数据精度的情况下减少数据的存储空间,从而减少存储设备的成本和IO操作次数。
  2. 压缩数据传输:在数据传输过程中,使用压缩算法对数据进行压缩,然后进行传输。这样可以在不损失数据精度的情况下减少数据的传输时间,从而提高程序的响应速度。
  3. 压缩算法选择:根据实际情况选择合适的压缩算法。不同的压缩算法有着不同的压缩比和解压速度,因此需要根据实际需求进行选择。
  4. 压缩与解压优化:对于需要频繁进行压缩和解压操作的数据,需要优化压缩和解压算法,以提高程序的性能。例如,可以使用缓存技术来减少重复压缩和解压的开销,或者使用多线程技术来并行处理压缩和解压操作。

需要注意的是,数据压缩虽然可以提高程序的性能,但是也可能会增加程序的复杂性和代码量。因此,在进行性能优化时,需要根据实际情况进行适当的权衡和选择。同时,还需要注意避免过度优化导致代码可读性和维护性下降的问题。

结果缓存

可以避免重复计算,从而提高程序的性能。在某些情况下,结果缓存可以避免CPU的计算量,从而提高程序的响应速度。

以下是使用结果缓存进行性能优化的主要方式:

  1. 内存缓存:将计算结果存储在内存中,当需要再次使用时,直接从内存中获取。这样可以避免重复计算,从而提高程序的性能。
  2. 磁盘缓存:将计算结果存储在磁盘上,当需要再次使用时,从磁盘中读取。这种方式适用于不经常修改的数据,可以避免重复计算,提高程序的性能。
  3. 缓存失效机制:设置缓存的失效时间,当缓存中的数据过期时,自动删除并重新计算。这样可以保证缓存数据的准确性,避免因为数据过期导致的错误。
  4. 缓存查询优化:对于频繁修改的数据,需要优化缓存查询算法,以提高程序的性能。例如,可以使用布隆过滤器等技术来快速判断数据是否存在,或者使用分布式缓存系统来分担负载。

需要注意的是,结果缓存虽然可以提高程序的性能,但是也可能会增加程序的复杂性和代码量。因此,在进行性能优化时,需要根据实际情况进行适当的权衡和选择。同时,还需要注意避免过度优化导致代码可读性和维护性下降的问题。

Stream并行流

利用多核CPU的优势,提高程序的并行处理能力。在Java 8之后,Stream API提供了并行流和串行流两种方式,其中并行流可以利用多核CPU的优势,加快数据处理速度。

以下是使用Stream并行流进行性能优化的主要方式:

  1. 并行流转换:将Stream转换为并行流,然后进行数据处理。这样可以利用多核CPU的优势,加快数据处理速度。但是需要注意,并行流并不一定适用于所有情况,需要根据实际情况进行选择。
  2. 并行操作:在并行流中使用并行操作,如parallelFilter()parallelMap()等,可以同时处理多个数据,提高程序的并行处理能力。
  3. 线程池设置:在使用并行流时,可以设置线程池的大小,以充分利用多核CPU的优势。线程池太小或太大都会影响程序的性能,因此需要根据实际情况进行设置。
  4. 数据分布均匀:在使用并行流时,需要保证数据分布均匀,避免出现数据倾斜的情况。如果数据分布不均匀,会导致部分线程处理过多数据,而其他线程处理数据较少,从而影响程序的性能。
  5. 关闭流操作:在使用并行流时,需要关闭流操作,以释放资源。如果不关闭流操作,会导致资源泄漏和其他问题。

需要注意的是,使用并行流虽然可以提高程序的并行处理能力,但是也需要注意线程安全和数据一致性问题。同时,也需要根据实际情况进行适当的权衡和选择,避免过度优化导致代码可读性和维护性下降的问题。

import java.util.ArrayList;  
import java.util.List;  
import java.util.Random;  
import java.util.stream.Collectors;  
  
public class StreamParallelExample {  
    public static void main(String[] args) {  
        int size = 10000; // 数据量大小  
        List<Integer> numbers = new ArrayList<>(); // 存储随机数的列表  
  
        // 生成随机数列表  
        for (int i = 0; i < size; i++) {  
            numbers.add(new Random().nextInt(100)); // 生成0~99之间的随机数  
        }  
  
        // 使用Stream并行流进行性能优化  
        long startTime = System.currentTimeMillis();  
        List<Integer> result = numbers.parallelStream() // 将列表转换为并行流  
                                   .filter(num -> num > 50) // 使用并行过滤操作,筛选出大于50的数  
                                   .map(num -> num * 2) // 使用并行映射操作,将每个数乘以2  
                                   .collect(Collectors.toList()); // 将结果收集到新的列表中  
        long endTime = System.currentTimeMillis();  
  
        System.out.println("处理结果:" + result); // 输出处理结果  
        System.out.println("处理时间:" + (endTime - startTime) + "ms"); // 输出处理时间  
    }  
}

在上述代码中,我们首先生成了一个包含10000个随机数的列表,然后使用parallelStream()方法将其转换为并行流,并依次进行了过滤和映射操作,最后将结果收集到新的列表中。由于使用了并行流,这些操作可以并行执行,从而提高了程序的性能。

GC调优

Java GC(Garbage Collection,垃圾收集)调优是指通过调整Java虚拟机(JVM)的垃圾收集器参数,以优化程序的性能和资源利用率。以下是一些常见的Java GC调优建议:

  1. 了解GC机制:了解Java GC的工作原理和各种垃圾收集器的特点,以便选择适合自己应用程序的垃圾收集器。
  2. 调整堆大小:根据应用程序的需求和系统资源,调整堆的大小。如果堆过大,会导致内存浪费和频繁的GC;如果堆过小,会导致内存不足和频繁的OutOfMemoryError。
  3. 选择合适的垃圾收集器:根据应用程序的特点和需求,选择适合的垃圾收集器。例如,对于CPU密集型应用程序,可以选择Concurrent Mark Sweep(CMS)收集器;对于内存敏感的应用程序,可以选择Serial收集器。
  4. 设置垃圾收集日志:开启垃圾收集日志,以便监控和诊断GC问题。可以通过在启动JVM时添加相关参数来启用垃圾收集日志。
  5. 避免内存泄漏:确保程序中没有内存泄漏。内存泄漏会导致内存占用不断增加,最终导致OutOfMemoryError。
  6. 合理使用finalize方法:如果需要释放对象持有的资源,可以使用finalize方法。但是,必须注意finalize方法的执行时机和执行次数,以避免影响程序性能。
  7. 使用WeakReference和SoftReference:对于不需要长时间持有的对象,可以使用WeakReference或SoftReference代替强引用。这样可以让垃圾收集器在合适的时候释放对象占用的内存。
  8. 避免不必要的对象创建:过多的对象创建会增加GC的负担,因此应该尽量避免不必要的对象创建。
  9. 使用并发集合:如果程序中需要使用集合类,应该优先考虑使用并发集合,以避免在多线程环境下出现竞争条件和死锁等问题。
  10. 监控和诊断:使用工具监控和诊断JVM的性能和GC情况,以便及时发现问题并进行调优。常见的工具包括JConsole、VisualVM和GCViewer等。

总之,Java GC调优是一个复杂的过程,需要根据应用程序的特点和需求进行适当的调整。在进行GC调优时,应该先了解GC机制和各种垃圾收集器的特点,然后根据实际情况选择合适的参数进行调整,并监控和诊断程序的性能和GC情况,以便及时发现问题并进行调优。

JVM内存分配调优

JVM内存分配调优主要是指根据应用程序的需求和系统资源,合理地分配和调整JVM的内存空间,以提高程序的性能和资源利用率。以下是一些常见的JVM内存分配调优建议:

  1. 了解内存模型:了解JVM的内存模型和各种内存区域的特点,以便合理地分配内存空间。
  2. 调整堆大小:根据应用程序的需求和系统资源,调整堆的大小。如果堆过大,会导致内存浪费和频繁的GC;如果堆过小,会导致内存不足和频繁的OutOfMemoryError。
  3. 选择合适的垃圾收集器:根据应用程序的特点和需求,选择适合的垃圾收集器。例如,对于CPU密集型应用程序,可以选择Concurrent Mark Sweep(CMS)收集器;对于内存敏感的应用程序,可以选择Serial收集器。
  4. 设置堆参数:根据应用程序的需求和系统资源,设置堆的相关参数,如初始堆大小、最大堆大小、堆增长方式等。
  5. 调整线程栈大小:根据应用程序的线程数量和线程栈的使用情况,调整线程栈的大小。如果线程栈过小,会导致线程切换频繁和性能下降;如果线程栈过大,会导致内存浪费和频繁的OutOfMemoryError。
  6. 禁用无用类加载器:如果应用程序中不需要使用自定义类加载器,应该禁用自定义类加载器,以提高JVM的启动速度和性能。(java -XX:+DisableExplicitGC -jar xxx.jar)
  7. 使用压缩指针:如果应用程序中需要使用大量对象,而且对象的大小小于32字节,可以使用压缩指针来节省内存空间。(java -XX:+UseCompressedOops -jar xx.jar)通过使用压缩指针,可以减少每个对象的内存占用,并提高内存的使用效率。
  8. 使用JVM监视工具:使用JVM监视工具来监控和诊断JVM的性能和内存情况,以便及时发现问题并进行调优。常见的工具包括JConsole、VisualVM和GCViewer等。

总之,JVM内存分配调优是一个复杂的过程,需要根据应用程序的特点和需求进行适当的调整。在进行内存分配调优时,应该先了解JVM的内存模型和各种内存区域的特点,然后根据实际情况选择合适的参数进行调整,并监控和诊断JVM的性能和内存情况,以便及时发现问题并进行调优。

SQL调优

常见的SQL调优方法:

  1. 优化数据模型:根据业务需求和数据特点,选择合适的数据模型和数据库架构,例如分区、索引、视图等。
  2. 优化查询语句:使用更有效的查询语句,例如使用EXPLAIN分析查询计划、避免在WHERE子句中使用非索引字段、使用UNION ALL代替UNION等。
  3. 优化数据库参数:调整数据库参数,例如缓冲区大小、连接数等,以提高数据库性能。
  4. 优化索引:使用合适的索引可以显著提高查询性能,例如在WHERE和ORDER BY子句中使用索引字段。
  5. 优化数据库硬件:升级硬件可以提高数据库性能,例如增加内存、使用更快的CPU和磁盘等。
  6. 优化数据库配置:调整数据库配置,例如调整缓冲区大小、连接数等,以提高数据库性能。
  7. 使用分区表:对于大型表,使用分区可以提高查询性能和管理便利性。
  8. 避免使用子查询:子查询可能会降低查询性能,可以使用JOIN代替子查询。
  9. 使用临时表:对于需要大量复杂计算的查询,可以使用临时表提高查询性能。
  10. 优化存储过程:存储过程是预编译的SQL语句,使用存储过程可以提高查询性能。

数据库

MongoDB

  • 介绍

MongoDB是一个基于分布式文件存储的数据库,由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。它是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

  • 安装

https://www.mongodb.com/try/download/community

可视化:MongoDB Compass

  • 概念
SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持
primary key primary key 主键,MongoDB自动将_id字段设置为主键

Untitled

  • 数据类型
数据类型 描述
String 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean 布尔值。用于存储布尔值(真/假)。
Double 双精度浮点值。用于存储浮点值。
Min/Max keys 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array 用于将数组或列表或多个值存储为一个键。
Timestamp 时间戳。记录文档修改或添加的具体时间。
Object 用于内嵌文档。
Null 用于创建空值。
Symbol 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID 对象 ID。用于创建文档的 ID。唯一主键
Binary Data 二进制数据。用于存储二进制数据。
Code 代码类型。用于在文档中存储 JavaScript 代码。
Regular expression 正则表达式类型。用于存储正则表达式。
  • SpringBoot集成
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring:
  application:
    name: mongodb-demo
  data:
    mongodb:
      uri: mongodb://peng:123456@127.0.0.1:27017/test?authSource=admin&authMechanism=SCRAM-SHA-1
package cn.peng.mongodb.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

/**
 * {@code @Author:} weiyupeng
 * {@code @Date:} 2023/11/25 10:24
 */
@Document(collection = "user")
public class User {

    @Id
    private String id;
    private String name;
    private int age;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}
package cn.peng.mongodb.repo;

import cn.peng.mongodb.model.User;
import org.springframework.data.mongodb.repository.MongoRepository;

/**
 * {@code @Author:} weiyupeng
 * {@code @Date:} 2023/11/25 10:26
 */
public interface UserRepository extends MongoRepository<User, String> {
}
package cn.peng.mongodb.service;

import cn.peng.mongodb.model.User;
import cn.peng.mongodb.repo.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * {@code @Author:} weiyupeng
 * {@code @Date:} 2023/11/25 10:27
 */
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public User saveUser(User user) {
        return userRepository.save(user);
    }

    public List<User> findAllUsers() {
        return userRepository.findAll();
    }

}
package cn.peng.controller;

import cn.peng.mongodb.model.User;
import cn.peng.mongodb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * {@code @Author:} weiyupeng
 * {@code @Date:} 2023/11/25 10:27
 */
@RestController
public class MongoDbTestController {

    @Autowired
    private UserService userService;

    @GetMapping("/mongo/insert")
    public String insert() {
        User user = new User();
        user.setName("peng");
        user.setAge(27);
        userService.saveUser(user);
        return "insert into mongodb success";
    }

    @GetMapping("/mongo/query")
    public List<User> query() {
        return userService.findAllUsers();
    }

}
[
    {
        "id": "65615ec49009af6a060ff72f",
        "name": "peng",
        "age": 27
    },
    {
        "id": "65615ed69009af6a060ff730",
        "name": "peng",
        "age": 27
    },
    {
        "id": "65615ed79009af6a060ff731",
        "name": "peng",
        "age": 27
    }
]