参考《redis实战》

需求

1、商品交易的主体是个人,个人拥有姓名、资产属性。
2、每个人都有N个商品,每个商品都有唯一的商品编号。
3、允许商品投放到市场进行交易,一经投放,则商品从个人转移到市场,市场的商品按照价格排序。
4、交易需要判断资产是否足够,如果足够,商品从市场转移到买家
5、商品从个人到市场,或者市场到个人,都需要事务判断

分析

第一个需求

可以用键值对保存姓名和资产信息,主键为用户的id,hincrBy方法对资产进行增加或减少。以下是hmset、hgetall、hincrBy的用法。

// 赋值
local:0>hmset person:1 name zhangsan funds 100
"OK"
// 显示hash内容
local:0>hgetall person:1
1) "name"
2) "zhangsan"
3) "funds"
4) "100"
// 对hash的某个key的值进行相加
local:0>hincrBy person:1 funds 20
"120"
// 显示hash内容
local:0>hgetall person:1
1) "name"
2) "zhangsan"
3) "funds"
4) "120"

第二个需求

可以用集合来存储商品,主键是商品的id,集合里放商品的编号。以下是sadd、smembers、sismember、srem的用法。

// 加入集合
local:0>sadd package:1 itemX
"1"
local:0>sadd package:1 itemY
"1"
local:0>sadd package:1 itemZ
"1"
// 显示集合元素
local:0>smembers package:1
1) "itemZ"
2) "itemX"
3) "itemY"
// 集合是否存在某个元素,1为是
local:0>sismember package:1 itemX
"1"
// 移除集合的元素
local:0>srem package:1 itemX
"1"
// 集合是否存在某个元素,0为否
local:0>sismember package:1 itemX
"0"
// 显示集合元素
local:0>smembers package:1
1) "itemZ"
2) "itemY"

第三个需求

市场的商品排序,需要按价格排序,所以可以用有序集合。以下是zadd、zrevrange、zrem的用法。

// 新增元素
local:0>zadd market: 10 itemX.1
"1"
local:0>zadd market: 11 itemY.1
"1"
local:0>zadd market: 12 itemY.2
"1"
// 从高到低排序
local:0>zrevrange market: 0 -1 withscores
1) "itemY.2"
2) "12"
3) "itemY.1"
4) "11"
5) "itemX.1"
6) "10"
// 移除
local:0>zrem market: itemY.1
"1"
local:0>zrevrange market: 0 -1 withscores
1) "itemY.2"
2) "12"
3) "itemX.1"
4) "10"

第四个需求

从hash取出资产,跟商品金额进行判断

第五个需求

在redis中,事务的开始是以MULTI开始,EXEC结束。在事务之间可以传输多个命令,但是实际上这些命令并不会被执行,直到调用EXEC的时候才会执行。
redis还提供了WATCH和UNWATCH对key进行监控和取消监控,如果事务执行之前,这些key被改动,则事务将会被中断。
UNWATCH在WATCH之后以及MULTI前取消对key的监控,DISCARD在MULTI执行之后,EXEC执行之前放弃执行事务块内的所有命令。
通过watch监听数据,而不是直接加锁,雷同于CAS的乐观锁和synchronized的悲观锁。

实践

定义用户

@Test
public void person() {
    Map<String, String> zhangsanMap = new HashMap();
    zhangsanMap.put("name", "张三");
    zhangsanMap.put("funds", "100");
    JedisUtils.hmset("person:1", zhangsanMap);

    Map<String, String> lisiMap = new HashMap();
    lisiMap.put("name", "李四");
    lisiMap.put("funds", "10");
    JedisUtils.hmset("person:2", lisiMap);

    System.out.println("张三的信息:" + JedisUtils.hgetAll("person:1"));
    System.out.println("李四的信息:" + JedisUtils.hgetAll("person:2"));
}

定义用户的商品

@Test
public void personPackage() {
    // 张三的商品
    JedisUtils.sadd("package:1", "itemX");
    JedisUtils.sadd("package:1", "itemY");
    JedisUtils.sadd("package:1", "itemZ");
    System.out.println("张三的商品:" + JedisUtils.smembers("package:1"));
    // 李四的商品
    JedisUtils.sadd("package:2", "itemA");
    JedisUtils.sadd("package:2", "itemB");
    JedisUtils.sadd("package:2", "itemC");
    System.out.println("李四的商品:" + JedisUtils.smembers("package:2"));
}

商品投放市场

@Test
public void putMarket() {
    System.out.println("张三的商品:" + JedisUtils.smembers("package:1"));
    System.out.println("市场的商品:" + JedisUtils.zrangeWithScores("market:", 0, -1));
    while (true) {
        Jedis jedis = JedisUtils.watch("package:1");
        // 已经不存在了,则不监听
        if (!JedisUtils.sismember("package:1", "itemX")) {
            jedis.unwatch();
            break;
        }
        Transaction transaction = jedis.multi();
        // 张三的商品移除
        transaction.srem("package:1", "itemX");
        // 市场的商品增加
        transaction.zadd("market:", 8, "itemX.1");
        List<Object> exec = transaction.exec();
        // 不为空说明执行成功,跳出循环
        if (null != exec) {
            break;
        }
    }
    System.out.println("张三的商品:" + JedisUtils.smembers("package:1"));
    System.out.println("市场的商品:" + JedisUtils.zrangeWithScores("market:", 0, -1));
}

购买商品

@Test
public void getMarket() {
    System.out.println("张三的信息:" + JedisUtils.hgetAll("person:1"));
    System.out.println("李四的信息:" + JedisUtils.hgetAll("person:2"));
    System.out.println("李四的商品:" + JedisUtils.smembers("package:2"));
    System.out.println("市场的商品:" + JedisUtils.zrangeWithScores("market:", 0, -1));
    while (true) {
        // 监听商品是否被其他人买走了
        Jedis jedis = JedisUtils.watch("market:");
        double price = JedisUtils.zscore("market:", "itemX.1");
        double funds = Double.valueOf(JedisUtils.hmget("person:2", "funds").get(0));
        if (price > funds) {
            jedis.unwatch();
            break;
        }
        Transaction transaction = jedis.multi();
        // 张三的金额新增
        transaction.hincrBy("person:1", "funds", new Double(price).longValue());
        // 李四的金额减少
        transaction.hincrBy("person:2", "funds", new Double(-price).longValue());
        // 市场的商品移除
        transaction.zrem("market:", "itemX.1");
        // 李四的商品新增
        transaction.sadd("package:2", "itemX");
        List<Object> exec = transaction.exec();
        // 不为空说明执行成功,跳出循环
        if (null != exec) {
            break;
        }
    }
    System.out.println("张三的信息:" + JedisUtils.hgetAll("person:1"));
    System.out.println("李四的信息:" + JedisUtils.hgetAll("person:2"));
    System.out.println("李四的商品:" + JedisUtils.smembers("package:2"));
    System.out.println("市场的商品:" + JedisUtils.zrangeWithScores("market:", 0, -1));
}

大军
847 声望183 粉丝

学而不思则罔,思而不学则殆