空白无上
Notes Redis-基础

Redis-基础

入门 简介 Redis 是用 C 语言开发的一个开源的高性能键值对(key value)数据库,官方提供的数据是可以达到100000+的 QPS(每秒内查询次数)。它存储的value类型比较丰富,也被称为结构化的 NoSql 数据库 NoSql(Not Only SQL),不仅仅是SQL,泛指非关系型数据库。NoSql

入门

简介

Redis 是用 C 语言开发的一个开源的高性能键值对(key-value)数据库,官方提供的数据是可以达到100000+的 QPS(每秒内查询次数)。它存储的value类型比较丰富,也被称为结构化的 NoSql 数据库

NoSql(Not Only SQL),不仅仅是SQL,泛指非关系型数据库。NoSql数据库并不是要取代关系型数据库,而是关系型数据库的补充

关系型数据库:

  • Mysql

  • Oracle

  • DB2

  • SQLServer

非关系型数据库:

  • Redis

  • Mongo DB

  • MenCached

Redis应用场景:

  • 缓存

  • 任务队列

  • 消息队列

  • 分布式锁

下载和安装

下载地址:https://github.com/MicrosoftArchive/redis/releases

Linux安装:

  1. 将Redis安装包上传到Linux

  2. 解压安装包,命令:tar -zxvf redis-4.0.0.tar.gc -C /usr/local

  3. 安装Redis的依赖环境gcc,命令:apt-get install gcc-c++

  4. 进入/usr/local/redis-4.0.0,进行编译,命令:make

  5. 进入Redis的src目录,进行安装,命令:make install

Windows:

Redis的Windows是绿色软件,直接解压使用即可,解压后目录结构如下:

Feishu image

服务启动和停止

Linux:

进入src目录,使用redis-server,默认端口号为6379

Ctrl+C停止Redis服务

修改redis.conf,将daemonize no修改为daemonize yes这样Redis就可以后台运行了

Windows:

双击redis-server.exe启动服务

Ctrl+C停止服务

设置密码

打开redis.windows-service.conf配置文件,找到requirepass foobared字样,在其后面追加一行,输入requirepass 123456。这是访问Redis时所需的密码,一般测试情况下可以不用设定密码。不过,即使是作为本地访问,也建议设定一个密码

Feishu image

Linux同理,Linux中配置文件为redis.conf

Linux登录:src/redis-cli -h localhost -a 123456

远程连接

将配置文件中的bind注释掉

数据类型

介绍

Redis存储的是key-value结构的数据,其中key是字符串类型,value有五种常用的数据类型

  • 字符串 string

  • 哈希 hash

  • 列表 list

  • 集合 set

  • 有序集合 sorted set

常用数据类型

Feishu image

常用命令

字符串常用命令

常用命令:

  • SET key value设置指定key的值

  • GET key获取指定key的值

  • SETEX key seconds value设置指定key的值,并将key的过期时间设置为seconds秒

  • SETNX key value只有在key不存在时设置key的值

Key的结构

Redis 的 Key 允许有多个单词组成层级结构,多个单词之间用 ':' 隔开,格式如下:

Feishu image

这个格式并非固定,也可以根据自己的需求来删除或添加词条

例如项目名叫 heima,有 user 和 product 两种不同类型的数据,我们可以这样定义 Key:

  • user 相关的 Key:heima:user:1

  • product 相关的 Key:heima:product:1

如果 Value 是一个 Java 对象,例如一个 User 对象,则可以将对象序列化为 JSON 字符串后存储:

Feishu image

哈希操作命令

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

  • HSET key field value将哈希表key中的字段field的值设置为value

  • HGET key field获取存储在哈希表中指定字段的值

  • HDEL key field删除存储在哈希表中的指定字段值

  • HKEYS key获取哈希表中所有的字段

  • HVALS key获取哈希表中所有值

  • HGETALL key获取在哈希表中指定key的所有字段和值

Feishu image

列表操作命令

Redis列表是简单的字符串列表,按照插入顺序排序,常用命令:

  • LPUSH key value1 [value2]将一个或多个值插入到列表头部

  • LRANGE key start stop获取列表指定范围内的元素

  • RPOP key移除并获取列表最后一个元素

  • LLEN key获取列表长度

  • BRPOP key1 [key2] timeout移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止

Feishu image

Feishu image

集合操作命令

Redis set是string类型的无序集合,集合成员是唯一的,这就意味着集合中不能出现重复的数据,常用命令:

  • SADD key member1 [member2]向集合中添加一个或多个成员

  • SMEMBERS key返回集合中的所有成员

  • SCARD key获取集合中的成员数

  • SINTER key1 [key2]返回给定所有集合的交集

  • SUNION key1 [key2]返回所有给定集合的并集

  • SDIFF key1 [key2]返回给定所有集合的差集

  • SREM key member1 [member2]移除集合中一个或多个成员

Feishu image

有序集合操作命令

Redis sorted set有序集合是string类型元素的集合,且不允许重复的成员。每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但是分数可以重复

常用命令:

  • ZADD key score1 member1 [score2 member2]向有序集合中添加一个或多个成员,或者更新已存在成员的分数

  • ZRANGE key start stop [WITHSCORE]通过索引区间返回有序集合中指定区间内的成员

  • ZINCRBY key increment member有序集合中对指定成员的分数加上增量increment

Feishu image

  • ZREM key member [member...]移除有序集合中的一个或多个成员

Feishu image

通用命令

  • KEYS pattern查找所有符合给定模式(pattern)的key

  • EXISTS key检查给定key是否存在

  • TYPE key返回key所存储的值的类型

  • TTL key返回给定key的剩余生存时间(TTL,time to live),以秒为单位

  • DEL key用于在key存在时删除key

注意

Redis默认操控的是0号数据库,可以通过select index来进行数据库切换,Redis默认提供了16个数据库,可以在配置文件中修改数量

在Java中操作Redis

介绍

Redis的Java客户端很多,官方推荐三种:

  • Jedis

  • Lettuce

  • Redission

Spring对Redis客户端进行了整合,提供了Spring Data Redis,在Spring boot项目中还提供了对应的Starter,即spring-boot-starter-data-redis

Jedis

Jedis的Maven坐标

<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>2.8.0</version>
</dependency>
xml

使用Jedis操作Redis的步骤是:

//1. 获取连接
Jedis jedis = new Jedis("localhost",6379);
//2. 执行操作
jedis.set("username","hsy");
//3. 关闭连接
jedis.close();
java

Spring Data Redis

在Spring boot项目中,可以使用Spring Data Redis来简化Redis操作,maven坐标:

<!--Redis依赖-->
<dependency>
        <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--连接池依赖-->
<dependency>
        <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
xml

Spring Data Redis中提供了一个高度封装的类:RedisTemplate,针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口,具体分类如下:

  • ValueOperations简单K-V操作

  • SetOperationsset类型数据操作

  • ZSetOperatonszset类型数据操作

  • HashOperationsmap类型数据操作

  • ListOperationslist类型数据操作

Feishu image

配置文件

spring:
  application:
    name: springdataredis
  #Redis相关配置
  redis:
    host: localhost
    port: 6379
    #password: 123456
    database: 0 #默认操控的是零号数据库
    jedis:
      #Redis连接池设置
      pool:
        max-active: 8 #最大连接数
        max-wait: 1ms #连接池阻塞最大等待时间
        max-idle: 4 #连接池中的最大空闲连接
        min-idle: 0 #连接池中的最小空闲连接
yaml
@Autowired
private RedisTemplate redisTemplate;

@Test
public void testString(){
    redisTemplate.opsForValue().set("city","beijing");
}
java

运行上述代码,会发现key有问题,是框架序列化导致的

Feishu image

解决方法:

创建一个配置类RedisConfig

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        // 创建Template
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 设置连接工厂
        redisTemplate.setConnectionFactory(connectionFactory);
        // 设置序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // key和hashKey采用String序列化
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        // value和hashValue采用JSON序列化
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);
        return redisTemplate;
    }
}
java

注意:

如果运行后报错,则表示需要引入依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
xml

在日常开发不需要引入这个依赖,因为 Spring MVC 中自带这个依赖

Feishu image

value的值不用管,在代码中获取之后会进行反序列化,会还原成原来的样子

例子:

package xyz.kbws;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
import java.util.Set;

@SpringBootTest
@RunWith(SpringRunner.class)
class SpringdataredisApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 操作string类型数据
     */
    @Test
    public void testString(){
        redisTemplate.opsForValue().set("city","beijing");
        String value = (String) redisTemplate.opsForValue().get("city");
        System.out.println(value);

        //如果key已经存在则返回false,表示没有执行操作
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent("city", "nanjing");
    }
    /**
     * 操作Hash类型数据
     */
    @Test
    public void testHash(){
        HashOperations hashOperations = redisTemplate.opsForHash();

        //存值
        hashOperations.put("002","name","hsy");
        hashOperations.put("002","age","20");
        hashOperations.put("002","address","bj");

        //取值
        String name = (String) hashOperations.get("002", "name");

        //获取Hash结构中的所有字段
        Set keys = hashOperations.keys("002");
        for (Object key:keys){
            System.out.println(key);
        }
        //获取Hash结构中所有的值
        List values = hashOperations.values("002");
        for (Object value : values) {
            System.out.println(value);
        }
    }
    /**
     * 操作List类型数据
     */
    @Test
    public void testList(){
        ListOperations listOperations = redisTemplate.opsForList();

        //存值
        listOperations.leftPush("mylist","a");
        listOperations.leftPushAll("mylist","b","c","d");
        //取值
        List<String> mylist = listOperations.range("mylist", 0, -1);
        for (String o : mylist) {
            System.out.println(o);
        }
        //获得列表长度
        Long size = listOperations.size("mylist");
        int lSize = size.intValue();
        for (int i = 0; i < lSize; i++) {
            //出队列
            String element = (String) listOperations.rightPop("mylist");
            System.out.println(element);
        }
    }
    /**
     * 操作Set类型的数据
     */
    @Test
    public void testSet(){
        SetOperations setOperations = redisTemplate.opsForSet();
        //存值
        setOperations.add("myset","a","b","c","a");
        //取值
        Set<String> myset = setOperations.members("myset");
        for (String s : myset) {
            System.out.println(s);
        }
        //删除
        setOperations.remove("myset","a","b");
    }
    /**
     * 操作ZSet
     */
    @Test
    public void testZSet(){
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        //存值
        zSetOperations.add("myZSet","a",10.0);
        zSetOperations.add("myZSet","b",11.0);
        zSetOperations.add("myZSet","c",12.0);
        zSetOperations.add("myZSet","a",13.0);
        //取值
        Set<String> myZSet = zSetOperations.range("myZSet", 0, -1);
        for (String s : myZSet) {
            System.out.println(s);
        }
        //修改分数
        zSetOperations.incrementScore("myZSet","b",20.0);
        //删除成员
        zSetOperations.remove("myZSet","a","b");
    }
    /**
     * 通用操作
     */
    @Test
    public void testCommon(){
        //获取Redis中所有的key
        Set<String> keys = redisTemplate.keys("*");
        for (String key : keys) {
            System.out.println(key);
        }
        //判断某个key是否存在
        Boolean itcast = redisTemplate.hasKey("itcast");
        //删除指定key
        redisTemplate.delete("myZSet");
        //获取指定key对应的value的数据类型
        DataType myset = redisTemplate.type("myset");
        System.out.println(myset.name());
    }
}
java

尽管 JSON 的序列化方式可以满足我们的需求,但依然存在一些问题,如图:

Feishu image

为了在反序列化时知道对象的类型,JSON 序列化器会将类的 Class 类型写入 JSON 结果中,存入 Redis,会带来额外的内存开销

为了节省内存空间,我们并不会使用 JSON 序列化器来处理 Value,而是统一使用 String 序列化器,要求只能存储 String 类型的 Key 和 Value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化

Feishu image

Spring 默认提供了一个 StringRedisTemplate 类,他的 Key 和 Value 的序列化方式默认就是 String 类型,省去了我们自定义 RedisTemplate 的过程

Feishu image