2024-09-04



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
 
@Service
public class SignService {
 
    @Autowired
    private StringRedisTemplate redisTemplate;
 
    @Autowired
    private DefaultRedisScript<Long> signScript;
 
    // 用户签到方法
    public boolean sign(String userId, String date) {
        // 使用BitMap记录用户签到情况,一天使用一个Bit
        String key = "sign_bitmap:" + date;
        Long result = redisTemplate.execute(signScript, Collections.singletonList(key), userId);
        return result != null && result == 1;
    }
 
    // 统计签到用户数方法
    public long countSign(String date) {
        String key = "sign_bitmap:" + date;
        // 使用Redis命令计算BitMap中值为1的个数
        return redisTemplate.execute((RedisCallback<Long>) con -> {
            byte[] keyByte = con.getByteName(key);
            return con.bitCount(keyByte);
        });
    }
}

这个代码实例展示了如何在Spring Boot应用中使用Redis的BitMap实现用户的签到以及签到用户数的统计。sign方法负责用户的签到操作,使用了一个Redis脚本来原子地将用户ID对应的位设置为1。countSign方法则用于统计某一天签到用户的数量,它通过Redis的BITCOUNT命令来实现。

2024-09-04



# 设置Redis的最大内存限制
maxmemory 2gb

# 设置内存淘汰策略
maxmemory-policy allkeys-lru

# 设置Redis的日志文件路径
logfile "/var/log/redis/redis-server.log"

# 设置Redis的持久化策略
save 900 1
save 300 10
save 60 10000

# 设置Redis的TCP连接队列长度,-1为不限制
tcp-backlog 511

# 设置Redis的最大连接数
maxclients 10000

# 设置Redis的最大数据库数量
databases 16

# 设置Redis服务器的端口号
port 6379

# 设置Redis的密码
requirepass yourpassword

# 设置Redis是否以守护进程方式运行
daemonize yes

# 设置Redis的工作目录
dir /var/lib/redis

以上配置示例展示了如何设置Redis的最大内存、淘汰策略、日志文件路径、持久化策略、TCP连接队列长度、最大连接数、数据库数量、端口号、密码以及是否以守护进程方式运行等参数。这些配置可以根据实际需求进行调整,以优化Redis的性能和安全性。

2024-09-04

在Spring框架中使用RedisTemplate获取满足特定条件的key,并进行批量获取,可以使用execute方法来执行原生的Lua脚本或者使用keys方法来匹配所有key,但需要注意keys命令在大数据集时性能不佳,应避免在生产环境使用。

以下是使用RedisTemplate进行批量获取满足条件的key和批量获取key的示例代码:




import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Component;
 
import java.util.ArrayList;
import java.util.List;
 
@Component
public class RedisService {
 
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
 
    // 获取满足特定模式的key列表
    public List<String> findKeys(String pattern) {
        List<String> keys = new ArrayList<>();
        // 使用scan命令进行模式匹配
        ScanOptions scanOptions = ScanOptions.scanOptions().match(pattern).count(1000).build();
        // 执行scan命令
        redisTemplate.execute((connection) -> {
            byte[] cursor = ScanOptions.UNSERIALIZED_NONE;
            do {
                ScanResult<byte[]> scanResult = connection.scan(cursor, scanOptions);
                keys.addAll(Arrays.asList(scanResult.getResult()));
                cursor = scanResult.getCursor();
            } while (!Arrays.equals(cursor, ScanOptions.UNSERIALIZED_ZERO));
            return null;
        }, true);
        return keys;
    }
 
    // 批量获取key对应的值
    public List<Object> getKeys(List<String> keys) {
        if (keys == null || keys.isEmpty()) {
            return new ArrayList<>();
        }
        // 批量获取key对应的值
        return redisTemplate.opsForValue().multiGet(keys);
    }
}

在这个示例中,findKeys方法使用了SCAN命令配合MATCH选项来查找满足特定模式的key。getKeys方法接受一个key列表,并使用multiGet方法批量获取这些key的值。

注意:SCAN命令是以游标的形式来遍历数据库的,可以避免KEYS命令的块问题,但在大数据集时可能仍然会对性能有影响。在生产环境中应当根据实际情况谨慎使用,并考虑使用更高效的解决方案,如使用SET数据结构来存储满足特定条件的key,以便可以快速检索。

2024-09-04

Redis的哈希(Hash)是一种用于存储键值对集合的数据类型,其中键与哈希之间是一对一的关系。哈希特别适合用于存储对象,因为它可以将一个大的对象拆分成多个小的键值对,从而减少内存使用和提高操作效率。

哈希的主要命令包括:

  • HSET key field value:设置哈希表中指定字段的值。
  • HGET key field:获取存储在哈希表中指定字段的值。
  • HMSET key field1 value1 [field2 value2]:同时设置多个字段的值。
  • HMGET key field1 [field2]:获取所有给定字段的值。
  • HGETALL key:获取在哈希表中指定key的所有字段和值。
  • HDEL key field1 [field2]:删除一个或多个哈希表字段。
  • HEXISTS key field:检查哈希表中是否存在指定字段。
  • HKEYS key:获取所有哈希表中的字段。
  • HVALS key:获取哈希表中所有字段的值。
  • HLEN key:获取哈希表中字段的数量。

下面是使用Redis哈希的Python示例代码:




import redis
 
# 连接Redis
r = redis.Redis(host='localhost', port=6379, db=0)
 
# 设置哈希值
r.hset('user:1000', 'name', 'John')
r.hset('user:1000', 'email', 'john@example.com')
 
# 获取哈希值
name = r.hget('user:1000', 'name')
email = r.hget('user:1000', 'email')
 
# 打印获取的值
print(name.decode('utf-8'))  # John
print(email.decode('utf-8'))  # john@example.com
 
# 批量设置哈希值
r.hmset('user:1000', {'name': 'Jane', 'email': 'jane@example.com'})
 
# 批量获取哈希值
user_info = r.hmget('user:1000', 'name', 'email')
for info in user_info:
    print(info.decode('utf-8'))  # Jane, jane@example.com
 
# 获取哈希表的所有字段和值
full_user_info = r.hgetall('user:1000')
for key, value in full_user_info.items():
    print(key.decode('utf-8'), value.decode('utf-8'))
 
# 删除哈希表中的字段
r.hdel('user:1000', 'name')
 
# 检查字段是否存在
has_email = r.hexists('user:1000', 'email')
print(has_email)  # True
 
# 获取哈希表字段数量
field_count = r.hlen('user:1000')
print(field_count)  # 1
 
# 获取哈希表中的所有字段
fields = r.hkeys('user:1000')
print(fields)  # ['email']
 
# 获取哈希表中的所有值
values = r.hvals('user:1000')
print(values)  # ['jane@example.com']

在实际应用中,哈希适合用于存储对象的属性,例如用户的个人信息、产品的详细信息等。通过将一个对象的多个属性存储在Redis的一个哈希结构中,可以减少内存的使用并提高数据的查询效率。

2024-09-04

Redisson提供了分布式锁的功能,以下是使用Redisson实现分布式锁的一个简单示例:

首先,添加Redisson的依赖到你的项目中(以Maven为例):




<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.XX.X</version> <!-- 请使用最新版本 -->
</dependency>

然后,使用Redisson提供的API来获取锁并执行同步的代码块:




import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
 
public class RedissonLockExample {
 
    public static void main(String[] args) {
        // 配置RedissonClient
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
 
        // 获取锁对象实例
        RLock lock = redisson.getLock("myLock");
 
        try {
            // 尝试获取锁,最多等待100秒,锁定之后最多持有锁10秒
            boolean isLocked = lock.tryLock(100, 10, TimeUnit.SECONDS);
            if (isLocked) {
                // 业务逻辑
                System.out.println("Lock acquired");
                // 你的业务代码
 
            } else {
                // 无法获得锁
                System.out.println("Lock not acquired");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                System.out.println("Lock released");
            }
        }
 
        // 关闭RedissonClient
        redisson.shutdown();
    }
}

在上述代码中,我们首先配置了RedissonClient,指定了Redis服务器的地址。然后,我们获取了一个锁对象,并在try-finally块中使用,以确保即使发生异常也能释放锁。tryLock方法允许我们指定等待锁和锁的持有时间。最后,在使用完Redisson后关闭RedissonClient以释放资源。

2024-09-04

这里提供一个简化版本的分布式锁实现,使用Redis和ZooKeeper的简单示例。

Redis分布式锁实现:




import redis.clients.jedis.Jedis;
 
public class RedisDistributedLock {
    private Jedis jedis;
    private String lockKey;
    private int expireTime; // 锁的超时时间
 
    public RedisDistributedLock(Jedis jedis, String lockKey, int expireTime) {
        this.jedis = jedis;
        this.lockKey = lockKey;
        this.expireTime = expireTime;
    }
 
    public boolean lock() {
        String result = jedis.set(lockKey, "locked", "NX", "PX", expireTime);
        return "OK".equals(result);
    }
 
    public boolean unlock() {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList("locked"));
        return result.equals(1L);
    }
}

Zookeeper分布式锁实现:




import org.apache.zookeeper.*;
 
public class ZkDistributedLock implements Watcher {
    private ZooKeeper zk;
    private String lockName;
    private String waitNode;
 
    public ZkDistributedLock(String hostPort, String lockName) throws Exception {
        this.lockName = lockName;
        zk = new ZooKeeper(hostPort, 3000, this);
        waitNode = zk.create("/locks/" + lockName, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
    }
 
    public void lock() throws KeeperException, InterruptedException {
        while (true) {
            try {
                zk.exists("/locks/" + lockName, true);
                System.out.println("Waiting for lock...");
                synchronized (this) {
                    wait();
                }
                System.out.println("Lock acquired");
                break;
            } catch (KeeperException.NoNodeException e) {
                // Lock acquired
                break;
            }
        }
    }
 
    public void unlock() throws KeeperException, InterruptedException {
        zk.delete(waitNode, -1);
        zk.close();
    }
 
    @Override
    public void process(WatchedEvent event) {
        if (event.getType() == Event.EventType.NodeDeleted) {
            synchronized (this) {
                notifyAll();
            }
        }
    }
}

以上代码提供了使用Redis和Zookeeper实现分布式锁的简单示例。在实际应用中,你需要根据具体需求进行安全性和性能的优化,例如使用参数化的Redis命令防止死锁,或者使用Zookeeper的有序节点来实现锁。

2024-09-04



# 添加元素到集合
sadd myset a b c

# 获取集合中的所有成员
smembers myset

# 检查元素是否在集合中
sismember myset a

# 计算集合中的成员数
scard myset

# 移除集合中的一个或多个成员
srem myset a

# 随机返回集合中的一个成员
srandmember myset

# 从集合中随机移除一个元素并返回
spop myset

# 对两个集合进行交集操作
sinter set1 set2

# 对两个集合进行并集操作
sunion set1 set2

# 对两个集合进行差集操作(set1中存在,而set2中不存在的元素)
sdiff set1 set2

# 有序集合操作
# 添加一个或多个成员及其分数到有序集合
zadd myzset 1 "one"

# 获取有序集合中的成员数
zcard myzset

# 计算有序集合中成员的分数
zscore myzset "one"

# 获取有序集合中的成员及其分数
zrange myzset 0 -1 withscores

# 移除有序集合中的一个或多个成员
zrem myzset "one"

# 通过分数范围或者成员索引范围来获取有序集合中的成员
zrangebyscore myzset 0 100
zrange myzset 0 -1 withscores

# 对有序集合进行交集操作
zinterstore result 2 set1 set2

# 对有序集合进行并集操作
zunionstore result 2 set1 set2

这些命令提供了集合类型和有序集合类型的基本操作,包括添加、移除、计算成员数、获取成员列表、按分数或索引范围查询等功能。它们是Redis进行数据管理时的核心操作之一。

2024-09-04



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
 
@Service
public class RedisService {
 
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
 
    public void setKey(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }
 
    public String getKey(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }
 
    public void deleteKey(String key) {
        stringRedisTemplate.delete(key);
    }
 
    public Long getExpire(String key) {
        return stringRedisTemplate.getExpire(key);
    }
 
    public void setExpire(String key, long timeout) {
        stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }
}

这个代码示例展示了如何在Spring Boot应用中使用StringRedisTemplate来操作Redis。这个服务类提供了设置、获取、删除键值对的方法,以及获取剩余过期时间和设置过期时间的方法。这些操作对于模拟秒杀场景中的商品库存管理非常有用。

2024-09-04

要通过Redis实现延时任务,可以使用Redis的有序集合(ZSET),其中成员是任务ID,分数是任务执行的时间戳。客户端会定期检查有序集合中分数小于或等于当前时间戳的任务,并将它们从集合中移除并执行。

以下是一个简单的Python示例,使用redis包实现延时任务:




import time
import redis
 
# 连接Redis
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
 
# 添加延时任务
def add_delay_task(task_id, execute_time):
    redis_client.zadd('delay_tasks', {task_id: execute_time})
 
# 执行到期的任务
def process_delay_tasks():
    while True:
        # 获取当前时间戳
        now = time.time()
        # 获取所有分数小于等于当前时间戳的任务
        tasks = redis_client.zrangebyscore('delay_tasks', 0, now)
        for task_id in tasks:
            # 移除任务
            redis_client.zrem('delay_tasks', task_id)
            # 执行任务
            execute_task(task_id)
        time.sleep(1)  # 每隔1秒检查一次
 
# 执行任务的函数
def execute_task(task_id):
    print(f"Executing task: {task_id}")
    # 实际的任务执行代码
 
# 示例:添加两个任务,一个5秒后执行,一个10秒后执行
add_delay_task('task1', time.time() + 5)
add_delay_task('task2', time.time() + 10)
 
# 启动循环,处理延时任务
process_delay_tasks()

这个脚本会创建一个无限循环,每隔1秒检查一次是否有任务到期。如果有,它会执行这些任务并将它们从Redis的ZSET中移除。这只是一个简单的例子,实际应用可能需要更复杂的逻辑,例如处理任务的优先级、重试机制等。

2024-09-04

Redis安装:

  1. 下载源码:从官网下载Redis的最新稳定版源码包。
  2. 编译安装:解压后进入目录,编译安装。

    
    
    
    make
    make install
  3. 配置文件:将/path/to/redis-source/redis.conf 复制到某个目录下作为配置文件。
  4. 启动Redis:使用redis-server命令启动Redis服务器,并指定配置文件。

    
    
    
    redis-server /path/to/your/redis.conf

Redis数据类型及使用场景:

Redis支持五种数据类型:字符串(String), 列表(List), 集合(Set), 有序集合(Sorted Set), 哈希(Hash)。

  • 字符串:简单的key-value存储,可用于缓存、计数等。
  • 列表:可以用来实现队列、堆栈等数据结构。
  • 集合:可以用来实现标签系统等,不含重复元素。
  • 有序集合:可以用来实现排行榜等功能,元素有分数,可排序。
  • 哈希:可以用来存储对象,每个字段是键值对。

Redis事务:

Redis事务可以一次执行多个命令,有以下三个保证:

  • 执行过程中任何命令执行失败,所有命令都不会执行。
  • 执行过程中可以使用DISCARD命令撤销事务。
  • 事务不支持回滚。

使用MULTI命令开启事务,EXEC命令执行事务中的所有命令。




MULTI
SET key1 value1
INCR key2
EXEC

Redis持久化:

Redis支持两种持久化方式:

  • RDB:定时将内存中的数据快照保存到磁盘的一个压缩二进制文件中。
  • AOF:将每个写命令保存到文件中。

可以同时使用两种方式,RDB用于快速恢复,AOF用于数据安全。

Redis淘汰策略:

Redis提供了多种淘汰策略:

  • noeviction:不进行淘汰,当内存不足时,新写入操作会报错。
  • allkeys-random:在键空间中随机淘汰键。
  • volatile-random:在设置了过期时间的键中随机淘汰。
  • allkeys-lru:基于最少最近使用算法淘汰键。
  • volatile-lru:基于最少最近使用算法淘汰设置了过期时间的键。
  • volatile-ttl:淘汰即将过期的键。

通过配置文件中的maxmemory-policy指定。




maxmemory-policy allkeys-lru