如果你还不知道redis的基本命令与基本使用方法,请看
写在前面
redis辣么多数据结构,这么多命令,具体一点,都可以应用在什么场景呢?用来解决什么具体的问题?
分布式锁
redis是网络单线程的,它只有一个线程负责接受请求,这个特性即降低了redis本身的开发成本,也提高了redis的可用性。
分布式环境下,数据一致性问题一直是一个比较重要的话题,分布式与单机情况下最大的不同在于其不是多线程而是多进程。
多线程由于可以共享堆内存,因此可以简单的采取内存作为标记存储位置,例如cas,java的synchronize。而进程之间可能不在同一台物理机上,因此需要将标记存储在一个所有进程都能看到的地方。
常见的场景,秒杀场景中的库存超卖问题、多机定时任务的并发执行问题等。
库存超卖问题
假如订单服务部署了多个实例。
现在做一个商品秒杀活动,商品一共只有2个,同时购买的用户则可能有几千上万。
理想状态下第一个和第二个用户能购买成功,其他用户提示购买失败,
实际可能出现的情况是,多个用户都同时查到商品还没卖完,第一个用户买到,更新库存之前,第二个用户又下了订单,导致出错。
下面用java代码做一个演示:
java实例都可以被正常运行在jdk1.8+,使用jedis连接redis实例
importredis.clients.jedis.Jedis;importredis.clients.jedis.JedisPool;importredis.clients.jedis.JedisPoolConfig;/*** JedisPool连接
*@authortaifeng zhang
**/
public classJedisPoolConnect {public staticJedisPool jedispool;/*** 连接并返回jedis实例
**/
public staticJedis connectJedis () {if (jedispool == null) {
JedisPoolConfig jedisPoolConfig= newJedisPoolConfig();
jedisPoolConfig.setMinIdle(1);
jedisPoolConfig.setMaxIdle(10);
jedisPoolConfig.setTestOnBorrow(true);
jedispool= new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);
}returnjedispool.getResource();
}
}import redis.clients.jedis.*;importredis.clients.jedis.Jedis;/*** 一个简单的超卖演示程序
**/
public classMarketWrong {public static String GOODS_LEN_KEY = "jedis:market:demo";private final Integer DECR_THREAD_LEN = 16;public voidsuperMarket () {//开线程去减库存
int i =DECR_THREAD_LEN;while (i > 0) {new Thread(() ->{boolean hasGoods = true;while (hasGoods) { //当库存大于0的时候
int goodsLen =getGoodsLen();if (goodsLen > 0) {
decrGoodsLen();//一般进来之后就直接减去库存了
System.out.println("现在库存为" +getGoodsLen());try{
Thread.sleep(100); //模拟中间处理流程
} catch(Exception e) {
System.out.println("执行减库存错误" + e.getMessage() + e.getLocalizedMessage() +e.getStackTrace());
}finally{//最后逻辑
}
}else{
System.out.println("======卖完啦=======");
hasGoods= false;
}
}
}).start();
i--;
}
}public voidsetGoodsLen (Integer len) {
Jedis jedis=JedisPoolConnect.connectJedis();try{
jedis.set(GOODS_LEN_KEY, String.valueOf(len));
}finally{
jedis.close();
}
}privateInteger getGoodsLen () {
Jedis jedis=JedisPoolConnect.connectJedis();try{
String val=jedis.get(GOODS_LEN_KEY);if (val != null) {returnInteger.parseInt(val);
}
}finally{
jedis.close();
}return 0;
}private voiddecrGoodsLen () {
Jedis jedis=JedisPoolConnect.connectJedis();try{//库存减1
jedis.decr(GOODS_LEN_KEY);
}finally{
jedis.close();
}
}
}
用junit测试上面的代码:
importorg.junit.Test;importorg.springframework.boot.test.context.SpringBootTest;
@SpringBootTestpublic classMarketWrongTests {/*** 测试超卖小程序*/@Testpublic void superMarket () throwsException {
MarketWrong marketWrong= newMarketWrong();//这次就卖500件吧
marketWrong.setGoodsLen(500);
marketWrong.superMarket();
Thread.sleep(60000); //卖一分钟
}
}
运行输出,每次库存都会变为负数,开了16个线程同时买东西:
//省略了几万行
现在库存为8
现在库存为8
现在库存为4
现在库存为4
现在库存为4
现在库存为4
现在库存为3
现在库存为-5现在库存为-5现在库存为-5现在库存为-5现在库存为-5现在库存为-5现在库存为-5现在库存为-5
===&