Redis

2024 年 7 月 24 日 星期三
/
19
摘要
redis学习文章

Redis

redistemplate

启动redis服务器并输入命令

运行redis

先进入redis的目录下(本机是E:/redis),然后输入下面的命令启动服务器

redis-server

接着另打开一个cmd,进入redis目录后,输入下面的命令即可(可连接远程的redis,输入对应的IP和端口号)

redis-cli -h 127.0.0.1 -p 6379    -a password        //连接远程的redis,输入对应的IP和端口号,以及密码(密码带引号:-a “mypass”)
redis-cli                                      //连接本地的redis

查看是否连接到redis服务

127.0.0.1:6379> ping
PONG                                    //表示连接上redis服务

配置redis

见网址:Redis 配置 | 菜鸟教程 (runoob.com)

redis基础配置文件,点击下载

持久化

ps:Redis 提供了两种主要的持久化机制:RDB (Redis Database) 快照和 AOF (Append-Only File) 日志

RDB 是通过创建数据集的时间点快照来实现持久化的。Redis 会周期性地将内存中的数据写入到磁盘中的 RDB 文件。这种方式的优点是恢复数据比较快,缺点是由于是周期性保存,可能会丢失最后一次保存和服务器崩溃之间的所有数据。

AOF 通过将每一个写操作记录到日志文件(appendonly.aof)中来实现持久化。优点是数据丢失最少,可以配置为几乎不丢数据。缺点是AOF 文件比 RDB 文件大,恢复速度较慢。对性能有一定影响,尤其是在 appendfsync always 模式下。AOF 文件可以通过重写机制进行压缩,减少磁盘空间使用。AOF 可以配置为不同的同步策略:

  • always: 每次写操作后立即同步日志到磁盘,保证数据最完整,但性能较低。
  • everysec: 每秒同步一次,提供较好的平衡,默认配置。
  • no: 不同步,完全依赖操作系统,性能最好,但最不安全。

redis.conf 配置文件

# 启用 AOF 持久化
appendonly yes

# AOF 文件的同步策略
# 可选值:always、everysec、no
appendfsync everysec

# AOF 文件重写策略
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
保护模式

protected-mode 保护模式是 Redis 的一种安全机制,默认值是 yes

如果protected-mode保护模式为yes,那么当redis没有设置密码的时候,无法接收其他ip的远程连接(即使你的bind 127.0.0.1注释掉了也没用),如果设置了密码,那么就可以接收到远程ip的连接。(注释掉bind 127.0.0.1

守护进程

daemonize 指令决定 Redis 服务器是否以守护进程(daemon)模式运行。守护进程模式下,Redis 将在后台运行,不会占用当前的终端会话。

设置为 no 的效果:当 daemonize 设置为 no 时,Redis 以前台进程模式运行,这在调试和开发环境中很有用,因为可以直接在终端中看到 Redis 的日志输出。

数据类型

String(字符串)

  • string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。

    • Memcached是一个高性能,分布式内存对象缓存系统
  • string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。

  • string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB

实例

redis 127.0.0.1:6379> SET runoob "菜鸟教程"
OK
redis 127.0.0.1:6379> GET runoob
"菜鸟教程"
  • 存取键值对用的关键字为SETGET

Hash(哈希)

  • Redis hash 是一个键值(key=>value)对集合。

  • Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

实例:

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> HMSET runoob field1 "Hello" field2 "World"
"OK"
redis 127.0.0.1:6379> HGET runoob field1
"Hello"
redis 127.0.0.1:6379> HGET runoob field2
"World"
  • DEL runoob 用于删除前面测试用过的 key,不然会报错:(error) WRONGTYPE Operation against a key holding the wrong kind of value

  • 实例中我们使用了 Redis HMSET, HGET 命令,HMSET 设置了两个 field=>value 对, HGET 获取对应 field 对应的 value

    每个 hash 可以存储 2^32^-1 键值对(40多亿)。

  • 存取关键字为HMSETHGET

List(集合)

  • Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> lpush runoob redis
(integer) 1
redis 127.0.0.1:6379> lpush runoob mongodb
(integer) 2
redis 127.0.0.1:6379> lpush runoob rabbitmq
(integer) 3
redis 127.0.0.1:6379> lrange runoob 0 10
1) "rabbitmq"
2) "mongodb"
3) "redis"
redis 127.0.0.1:6379>
  • 列表最多可存储 2^32^-1 元素 (4294967295, 每个列表可存储40多亿)。

Set(集合)

Redis 的 Set 是 string 类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

sadd 命令

添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0。

sadd key member

实例:

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> sadd runoob redis
(integer) 1
redis 127.0.0.1:6379> sadd runoob mongodb
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabbitmq
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabbitmq
(integer) 0
redis 127.0.0.1:6379> smembers runoob

1) "redis"
2) "rabbitmq"
3) "mongodb"

注意:以上实例中 rabbitmq 添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。

  • 集合中最大的成员数为 2^32^-1 (4294967295, 每个集合可存储40多亿个成员)。

zset(有序集合)

  • Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

  • 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

  • zset的成员是唯一的,但分数(score)却可以重复。

zadd 命令

添加元素到集合,若元素在集合中存在则更新对应score

zadd key score member

实例:

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> zadd runoob 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabbitmq
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabbitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE runoob 0 1000
1) "mongodb"
2) "rabbitmq"
3) "redis"

HyperLogLog

描述

  • HyperLogLog是用来做基数统计的算法,即使输入的键值对再多,也不会占用太大的空间,仅仅是为了计算基数

  • 在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
  • HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身

基数

  • 比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8},
    • 基数(不重复元素个数)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
  • 总结:基数就是不重复的元素的个数

实例

redis 127.0.0.1:6379> PFADD runoobkey "redis"

1) (integer) 1

redis 127.0.0.1:6379> PFADD runoobkey "mongodb"

1) (integer) 1

redis 127.0.0.1:6379> PFADD runoobkey "mysql"

1) (integer) 1

redis 127.0.0.1:6379> PFCOUNT runoobkey

(integer) 3

命令

  • PFADD key element [element ...] 添加指定元素到 HyperLogLog 中。
  • PFCOUNT key [key ...] 返回给定 HyperLogLog 的基数估算值。
  • PFMERGE destkey sourcekey [sourcekey ...] 将多个 HyperLogLog 合并为一个 HyperLogLog

Redis发布订阅

image-20230423150611290

image-20230423150611290

描述:

如图,三个客户端订阅(subscribe)一个频道,当发送(publish)信息到频道时,三个客户端均能接收到信息

命令:

订阅频道

  • subscribe channel[channel… …]

    订阅一个或多个频道的信息

  • unsubscribe channel[channel… …]

    退订一个或多个频道

  • publish channel message(带双引号)

    向指定频道推送信息

实例:

第一个redis-cli客户端

127.0.0.1:6379> subscribe runoobChat            //订阅频道runoobChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "runoobChat"
3) (integer) 1

第二个redis-cli客户端

127.0.0.1:6379> publish runoobChat "redis publish test"        //推送信息:redis publish test
(integer) 1
127.0.0.1:6379> publish runoobChat "learn redis"            //推送信息:learn redis
(integer) 1

返回第一个订阅频道runoobChat的客户端

显示结果如下:

1) "message"
2) "runoobChat"
3) "redis publish test"
1) "message"
2) "runoobChat"
3) "learn redis"

Redis事务

描述:

可以一次执行多个命令

保证:

  • 收到执行事务命令后,开始执行事务队列中的命令,但事务中的任意命令失败后,其余命令依然被执行
  • 事务执行过程中,其他客户端提交的命令不会插入到队列中

三个阶段:

  • 开始事务
  • 命令入队
  • 执行事务

命令:

  • discard

    取消事务,放弃执行事务块内的所有命令

  • exec

    执行事务块内的所有命令

  • multi

    标记一个事务块的开始

  • watch key[key…]

    监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

实例:

redis 127.0.0.1:6379> MULTI
OK

redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED

redis 127.0.0.1:6379> GET book-name
QUEUED

redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED

redis 127.0.0.1:6379> SMEMBERS tag
QUEUED

redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
   2) "C++"
   3) "Programming"

在java中使用

准备工作

pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.32</version>
</dependency>
application.yml
spring:
  redis:
    host: localhost
    port: 6379
    #    password: 123456
    database: 0
    lettuce:
      pool:
        max-idle: 16
        max-active: 32
        min-idle: 8
RedisConfig
package com.jx.config;

import com.jx.utils.FastJsonRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);

        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);

        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }
}
FastJsonRedisSerializer
package com.jx.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.nio.charset.Charset;

/**
 * Redis使用FastJson序列化
 *
 * @author sg
 */
public class FastJsonRedisSerializer<T> implements RedisSerializer<T>
{

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class<T> clazz;

    static
    {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    }

    public FastJsonRedisSerializer(Class<T> clazz)
    {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException
    {
        if (t == null)
        {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException
    {
        if (bytes == null || bytes.length <= 0)
        {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);

        return JSON.parseObject(str, clazz);
    }


    protected JavaType getJavaType(Class<?> clazz)
    {
        return TypeFactory.defaultInstance().constructType(clazz);
    }
}
RedisCache
package com.jx.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }

    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 删除Hash中的数据
     *
     * @param key
     * @param hkey
     */
    public void delCacheMapValue(final String key, final String hkey)
    {
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, hkey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }
}

基本使用

tips:自动装配RedisCache,调用其中的方法即可

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...