redis 使用示例

使用示例

示例 1:设置和获取字符串值

public class StringValueExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 设置键值对
        redisCache.set("myKey", "myValue");

        // 获取值
        String value = redisCache.get("myKey");
        System.out.println("The value of 'myKey' is: " + value);

        // 设置带有过期时间的键值对
        redisCache.setex("expiringKey", 60, "expiringValue"); // 60秒后过期

        // 获取值
        String expiringValue = redisCache.get("expiringKey");
        System.out.println("The value of 'expiringKey' is: " + expiringValue);
    }
}

示例 2:操作整数值

public class IntegerValueExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 设置整数值
        redisCache.setInt("intKey", 100);

        // 获取整数值
        Integer intValue = redisCache.getInt("intKey");
        System.out.println("The value of 'intKey' is: " + intValue);

        // 增加整数值
        redisCache.incrBy("intKey", 50);
        intValue = redisCache.getInt("intKey");
        System.out.println("The value of 'intKey' after increment is: " + intValue);

        // 减少整数值
        redisCache.decrBy("intKey", 30);
        intValue = redisCache.getInt("intKey");
        System.out.println("The value of 'intKey' after decrement is: " + intValue);
    }
}

示例 3:操作哈希表

public class HashValueExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 设置哈希表字段
        redisCache.hset("user:1001", "name", "John Doe");
        redisCache.hset("user:1001", "age", 30);

        // 获取哈希表字段的值
        String name = redisCache.hget("user:1001", "name");
        Integer age = redisCache.hget("user:1001", "age");

        System.out.println("User 1001 Name: " + name);
        System.out.println("User 1001 Age: " + age);

        // 获取整个哈希表
        Map<String, Object> user = redisCache.hgetAll("user:1001");
        System.out.println("User 1001 Details: " + user);
    }
}

示例 3:将对象存入哈希表

import java.util.ArrayList;
import java.util.List;

import com.litongjava.jfinal.plugins.model.User;
import com.litongjava.redis.Redis;
import com.litongjava.redis.RedisPlugin;

public class HashDemoWithUser {

  public static void main(String[] args) {
    // 用于缓存bbs模块的redis服务
    RedisPlugin bbsRedis = new RedisPlugin("main", "192.168.3.9", 6379, 3000, "123456");
    bbsRedis.start();

    List<User> list = new ArrayList<>();
    list.add(new User("Tong Li", "0000000"));
    list.add(new User("Tong Li1", "0000000"));

    // set list
    Redis.use().hsetList("user", "001", list);
    // get list
    List<User> hgetList = Redis.use().hgetList("user", "001", User.class);
    System.out.println(hgetList.size());
    bbsRedis.stop();
  }
}

示例 5:操作列表

public class ListValueExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 将元素压入列表头部
        redisCache.lpush("myList", "element1", "element2", "element3");

        // 从列表尾部弹出元素
        String element = redisCache.rpop("myList");
        System.out.println("Popped element from myList: " + element);

        // 获取列表中的所有元素
        List<String> elements = redisCache.lrange("myList", 0, -1);
        System.out.println("All elements in myList: " + elements);
    }
}

示例 6:检查键是否存在

public class KeyExistenceExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 设置一个键值对
        redisCache.set("someKey", "someValue");

        // 检查键是否存在
        boolean exists = redisCache.hasKey("someKey");
        System.out.println("Does 'someKey' exist? " + exists);

        // 删除键
        redisCache.del("someKey");

        // 再次检查键是否存在
        exists = redisCache.hasKey("someKey");
        System.out.println("Does 'someKey' exist after deletion? " + exists);
    }
}

示例 7:发布和订阅消息

public class PubSubExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 订阅一个频道
        new Thread(() -> {
            redisCache.subscribe(new JedisPubSub() {
                @Override
                public void onMessage(String channel, String message) {
                    System.out.println("Received message: " + message + " from channel: " + channel);
                }
            }, "myChannel");
        }).start();

        // 发布消息到频道
        redisCache.publish("myChannel", "Hello, Redis!");
    }
}

示例 8:使用 Redis 脚本执行操作

public class ScriptExecutionExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // Lua 脚本示例:获取一个键的值并设置一个新值
        String script = "local value = redis.call('get', KEYS[1]); " +
                        "redis.call('set', KEYS[2], ARGV[1]); " +
                        "return value;";

        // 执行 Lua 脚本
        Object result = redisCache.eval(script, 2, "key1", "key2", "newValue");
        System.out.println("Result of script execution: " + result);
    }
}

示例 9:使用扫描命令查找匹配的键

public class ScanKeysExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 扫描匹配的键
        redisCache.scan(0, "user:*", 10, keys -> {
            System.out.println("Found keys: " + keys);
            return true; // 返回 true 继续扫描,返回 false 停止扫描
        });
    }
}

示例 10:获取随机元素(集合操作)

public class SetOperationsExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 添加元素到集合
        redisCache.sadd("mySet", "element1", "element2", "element3");

        // 获取集合中的随机元素
        String randomElement = redisCache.srandmember("mySet");
        System.out.println("Random element from mySet: " + randomElement);

        // 获取集合中的所有元素
        Set<String> elements = redisCache.smembers("mySet");
        System.out.println("All elements in mySet: " + elements);
    }
}

示例 11:为键设置过期时间

public class ExpireKeyExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 设置键值对
        redisCache.set("tempKey", "tempValue");

        // 设置键的过期时间为 30 秒
        redisCache.expire("tempKey", 30);

        // 检查剩余时间
        Long ttl = redisCache.ttl("tempKey");
        System.out.println("TTL of 'tempKey': " + ttl + " seconds");
    }
}

示例 12 :使用 RedisCache.lock 进行分布式锁

public class DistributedLockExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 定义锁的名称
        String lockName = "distributed_lock";

        // 锁的超时时间,单位:秒
        int lockExpireTime = 120; // 锁的过期时间为 120 秒

        // 获取锁的等待时间,单位:秒
        int lockTimeout = 5; // 如果在 5 秒内没有获取到锁,则放弃

        // 尝试获取锁
        String lockId = redisCache.lock(lockName, lockExpireTime, lockTimeout);

        if (lockId != null) {
            try {
                // 成功获取到锁,执行需要保护的业务逻辑
                System.out.println("成功获取锁,正在执行业务逻辑...");

                // 业务逻辑代码
                performBusinessLogic();

            } finally {
                // 释放锁
                redisCache.unlock(lockName, lockId);
                System.out.println("锁已释放。");
            }
        } else {
            // 未能获取到锁
            System.out.println("未能获取锁,可能另一个进程持有该锁。");
        }
    }

    private static void performBusinessLogic() {
        // 模拟业务逻辑的执行
        try {
            Thread.sleep(3000); // 假设业务逻辑需要 3 秒钟才能执行完毕
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("业务逻辑执行成功。");
    }
}

代码说明

  1. 获取 RedisCache 实例

    • 通过 Redis.use() 获取 RedisCache 实例。
  2. 定义锁相关参数

    • lockName: 锁的名称,通常与业务逻辑相关联。
    • lockExpireTime: 锁的过期时间,设置为 120 秒。
    • lockTimeout: 尝试获取锁的超时时间,设置为 5 秒。
  3. 尝试获取分布式锁

    • 调用 redisCache.lock(lockName, lockExpireTime, lockTimeout) 尝试获取锁。
    • 如果成功获取到锁,返回 lockId;否则返回 null
  4. 执行业务逻辑

    • 在成功获取锁后,执行需要保护的业务逻辑。
  5. 释放锁

    • 在业务逻辑执行完毕后,调用 redisCache.unlock(lockName, lockId) 释放锁。

注意事项

  • 分布式锁通常用于确保在分布式环境下,多个进程不会同时执行同一段代码,从而避免资源竞争导致的数据不一致问题。
  • 在高并发场景下,设置合理的锁过期时间和获取锁的超时时间非常重要。

示例 12 :使用 RedisCachewithLock 方法进行分布式锁

以下是一个使用 RedisCachewithLock 方法进行分布式锁的示例。withLock 方法会自动处理锁的获取和释放逻辑,用户只需专注于需要加锁的业务逻辑。

public class DistributedLockWithLockMethodExample {

    public static void main(String[] args) {
        // 获取 RedisCache 实例
        RedisCache redisCache = Redis.use();

        // 定义锁的名称
        String lockName = "distributed_lock_with_withLock";

        // 锁的超时时间,单位:秒
        int lockExpireTime = 120; // 锁的过期时间为 120 秒

        // 获取锁的等待时间,单位:秒
        int lockTimeout = 5; // 如果在 5 秒内没有获取到锁,则放弃

        // 使用 withLock 方法执行加锁的业务逻辑
        boolean success = redisCache.withLock(lockName, lockExpireTime, lockTimeout, () -> {
            // 业务逻辑代码,这段代码在获取锁后执行
            System.out.println("成功获取锁,正在执行业务逻辑...");
            performBusinessLogic();
        });

        if (success) {
            System.out.println("业务逻辑执行完毕,锁已释放。");
        } else {
            System.out.println("未能获取锁,可能另一个进程持有该锁。");
        }
    }

    private static void performBusinessLogic() {
        // 模拟业务逻辑的执行
        try {
            Thread.sleep(3000); // 假设业务逻辑需要 3 秒钟才能执行完毕
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("业务逻辑执行成功。");
    }
}

代码说明

  1. 获取 RedisCache 实例

    • 通过 Redis.use() 获取 RedisCache 实例。
  2. 定义锁相关参数

    • lockName: 锁的名称,用于标识此锁。
    • lockExpireTime: 锁的过期时间,设置为 120 秒。
    • lockTimeout: 尝试获取锁的超时时间,设置为 5 秒。
  3. 使用 withLock 方法加锁执行业务逻辑

    • 调用 redisCache.withLock(lockName, lockExpireTime, lockTimeout, () -> { ... }),并在 Lambda 表达式中编写需要保护的业务逻辑。
    • 如果成功获取锁并执行业务逻辑,则返回 true;否则返回 false
  4. 锁的自动释放

    • withLock 方法会在业务逻辑执行完毕后自动释放锁,因此不需要手动调用 unlock

使用 withLock 方法的优点

  • 简洁易用withLock 方法封装了锁的获取、业务逻辑的执行和锁的释放,简化了代码。
  • 自动释放锁:即使在业务逻辑抛出异常的情况下,withLock 方法也能确保锁被正确释放,避免死锁问题。

防止重复提交

问题

在接口 bindUser 中,为了防止前端重复提交设备标识 userEquipment,使用 Redis 的 SET 数据结构,检测是否已经存在重复提交:

解决办法:

  • 在 Redis 中使用 SET 数据结构存储设备标识 userEquipment,并设置 120 秒的过期时间
  • 检测设备标识是否已经存在于 Redis 集合中

实现方法:使用 SISMEMBER 检查去重

Redis 的 SISMEMBER 是一种高效的去重检查方式。我们可以在接收到请求时,直接判断设备码是否已存在,如果存在,则阻止继续提交,避免重复请求。只需通过 SISMEMBER 检查,避免了原子性问题。

boolean isExist = Redis.use().sismember(RedisKeys.equipment_submitted, userEquipment);
if (isExist) {
    return ResponseResult.fail("重复提交,请等待");
}
Redis.use().sadd(RedisKeys.equipment_submitted, userEquipment); // 保存设备码
Redis.use().expire(RedBookRedisKeys.equipment_submitted, 120); //120秒后过期
  • SISMEMBER:检查 SET 中是否包含该设备码,若存在则说明已提交,直接返回错误提示,阻止重复提交。
  • SADD:若设备码不存在,则添加到 SET 中,设置一个合理的过期时间来定时清除已提交的记录。