Redis——某马点评day02——商铺缓存

2023-12-07 15:30

本文主要是介绍Redis——某马点评day02——商铺缓存,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

什么是缓存

添加Redis缓存

添加商铺缓存

Controller层中

    /*** 根据id查询商铺信息* @param id 商铺id* @return 商铺详情数据*/@GetMapping("/{id}")public Result queryShopById(@PathVariable("id") Long id) {return shopService.queryById(id);}

Service层中

 */
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {String key="cache:shop:" + id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}//4.不存在,根据id查询数据库Shop shop = getById(id);if (shop==null) {//5.不存在,返回错误return Result.fail("店铺不存在");}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));//7.返回return Result.ok(shop);}
}

练习添加店铺类型缓存

Controller层中

@RestController
@RequestMapping("/shop-type")
public class ShopTypeController {@Resourceprivate IShopTypeService typeService;@GetMapping("list")public Result queryTypeList() {return typeService.queryTypeList();}
}

Service层中

    @Overridepublic Result queryTypeList() {String key="cache:shopType";//1.从Redis查询缓存String shopType = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopType)) {//3.存在,直接返回List<ShopType> typeList = JSONUtil.toList(shopType, ShopType.class);return Result.ok(typeList);}//4.不存在,查询数据库List<ShopType> typeList = query().orderByAsc("sort").list();//5.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(typeList));//7.返回return Result.ok(typeList);}

缓存更新策略

 通常选择的方案都是第一种

单体系统可以通过@Transactional注解完成事务。

通常是先操作数据库,再删除缓存,出现问题的几率极小。

 

 实现商铺缓存和数据库的双写一致

第一个地方,写入Redis时加上超时时间。 

  //6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);

 第二个地方

controller中

    /*** 更新商铺信息* @param shop 商铺数据* @return 无*/@PutMappingpublic Result updateShop(@RequestBody Shop shop) {return shopService.update(shop);}

service中

    @Overridepublic Result update(Shop shop) {Long id = shop.getId();if(id==null){return Result.fail("店铺id不能为空");}//1.更新数据库updateById(shop);//2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY+id);return null;}

缓存穿透

布隆过滤器的实现不是真的存储数据,而是用某种Hash算法计算之后用二进制压缩之类的方法保存是否存在。但是,也有可能多个数据hash值相同导致错误结果。

编码解决商铺查询的缓存穿透(缓存空对象做法)

 代码修改

    @Overridepublic Result queryById(Long id) {String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return Result.fail("店铺不存在");}//4.不存在,根据id查询数据库Shop shop = getById(id);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return Result.fail("店铺不存在");}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);//7.返回return Result.ok(shop);}

 限流可以用sentinel实现.

缓存雪崩

宕机时降级限流也是用sentinel实现。

nginx缓存也是一级缓存.

tmd,一直在说springcloud里面有讲。

缓存击穿

常见解决方案

这里可以参考一下redisson的源码设计思路,设计一个监听通知机制! 

逻辑过期解决方案不会设置ttl过期时间,而是新增一个exprie字段,从redis里面查询发现是过期数据时就需要加锁开启一个新线程去更新缓存,然后直接返回旧数据。有别的线程来获取锁失败时说明已经有线程在进行更新,所以就直接返回过期数据,避免了过多线程等待锁。

 

利用互斥锁解决缓存击穿问题(重点)

这里的锁不能用lock和synchronized进行互斥实现,这两个会一直等待.这里用到Redis的一个命令setnx, 这个是一旦设置之后就不能修改,只能删除,但是如果因为意外原因导致迟迟不能删除会有大问题,所以这里会给锁设置一个有效期.

 代码修改

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {//缓存穿透//Shop shop=queryWithPassThrouh(id);//互斥锁解决缓存击穿Shop shop = queryWithMutex(id);if(shop==null){return Result.fail("店铺不存在!");}//7.返回return Result.ok(shop);}public Shop queryWithMutex(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return null;}//4.实现缓存重建//4.1获取互斥锁String lockkey="lock:shop:"+id;Shop shop = null;try {boolean isLock = tryLock(lockkey);//4.2判断是否获取成功if(!isLock){//4.3失败,休眠并重试Thread.sleep(50);return  queryWithMutex(id);   //这里有可能会出现栈溢出的情况。}//获取成功之后应该再次检查缓存是否存在,有可能别的线程已经重建完了缓存,所以这里就无需再重建缓存shopJson = stringRedisTemplate.opsForValue().get(key);//再次判断是否存在if (StrUtil.isNotBlank(shopJson)) {//存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//4.4根据id查询数据库shop = getById(id);//模拟重建的延时Thread.sleep(200);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return shop;}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {//7.释放互斥锁unlock(lockkey);}//8.返回return shop;}public Shop queryWithPassThrouh(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return null;}//4.不存在,根据id查询数据库Shop shop = getById(id);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return shop;}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);//7.返回return shop;}private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unlock(String key){stringRedisTemplate.delete(key);}@Overridepublic Result update(Shop shop) {Long id = shop.getId();if(id==null){return Result.fail("店铺id不能为空");}//1.更新数据库updateById(shop);//2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY+id);return null;}
}

这里可以上Jmeter进行压测,上100个线程进行测试

但是最终实际只查询了一次数据库. 

利用逻辑过期解决缓存击穿问题(重点)

 为了能增加一个逻辑过期时间的字段,新建一个对象

@Data
public class RedisData {private LocalDateTime expireTime;private Object data;
}

代码修改

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {//缓存穿透//Shop shop=queryWithPassThrouh(id);//互斥锁解决缓存击穿
//        Shop shop = queryWithMutex(id);//逻辑过期解决缓存击穿问题Shop shop = queryWithLogicalExpire(id);if(shop==null){return Result.fail("店铺不存在!");}//7.返回return Result.ok(shop);}private static final ExecutorService CACHE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);public Shop queryWithLogicalExpire(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isBlank(shopJson)) {//3.存在,直接返回nullreturn null;}//4.命中,需要先把json反序列化为对象RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);Shop shop = JSONUtil.toBean((JSONObject)redisData.getData(), Shop.class);LocalDateTime expireTime = redisData.getExpireTime();//5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())){//5.1未过期,直接返回店铺信息return shop;}//5.2已过期,需要缓存重建//6.缓存重建//6.1获取互斥锁String lockKey=LOCK_SHOP_KEY+id;boolean isLock = tryLock(lockKey);//6.2判断是否获取锁成功if(isLock){//这里应该再次检测缓存是否过期,做双重判断,如果没过期就不需重建了,因为可能别的线程已经重建了shopJson = stringRedisTemplate.opsForValue().get(key);redisData = JSONUtil.toBean(shopJson, RedisData.class);expireTime = redisData.getExpireTime();if(expireTime.isAfter(LocalDateTime.now())){//返回前先释放锁unlock(lockKey);//5.1未过期,直接返回店铺信息return shop;}//6.3成功,开启独立线程,实现缓存重建CACHE_REBUILD_EXECUTOR.submit(()->{try {//重建缓存this.saveShop2Redis(id,20L);} catch (Exception e) {throw new RuntimeException(e);} finally {//释放锁unlock(lockKey);}});}//6.4失败,返回过期商铺信息。return shop;}public Shop queryWithMutex(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return null;}//4.实现缓存重建//4.1获取互斥锁String lockkey="lock:shop:"+id;Shop shop = null;try {boolean isLock = tryLock(lockkey);//4.2判断是否获取成功if(!isLock){//4.3失败,休眠并重试Thread.sleep(50);return  queryWithMutex(id);   //这里有可能会出现栈溢出的情况。}//获取成功之后应该再次检查缓存是否存在,有可能别的线程已经重建完了缓存,所以这里就无需再重建缓存shopJson = stringRedisTemplate.opsForValue().get(key);//再次判断是否存在if (StrUtil.isNotBlank(shopJson)) {//存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//4.4根据id查询数据库shop = getById(id);//模拟重建的延时//Thread.sleep(200);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return shop;}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {//7.释放互斥锁unlock(lockkey);}//8.返回return shop;}public Shop queryWithPassThrouh(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return null;}//4.不存在,根据id查询数据库Shop shop = getById(id);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return shop;}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);//7.返回return shop;}private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unlock(String key){stringRedisTemplate.delete(key);}public void saveShop2Redis(Long id,Long expireSeconds) throws InterruptedException {//1.查询店铺数据Shop shop = getById(id);//模拟延时
//        Thread.sleep(200);//2.封装逻辑过期时间RedisData redisData = new RedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));//3.写入RedisstringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));}@Overridepublic Result update(Shop shop) {Long id = shop.getId();if(id==null){return Result.fail("店铺id不能为空");}//1.更新数据库updateById(shop);//2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY+id);return null;}
}

缓存工具封装(重点)

封装工具类里用到的实体

@Data
public class RedisData {private LocalDateTime expireTime;private Object data;
}

工具类代码

@Slf4j
@Component
public class CacheClient {@Resourceprivate StringRedisTemplate stringRedisTemplate;String LOCK_SHOP_KEY="lock:shop:";public CacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public  void set(String key, Object value, Long time, TimeUnit timeUnit){stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,timeUnit);}public  void setWithLogicalExpire(String key, Object value, Long time, TimeUnit timeUnit){//设置逻辑过期RedisData redisData = new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(timeUnit.toSeconds(time)));//写入RedisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));}public <R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID,R>dbFallback, Long time, TimeUnit timeUnit){String key=keyPrefix+id;//1.从Redis查询缓存String json = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(json)) {//3.存在,直接返回return  JSONUtil.toBean(json, type);}//判断命中的是否是空值if(json!=null){//返回一个错误信息return null;}//4.不存在,根据id查询数据库R r=dbFallback.apply(id);//5.不存在,返回错误if (r==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",2L, TimeUnit.MINUTES);return null;}//6.存在,写入Redisthis.set(key,r,time,timeUnit);//7.返回return r;}private static final ExecutorService CACHE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);public <R,ID> R queryWithLogicalExpire(String keyPrefix, ID id, Class<R> type, Function<ID,R>dbFallback, Long time, TimeUnit timeUnit){String key=keyPrefix+ id;//1.从Redis查询缓存String json = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isBlank(json)) {//3.存在,直接返回nullreturn null;}//4.命中,需要先把json反序列化为对象RedisData redisData = JSONUtil.toBean(json, RedisData.class);R r = JSONUtil.toBean((JSONObject)redisData.getData(), type);LocalDateTime expireTime = redisData.getExpireTime();//5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())){//5.1未过期,直接返回店铺信息return r;}//5.2已过期,需要缓存重建//6.缓存重建//6.1获取互斥锁String lockKey=LOCK_SHOP_KEY+id;boolean isLock = tryLock(lockKey);//6.2判断是否获取锁成功if(isLock){//这里应该再次检测缓存是否过期,做双重判断,如果没过期就不需重建了,因为可能别的线程已经重建了json = stringRedisTemplate.opsForValue().get(key);redisData = JSONUtil.toBean(json, RedisData.class);r = JSONUtil.toBean((JSONObject)redisData.getData(), type);expireTime = redisData.getExpireTime();if(expireTime.isAfter(LocalDateTime.now())){//返回前先释放锁unlock(lockKey);//5.1未过期,直接返回店铺信息return r;}//6.3成功,开启独立线程,实现缓存重建CACHE_REBUILD_EXECUTOR.submit(()->{try {//重建缓存//查询数据库R r1 = dbFallback.apply(id);//写入Redisthis.setWithLogicalExpire(key,r1,time,timeUnit);} catch (Exception e) {throw new RuntimeException(e);} finally {//释放锁unlock(lockKey);}});}//6.4失败,返回过期信息。return r;}private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unlock(String key){stringRedisTemplate.delete(key);}
}

Service层修改后代码

里面有缓存穿透的调用,也有缓存击穿的调用.

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate CacheClient cacheClient;@Overridepublic Result queryById(Long id) {//缓存穿透Shop shop=cacheClient.queryWithPassThrough(CACHE_SHOP_KEY,id, Shop.class,id2->getById(id2),CACHE_SHOP_TTL,TimeUnit.MINUTES);//Shop shop=cacheClient.queryWithPassThrough(CACHE_SHOP_KEY,id, Shop.class,this::getById,,CACHE_SHOP_TTL,TimeUnit.MINUTES);//互斥锁解决缓存击穿//Shop shop = queryWithMutex(id);//逻辑过期解决缓存击穿问题
//        Shop shop = cacheClient
//                .queryWithLogicalExpire(CACHE_SHOP_KEY,id,Shop.class,this::getById,CACHE_SHOP_TTL,TimeUnit.SECONDS);if(shop==null){return Result.fail("店铺不存在!");}//7.返回return Result.ok(shop);}@Overridepublic Result update(Shop shop) {Long id = shop.getId();if(id==null){return Result.fail("店铺id不能为空");}//1.更新数据库updateById(shop);//2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY+id);return null;}
}

内容总结:

去看文档资料里面xmind文档,那个里面总结的很好。

这篇关于Redis——某马点评day02——商铺缓存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/466382

相关文章

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

缓存雪崩问题

缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。 解决方案: 1、使用锁进行控制 2、对同一类型信息的key设置不同的过期时间 3、缓存预热 1. 什么是缓存雪崩 缓存雪崩是指在短时间内,大量缓存数据同时失效,导致所有请求直接涌向数据库,瞬间增加数据库的负载压力,可能导致数据库性能下降甚至崩溃。这种情况往往发生在缓存中大量 k

Redis中使用布隆过滤器解决缓存穿透问题

一、缓存穿透(失效)问题 缓存穿透是指查询一个一定不存在的数据,由于缓存中没有命中,会去数据库中查询,而数据库中也没有该数据,并且每次查询都不会命中缓存,从而每次请求都直接打到了数据库上,这会给数据库带来巨大压力。 二、布隆过滤器原理 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用多个不同的哈希函数将一个元素映射到一个位数组中的多个位置,并将这些位置的值置

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

防止缓存击穿、缓存穿透和缓存雪崩

使用Redis缓存防止缓存击穿、缓存穿透和缓存雪崩 在高并发系统中,缓存击穿、缓存穿透和缓存雪崩是三种常见的缓存问题。本文将介绍如何使用Redis、分布式锁和布隆过滤器有效解决这些问题,并且会通过Java代码详细说明实现的思路和原因。 1. 背景 缓存穿透:指的是大量请求缓存中不存在且数据库中也不存在的数据,导致大量请求直接打到数据库上,形成数据库压力。 缓存击穿:指的是某个热点数据在

laravel框架实现redis分布式集群原理

在app/config/database.php中配置如下: 'redis' => array('cluster' => true,'default' => array('host' => '172.21.107.247','port' => 6379,),'redis1' => array('host' => '172.21.107.248','port' => 6379,),) 其中cl

PHP APC缓存函数使用教程

APC,全称是Alternative PHP Cache,官方翻译叫”可选PHP缓存”。它为我们提供了缓存和优化PHP的中间代码的框架。 APC的缓存分两部分:系统缓存和用户数据缓存。(Linux APC扩展安装) 系统缓存 它是指APC把PHP文件源码的编译结果缓存起来,然后在每次调用时先对比时间标记。如果未过期,则使用缓存的中间代码运行。默认缓存 3600s(一小时)。但是这样仍会浪费大量C

缓存策略使用总结

缓存是提高系统性能的最简单方法之一。相对而言,数据库(or NoSQL数据库)的速度比较慢,而速度却又是致胜的关键。 如果使用得当,缓存可以减少相应时间、减少数据库负载以及节省成本。本文罗列了几种缓存策略,选择正确的一种会有很大的不同。缓存策略取决于数据和数据访问模式。换句话说,数据是如何写和读的。例如: 系统是写多读少的吗?(例如基于时间的日志)数据是否是只写入一次并被读取多次?(例如用户配

Redis的rehash机制

在Redis中,键值对(Key-Value Pair)存储方式是由字典(Dict)保存的,而字典底层是通过哈希表来实现的。通过哈希表中的节点保存字典中的键值对。我们知道当HashMap中由于Hash冲突(负载因子)超过某个阈值时,出于链表性能的考虑,会进行Resize的操作。Redis也一样。 在redis的具体实现中,使用了一种叫做渐进式哈希(rehashing)的机制来提高字典的缩放效率,避

【吊打面试官系列-Redis面试题】说说 Redis 哈希槽的概念?

大家好,我是锋哥。今天分享关于 【说说 Redis 哈希槽的概念?】面试题,希望对大家有帮助; 说说 Redis 哈希槽的概念? Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽, 集群的每个节点负责一部分 hash 槽。