一条复杂的业务线往往需要多个系统之间相互协调才能实现预期的功能,而系统之间的数据传输直接读库,或者业务逻辑复杂点的就需要提供方为调用方提供数据接口,常用http请求来传输json格式的数据,有一个特点:慢,高并发时数据同步会受到一定影响;另外并发量比较高的时候,接口频繁调用,会增加系统压力,甚至崩溃;
例如并发时限定用户数(A系统设定最大值,B系统进行校验):通过redis设定最大接收数,然后每进一个用户最大数减1,处理完一个用户最大数加1;保持最大负载用户不变;
A:
public void set(String key, String value) {
DataType type = this.template.type(key);
if(!DataType.NONE.equals(type) && !DataType.STRING.equals(type)) {
this.template.delete(key);
}
this.ops().set(key, value);
}
public void set(String key, String value, long timeout) {
this.set(key, value);
RedisConnection redisConnectionTemp = this.template.getConnectionFactory().getConnection();
try {
redisConnectionTemp.expire(key.getBytes(), timeout / 1000L);
} catch (Exception var10) {
logger.error("设置超时时间出错", var10);
} finally {
redisConnectionTemp.close();
}
}
public String get(String key) {
DataType type = this.template.type(key);
return !DataType.NONE.equals(type) && DataType.STRING.equals(type)?(String)this.ops().get(key):null;
}
public void hset(String key, String field, String value) {
DataType type = this.template.type(key);
if(!DataType.NONE.equals(type) && !DataType.HASH.equals(type)) {
this.template.delete(key);
}
this.hops().put(key, field, value);
}
public String hget(String key, String field) {
DataType type = this.template.type(key);
return !DataType.NONE.equals(type) && DataType.HASH.equals(type)?(String)this.hops().get(key, field):null;
}
public void del(String key) {
this.template.delete(key);
}
public void hdel(String key, String field) {
this.hops().delete(key, new Object[]{field});
}
public void setMax(String key, long num, long timeout) {
this.set(key, String.valueOf(-9223372036854775808L + num), timeout);
}
B:
public synchronized boolean decr(String key) {
RedisConnection redisConnectionTemp = this.template.getConnectionFactory().getConnection();
boolean e;
try {
if(redisConnectionTemp.exists(key.getBytes()).booleanValue()) {
long e1 = redisConnectionTemp.decr(key.getBytes()).longValue();
logger.info("redisConnection.decr =={}:{}", key, Long.valueOf(e1));
boolean var5;
if(e1 < -4611686018427387904L) {
var5 = true;
return var5;
}
logger.info("r > limitValue......");
var5 = false;
return var5;
}
logger.info("redisConnection.exists");
e = false;
} catch (Exception var9) {
logger.error("redis desc error======:{}", key, var9);
boolean var4 = false;
return var4;
} finally {
RedisConnectionUtils.releaseConnection(redisConnectionTemp, this.template.getConnectionFactory());
}
return e;
}
public synchronized boolean incr(String key) {
RedisConnection redisConnectionTemp = this.template.getConnectionFactory().getConnection();
boolean e;
try {
if(redisConnectionTemp.exists(key.getBytes()).booleanValue()) {
long e1 = redisConnectionTemp.incr(key.getBytes()).longValue();
boolean var5;
if(e1 < -4611686018427387904L) {
var5 = true;
return var5;
}
var5 = false;
return var5;
}
e = false;
} catch (Exception var9) {
logger.error("", var9);
boolean var4 = false;
return var4;
} finally {
RedisConnectionUtils.releaseConnection(redisConnectionTemp, this.template.getConnectionFactory());
}
return e;
}
用户限购问题:
long count = template.opsForValue().increment(key,prizeLimitParam.getCount());
if (limitNum >= count){
return true;
} else {
return false;
}
乐观锁
String lockKey = "LockKey_"+param.getUserId(); Random rd = new Random(); int expireTime = 60; try { while(jedisCluster.setnx(lockKey, lockKey) == EMPTY.getCode()){//redis锁定 Thread.sleep(rd.nextInt(2)); if (jedisCluster.setnx(lockKey, lockKey) == INC.getCode()) { break; } } logger.info("锁定设置成功 lockKey{}"+lockKey); jedisCluster.setex(lockKey, expireTime, lockKey); // todo 逻辑处理 jedisCluster.del(lockKey); return ResponseUtil.getOkResponse(null); } catch (JyBizException e) { jedisCluster.del(lockKey); logger.error("接口异常", e); return ResponseUtil.getBizErrorResponse(ErrorCode.BIZ_ERROR.value(), e.getMessage()); }
缓存:用key去缓存中查找,如果存在使用;不存在,去DB查找,找到放进缓存并使用;
穿透:一个key在缓存和DB中都不存在,这样反复调用,影响效率,如果是恶意攻击,容易连累DB;
解决方案:用个map存放所有有值的key,请求过来先取map,不存在直接返回;该key第一次查到没值,将该key和对应的空值也放进缓存,过期时间设置短一点,避免并发访问DB。
雪崩:初始化数据时,同一时刻将大量的数据放进缓存,缓存同时失效,造成同一时刻访问数据库引起雪崩;
解决方案:初始化时,失效时间添加随机数;
击穿:并发访问某个key,该key刚好失效,导致同一时刻访问DB,并设置缓存;
解决方案:使用setnx锁定key,第一个人发现失效先查DB并设定,必要时添加延时或循环;为该key设定预的失效时间,该时间小于真实失效时间,发现该key即将失效,立刻重新设置;