Redis——某马点评day03

2023-12-05 00:04
文章标签 redis 点评 day03 某马

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

全局唯一ID

创建一个工具类

@Component
public class RedisIdWorker {/*** 开始时间戳*/private static final long BEGIN_TIME_STAMP=1672531200L;/*** 序列号的位数*/private static final int COUNT_BITS=32;private StringRedisTemplate stringRedisTemplate;public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public long nextId(String keyPrefix){//1.生成时间戳LocalDateTime now = LocalDateTime.now();long nowSecond = now.toEpochSecond(ZoneOffset.UTC);long timestamp = nowSecond - BEGIN_TIME_STAMP;//2.生成序列号//2.1获取当前日期,精确到天String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));//2.2自增长long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);//3.拼接并返回return timestamp<<COUNT_BITS|count;}public static void main(String[] args) {LocalDateTime time = LocalDateTime.of(2023, 1, 1, 0, 0, 0);long second = time.toEpochSecond(ZoneOffset.UTC);System.out.println("second= "+second);}
}

对工具类进行测试

这里开启了300个线程,每个线程执行100次,最后自增长会达到30000.

@SpringBootTest
class HmDianPingApplicationTests {@Resourceprivate RedisIdWorker redisIdWorker;private ExecutorService es= Executors.newFixedThreadPool(500);@Testvoid test() 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();long end=System.currentTimeMillis();System.out.println("time = "+(end-begin));}
}

结果如下,可以看见每个id都是不一样的。 

然后看见redis里面的数据,的确是30000没错. 

实现优惠券秒杀下单

 

代码实现(未考虑线程安全)

Controller层中

@RestController
@RequestMapping("/voucher-order")
public class VoucherOrderController {@Resourceprivate IVoucherOrderService voucherOrderService;@PostMapping("seckill/{id}")public Result seckillVoucher(@PathVariable("id") Long voucherId) {return voucherOrderService.seckillVoucher(voucherId);}
}

Service层中

@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Resourceprivate RedisIdWorker redisIdWorker;@Resourceprivate ISeckillVoucherService seckillVoucherService;@Override@Transactionalpublic Result seckillVoucher(Long voucherId) {//1.查询优惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);//2.判断秒杀是否开始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {//尚未开始return Result.fail("秒杀尚未开始");}//3.判断秒杀是否已经结束if (voucher.getBeginTime().isBefore(LocalDateTime.now())) {//尚未开始return Result.fail("秒杀已经结束");}//4.判断库存是否充足if (voucher.getStock()<1) {//库存不足return Result.fail("库存不足");}//5.扣减库存boolean success=seckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id",voucherId).update();if(!success){return Result.fail("库存不足!");}//6.创建订单VoucherOrder voucherOrder = new VoucherOrder();//6.1订单Idlong orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);//6.2用户IdLong userId = UserHolder.getUser().getId();voucherOrder.setUserId(userId);//6.3代金券IdvoucherOrder.setVoucherId(voucherId);save(voucherOrder);//7.返回订单IDreturn Result.ok(orderId);}
}

 

超卖问题

真实场景下肯定是1秒会有成百上千的请求同时发送到后端的,使用Jmeter进行测试得到如下.

使用50个线程抢20个资源出现超卖情况 

问题出现原因如下,多个线程读取到了同一个数据,然后进行修改。 

 解决方案分析

 这里的版本号实际可以用库存数量作为版本号进行使用。

乐观锁解决超卖问题

        //5.扣减库存boolean success=seckillVoucherService.update().setSql("stock = stock - 1") //set stock =stock - 1.eq("voucher_id",voucherId).eq("stock",voucher.getStock()) //where id=? and stock =?.update();if(!success){return Result.fail("库存不足!");}

对这部分代码使用乐观锁优化之后再次50抢20结果如下,反而只抢了13张,很多都是报库存不足.

因为多个线程抢同一张票时只有一个线程可以成功.

缺点:

成功率太低。

优化修改

将stock=?改为stock>0即可

提示:这块还是基于了数据库的update语句自带行锁,自带互斥的,库存只是在原来的基础上进行--操作,所以可以保证不会超卖

        //5.扣减库存boolean success=seckillVoucherService.update().setSql("stock = stock - 1") //set stock =stock - 1.eq("voucher_id",voucherId).gt("stock",0)//where id=? and stock > 0.update();if(!success){return Result.fail("库存不足!");}

库存恰好为0 

 

去看mysql进阶和java并发之后再看多表分段锁.

一人一单

新的业务流程如下所示

初版代码

这个代码在多线程时有并发安全问题,有可能多个线程都查到了0,然后都抢到了票

    @Override@Transactionalpublic Result seckillVoucher(Long voucherId) {//1.查询优惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);//2.判断秒杀是否开始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {//尚未开始return Result.fail("秒杀尚未开始");}//3.判断秒杀是否已经结束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {//尚未开始return Result.fail("秒杀已经结束");}//4.判断库存是否充足if (voucher.getStock()<1) {//库存不足return Result.fail("库存不足");}//5.一人一单Long userId = UserHolder.getUser().getId();//5.1查询订单int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();//5.2判断是否存在if(count>1){//用户已经购买过了return Result.fail("用户已经购买过一次");}//6.扣减库存boolean success=seckillVoucherService.update().setSql("stock = stock - 1") //set stock =stock - 1.eq("voucher_id",voucherId).gt("stock",0)//where id=? and stock > 0.update();if(!success){return Result.fail("库存不足!");}//7.创建订单VoucherOrder voucherOrder = new VoucherOrder();//7.1订单Idlong orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);//7.2用户IdvoucherOrder.setUserId(userId);//7.3代金券IdvoucherOrder.setVoucherId(voucherId);save(voucherOrder);//8.返回订单IDreturn Result.ok(orderId);}

优化代码

这里要对一整段代码加锁,将其抽取出来作为独立的方法,但是使用synchronized锁一个方法的话每次都只能有一个用户执行抢票,但是这里的加锁应该是针对同一个用户的多个请求.

锁id.toString()的话每次都是锁一个全新的对象. 不能起到作用。锁userId.toString().intern()的话就可以,这个是去字符串常量池找对象,常量池里每个字符串都有唯一的对象.

但是这个代码也有问题,事务注解是加在方法上的,锁的内容执行完之后到事务提交之前的这一段时间可能会有别的线程进来继续查询,查询到的也是旧数据,因为上一个事务没有提交.

    @Overridepublic Result seckillVoucher(Long voucherId) {//1.查询优惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);//2.判断秒杀是否开始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {//尚未开始return Result.fail("秒杀尚未开始");}//3.判断秒杀是否已经结束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {//尚未开始return Result.fail("秒杀已经结束");}//4.判断库存是否充足if (voucher.getStock()<1) {//库存不足return Result.fail("库存不足");}return createVoucherOrder(voucherId);}@Transactionalpublic   Result createVoucherOrder(Long voucherId) {//5.一人一单Long userId = UserHolder.getUser().getId();synchronized(userId.toString().intern()) {//5.1查询订单int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();//5.2判断是否存在if (count > 1) {//用户已经购买过了return Result.fail("用户已经购买过一次");}//6.扣减库存boolean success = seckillVoucherService.update().setSql("stock = stock - 1") //set stock =stock - 1.eq("voucher_id", voucherId).gt("stock", 0)//where id=? and stock > 0.update();if (!success) {return Result.fail("库存不足!");}//7.创建订单VoucherOrder voucherOrder = new VoucherOrder();//7.1订单Idlong orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);//7.2用户IdvoucherOrder.setUserId(userId);//7.3代金券IdvoucherOrder.setVoucherId(voucherId);save(voucherOrder);//8.返回订单IDreturn Result.ok(orderId);}}

最终代码

如果是像下面这样,在锁里面调用加了事务的方法的话,会有事务失效的问题,

下面的调用createVoucherOrder实际是this.createVoucherOrder这样的话,事务注解拿到的是当前的VoucherOrderServiceImpl对象,而不是其代理对象.

事务能生效是因为拿到了VoucherOrderServiceImpl的代理对象,做了一个动态代理,对代理对象做了事务处理.this指的是目标对象,是没有事务功能的,这是spring事务失效的几种可能性之一.

        Long userId = UserHolder.getUser().getId();synchronized(userId.toString().intern()) {return createVoucherOrder(voucherId);}

这里为了拿到代理对象,需要导入一个新依赖,并在启动类上开启代理对象暴露

        <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency>

@MapperScan("com.hmdp.mapper")
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class HmDianPingApplication {public static void main(String[] args) {SpringApplication.run(HmDianPingApplication.class, args);}
}

 接口层里新增一个方法

public interface IVoucherOrderService extends IService<VoucherOrder> {Result seckillVoucher(Long voucherId);Result createVoucherOrder(Long voucherId);}

 实现层里代码

@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Resourceprivate RedisIdWorker redisIdWorker;@Resourceprivate ISeckillVoucherService seckillVoucherService;@Overridepublic Result seckillVoucher(Long voucherId) {//1.查询优惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);//2.判断秒杀是否开始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {//尚未开始return Result.fail("秒杀尚未开始");}//3.判断秒杀是否已经结束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {//尚未开始return Result.fail("秒杀已经结束");}//4.判断库存是否充足if (voucher.getStock()<1) {//库存不足return Result.fail("库存不足");}//5.一人一单Long userId = UserHolder.getUser().getId();synchronized(userId.toString().intern()) {//取到了当前代理对象IVoucherService proxy =(IVoucherService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);}}@Transactionalpublic   Result createVoucherOrder(Long voucherId) {Long userId = UserHolder.getUser().getId();//5.1查询订单int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();//5.2判断是否存在if (count > 0) {//用户已经购买过了return Result.fail("用户已经购买过一次");}//6.扣减库存boolean success = seckillVoucherService.update().setSql("stock = stock - 1") //set stock =stock - 1.eq("voucher_id", voucherId).gt("stock", 0)//where id=? and stock > 0.update();if (!success) {return Result.fail("库存不足!");}//7.创建订单VoucherOrder voucherOrder = new VoucherOrder();//7.1订单Idlong orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);//7.2用户IdvoucherOrder.setUserId(userId);//7.3代金券IdvoucherOrder.setVoucherId(voucherId);save(voucherOrder);//8.返回订单IDreturn Result.ok(orderId);}
}

50抢20测试结果:

成功实现只能抢一张票

在代码里面,如果是大于1就是允许抢两张票,想控制用户可抢票数量可以根据这里进行修改

            //5.2判断是否存在if (count > 0) {//用户已经购买过了return Result.fail("用户已经购买过一次");}

集群下的线程并发安全问题

两个系统的代码互不干涉,所以肯定会有并发问题. 

两台tomcat,两个jvm,两个字符串常量池,两把锁。

分布式下需要使用同一把锁,由此引出分布式锁. 

分布式锁

基本原理和不同实现方式对比

 

Redis实现分布式锁的基本思路 

 代码实现

public class SimpleRedisLock implements ILock{private String lockname;private StringRedisTemplate stringRedisTemplate;private static final String KEY_PREFIX="lock:";public SimpleRedisLock(String lockname, StringRedisTemplate stringRedisTemplate) {this.lockname = lockname;this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean trylock(long timeoutSec) {//获取线程标识long threadId = Thread.currentThread().threadId();//获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX+lockname, threadId+"", timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success); //自动拆箱有风险}@Overridepublic void unlock() {//释放锁完成stringRedisTemplate.delete(KEY_PREFIX+lockname);}
}

使用分布式锁对代码进行优化

@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Resourceprivate RedisIdWorker redisIdWorker;@Resourceprivate ISeckillVoucherService seckillVoucherService;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result seckillVoucher(Long voucherId) {//1.查询优惠券...//2.判断秒杀是否开始...//3.判断秒杀是否已经结束...//4.判断库存是否充足...//5.一人一单Long userId = UserHolder.getUser().getId();//创建锁对象SimpleRedisLock lock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);//获取锁boolean isLock = lock.trylock(1200);//判断是否获取锁成功if(!isLock){//获取锁失败,返回报错return Result.fail("不允许重复下单");}try {//取到了当前代理对象IVoucherOrderService proxy =(IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);}finally {//释放锁lock.unlock();}}@Override@Transactionalpublic   Result createVoucherOrder(Long voucherId) {...}
}

分布式锁误删问题

如下图所示,线程1释放了线程2的锁,然后这时候线程3过来获取了锁,然后就有两个线程在同时跑。这里可以取出锁之后判断是不是自己的标识.是才可以释放锁.

 

之前直接使用线程ID作为标识是不够的,可能在分布式时有多个线程有相同id

代码优化

public class SimpleRedisLock implements ILock{private String lockname;private StringRedisTemplate stringRedisTemplate;private static final String KEY_PREFIX="lock:";private static final String ID_PREFIX= UUID.randomUUID().toString(true)+"-";public SimpleRedisLock(String lockname, StringRedisTemplate stringRedisTemplate) {this.lockname = lockname;this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean trylock(long timeoutSec) {//获取线程标识String threadId = ID_PREFIX+Thread.currentThread().getId();//获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX+lockname, threadId, timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success); //自动拆箱有风险}@Overridepublic void unlock() {//获取线程标识String threadId = ID_PREFIX+Thread.currentThread().getId();//获取锁中的标识String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + lockname);//判断标识是否一致if(threadId.equals(id)) {//释放锁完成stringRedisTemplate.delete(KEY_PREFIX + lockname);}}
}

分布式锁的原子性问题

在执行完业务到释放锁期间可能会因为jvm的垃圾回收fullGC发生阻塞。时间一长就触发超时释放。此时别的项目里的线程2过来加锁,又因为线程1里面已经判断过锁标识一致,所以会直接释放线程2的锁。

Lua脚本解决多条命令原子性问题

使用Lua脚本改造分布式锁

要准备一个脚本文件unlock.lua,这个文件放在和application.yaml同级的目录下

-- 比较线程标识与锁中的标识是否一致
if(redis.call('get',KEYS[1])==ARGV[1]) then-- 释放锁 del keyreturn redis.call('del',KEYS[1])
end
return 0

 改造后代码

public class SimpleRedisLock implements ILock{private String lockname;private StringRedisTemplate stringRedisTemplate;private static final String KEY_PREFIX="lock:";private static final String ID_PREFIX= UUID.randomUUID().toString(true)+"-";private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;static{UNLOCK_SCRIPT=new DefaultRedisScript<>();UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));UNLOCK_SCRIPT.setResultType(Long.class);}public SimpleRedisLock(String lockname, StringRedisTemplate stringRedisTemplate) {this.lockname = lockname;this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean trylock(long timeoutSec) {//获取线程标识String threadId = ID_PREFIX+Thread.currentThread().getId();//获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX+lockname, threadId, timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success); //自动拆箱有风险}@Overridepublic void unlock()  {
//        //获取线程标识
//        String threadId = ID_PREFIX+Thread.currentThread().getId();
//        //获取锁中的标识
//        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + lockname);
//        //判断标识是否一致
//        if(threadId.equals(id)) {
//            //模拟此处发生阻塞导致锁超时释放
//            //Thread.sleep(1000);
//            //释放锁完成
//            stringRedisTemplate.delete(KEY_PREFIX + lockname);
//        }//调用lua脚本stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX+lockname),ID_PREFIX+Thread.currentThread().getId());}}

总结

Redisson

快速入门

改造下单业务代码

经过测试也是可以正常使用。

@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Resourceprivate RedissonClient redissonClient;@Overridepublic Result seckillVoucher(Long voucherId) {//创建锁对象
//        SimpleRedisLock lock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);RLock lock = redissonClient.getLock("lock:order:" + userId);//获取锁
//        boolean isLock = lock.trylock(1200);boolean isLock = lock.tryLock();//判断是否获取锁成功if(!isLock){//获取锁失败,返回报错return Result.fail("不允许重复下单");}try {//取到了当前代理对象IVoucherOrderService proxy =(IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);}finally {//释放锁lock.unlock();}}
}

Redisson可重入锁原理

在下图左边的demo里面,是一个线程先后尝试获取锁共两次,在之前自己写的业务流程是没办法获取两次的。

参考jdk实现可重入锁的思路,同一个线程每次获取的时候记录次数加1,释放的时候次数减1.要记录锁的标识的和锁的重入次数可以使用hash结构。

每次释放锁的时候根据业务判断是减1还是删除锁。

使用Lua脚本完成可重入锁

获取锁的脚本

释放锁的脚本

Redisson的锁重试和WatchDog机制

整个流程如下所示

获取锁成功,发现锁的不过期的就利用看门狗机制一直刷新....逻辑很复杂。

 Redisson的multiLock原理(解决主从一致性问题)

主节点宕机之后锁就失效了,别的线程就可以来获取锁了。

 这个是必须所有主节点都获取到锁才算成功获取。

 

 

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



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

相关文章

零基础学习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 ...]

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

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

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

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

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

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 槽。

Redis地理数据类型GEO

通常要计算两个地理位置的距离不是很方便,这里可以直接通过Redis提供的GEO操作来完成地理位置相关的计算 1)添加地理位置 语法:geoadd key longitude latitude member [longitude latitude member] ...字段说明:key:存放地理位置的集合名称longitude:地理坐标的经度latitude:地理坐标的纬度member:表示这

Redis-主从集群

主从架构 单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。 主从数据同步原理 全量同步 主从第一次建立连接时,会执行全量同步,将master节点的所有数据都拷贝给slave节点,流程: 判断是否是第一次同步,如果是,返回版本信息(replication id 和offset),将salve节点的版本信息变为master的

Redis安装使用总结

一、下载安装 从 github 下载:https://github.com/MSOpenTech/redis/releases 或者 https://github.com/ServiceStack/redis-windows 解压缩,如图: 二、配置 打开redis.windows-sevice.conf文件, 2.1 绑定ip:搜索127.0.0.1 ,发现bind 127.0.0.

面对Redis数据量庞大时的应对策略

面对Redis数据量庞大时的应对策略,我们可以从多个维度出发,包括数据分片、内存优化、持久化策略、使用集群、硬件升级、数据淘汰策略、以及数据结构选择等。以下是对这些策略的详细探讨: 一、数据分片(Sharding) 当Redis数据量持续增长,单个实例的处理能力可能达到瓶颈。此时,可以通过数据分片将数据分散存储到多个Redis实例中,以实现水平扩展。分片的主要策略包括: 一致性哈希:使用一