扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
在登陆服务中,如果将数据全部存储到tomcat中,当存在多个tomcat的时候,数据是无法同步的,这就导致了数据的共享问题:
为岢岚等地区用户提供了全套网页设计制作服务,及岢岚网站建设行业解决方案。主营业务为网站设计、网站制作、岢岚网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!1、每台服务器中都有完整的一份session数据,服务器压力过大。
2、session拷贝数据时,可能会出现延迟
解决办法就是采用redis, redis完美的符合了以下的要求:
1.同步性
2.基于内存,高效率
3.满足key-value结构
同时注意,redis的string与hashset的区别
优化后的流程如下图
首先我们很容易想到,将用户的登录手机号作为key,而后对于存储到redis中的用户的凭证,我们选择生成随机的token,使用token是为了不暴露用户的手机号(隐私处理).同时我们还要注意,session是能在tomcat中自动同步的,但是token是做不到的,我们需要手动同步token.而token是怎么做到帮助我们识别用户的登录状态的呢,答案就是在每次的服务器回传的过程中,都会在头部添加一个token字段.
2.商品缓存缓存穿透:指的是客户端在请求数据的过程中,缓存和数据库中都没有要请求的数据,这样缓存永远都不会生效,请求都会打到数据库.这样就导致数据库在频繁的查找数据.服务器性能大幅度下降.
解决办法:1.缓存空对象: 对于同一个请求的资源,比如请求不存在的id= -1的资源,在缓存中添加id=-1的键,将其值设置为null,这样避免了同样的id导致的反复查找,下次查找id=-1的资源可以直接从缓存中查出来.
优点: 实现简单,维护方便
缺点: 一旦请求的资源变换,还是需要继续查找.
额外的内存消耗
可能造成短期的不一致(第一次请求的时候数据没有,缓存中设置为null,但是第二次请求时数据库中已经有了上次请求的id对应的数据,仍然会返回null)
2.添加布隆过滤器:它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
优点: 内存占用少,没有多余的key
缺点: 实现很复杂,并且存在误判的可能
3.redis加锁的办法: 在后面指定了锁的有效时间,是用锁的超时时间进行兜底的.private boolean tryLock(String key){
Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 2, TimeUnit.MINUTES);
//如果直接返回的话会自动拆箱,可能导致出现空值
return BooleanUtil.isTrue(ifAbsent);
}
如果是redis中设置锁的语句就如下所示:
//给线程1设置锁
setnx lock thread1
//设置锁的时长
expire lock 5
//合并起来
//(nx指的是不存在的时候才可以设置)
set lock thread1 ex 5 nx
//删除锁
del lock
4.redis将两个操作转换为原子操作(并行的情况下)使用lua脚本
lua脚本数组是从下标为1开始的
java代码的改造:
改造之前:
@Override
public void deleteLock() {
//判断redis中的锁和当前的锁是否是同一把
//当前的锁
String threadNameUUID = ID_NAME + Thread.currentThread().getId();
//redis数据库里面的数据是否一致
String redisLock = stringRedisTemplate.opsForValue().get(KEY_NAME + name);
if(threadNameUUID.equals(redisLock)){
stringRedisTemplate.delete(KEY_NAME + name);
}
}
改造后:
unlock.lua在resources目录下
//初始化lua脚本
private static final DefaultRedisScriptUNLOCK_LUA;
//在static]块中初始化
static{
UNLOCK_LUA = new DefaultRedisScript<>();
UNLOCK_LUA.setLocation(new ClassPathResource("unlock.lua"));
UNLOCK_LUA.setResultType(Long.class);
}
unlock.lua
--比较线程标识与redis锁中的标识是否一致
if(redis.call('get', KEYS[1]) == ARGV[1]) then
--释放锁
return redis.call('del', KEYS[1])
end
return 0
调用代码:
@Override
public void deleteLock() {
//调用lua脚本
stringRedisTemplate.execute(
UNLOCK_LUA,
Collections.singletonList(KEY_NAME + name),
ID_NAME + Thread.currentThread().getId());
}
lua传入空参,比如java调用lua脚本的时候key是空的这时不能传空对象,要传Collections.emptyList()下面举例:
Long execute = stringRedisTemplate.execute(SECKILL_SCRIPT,
Collections.emptyList(),
voucherId.toString(), id.toString());
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流