秒杀系统Redis中的Key设计

概述

在秒杀系统中通常会需要将key存储到redis中,key的一般设计通常通过类名:midKey+后缀key的形式构成,一般情况下在加个过期时间,大概key设计,通常情况下会将key设计为一个接口一个抽象类多个实现类的形式,如下

demo

在这里插入图片描述
接口中有过期时间和前缀prefix两个方法,抽象类实现整个接口,重写prefix为类名+前缀prefix的形式,这样做的目的在于set或get参数时,可以通过传入实现类用接口就可以接,从接口中获取具体的前缀、过期时间,存放到redis中,更易于拓展

public interface KeyPrefix {
		
	int expireSeconds();
	
	String getPrefix();
	
}

抽象类则实现接口并重写getPrefix方法

在这里插入图片描述

其中,抽象类这个方法是设置过期时间的随机值,用于防止缓存雪崩
在这里插入图片描述

具体代码如下

public abstract class BasePrefix implements KeyPrefix{
	
	private int expireSeconds;
	
	private String prefix;
	
	public BasePrefix(String prefix) {//0代表永不过期
		this(0, prefix);
	}
	
	public BasePrefix(int expireSeconds, String prefix) {
		this.expireSeconds = expireSeconds;
		this.prefix = prefix;
	}
	
	public BasePrefix(int expireSeconds, boolean addRandom, String prefix) {
		if(addRandom) {
			Random random = new Random(System.currentTimeMillis());
			int extend = expireSeconds / 5;
			int ran = random.nextInt(extend);
			int finalExtend = extend / 2 - ran;
			this.expireSeconds = expireSeconds + finalExtend;
		} else {
			this.expireSeconds = expireSeconds;
		}
		this.prefix = prefix;
	}

	
	public int expireSeconds() {//默认0代表永不过期
		return expireSeconds;
	}

	public String getPrefix() {
		String className = getClass().getSimpleName();
		//类名:prefix
		return className+":" + prefix;
	}

}

GoodsKey则具体实现了抽象类

public class GoodsKey extends BasePrefix {
	
	public GoodsKey(int expireSeconds, boolean addRandom, String prefix) {
		
		super(expireSeconds, addRandom, prefix);
	}

}

RedisUtil则将相应的类型转换为String并存到redis中
在这里插入图片描述
或是将String类型转换为bean返回
在这里插入图片描述

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate redisTemplate;

    public <T> boolean set(KeyPrefix prefix, String key, T value) {

        try {
            String str = beanToString(value);
            if (StringUtils.isEmpty(str)){
                return false;
            }
            //GoodKey:prefix+key 类名:prefix+key
            String realKey = prefix.getPrefix() + key;
            if (prefix.expireSeconds()<=0){
                redisTemplate.opsForValue().set(realKey,str);
            }else {
                redisTemplate.opsForValue().set(realKey,str,prefix.expireSeconds(), TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public <T> T get(KeyPrefix prefix, String key, Class<T> clazz) {

        try {
            String realKey = prefix.getPrefix() + key;
            String str =(String)redisTemplate.opsForValue().get(realKey);
            T t = stringToBean(str, clazz);
            return t;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> T stringToBean(String str, Class<T> clazz) throws JsonProcessingException {
        if (str == null || str.length() == 0 || clazz == null) {
            return null;
        }
        if (clazz == int.class || clazz == Integer.class) {
            return (T) Integer.valueOf(str);
        } else if (clazz == String.class) {
            return (T) str;
        } else if (clazz == long.class || clazz == Long.class) {
            return (T) Long.valueOf(str);
        } else {
            return new ObjectMapper().readValue(str,clazz);
        }
    }

    public static <T> String beanToString(T value) throws JsonProcessingException {
        if (value == null) {
            return null;
        }
        Class<?> clazz = value.getClass();
        if (clazz == int.class || clazz == Integer.class) {
            return "" + value;
        } else if (clazz == String.class) {
            return (String) value;
        } else if (clazz == long.class || clazz == Long.class) {
            return "" + value;
        } else {
            return new ObjectMapper().writeValueAsString(value);
        }
    }

}

来做个测试吧

@RequestMapping("redis")
@RestController
public class TestController {

    @Autowired
    private RedisUtil redisUtil;

    @GetMapping("set")
    public void set(){

        redisUtil.set(new GoodsKey(60,true,"gl"),"",String.class);

    }

    @GetMapping("get")
    public void get(){

        String gl = redisUtil.get(new GoodsKey(60, true, "gl"), "", String.class);
        System.out.println("gl:"+gl);

    }

}

可以看到相应的值了,如此
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 对于您的问题,我可以提供一些设计基于 Redis 的秒架构的建议。 首先,Redis 是一个快速、开源的内存键值存储系统,支持多种数据结构。在实现秒架构时,Redis 的高性能和支持原子性操作的特性使得其成为一个非常好的选择。 以下是一些基于 Redis 的秒架构设计建议: 1. 使用 Redis 存储商品信息和库存。可以使用 Redis 的 Hash 数据类型来存储每个商品的详细信息,如商品名称、描述、价格、剩余库存等。使用 Redis 的计数器数据类型来实现商品的库存管理。 2. 使用 Redis 的分布式锁来实现商品的售卖和库存的减少。当一个用户购买商品时,使用分布式锁来确保在同一时间只有一个用户可以购买该商品。如果锁定成功,则将商品的库存减少一个数量,并将订单信息存储在 Redis 。 3. 使用消息队列来处理用户订单。在秒活动期间,订单量可能非常大。为了避免订单丢失或延迟处理,可以使用消息队列来确保所有订单都能被及时处理。 4. 可以考虑使用 Redis 集群来扩展系统的性能和容量。Redis 集群可以自动将数据分布在多个节点上,并实现高可用性和故障转移。 5. 最后,需要对系统进行充分的压力测试和性能测试,以确保其在高并发环境下能够正常运行。 这些是设计基于 Redis 的秒架构的一些建议,希望能对您有所帮助。 ### 回答2: 秒是一种高并发的业务场景,为了保证系统能够在短时间内处理大量的并发请求,可以设计一个基于Redis的秒架构。 首先,为了确保系统的高可用性和水平扩展性,可以使用主从复制的方式部署Redis服务器。将读写请求分发到不同的Redis实例,提高系统的并发处理能力。 在设计业务的数据库模型时,可以使用Redis的Hash数据结构,将商品ID作为Key,将商品库存和商品信息等存储在Hash。这样可以将商品信息保存在内存,提高读取速度。 为了防止超卖和维护商品库存的一致性,可以使用Redis的事务机制和CAS(Compare and Set)操作。在用户发起秒请求时,首先判断商品库存是否大于0,如果大于0,则使用Redis事务机制将商品库存减1,并将秒成功的用户信息加入到一个集合(Set)。如果库存小于等于0,则秒失败。通过CAS操作,可以保证商品库存的准确性,避免多个请求同时减少库存而导致超卖的问题。 为了应对高并发请求,可以使用分布式锁来控制用户的并发访问。Redis提供了分布式锁的实现方式,如使用SETNX命令来获取锁以及使用DEL命令来释放锁。当用户发起秒请求时,先尝试获取锁,如果获取成功,则执行秒逻辑,否则等待一段时间后重新尝试。 为了减轻数据库的压力,可以结合异步处理的方式。将秒请求放入消息队列,通过消费者的方式异步处理秒逻辑,这样可以将高并发的请求分散到不同的时间段内进行处理,提高系统的并发处理能力。 最后,为了保证系统的稳定性和故障恢复能力,可以设置监控和恢复机制。通过Redis的监控工具对Redis服务器进行监控,并设置服务器宕机时的自动切换机制,将流量引导到备用节点上,确保系统的可用性。 总之,基于Redis的秒架构需要考虑高可用性、水平扩展性、数据一致性和并发处理能力等方面,并结合分布式锁、事务机制、异步处理和监控机制等技术手段来实现。 ### 回答3: 秒架构是一种高并发场景下常见的设计方案,旨在解决大量用户同时请求同一商品的情况下保证系统的可用性和稳定性。基于Redis的秒架构可以采用以下设计方案: 1. 商品库存管理:使用Redis的Hash结构来存储商品的库存信息。每个商品对应一个Hash结构,包括库存数量、已售数量、商品ID等字段。可以通过Redis的原子操作将库存数量进行减少和增加,保证库存的实时性和一致性。 2. 请求限流:为了控制系统的并发请求量,可以使用Redis的计数器功能实现请求的限流。每次用户发起秒请求时,利用Redis的INCR操作对计数器进行自增操作,同时设置过期时间,超过限定值的请求将被拒绝。 3. 重复请求处理:由于高并发场景下,用户可能多次提交秒请求,为了避免重复购买商品,引入Redis的Set数据结构记录已经购买过的用户ID。每次用户发起秒请求前,先判断用户ID是否存在于Set,若存在则拒绝请求,否则可以继续进行秒操作。 4. 异步下单:为了提高系统的并发处理能力,并降低响应时间,可以使用消息队列来实现异步下单的操作。秒成功后,将下单的请求存入消息队列,由消费者进行实际的下单操作,将订单信息写入数据库。 5. 分布式部署:为了进一步提高系统的稳定性和可扩展性,可以采用分布式部署架构。将商品的库存和用户ID等信息分片存储在不同的Redis节点上,通过分布式缓存间件来实现数据的一致性和负载均衡。 通过以上的设计方案,基于Redis的秒架构可以实现高并发场景下的安全、稳定和高效的秒操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值