伙伴匹配系统(移动端 H5 网站(APP 风格)基于Spring Boot 后端 + Vue3 - 03
项目地址:
- Github:https://github.com/China-Rainbow-sea/yupao
- Gitee:https://gitee.com/Rainbow--Sea/yupao
开发用户修改页面:后端
开发用户登录功能:后端
to do
更新 脱敏,bean(注意没有映射,以及逻辑删除注解),以及 xml
前端:抽象通用列表组件
批量导入数据 | 多种方案介绍对比
尽量不要一次性,导入太多数据,而是一点一点的加量导入数据。比如:10,100,1000,10000
导入数据
- 用可视化界面:适合一次性导入,数据量可控。
- 写程序: for 循环,建议分批,不要一把梭哈(可以用接口来控制),要保证可控,幂等,注意:线上环境和测试环境是有区别的。
编写一次性任务:
for 循环插入数据的问题:
- 建立和释放数据连接:
- for 循环是绝对线性的:(并发)
补充:Spring 启动定时任务,@EnableScheduling // 启动定时任务 - @EnableScheduling // 启动定时任务
复制代码- package com.rainbowsea.yupao;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.scheduling.annotation.EnableScheduling;
- @SpringBootApplication
- @MapperScan("com.rainbowsea.yupao.mapper")
- @EnableScheduling // 启动定时任务
- public class YuPaoApplication {
- public static void main(String[] args) {
- <template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> SpringApplication.run(YuPaoApplication.class, args);
- }
- }
复制代码 并发编程(多线程池)
并发要注意执行的先后顺序无所谓,不要用到非并发类的集合(eg: 不要用 List 默认是线程不安全的),要转成线程安全的集合。
for 循环插入数据的特点:
- 频繁建立和释放数据库连接(用批量查询解决)
- for 循环是绝对线性的(可以并发提速)
使用简单的 for 循环进行一个并发提速插入,批量元素内容。使用定时任务,完成并发处理采用异步执行,并发执行的方式注意:使用并发时要注意数据的插入先后顺序是否无所谓。
并发时不要用到非并发类的集合。
建立执行器(线程池),自己创建线程池,进行一个获取自己创建的线程池,执行并发,获取线程池连接。- private ExecutorService executorService = new ThreadPoolExecutor(16, 1000, 10000, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10000));
复制代码 连接池参数设置:- // CPU 密集型:分配的核心线程数 = CPU - 1
- // IO 密集型:分配的核心线程数可以大于 CPU 核数
复制代码 页面加载太慢
- 优化:数据库慢,用户不登录,用户查看的数据是一样的,既然是一样的,我们就不需要重复查多次了,那我们就可以预先把数据查出来,放到一个更快读取的地方,不用再查数据库了。缓存。为了防止第一个用户,查询的时候,没有走缓存——>预加载缓存,定时更新缓存。(可以采用定时任务)
- 多个机器都要执行任务呢?(分布式锁:控制同一时间只有一台机器去执行定时任务,其他机器不用重复执行了)
- 比较慢的地方,可以想想可不可以复用,减少慢的次数。
多个数据,前端显示,进行分页处理,如果一下子将 10W 个数据信息给前端进行渲染,前端是无法渲染处理的,是会直接报错处理的,所以这里进行一个 MyBatis-Plus 的一个分页插件进行处理,运行观察执行的 SQL 日志,并不是直接一次性将 10W / 所有数据查询出来,而是通过一个分页查询,查询其中所需的一部分,发送给前端,提高查询效率,同时避免前端接收过多数据,渲染加载数据失败。同时需要在 在 Spring Boot 项目中,你可以通过 Java 配置来添加分页插件:
- @Configuration
- @MapperScan("scan.your.mapper.package")
- public class MybatisPlusConfig {
- /**
- * 添加分页插件
- */
- @Bean
- public MybatisPlusInterceptor mybatisPlusInterceptor() {
- <template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
- <template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
- <template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
- <template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> return interceptor;
- }
- }
复制代码
前端添加上相关,分页携带的数据信息:
- <template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template>
复制代码 运行观察执行的 SQL 日志,并不是直接一次性将 10W / 所有数据查询出来,而是通过一个分页查询,查询其中所需的一部分,发送给前端,提高查询效率,同时避免前端接收过多数据,渲染加载数据失败。
- 首先,模拟插入 50W 条数据,测试。这里我们创建一个用于测试的数据库名为 "usercentertest"
- create table tag( id<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> bigint auto_increment comment 'id'<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> primary key, tagName varchar(256)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '标签名称', userId bigint<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '用户id', parenId bigint<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '父标签 id', isParent tinyint<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '0 - 不是, 1- 父标签', createTime datetime default CURRENT_TIMESTAMP null comment '创建时间', updateTime datetime default CURRENT_TIMESTAMP null comment '更新时间', isDelete tinyint default 0<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> not null comment '是否删除 0 1(逻辑删除)', constraint uniIdx_tagName<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> unique (tagName)) comment '标签';create index idx_userId on tag (userId);create table user( username varchar(256)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '用户昵称', id<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> bigint auto_increment comment 'id'<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> primary key, userAccount varchar(256)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '账号', avatarUrl varchar(1024)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '用户头像', gender<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> tinyint<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '性别', userPassword varchar(512)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> not null comment '密码', phone<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> varchar(128)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '电话', email<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> varchar(512)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '邮箱', userStatus int<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template>default 0<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> not null comment '状态-0-正常', createTime datetime default CURRENT_TIMESTAMP null comment '创建时间', updateTime datetime default CURRENT_TIMESTAMP null comment '更新时间', isDelete tinyint default 0<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> not null comment '是否删除 0 1(逻辑删除)', userRole int<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template>default 0<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> not null comment '用户角色 0- 普通用户 1 - 管理员 2 - vip', planetCode varchar(512)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '用户的编号', tags<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> varchar(1024)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '标签列表 json', profile<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template>varchar(512)<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template><template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> null comment '个人简介') comment '用户';
复制代码 这里我们利用 for 批量插入 50W 条数据,注意:对于这种大数据插入的测试,必须分组,分批进行,防止插入时中间发生报错处理。访问前端,查看前端是否会因为 50 W 数据,渲染加载失败。
这里未使用 MyBatis-Plus 进行分页查询,前端渲染失败。

使用了 MyBatis-Plus 分页插件,测试的效果
关于:页面加载太慢的问题,如下:使用缓存和分布式缓存策略,解决。
缓存和分布式缓存
数据查询慢怎么办?
- 用缓存:提前把数据取出来保存好(通常保存到读写更快的介质,eg 内存),就可以更快地读写。
缓存:
- Redis(分布式缓存)
- memcached(分布式)
- Etcd( 云原生架构的一个分布式存储,存储配置,扩容能力)
进程(单机)缓存
- ehcache
- Java 内存集合,如 HashMap
- Caffeine ( Java 内存缓存性能之王,高性能)
- Google Guava
Java 操作 Redis
Spriing Data Redis(推荐)
地址:https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis
Spring Data : 通用的数据访问框架,定义了一组 增删改查的接口。
还可以操作:MySQL,Redis,JPA
使用方式如下:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- spring-boot-starter-data-redis</artifactId>
- <version>2.6.4</version>
- </dependency>
复制代码 也可以添加上:Lettuce是一个高级redis客户端,支持高级的redis特性,比如Sentinel、集群、流水线、自动重新连接和redis数据- <dependency>
- <groupId>io.lettuce</groupId>
- lettuce-core</artifactId>
- <version>6.1.6.RELEASE</version>
- </dependency>
复制代码- spring:
- # redis 配置
- redis:
- port: 6379
- host: localhost
- database: 0 # 显式使用的是 Redis 0 号数据库
复制代码 Redis 数据结构:
- String 字符串类型:name:"yupi"
- List 列表:names:["yupi","dogyupi"]。 List 和 数组的区别。数组的大小是固定的,而 List 大小是动态的
- Set 集合:names:["yupi","dogyupi"](值不重复)
- Hash 哈希:nameAge:
- Zset 集合:names: '{yupi - 9,dogyupi - 12 }' 适合排行榜
高级:
- bloomfilter (布隆过滤器,主要从大量的数据中快速过滤值,比如邮件黑名单拦截)
- geo(计算地理位置)
- hyperloglog (pv / uv)
- pub / sub(发布订阅,类似消息队列)
- BitMap (100101010101010101010101)
引入一个新库时,记得先写测试类。
测试:使用成功连接上了 Redis ,是否可以成功操作 Redis 数据库Reids 自定义序列化:RedisTemplateConfig
为了防止写入的 Redis 的数据乱码,浪费空间等,可以自定义序列化器。示例代码如下:- package com.yupi.yupao.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.RedisSerializer;@Configurationpublic class RedisTemplateConfig { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> RedisTemplate redisTemplate = new RedisTemplate();<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> redisTemplate.setConnectionFactory(connectionFactory);<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> redisTemplate.setKeySerializer(RedisSerializer.string());<template>
- <user-card-list :user-list="userList"/>
-
- <van-empty v-if="!userList || userList.length < 1" description="数据为空"/>
- </template> return redisTemplate; }}
复制代码
设计缓存 key
不同用户看到的数据不同。
建议格式:
systemID:moduleID:funcu: (注意:就是不要和别的冲突,因为可能存在多个人/项目共用一个 Redis 缓存)
eg:这里我们设计:**yupao:user:recommed:userId**
**注意:Redis 内存不能无限增加,一定要设置过期时间。Redis 内存是有限的,而且在服务器当中特别贵。非常重要<strong><strong>
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |