1、基于session登录,不需要返回用户凭证,因为session有sessionID,全局唯一,前端的JSESSIONID就是凭证。缺点:如果换了tomcat服务器,会消失凭证,解决方法:使用Redis,达到全局唯一
2、基于Redis存储验证码,key:value对应手机号(不推荐):验证码,分别有String用JSON保存,和Hash结构:field:value
3、最佳实践,用token作为key,value存储用户信息,前端每次请求携带token,从token返回用户数据,
4、写操作:先操作数据库,再删除缓存
缓存穿透:
解决方案:
1、缓存空对象
2、布隆过滤器:先于redis,如果key不存在,直接拒绝
缓存雪崩:
解决方案:
1、设置随机TTL
2、多级缓存
缓存击穿:
部分key(热点且恢复复杂)过期,缓存重建应该只由第一个访问的线程进行重建(通过加锁进行互斥),并把key设置为逻辑过期
1、解决方案:
线程一访问数据,发现逻辑过期,获取锁,读取旧数据后,新建线程二,让线程二进行缓存重建,其他线程发现逻辑过期,并且获取锁失败,则读取旧数据
全局唯一ID:
1、主键不采用自增长,因为容易被猜测
2、用64个bit位,即Long型,存储:符号位(1),时间戳(31)和序列号(32)
RedisIdWorker类
/**
* 开始的时间戳,2024-2-5
*/
private final static long BEGIN_TIMESTAMP = 1707091200L;
/**
* 序列号位数
*/
private final static int COUNT_BITS=32;
@Autowired
StringRedisTemplate stringRedisTemplate;
public long nextId(String keyPrefix){
//生成时间戳
LocalDateTime now = LocalDateTime.now();
long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
long timestamp = nowSecond-BEGIN_TIMESTAMP;
//生成序列号
//获取当天日期
String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
//拼接并返回
return timestamp<<COUNT_BITS | count;
}
测试:
@Autowired
RedisIdWorker redisIdWorker;
private ExecutorService es = Executors.newFixedThreadPool(500);
@Test
void test3() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(300);
Runnable task = ()->{
for (int i = 0; i < 100; i++) {
long id = redisIdWorker.nextId("order");
System.out.println("id="+id);
}
latch.countDown();
//每次减一
};
long begin = System.currentTimeMillis();
for (int i = 0; i < 300; i++) {
es.submit(task);
}
latch.await(); //等300减完再唤醒线程
long end = System.currentTimeMillis();
System.out.println("time="+(end-begin));
}