Redis

Posted dins

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis相关的知识,希望对你有一定的参考价值。

一、NoSQL 概述

  1. 什么是 NoSQL

    NoSQL = Not Only SQL

    一种全新的数据库理念,泛指非关系型数据库。

  2. 为什么需要 NoSQL?

    传统关系型数据库在应付 web2.0 网站时,特别是超大规模、高并发的 SNS web2.0 动态网站,已经力不从心,并暴露出一些难以克服的问题。

    • High performance - 高并发读写
    • Huge Storage - 海量数据的高效率存储和访问
    • High Scalability && High Availability - 高可扩展性和高可用性
  3. 主流产品

    • CouchDB
    • Redis
    • MongoDB

    技术图片

  4. 四大分类
    技术图片

  5. NoSQL 的特点

    • 易扩展。去掉了关系型数据库中的关系。
    • 灵活的数据模型
    • 大数据量、高性能的存储
    • 高可用

二、Redis 概述

技术图片

  1. Redis 的由来
  2. 什么是 Redis?
    一款用 C 语言开发的,开源的、高性能的、键值对的数据库,通过提供多种键值数据类型,来适应不同场景下的存储需求。


    目前支持的键值数据类型:

    • 字符串类型
    • 列表类型
    • 有序集合类型
    • 散列类型
    • 集合类型
  3. Redis 的应用场景

    • 提供缓存服务,存储访问频率高的热数据,防止穿透到数据库
    • 任务队列
    • 网站访问统计
    • 数据过期处理
    • 应用排行榜
    • 分布式集群架构中的 session 分离
    • 在分布式系统中可以作为实现分布式锁的一种实现方案

三、Jedis 入门

Jedis 是 Redis 官方首选的 java 客户端开发包。
技术图片

地址:https://redis.io/clients#java

jedis 集成了 redis 的一些命令操作,提供了连接池管理。

添加 maven 依赖:

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
    <version>2.0.10.RELEASE</version>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    <scope>compile</scope>
</dependency>

简单的使用:

public class JedisDemo {
    /**
     * 单实例测试
     */
    @Test
    public void demo1() {
        // 1. 设置 ip 和端口
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        // 2. 保存数据
        jedis.set("name", "jack");
        // 3. 获得数据
        String value = jedis.get("name");
        System.out.println(value);
        // 4. 释放资源
        jedis.close();
    }

    /*  连接池方式连接 */
    @Test
    public void demo2() {
        // 获得连接池配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        // 设置最大连接数
        config.setMaxTotal(20);
        // 设置最大空闲连接数
        config.setMinIdle(10);

        // 获取连接池
        JedisPool pool = new JedisPool(config, "127.0.0.1", 6379);

        // 获取核心对象
        Jedis jedis = null;
        try {
            // 通过连接池获的连接
            jedis = pool.getResource();
            // 设置数据
            jedis.set("name", "root");
            // 获取数据并打印
            System.out.println(jedis.get("name"));
        }catch (Exception e) {
            if (jedis != null) {
                jedis.close();
            }
            if (pool != null) {
                pool.close();
            }
        }
    }
}

四、Redis 的数据类型

Redis 中 key 定义的注意点:

  1. 不要过长,不要超过 1024 个字节,消耗内存,降低查找效率;
  2. 不要过短,影响可读性;
  3. 统一的命名规范。

Redis 中可以存储的数据类型:

  1. 字符串(String)
  2. 字符串列表(list)
  3. 有序字符串集合(sorted set)
  4. 哈希(hash)
  5. 字符串集合(set)

4.1 存储 String

  • 二进制安全的,存入和获取的数据相同;
  • 字符串类型的 value 可以容纳 512M。

常用命令:

  1. 赋值

    $ redis-cli   
    127.0.0.1:6379> set name Tom
    OK
    {
        // 添加数据
    jedis.set("name", "Jack");
    }
  2. 取值

    127.0.0.1:6379> get name
    "Tom"
    127.0.0.1:6379> getset name Jack
    "Tom"
    127.0.0.1:6379> get name
    "Jack"
    {
        // 获取数据
    jedis.get("name")
    }

    getset 命令相当于先执行 get 获取命令再执行 set 设置命令。

  3. 删除

    127.0.0.1:6379> del name
    (integer) 1
    127.0.0.1:6379> get name
    (nil)
    {
        // 删除数据
    jedis.del("name");
    }
  4. 数值增减

    127.0.0.1:6379> incr num
    (integer) 1
    127.0.0.1:6379> get num
    "1"
    127.0.0.1:6379> incr num
    (integer) 2
    127.0.0.1:6379> get num
    "2"
    127.0.0.1:6379> set name Tom
    OK
    127.0.0.1:6379> incr name
    (error) ERR value is not an integer or out of range

    incr 将指定的 key 的 value 值递增 1,如果这个值不存在,就将这个值初始为 0,再执行 +1;如果这个 value 不是整型,则命令执行失败。

    127.0.0.1:6379> decr num
    (integer) 1
    127.0.0.1:6379> decr name
    (error) ERR value is not an integer or out of range

    decr 将指定的 key 的 value 值递增 1,如果这个值不存在,就将这个值初始为 0,再执行 -1;如果这个 value 不是整型,则命令执行失败。

  5. 扩展命令

    127.0.0.1:6379> get num
    "1"
    127.0.0.1:6379> incrby num 3
    (integer) 4
    127.0.0.1:6379> get num
    "4"
    {
        // 用于将键的整数值递增 1。
    // 如果键不存在,则在执行操作之前将其设置为 0;如果键包含错误类型的值或包含无法表示为整数的字符串,则会返回错误。
    // 此操作限于 64 位有符号整数
    jedis.incr("age");
    }

    incrby 将指定的 key 的 value 增加指定数值,如果这个值不存在,就将这个值初始为 0,再增加;如果这个 value 不是整型,则命令执行失败。

    127.0.0.1:6379> get num
    "4"
    127.0.0.1:6379> decrby num 5
    (integer) -1
    127.0.0.1:6379> decrby name
    (error) ERR wrong number of arguments for 'decrby' command

    decrby 将指定的 key 的 value 增加指定数值,如果这个值不存在,就将这个值初始为 0,再增加;如果这个 value 不是整型,则命令执行失败。

    127.0.0.1:6379> get num
    "-1"
    127.0.0.1:6379> append num 12
    (integer) 4
    127.0.0.1:6379> get num
    "-112"
    127.0.0.1:6379> incr num
    (integer) -111
    127.0.0.1:6379> append password 123456
    (integer) 6
    127.0.0.1:6379> get password
    "123456"
    {
        // 拼接数据
    jedis.append("name", " Ma");
    }

    append 拼凑字符串。如果 value 存在,就在原来的字符串后面追加;如果不存在,就创建一个新字符串,value 等于要拼接的字符串。命令返回结果字符串的长度。

4.2 存储 Hash

  • 具有 String 类型的 Key 和 String 类型的 Value 的 map 容器。适合存储值对象的信息,用户名、密码等。
  • 每一个 Hash 可以存储 4294967295 个键值对。

常用命令:

  1. 赋值

    127.0.0.1:6379> hSet myhash username jack
    (integer) 1
    127.0.0.1:6379> hSet myhash age 18
    (integer) 1
    127.0.0.1:6379> hmset myhash2 username rose age 21
    OK
    {
        // 添加数据
    Map<String, String> map = new HashMap<String, String>();
    map.put("username", "jack");
    map.put("age", "18");
    jedis.hmset("myhash", map);
    }

    hmset 一次设置多个键值对。

  2. 取值

    127.0.0.1:6379> hget myhash username
    "jack"
    127.0.0.1:6379> hmget myhash username age
    1) "jack"
    2) "18"
    127.0.0.1:6379> hgetall myhash
    1) "username"
    2) "jack"
    3) "age"
    4) "18"
    127.0.0.1:6379> hget myhash company
    (nil)
    {
        // 返回结果是一个泛型的 List
    List<String> list = jedis.hmget("myhash", "username", "age");
    System.out.println(list);
    // 获取 map 中某个值
    jedis.hget("myhash", "age")
    }

    hmget 一次获取 hash 中多个指定 key 的 vlaue。
    hgetall 获取 hash 中所有的键值对信息,key 和 value 的值都打印出来。

  3. 删除

    127.0.0.1:6379> hdel myhash username age
    (integer) 2
    127.0.0.1:6379> hgetall myhash
    (empty list or set)
    127.0.0.1:6379> hdel myhash username
    (integer) 0
    127.0.0.1:6379> hmset myhash username jack age 18
    OK
    127.0.0.1:6379> del myhash
    (integer) 1
    127.0.0.1:6379> hget myhash username
    (nil)
    {
        jedis.del("myhash");
    }

    hdel 删除 hash 中没有的 key 时,返回 0,操作失败。

  4. 增加数字

    127.0.0.1:6379> hincrby myhash age 5
    (integer) 23
    127.0.0.1:6379> hget myhash age
    "23"
  5. 自学命令

    127.0.0.1:6379> hexists myhash username
    (integer) 1
    127.0.0.1:6379> hexists myhash password
    (integer) 0

    hexists 判断 myhash 中是否存在为 username 的 key。0 表示不存在,1 表示存在。

    127.0.0.1:6379> hgetall myhash
    1) "username"
    2) "jack"
    3) "age"
    4) "23"
    127.0.0.1:6379> hlen myhash
    (integer) 2

    hlen 获取 hash 中键值对的个数。

    127.0.0.1:6379> hkeys myhash
    1) "username"
    2) "age"

    hkeys 获取 hash 中所有的键。

    127.0.0.1:6379> hvals myhash
    1) "jack"
    2) "23"

    myhash 获取 hash 中所有的值。

4.3 存储 list

Redis 的 list 是按照插入顺序排序的一个字符串的链表,和数据结构中的普通链表一样,可以在它的头部(左侧)和尾部(右侧)添加新的元素。

在插入的时候,如果该键不存在,那么 redis 会为这个键创建一个新的链表,与此相反,如果链表中的所有元素都被移除了,那么该键也会被从数据库中删除。

如果是在链表的两头插入和删除元素,是非常高效的;如果元素插入和删除是作用在链表的中间,是比较低效的。

数据存储的方式:

  1. ArrayList
    使用数组方式。
    根据索引查询速度快,插入和删除需要涉及位移操作,所以比较慢。

  2. LinkedList
    使用双向链接方式。
    插入删除操作只改变前后元素的指针指向,速度非常快。

常用命令:

  1. 两端添加

    127.0.0.1:6379> lpush mylist a b c
    (integer) 3
    127.0.0.1:6379> lpush mylist 1 2 3
    (integer) 6

    lpush 左侧添加数据。

    技术图片

    127.0.0.1:6379> rpush mylist2 a b c
    (integer) 3
    127.0.0.1:6379> rpush mylist2 1 2 3
    (integer) 6

    rpush 右侧插入数据。

    技术图片

  2. 查看列表

    127.0.0.1:6379> lrange mylist 0 5
    1) "3"
    2) "2"
    3) "1"
    4) "c"
    5) "b"
    6) "a"
    127.0.0.1:6379> lrange mylist2 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "1"
    5) "2"
    6) "3"
    127.0.0.1:6379> lrange mylist2 0 -3
    1) "a"
    2) "b"
    3) "c"
    4) "1"

    lrange key start stop

    lrange 命令是显示左侧 start 位置到 stop 位置间的元素内容。如果 start/stop 是负数,表示从尾部开始算,-1 表示倒数第一个,-2 表示倒数第二个...。

    简而言之,正数是从左向右数,负数是从右向左数。

  3. 两端弹出

    127.0.0.1:6379> lpop mylist
    "3"
    127.0.0.1:6379> lrange mylist 0 -1
    1) "2"
    2) "1"
    3) "c"
    4) "b"
    5) "a"
    127.0.0.1:6379> rpop mylist2
    "3"
    127.0.0.1:6379> lrange mylist2 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "1"
    5) "2"

    lpop 从左侧弹出一个元素,rpop 从右侧弹出一个元素。弹出的元素从 list 中移除。

  4. 获取列表元素个数

    127.0.0.1:6379> llen mylist
    (integer) 5
    127.0.0.1:6379> llen mylist2
    (integer) 5
  5. 扩展命令

    127.0.0.1:6379> lpushx mylist x
    (integer) 6
    127.0.0.1:6379> lrange mylist 0 -1
    1) "x"
    2) "2"
    3) "1"
    4) "c"
    5) "b"
    6) "a"
    127.0.0.1:6379> lpushx mylist3 x
    (integer) 0
    127.0.0.1:6379> rpushx mylist2 y
    (integer) 6
    127.0.0.1:6379> lrange mylist2 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "1"
    5) "2"
    6) "y"

    lpushx 仅当指定的 key 存在时,就可以向关联的 list 中从左侧插入相应的值。上面的 mylist3 不存在,所以返回 0,不进行操作。
    rpushx 从右侧插入相应的值。

    127.0.0.1:6379> lpush mylist3 1 2 3
    (integer) 3
    127.0.0.1:6379> lpush mylist3 1 2 3
    (integer) 6
    127.0.0.1:6379> lpush mylist3 1 2 3
    (integer) 9
    127.0.0.1:6379> lrange mylist3 0 -1
    1) "3"
    2) "2"
    3) "1"
    4) "3"
    5) "2"
    6) "1"
    7) "3"
    8) "2"
    9) "1"
    127.0.0.1:6379> lrem mylist3 2 3
    (integer) 2
    127.0.0.1:6379> lrange mylist3 0 -1
    1) "2"
    2) "1"
    3) "2"
    4) "1"
    5) "3"
    6) "2"
    7) "1"
    127.0.0.1:6379> lrem mylist3 -2 1
    (integer) 2
    127.0.0.1:6379> lrange mylist3 0 -1
    1) "2"
    2) "1"
    3) "2"
    4) "3"
    5) "2"
    127.0.0.1:6379> lrem mylist3 0 2
    (integer) 3
    127.0.0.1:6379> lrange mylist3 0 -1
    1) "1"
    2) "3"

    lrem key count value

    lrem 如果指定的 key 存在,从指定方向遍历,删除 countvalue,如果 count = 2,value = 3,表示从左向右删除 2 个“3”;如果 count = 0,value = 3,表示从左向右删除所有的“3”;如果 count = -2,value = 3,表示从右向左删除 2 个“3”。

    127.0.0.1:6379> lrange mylist 0 -1
    1) "x"
    2) "2"
    3) "1"
    4) "c"
    5) "b"
    6) "a"
    127.0.0.1:6379> lset mylist 3 mmm
    OK
    127.0.0.1:6379> lrange mylist 0 -1
    1) "x"
    2) "2"
    3) "1"
    4) "mmm"
    5) "b"
    6) "a"

    lset 在指定位置插入指定元素。注意:插入索引是从 0 开始。

    127.0.0.1:6379> lpush mylist4 a b c
    (integer) 3
    127.0.0.1:6379> lpush mylist4 a b c
    (integer) 6
    127.0.0.1:6379> lrange mylist4 0 -1
    1) "c"
    2) "b"
    3) "a"
    4) "c"
    5) "b"
    6) "a"
    127.0.0.1:6379> linsert mylist4 before b 11
    (integer) 7
    127.0.0.1:6379> lrange mylist4 0 -1
    1) "c"
    2) "11"
    3) "b"
    4) "a"
    5) "c"
    6) "b"
    7) "a"
    127.0.0.1:6379> linsert mylist4 after b 22
    (integer) 8
    127.0.0.1:6379> lrange mylist4 0 -1
    1) "c"
    2) "11"
    3) "b"
    4) "22"
    5) "a"
    6) "c"
    7) "b"
    8) "a"

    linsert 在指定元素的前面/后面插入指定元素。

    127.0.0.1:6379> lpush mylist5 1 2 3
    (integer) 3
    127.0.0.1:6379> lpush mylist6 a b c
    (integer) 3
    127.0.0.1:6379> lrange mylist5 0 -1
    1) "3"
    2) "2"
    3) "1"
    127.0.0.1:6379> lrange mylist6 0 -1
    1) "c"
    2) "b"
    3) "a"
    127.0.0.1:6379> rpoplpush mylist5 mylist6
    "1"
    127.0.0.1:6379> lrange mylist5 0 -1
    1) "3"
    2) "2"
    127.0.0.1:6379> lrange mylist6 0 -1
    1) "1"
    2) "c"
    3) "b"
    4) "a"
    127.0.0.1:6379> rpoplpush mylist6 mylist6
    "a"
    127.0.0.1:6379> lrange mylist6 0 -1
    1) "a"
    2) "1"
    3) "c"
    4) "b"

    rpoplpush source destination

    rpoplpush 将 source 中的尾部(右侧)元素移除并压入 destination 的头部(左侧)。

    技术图片

4.4 存储 Set

将 Redis set 看作为无序的字符集合,和 List 一样。可以执行添加、删除、判断是否存在等操作。

  • 和 List 类型不同的是,Set 集合中不允许出现重复的元素
  • 在服务器端完成多个 set 的聚合操作,如 union 等;
  • Set 可包含的最大元素数量是 4294967295。

常用命令:

  1. 添加/删除元素

    127.0.0.1:6379> sadd myset a b c
    (integer) 3
    127.0.0.1:6379> sadd myset a
    (integer) 0
    127.0.0.1:6379> sadd myset 1 2 3
    (integer) 3

    sadd 如果集合中已经有了元素,再次添加时,返回 0,操作失败。

    127.0.0.1:6379> srem myset 1 2
    (integer) 2
    
  2. 获得集合中的元素

    127.0.0.1:6379> smembers myset
    1) "c"
    2) "3"
    3) "b"
    4) "a"
    127.0.0.1:6379> sismember myset a
    (integer) 1
    127.0.0.1:6379> sismember myset x
    (integer) 0

    smembers 查看集合中的元素。
    sismember 判断集合中是否有指定的元素,如果有返回 1,没有返回 0。

  3. 集合中的差集行算

    127.0.0.1:6379> sadd myset1 a b c
    (integer) 3
    127.0.0.1:6379> sadd myset2 a b 1 2
    (integer) 4
    127.0.0.1:6379> sdiff myset1 myset2
    1) "c"
    127.0.0.1:6379> sdiff myset2 myset1
    1) "1"
    2) "2"

    注意:两个参数 key 的位置不同,得到的结果不同。

    技术图片

  4. 集合中的交集运算

    127.0.0.1:6379> sinter myset1 myset2
    1) "b"
    2) "a"

    技术图片

  5. 集合中的并集运算

    127.0.0.1:6379> sunion myset1 myset2
    1) "a"
    2) "c"
    3) "1"
    4) "2"
    5) "b"

    技术图片

  6. 扩展命令

    127.0.0.1:6379> smembers myset
    1) "c"
    2) "3"
    3) "b"
    4) "a"
    127.0.0.1:6379> scard myset
    (integer) 4

    scard 计算集合的元素个数。

    127.0.0.1:6379> srandmember myset
    "a"
    127.0.0.1:6379> srandmember myset
    "c"
    127.0.0.1:6379> srandmember myset
    "3"
    127.0.0.1:6379> srandmember myset
    "c"

    srandmember 获取集合中随机的元素。

        127.0.0.1:6379> sdiffstore mydiff myset1 myset2
    (integer) 1
    127.0.0.1:6379> smembers mydiff
    1) "c"
    127.0.0.1:6379> sinterstore myinter myset1 myset2
    (integer) 2
    127.0.0.1:6379> smembers myinter
    1) "b"
    2) "a"
    127.0.0.1:6379> sunionstore myunion myset1 myset2
    (integer) 5
    127.0.0.1:6379> smembers myunion
    1) "a"
    2) "c"
    3) "1"
    4) "2"
    5) "b"

    sunionstore destination key [key ...]

    将两个集合的交集、并集、差集存储到 destination 集合中。

存储 Set 的使用场景:

  • 跟踪一些唯一性数据;
  • 用于维护数据对象之间的关联关系。

4.5 存储 Sorted-Set

Sorted-Set 和 Set 的区别:Sorted-Set 中的每一个元素都有一个“分数”,根据这个分数判断它们的先后顺序。

Sorted-Set 的元素在集合中的位置是有序的。

常用命令:

  1. 添加元素

    127.0.0.1:6379> zadd mysort 70 zhangsan 80 lisi 90 wangwu
    (integer) 3
    127.0.0.1:6379> zadd mysort 100 zhangsan
    (integer) 0
    127.0.0.1:6379> zadd mysort 60 tom
    (integer) 1

    zadd 命令返回新插入元素的个数,如果集合中已有同名元素,那么将新的 score 替换已有的,并返回 0。

  2. 获得元素

    127.0.0.1:6379> zscore mysort zhangsan
    "100"
    127.0.0.1:6379> zcard mysort
    (integer) 4

    zscore 返回指定元素的分数。
    zcard 返回集合中的元素个数。

  3. 删除元素

    127.0.0.1:6379> zrem mysort tom wangwu
    (integer) 2
    

    返回被删除的元素个数。

  4. 范围查询

    127.0.0.1:6379> zrange mysort 0 -1
    1) "lisi"
    2) "zhangsan"
    127.0.0.1:6379> zrange mysort 0 -1 withscores
    1) "lisi"
    2) "80"
    3) "zhangsan"
    4) "100"

    zrange key start stop [WITHSCORES]

    zrange 在指定 key 对应集合中,查找从 start 位置到 stop 位置之间的元素。[WITHSCORES] 是可选项,表示是否同时返回元素对应的分数。

    127.0.0.1:6379> zrevrange mysort 0 -1 withscores
    1) "zhangsan"
    2) "100"
    3) "lisi"
    4) "80"

    默认 zrange 是按照分数升序排列,分数低的先返回。zrevrange 是按照分数倒序排序。

  5. 扩展命令

    127.0.0.1:6379> zremrangebyrank mysort 0 1
    (integer) 2
    127.0.0.1:6379> zcard mysort
    (integer) 0

    zremrangebyrank key start stop

    zremrangebyrank 删除 start~stop 范围内的元素。

    127.0.0.1:6379> zadd mysort 80 lucy 90 lily 100 lucky
    (integer) 3
    127.0.0.1:6379> zremrangebyscore mysort 80 100
    (integer) 3

    zremrangebyscore key min max

    zremrangebyscore 删除 min~max 之间分数的元素。

    127.0.0.1:6379> zadd mysort 70 zhangsan 80 lisi 90 wangwu
    (integer) 3
    127.0.0.1:6379> zrangebyscore mysort 0 100
    1) "zhangsan"
    2) "lisi"
    3) "wangwu"
    127.0.0.1:6379> zrangebyscore mysort 0 100 withscores
    1) "zhangsan"
    2) "70"
    3) "lisi"
    4) "80"
    5) "wangwu"
    6) "90"
    127.0.0.1:6379> zrangebyscore mysort 0 100 withscores limit 0 2
    1) "zhangsan"
    2) "70"
    3) "lisi"
    4) "80"

    zrangebyscore key min max [WITHSCORES] [LIMIT offset count]

    zrangebyscore 从指定 key 对应的集合中,查找 min~max 之间分数的元素。[WITHSCORES] 是可选项,表示是否显示它们的分数;[LIMIT offset count] 是可选项,表示从 offset 位置开始,共 count 个元素中查询。

    127.0.0.1:6379> zincrby mysort 3 lisi
    "83"
    127.0.0.1:6379> zscore mysort lisi
    "83"

    增加指定元素对应的分数。

    127.0.0.1:6379> zcount mysort 80 90
    (integer) 2
    

    zcount key min max

    统计 min~max 之间分数的元素个数。

Sorted-Set 使用场景:

  • 大型在线游戏积分排行榜
  • 构建索引数据

五、Keys 的通用操作

127.0.0.1:6379> keys *
 1) "mydiff"
 2) "myset2"
 3) "company"
 4) "mylist6"
 5) "mylist2"
 6) "mylist5"
 7) "mylist4"
 8) "myhash"
 9) "password"
10) "myset"
11) "mysort"
12) "mylist"
13) "myset1"
14) "num"
15) "myunion"
16) "name"
17) "mylist3"
18) "myinter"
127.0.0.1:6379> keys my?
(empty list or set)
127.0.0.1:6379> keys my*
 1) "mydiff"
 2) "myset2"
 3) "mylist6"
 4) "mylist2"
 5) "mylist5"
 6) "mylist4"
 7) "myhash"
 8) "myset"
 9) "mysort"
10) "mylist"
11) "myset1"
12) "myunion"
13) "mylist3"
14) "myinter"

keys 查询指定规则的 key。

127.0.0.1:6379> exists mylist6
(integer) 1
127.0.0.1:6379> del mylist6
(integer) 1
127.0.0.1:6379> exists mylist6
(integer) 0

exists 查询指定 key 是否存在。

127.0.0.1:6379> get name
"Tom"
127.0.0.1:6379> rename name newName
OK
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> get newName
"Tom"

rename 重命名 key。

127.0.0.1:6379> expire newName 1000
(integer) 1
127.0.0.1:6379> ttl newName
(integer) 994

expire key seconds

expire 设置 key 的超时时间。
ttl 查询 key 的剩余超时时间。

127.0.0.1:6379> type newName
string
127.0.0.1:6379> type mylist
list
127.0.0.1:6379> type myset
set
127.0.0.1:6379> type myhash
hash
127.0.0.1:6379> type mysort
zset

type 查看 key 对应 value 的数据类型。

六、Redis 的特性

  • 多数据库
  • Redis 事务

6.1 多数据库

技术图片
一个 Redis 实例可以包含多个数据库,客户端可以指定连接 redis 实例的哪个数据库。最多包含 16 个数据库,从 0~15,默认为 0 号数据库。

127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
 1) "mydiff"
 2) "myset2"
 3) "company"
 4) "mylist2"
 5) "mylist5"
 6) "mylist4"
 7) "myhash"
 8) "newName"
 9) "password"
10) "myset"
11) "mysort"
12) "mylist"
13) "myset1"
14) "num"
15) "myunion"
16) "mylist3"
17) "myinter"

select 命令选择不同的数据库。

127.0.0.1:6379> keys *
 1) "mydiff"
 2) "myset2"
 3) "company"
 4) "mylist2"
 5) "mylist5"
 6) "mylist4"
 7) "myhash"
 8) "newName"
 9) "password"
10) "myset"
11) "mysort"
12) "mylist"
13) "myset1"
14) "num"
15) "myunion"
16) "mylist3"
17) "myinter"
127.0.0.1:6379> move myset 1
(integer) 1
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "myset"
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
 1) "mydiff"
 2) "myset2"
 3) "company"
 4) "mylist2"
 5) "mylist5"
 6) "mylist4"
 7) "myhash"
 8) "newName"
 9) "password"
10) "mysort"
11) "mylist"
12) "myset1"
13) "num"
14) "myunion"
15) "mylist3"
16) "myinter"

move key db

move 将当前数据库中的 key 移入到目标数据库 db 中。注意:移动的 key 会从当前的数据库中移除。

6.2 事务

Redis 作为 NoSQL 数据库,也提供了事务的机制。在 Redis 中可以使用 multiexecdiscard 命令来实现事务,在事务中,所有的命令都将会串行化、顺序执行,事务执行期间,Redis 不会为其他的客户端提供任何的服务,从而保证事务中的所有命令都会被原子化执行。

和关系型数据库的事务相比,在 Redis 中,如果某一个命令执行失败了,它后面的命令还会被执行。

multi 命令开启事务。在执行 multi 命令之后执行的命令,都视为在事务里面执行的。
exec 命令提交事务。
discard 命令回滚。

在事务开启之前,如果客户端和服务器之间出现通信故障,并导致网络断开,那么它之后执行的语句,都不会被服务器执行;如果网络发生中断是在 exec 命令之后,那么事务中的所有命令都会被服务器执行。

127.0.0.1:6379> set num 2
OK
127.0.0.1:6379> get num
"2"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr num
QUEUED
127.0.0.1:6379> incr num
QUEUED
127.0.0.1:6379> exec
1) (integer) 3
2) (integer) 4
127.0.0.1:6379> get num
"4"

七、Redis 的持久化

Redis 的高性能是由于所有的数据都存储在内存中,为了使 Redis 在重启之后数据不丢失,那么就需要将数据从内存同步到磁盘当中,这个过程称为持久化操作。

Redis 的持久化方式:

  • RDB 方式
  • AOF 方式
  • 无持久化
  • 同时使用 RDB 和 AOF

7.1 RDB 持久化

默认支持的,不需要配置。

RDB 方式:

在指定的时间间隔内,将内存中的数据集快照写入到磁盘。

优势:

  • 整个 Redis 的数据库将会只包含一个文件,这对于文件备份而言非常好;
  • 对于灾难恢复而言,RDB 是非常不错的选择,因为我们可以将单独的文件拷贝,并压缩;
  • 性能最大化。对于 Redis 的进程而言,需要做的,只是分叉出一些子进程,之后,再由子进程完成持久化的工作,这样就能极大的避免服务端进程执行 IO 的操作
  • 相比 AOF 方式,如果数据集很大,RDB 启动速度会更快。

劣势:

  • 如果想保证数据的高可用性,最大限度避免数据丢失,RDB 不是一种很好的方式。
$ cd /Users/D/Downloads/redis-5.0.7/etc
$ vim redis.conf

在配置文件中,找到如下的内容

技术图片

save 900 1 表示每 900 秒至少有 1 个 key 发生变化,触发持久化操作;
save 300 10 表示每 300 秒至少有 10 个 key 发生变化,触发持久化操作;
save 60 10000 表示每 60 秒至少有 10000 个 key 发生变化,触发持久化操作;

技术图片

数据保存的位置。

技术图片

7.2 AOF 持久化

AOF 方式:

将以日志的形式记录服务器所处理的每一个操作,在 Redis 服务器启动之初,它会读取该文件,重新构建数据库,来保障启动后的数据时完整的。

Redis 的 AOF 持久化策略是将发送到 Redis 服务端的每一条命令都记录下来,并且保存到硬盘中的 AOF 文件中,类似修改日志文件,来一条命令就记录一条。

优势:

  • 带来更高的数据安全性。Redis 提供了三种同步策略:每秒同步、每修改同步、不同步。每秒同步是异步完成的,效率也很高,但存在宕机时数据丢失问题;每修改同步是每次发生数据的变化,会立即记录到磁盘中,效率更低,更安全;
  • 对日志文件的写入操作是 append 模式,因此在写入过程中,即使在发生了宕机,也不会破坏已经存在的内容。如果在写入一半之后出现了系统崩溃的问题,在 Redis 下一次启动之前,我们可以通过 redis/check/aof 工具来帮助我们解决数据一致性的问题。

    技术图片

  • 如果日志过大,Redis 可以自动启动重写机制,那么 Redis 以 append 模式不断的将修改的数据写入到老的磁盘当中,同时 Redis 还会创建新的文件,来记录此期间有哪些修改命令被执行了,因此,在进行重写切换的时候可以更好的保证数据的安全性。

  • AOF 方式包含一个格式非常清晰的、易于理解的日志文件,用于记录所有的修改操作。可以通过这个文件,进行数据的重建。

劣势:

  • 对于相同数量的数据集而言,AOF 文件比 RDB 文件要大;
  • 根据同步策略的不同,运行效率往往低于 RDB。

技术图片

appendfsync always 每修改一次就同步到磁盘。
appendfsync everysec 每秒同步。

127.0.0.1:6379> shutdown
not connected> exit
$ redis-server /Users/D/Downloads/redis-5.0.7/etc/redis.conf 

修改成 AOF 方式之后,先关闭服务器,然后重新启动。注意:启动要带有 conf 的文件路径。

$ redis-cli
127.0.0.1:6379> set num1 1
OK
127.0.0.1:6379> set num2 2
OK
127.0.0.1:6379> keys *
1) "num2"
2) "num1"
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> keys *
(empty list or set)

flushall 清空数据库。

技术图片

清空之后,可以手动修改 appendonly.aof 文件,删除文件中记录的 flushall 命令,然后再次打开 redis 服务器和客户端,清除的数据就回来了。

注意:再次打开数据库的时候使用 $ redis-server xx/redis.conf,而不是 $ redis-server

技术图片

八、Redis 连接池

public final class RedisPool {
    // Redis 服务器 IP
    private static String IP = "127.0.0.1";
    // Redis 的端口号
    private static Integer PORT = 6379;

    /* 可用连接实例的最大数目。
    
        默认为 8,如果 MAX_TOTAL = -1,则表示不限制。
        如果 pool 已经分配了 maxActive 个 jedis 实例,则此时 pool 的状态为 exhausted(耗尽)
    */
    private static Integer MAX_TOTAL = 20;
    // 控制一个连接池最多有多少个状态为空闲的 jedis 实例,默认值是 8。
    private static Integer MAX_IDLE = 10;
    // 等待可用连接的最大时间,单位是毫秒,默认值为-1,表示永不超时。
    // 如果超过等待时间,则直接抛出 JedisConnectionException 异常
    private static Integer MAX_WAIT_MILLIS = 10000;
    private static Integer TIMEOUT = 10000;
    // 在用一个 jedis 实例时,是否提前进行 validate(验证)操作;
    // 如果为 true,则得到的 jedis 实例均是可用的
    private static Boolean TEST_ON_BORROW = true;
    private  static JedisPool jedisPool = null;

    static {
        try {
            // 获得连接池配置对象
            JedisPoolConfig config = new JedisPoolConfig();
            /*
                在高版本的 jedis jar 包中,JedisPoolConfig 没有 maxActive 和 maxWait 属性。
                
                maxActive  ==>  maxTotal
                maxWait==>  maxWaitMillis
            */
            config.setMaxTotal(MAX_TOTAL);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWaitMillis(MAX_WAIT_MILLIS);
            config.setTestOnBorrow(TEST_ON_BORROW);
            jedisPool = new JedisPool(config, IP, PORT, TIMEOUT);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public synchronized static Jedis getJedis(){
        try {
            if(jedisPool != null){
                Jedis jedis = jedisPool.getResource();
                return jedis;
            }
            else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

九、文章来源

慕神 & Redis 入门
redis ---- java 操作redis 及 redisClient可视化工具的安装使用
Redis客户端,提取码: kxee

以上是关于Redis的主要内容,如果未能解决你的问题,请参考以下文章

redis存储session配制方法

Redis实现分布式锁(设计模式应用实战)

Redis实现分布式锁(设计模式应用实战)

Redis缓存:java语言注释符号,附超全教程文档

spring boot 整合 redis

Redis 学习 —— 数据类型及操作