Commit fb6be832 authored by yanzg's avatar yanzg

提交

parent f44b923c
# 开发基础
## 开发6原则
1. SQL编写原则
SQL使用原则能帮助我们快速分析功能该如何去实现业务逻辑,该如何去确定性能问题优化.
2. 缓存使用原则
缓存是对整个系统性能提升最大的技术,但是用不好缓存则达不到提升性能的效果
3. MQ使用原则
MQ是拆分业务逻辑的最好方式,将一个复杂的功能拆分成简单的功能,从而降低单个业务逻辑的复杂度.降低复杂度后减少主业务的执行时间,从而给客户更好的体验.注意:不能提升系统的整体性能,但是可以提升某个业务的响应时间,并保证系统的健壮性(MQ重试机制).
4. 代码编写原则
通过代码编写原则,能够编写出可阅读可理解的代码,并降低业务的复杂度.
5. 需求分析原则
需求分析能帮助我们快速的理解开发的功能,从而发现隐藏的漏洞和逻辑,进而分析出合适的流程,建立出准确的接口文档.
6. 表结构设计原则
通过表结构设计原则,能够准确的实现需求.
## SQL编写原则
1. 分析输入数据所在的表
2. 分析输出数据所在的表
3. 分析所涉及的业务表之间的关系
4. 分析输入数据是否可以整合.如:多条创建语句是否可以整合到一起,用createList去操作.多条update语句可以整合成一句update语句.
5. 分析输出数据是否需要分页后再组合还是分页前组合.如:Left Join并没有包含在条件的数据可以分页后在组合,从而提升查询语句性能.
- 主表5000条数据
- Left Join表有 10万条数据
- 那么假如你通过关联后再 where条件,会在5000*10万条数据中进行判断.
- 但是假如先分页,则首先从 5000条中取出20条,再通过 20*10000条进行判断.从而提升性能非常明显.
6. 将组合后的语句结果拆分到实现语言的不同实体中.
7. 不同模块之间的表结构不要互相关联.如: 产品和讲解配置为同一个模块,但是产品和订单分属不同模块,产品和系统分属不同模块.
8. 同一个模块之间的信息,尽量通过表结构关联取得结果.
9. 不同模块之间的名称同步,可以将名称转换为枚举,并通过mq枚举同步,最后关键枚举表获取名称.
10. 在更新状态金额时,尽量通过状态历史判断和金额累加的方式去实现.
```sql
update table set status=@newStatus where id=@id AND status=@oldStatus;
update table set money=money+@money where id=@id;
```
## 缓存使用原则
1. 多级缓存原则
缓存全部讲解请看博客地址: [缓存应用案例分析](http://www.yanzuoguang.com/article/1553.html)
- 内存缓存(本地缓存), 一般用于临时缓存(3-10秒,定时刷新自动缓存),或者用于不会变动的数据长时间缓存(节假日)
- Redis缓存,一般用于主键缓存,定时查询自动刷新缓存.缓存锁(防止同一个任务同时运行多次).
- 内存缓存+Redis缓存,该缓存的使用场景为内存缓存+Redis缓存的组合.
- 具体实现方式如下:
```java
// 实现方式1
@Cached(
// 默认规则名称:缓存时效规则,以及存储方式
area="shortTime",
// 缓存名称,通常定义到缓存常量中,防止缓存名称冲突
name = CacheName.SALE_SALE_QUERY,
// 缓存关键字,用于做唯一性判断,可以不配置,则由框架自动进行key值生成
key = "#req.toJsonMd5()",
// 结果为空时是否缓存
cacheNullValue = true,
// 同时缓存到redis,内存中(降低redis压力),可以分别配置redis缓存时间和
cacheType = CacheType.BOTH ,
// 本次配置时效单位.注意:配置文件中的值和这个单位无关.
timeUnit=TimeUnit.SECONDS,
// 本地内存缓存失效时间,可以单独配置,一般通过area属性来控制
localExpire = 3,
// Redis缓存失效时间,可以单独配置,一般通过area属性来控制
expire = 3
)
// 开启自动刷新
@CacheRefresh(
// 自动刷新时间
refresh = CacheName.REFRESH_TIME,
// 不访问后多长时间停止刷新
stopRefreshAfterLastAccess = CacheName.REFRESH_STOP
)
// 开启多线程缓存保护,即 @Cached.key 相等时则不缓存
@CachePenetrationProtect
PageSizeData<SaleSaleLoadResVo> query(SaleSaleQueryReqVo req);
// 实现方式2
@CreateCached(
// 默认规则名称:缓存时效规则,以及存储方式
area="shortTime",
// 缓存名称,通常定义到缓存常量中,防止缓存名称冲突
name = CacheName.SALE_SALE_QUERY,
// 同时缓存到redis,内存中(降低redis压力),可以分别配置redis缓存时间和
cacheType = CacheType.BOTH ,
// 本次配置时效单位.注意:配置文件中的值和这个单位无关.
timeUnit=TimeUnit.SECONDS,
// 本地内存缓存失效时间,可以单独配置,一般通过area属性来控制
localExpire = 3,
// Redis缓存失效时间,可以单独配置,一般通过area属性来控制
expire = 3
)
private Cache<String,SaleSaleLoadResVo> cache;
```
2. 缓存场景使用缓存类型原则
缓存究竟该用内存缓存还是Redis缓存?一般包含如下几个场景.
- 业务实时性要求较高的,并使用不频繁的,用Redis. 如: 订单模块单个订单,一般使用N次,但是使用频率不高,但是关联表较多,可以通过NoSql来进行缓存. 并一般会在缓存中增加判断,判断请求次数,当N秒内超过多少次时,提示请求过于频繁.并在内存中将该订单的操作锁定一定时间.
- 业务实时型要求较高的,并使用频率较多的,可以考虑内存缓存定时刷刷新+Redis缓存一定时间的二级缓存分开实现策略或单用Redis缓存一定时间. 如: 用户订单列表,可以通过15秒自动刷新内存缓存定时刷新,30秒未访问后停止刷新,防止用户不断的发送请求过来,从而让Redis崩溃,Redis缓存按照用户Id存储,并在用户新建订单时实时删除缓存+延迟 5秒删除缓存(防止删除之前开始缓存,删除之后读取到结果后写入缓存,从而导致缓存不是最新).
- 变动非常少的和使用次数非常频繁的,可以通过内存缓存+Redis缓存定时刷新.如:产品列表和库存.可以设置本地缓存实效为5秒,Redis缓存时效60秒,每10秒钟刷新缓存写入Redis,并开始缓存保护.防止多人同时请求缓存.变动次数少可以设置为较长的Redis缓存时间.
- 主键缓存,可以通过Redis缓存较长时间,在主键变动时修改Redis缓存或实时删除缓存+延迟删除缓存策略.
3. 缓存重复原则+缓存就近原则
- 查询时可以通过产品ID+公司ID查询可重复得到的数据.
- 尽量要建立可以多次使用的纬度
如: 坐标纬度重复概率较低,那么则可以通过坐标查询该坐标最近的景区,以景区坐标为开始点进行缓存,查询该景区的数据,并根据坐标整合得到当前距离.从而提升查询速度. 又如:可以对坐标进行分析,发现坐标差距有多大,建立每1公里的坐标纬度,该1公里内共用缓存,并在缓存中排序过滤数据.
4. 缓存刷新原则
- 当请求参数重复性较高,并应用较多时,可以设置缓存自动刷新.从而提升系统效率.
- 下单时获取分销库存信息.可以拆分为订单缓存(主键缓存,不自动刷新),订单对分销库存进行缓存(内存+Redis的查询条件缓存,自动刷新5秒), Fegin请求分销库存模块,分销库存模块对分销库存进行缓存(内存+Redis的查询条件缓存,自动刷新5秒).则会达到这种效果,当第一次请求时,会执行整个流程,而当该分销产品的销售请求较为频繁时,订单模块不需要调用到分销模块,则可以获取到分销库存信息.从而加快下单的速度.
5. 预缓存原则
- 在使用较为频繁的景区+产品,可以建立预查询机制,从而提升第一次查询的性能.较快的速度出现查询结果.
\ No newline at end of file
## 为什么需要缓存
1. 降低数据库查询压力
2. 提前准备好结果数据,从而提升服务器性能
3. 幂等性判断
## 什么数据需要缓存
1. 列表信息
- 变动不频繁列表,如: 员工列表
- 变动频繁的列表,如: 库存列表
2. 单个信息通过主键缓存
- 加载产品信息
- 加载渠道信息
3. 多服务器部署中,幂等性任务处理
- 订单处理中对订单进行缓存,并防止同时下单
## 环境准备工作
1. 配置JetCache运行环境配置文件"tbd-redis.yml":
```yml
# 端口
#spring:
# redis:
# # redis服务器地址(默认为loaclhost)
# host: 192.168.0.206
# # redis端口(默认为6379)
# port: 6379
# # redis数据库索引(默认为0),避免和其他应用冲突,途比达序号为8,胖丁序号为6,数据中心序号为7
# database: 8
# # redis访问密码(默认为空)
# password: root,.123
# #超时连接
# timeout: 1000ms
# jedis:
# pool:
# #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
# max-wait: 1000ms
# #最大连接数据库连接数,设 0 为没有限制
# max-active: 100
# #最大等待连接中的数量,设 0 为没有限制
# max-idle: 8
# #最小等待连接中的数量,设 0 为没有限制
# min-idle: 0
remoteCache: &remoteCache
# type: redis
# keyConvertor: fastjson
# valueEncoder: kryo
# valueDecoder: kryo
# host: ${spring.redis.host:localhost}
# port: ${spring.redis.port:6379}
# password: ${spring.redis.password}
# database: ${spring.redis.database}
# poolConfig:
# minIdle: 5
# maxIdle: 20
# maxTotal: 50
type: redis.lettuce
keyConvertor: fastjson
valueEncoder: kryo
valueDecoder: kryo
# uri格式:redis://密码@ip:端口/redis库名?timeout=5s url[0]
# uri: redis://${spring.redis.password}@${spring.redis.password}:${spring.redis.port:6379}/7?timeout=5s
# redis数据库索引(默认为0),避免和其他应用冲突,途比达序号为8,胖丁序号为6,数据中心序号为7
# uri格式:redis://密码@ip:端口/redis库名?timeout=5s
uri: redis://root,.123@192.168.100.4:6379/8?timeout=1s
poolConfig:
minIdle: 0
maxIdle: 8
maxTotal: 100
localCache: &localCache
type: caffeine
keyConvertor: fastjson
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
# 默认1小时本地缓存
default:
<<: *localCache
expireAfterWriteInMillis: 60000
expireAfterAccessInMillis: 60000
# 長時本地緩存,主要用于要求时效一般
longTime:
<<: *localCache
expireAfterWriteInMillis: 300000
expireAfterAccessInMillis: 180000
# 短時本地緩存,主要用于要求时效较高的配置
shortTime:
<<: *localCache
expireAfterWriteInMillis: 3000
expireAfterAccessInMillis: 3000
remote:
# 默认1小时的远程缓存
default:
expireAfterWriteInMillis: 3600000
<<: *remoteCache
# 长时远程緩存,主要用于要求时效要求一般的集中式缓存
longTime:
expireAfterWriteInMillis: 7200000
<<: *remoteCache
# 短時远程緩存,主要用于要求时效较高的集中式缓存
shortTime:
expireAfterWriteInMillis: 300000
<<: *remoteCache
```
2. pom.xml中引入jar包
```xml
<dependency>
<groupId>com.yanzuoguang</groupId>
<artifactId>yzg-util-redis</artifactId>
</dependency>
或者
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis-lettuce</artifactId>
</dependency>
```
3. 项目中 "bootstrap.yml" 增加 引入配置文件"tbd-redis.yml "
```yml
spring:
cloud:
config:
name: tbd-redis
```
## 列表信息缓存
一般来说列表信息缓存信息都需要时效处理,这种缓存不需要删除,但是缓存时间不能太长(建议最长1-5分钟),但是需要定时失效.并通过整个对象转换为Json字符串后MD5,来确定是否自动重新刷新.
实现方式:
```java
/**
* 查询分销渠道
*
* @param req
* @return
*/
@Cached(
// 默认规则名称:缓存时效规则,以及存储方式
area="shortTime",
// 缓存名称,通常定义到缓存常量中,防止缓存名称冲突
name = CacheName.SALE_SALE_QUERY,
// 缓存关键字,用于做唯一性判断,可以不配置,则由框架自动进行key值生成
key = "#req.toJsonMd5()",
// 结果为空时是否缓存
cacheNullValue = true,
// 同时缓存到redis,内存中(降低redis压力),可以分别配置redis缓存时间和
cacheType = CacheType.BOTH ,
// 本次配置时效单位.注意:配置文件中的值和这个单位无关.
timeUnit=TimeUnit.SECONDS,
// 本地内存缓存失效时间,可以单独配置,一般通过area属性来控制
localExpire = 3,
// Redis缓存失效时间,可以单独配置,一般通过area属性来控制
expire = 3
)
// 开启自动刷新
@CacheRefresh(
// 自动刷新时间
refresh = CacheName.REFRESH_TIME,
// 不访问后多长时间停止刷新
stopRefreshAfterLastAccess = CacheName.REFRESH_STOP
)
// 开启多线程缓存保护,即 @Cached.key 相等时则不缓存
@CachePenetrationProtect
PageSizeData<SaleSaleLoadResVo> query(SaleSaleQueryReqVo req);
```
## 单个信息缓存
一般来说单个信息缓存信息都可以保留较长时效,这种缓存需要在增加,修改时自动删除,缓存可以设置较长时间(建议最长30-60分钟),可以支持定时刷新,从而提升加载速度.并开启多线程保护.多个请求时,只一个人去加载.这个通过主键加载时不能通过扩展参数返回2种以上的不同结果.
实现方式:
```java
/**
* 查询分销渠道
*
* @param req
* @return
*/
@Cached(
// 默认规则名称:缓存时效规则,以及存储方式,在配置文件中定义,默认为: default
area="default",
// 缓存名称,通常定义到缓存常量中,防止缓存名称冲突
name = CacheName.SALE_SALE,
// 缓存关键字,用于做唯一性判断,单个缓存中必须配置.
key = "#req.saleId",
// 结果为空时是否缓存
cacheNullValue = true,
// 同时缓存到redis,内存中(降低redis压力),可以分别配置redis缓存时间和
cacheType = CacheType.BOTH
)
// 开启自动刷新
@CacheRefresh(
// 自动刷新时间
refresh = CacheName.REFRESH_TIME,
// 不访问后多长时间停止刷新
stopRefreshAfterLastAccess = CacheName.REFRESH_STOP
)
// 开启多线程缓存保护,即 @Cached.key 相等时则不缓存
@CachePenetrationProtect
SaleSaleLoadResVo load(SaleSaleLoadReqVo req);
/**
* 分销渠道规则保存/修改
*
* @param req
* @return
*/
// 强制现有缓存失效
@CacheInvalidate(
// 默认规则名称:缓存时效规则,以及存储方式,在配置文件中定义,默认为: default
area="default",
// 缓存名称
name = CacheName.SALE_SALE,
// 缓存主键,得和 @Cached.key 相等
key = "#req.saleId"
)
String save(SaleSaleSaveReqVo req);
/**
* 删除分销信息
*
* @param req
* @return
*/
// 强制现有缓存失效
@CacheInvalidate(
// 默认规则名称:缓存时效规则,以及存储方式,在配置文件中定义,默认为: default
area="default",
// 缓存名称
name = CacheName.SALE_SALE,
// 缓存主键,得和 @Cached.key 相等
key = "#req.saleId"
)
int remove(SaleSaleRemoveReqVo req);
/**
* 渠道上下架
*
* @param req
* @return
*/
// 强制现有缓存失效
@CacheInvalidate(
// 默认规则名称:缓存时效规则,以及存储方式,在配置文件中定义,默认为: default
area="default",
// 缓存名称
name = CacheName.SALE_SALE,
// 缓存主键,得和 @Cached.key 相等
key = "#req.saleId"
)
String inline(SaleSaleInlineReqVo req);
```
## 多服务器部署中,幂等性任务处理
这种一般用于判断请求是否重复,并把一些重复的资源放入缓存中.从而提升最近一段时间该订单的加载速度.
```java
@Compent
class Classs{
@CreateCache(name = "order:save:", expire = 3600, cacheType = CacheType.BOTH)
private Cache<String, OrderLoadResVo> cacheOrderSave;
// 生成分布式锁执行函数,同一个key同一时间只会有一个函数执行,100秒内每1秒钟检测一次
CacheLock.run(
// 缓存对象
cacheOrderSave,
// 等待最长时长
waitTime,
// 等待时间间隔
waitUnit,
// 等待关键字
key + "_RUN",
// 当key对应函数,没有其他人执行时,开始执行本函数.奇谈人执行则等待
new Runnable() {
@Override
public void run() {
// 读取缓存
OrderLoadResVo loadCache = cacheOrderSave.get(key);
// ....
// 写入缓存
cacheOrderSave.put(order.getOrderId(), load);
cacheOrderSave.put(channelKey, load);
}
});
}
```
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment