redis查找和更新的缓存问题

1.首先先进行查找uuid为123的用户

    @GetMapping("/{uuid}")
    public User get(@PathVariable String uuid) {
        return userService.findByUuid(uuid);
    }

        @Cacheable(value = "userInfo", key = "'findByUuid-' + #uuid")
    @Override
    public User findByUuid(String uuid) {
        return userDao.findOne(uuid);
    }

能确保uuid为123的用户查找得出来而且查找到的用户也存入缓存.(多查找几次控制台也只显示一次的sql语句)

2.更新uuid为123的用户

    @PutMapping("/{uuid}")
    public String update(@PathVariable String uuid,@RequestBody User updateUser){
        User user = userService.findByUuid(uuid);
        user.setUserName(updateUser.getUserName());
        user.setPassword(updateUser.getPassword());
        user.setAge(updateUser.getAge());
        user.setPhone(updateUser.getPhone());
        userService.save(user);
        return "success";
    }

        @CacheEvict(value = "userInfo", key = "'findByUuid-' + #user.getUuid()")
    @Override
    public User save(User user) {
        return userDao.save(user);
    }

更新时执行却又执行了select ,按道理来说findByUuid(123)应该被存入缓存,而不会打印select 语句才对啊.
以下是更新uuid为123时控制台打印的语句

   Hibernate: 
    select
        user0_.uuid as uuid1_0_0_,
        user0_.age as age2_0_0_,
        user0_.password as password3_0_0_,
        user0_.phone as phone4_0_0_,
        user0_.user_name as user_nam5_0_0_ 
    from
        user user0_ 
    where
        user0_.uuid=?
   Hibernate: 
    update
        user 
    set
        age=?,
        password=?,
        phone=?,
        user_name=? 
    where
        uuid=?

更新时执行却又执行了select ,按道理来说findByUuid(123)应该被存入缓存,而不会打印select 语句才对啊.
以下是更新uuid为123时控制台打印的语句

[主动]需要操作人员去操作,或者定时调度
  [被动]由用户触发更新
  [预加载]提前加载好数据
方案1
[主动]后台点击更新缓存按钮,从DB查找最新数据集合,删除原缓存数据,存储新数据到缓存;
问题:更新过程中删除掉缓存后刚好有业务在查询,那么这个时候返回的数据会是空,会影响用户体验
方案2
[被动]前台获取数据时发现没有缓存数据就会去数据库同步数据到缓存
问题:当并发请求获取缓存数据不存在的时候,就会产生并发的查询数据的操作。
方案3
[主动]后台点击更新缓存按钮,从DB查找最新数据集合,这里不删除缓存,通过遍历数据覆盖和删除掉无效的数据
问题:逻辑相对麻烦,而且更新机制无法通用;
推荐
  以上的几种更新方案我都遇到过,因为产生了各种问题,所以我想到了一个相对好的方案,类似预加载功能,先把数据加载到缓存中,缓存成功后再切换显示最新的数据,将旧数据设置过期;
方案4
  [主动][预加载]前台获取缓存的时候需要先得到缓存数据对应的Redis Key(简称:[ShowingKey]),然后根据[ShowingKey]去读取缓存数据(简称:[缓存];
[ShowingKey](可以是最近一次更新缓存的时间戳或者根据自己规则自定义)
[缓存](需要缓存的数据,如:DB数据等)
需要两块数据:
举个栗子:
  我们现在有个业务需要缓存今日上新商品数据,缓存到Hash中
  更新逻辑:
  后台编辑人员操作完数据的时候点击更新按钮,获取服务器当前时间=1469938351000=[更新时间戳],然后获取DB数据,缓存到Goods:Todays:1469938351000中,添加缓存数据成功后,获取Goods:Todays:ing中的时间戳1449538371020=[上一次更新时间戳],更新Goods:Todays:ing值=[更新时间戳]=1469938351000,更新成功后可以把[上一次更新时间戳]对应的缓存设置过期时间,我一般是设置5秒后过期。(注意旧数据一定要设置过期时间,不能直接删除,因为在切换[ShowingKey]的过程中可能还有业务在使用)
Key string key=Goods:Todays:ing 内容=最近一次的更新时间戳
Hash Key=Goods:Todays:{0} {0}=时间戳
[缓存]对应Redis Key 规则
[ShowingKey]对应的Redis Key
更新总结
第1种更新方案影响用户体验一般不推荐使用
第2种更新方案可以通过程序锁,锁住更新操作只能有一个进入DB查询,可以避免问题
第3种更新方案不会有第1,2 的问题,但是更新逻辑写起来比较麻烦,而且更新方案不能抽象通用
第4种更新方案使用提前加载到缓存,然后在切换需要显示的缓存数据,可以完美解决1,2,3中的问题
redis内存不足,滥用
问题
数据不断累加,无效数据未清理,缓存未设置过期时间
存储数据中包含未使用到字段,整个对象序列化到redis中
冷数据,或者根本不会再去使用的无效数据没有清理
解决
数据区分无效时间,设置过期时间,使无效数据过期;(如:通过日期后缀命名Key)
区分冷数据,清理掉冷数据;
缓存数据从简,redis key命名从简,数据字段命名从简,无效字段不添加在缓存中;
键命名规范
内存数据库,键名长度影响有限内存空间,所以命名应该控制长度,简短易懂;
大小写规范
根据业务命名,相同业务统一的Key前缀
其他经验
数值累加,get,set+1并发导致累加不准确
使用redis increment 自增数值的机制不会有累加不准确的问题
.net 类库 ServiceStack 3.9.71.0 的一个问题
  SetEntryInHash 返回 bool,只有第一次新增的时候返回的是true,后面修改成功了也都是返回false
  源码:SetEntryInHash 方法,读取hset的结果 判断是否等于1,返回bool
  
  我们通过命令: hset 第一次sflyq key不存在,添加成功返回的执行结果是:1 hset 第一次sflyq key已经存在,修改成功购返回结果:0
  
  所以结果很明显,通过SetEntryInHash 判断hash是否key value 是否设置成功是有问题的,只有第一次设置会返回 ture

可以考虑用户长时间不操作自动清除redis内的数据,这样可以通过 在redis设置用户数据的过期时间,减少redis的整体数据量。用户数据在redis里面无法取得的时候,再从数据库取得,然后存入redis,同时设置过期时间。

redis-cli下 用 keys + 通配符 可以查到现有的所有键名。list里面的数据 可以通过 lrange 查看。

http://blog.csdn.net/mdw0730/article/details/71453595?utm_source=itdadao&utm_medium=referral

hibernate更新先查找session缓存(一级),如果没有查找数据库(这时候才是二级缓存)

http://redisdoc.com/list/index.html