Redis入门

Redis是一个使用ANSI C编写的开源、支持网络、基于内存、分布式、可选持久性的键值对存储数据库。从2015年6月开始,Redis(Remote Dictionary Server,远程字典服务)的开发由Redis Labs赞助。

NoSQL

NoSQL(Non-SQL,Not only SQL),是对不同于传统的关系数据库的数据库管理系统的统称。

允许部分资料使用SQL系统存储,而其他资料允许使用NOSQL系统存储。其数据存储可以不需要固定的表格模式以及元数据(metadata),也经常会避免使用SQL的JOIN操作,一般有水平可扩展性的特征。

NOSQL一词最早出现于1998年,是Carlo Strozzi开发的一个轻量、开源、不提供SQL功能的关系数据库。

2009年在亚特兰大举行的no:sql(east)讨论会是一个里程碑,其口号是select fun, profit from real_world where relational=false;。因此对NOSQL普遍解释是非关联型的,强调键-值存储和面向文档数据库的优点,而不是单纯的反对RDBMS

常见的NoSQL数据库是MongoDB,Apache Cassandra和Redis。


关系型数据库的典型实现主要被调整用于执行规模小而读写频繁,或者大批量读而极少写访问的事务,而非数据敏感的应用,如为巨量文档创建索引、高流量网站的网页服务,以及发送流式媒体。

NOSQL的结构通常提供弱一致性的保证,如最终一致性,或交易仅限于单个的数据项。不过,有些系统,提供完整的ACID保证在某些情况下,增加了补充中间件层(如CloudTPS)。有两个成熟的系统有提供快照隔离的列存储:像是Google基于过滤器系统的BigTable,和滑铁卢大学开发的HBase。这些系统,自主开发,使用类似的概念来实现多行(multi-row)分布式ACID交易的快照隔离(snapshot isolation)保证为基础列存储,无需额外的资料管理开销,中间件系统部署或维护,减少了中间件层。

少数NOSQL系统部署了分布式结构,通常使用分布式散列表(DHT)将数据以冗余方式保存在多台服务器上。依此,扩充系统时候添加服务器更容易,并且扩大了对服务器失效的承受能程度。


分类

  • 文档存储

    • BaseX
    • CouchDB
    • ElasticSearch
    • eXist
    • iBoxDB
    • Jackrabbit
    • Lotus Notes
    • MarkLogic Server
    • MongoDB
    • No2DB
    • OrientDB
    • RethinkDB
    • SimpleDB
    • Terrastore
  • 图数据库

    • AllegroGraph
    • Sparksee
    • Neo4j
    • FlockDB
    • JanusGraph
  • 键-值(key‐value)储存

    • 最终一致性的键-值储存

      • Cassandra
      • Dynamo
      • Hibari
      • Project Voldemort
      • Riak
    • 架构性键-值储存

      • GT.M
      • InterSystem Globals
  • 主机式服务

    • Key/value硬盘存储
      • BigTable
      • MemcacheDB
      • LevelDB
      • Tokyo Cabinet
      • Tuple space
      • TreapDB
    • Key/value RAM存储
      • Oracle Coherence
      • memcached
      • Citrusleaf database
      • Velocity (memory cache)
      • Redis
      • Tuple space
    • Key-value基于Paxos算法的存储
      • Keyspace (data store)
  • 多数据库

    • OpenQM
    • Rocket U2
    • Revelation Software’s OpenInsight
    • Extensible Storage Engine(ESE/NT)
  • 时序型数据库

    • Graphite
    • InfluxDB
    • Informix TimeSeries
    • OpenTSDB
    • RRDtool
    • IoTDB
  • 对象数据库

    • Versant Object Database
    • db4o
    • GemStone/S
    • InterSystems Caché
    • Objectivity/DB
    • ZODB
    • ObjectStore
  • 列存储

    • Cassandra
    • HBase
    • Hypertable

Redis

Redis

Redis教程

概述

  • 数据模型

    Redis的外围由一个键、值映射的字典构成。与其他非关系型数据库主要不同在于:Redis中值的类型不仅限于字符串,还支持如下抽象数据类型:

    • 字符串列表
    • 无序不重复的字符串集合
    • 有序不重复的字符串集合
    • 键、值都为字符串的哈希表

    值的类型决定了值本身支持的操作。Redis支持不同无序、有序的列表,无序、有序的集合间的交集、并集等高级服务器端原子操作。

  • 持久化
    Redis通常将全部的数据存储在内存中。

    目前通过两种方式实现持久化:

    • 使用快照,一种半持久耐用模式。不时的将数据集以异步方式从内存以RDB格式写入硬盘。
    • AOF格式(1.1版本开始,更安全)替代,一种只能追加的日志类型。将数据集修改操作记录起来。Redis能够在后台对只可追加的记录进行修改,从而避免日志的无限增长。
  • 同步
    Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。从盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。

  • 性能
    当数据依赖不再需要,Redis这种基于内存的性质,与在执行一个事务时将每个变化都写入硬盘的数据库系统相比就显得执行效率非常高。写与读操作速度没有明显差别。


安装

Windows

1.下载

redis·GitHub

microsoftarchive/redis

Native port of Redis for Windows

2.启动

  • 点击redis-server.exe
1
2
#启动失败
redis-server.exe redis.windows.conf
  • 客户端:redis-cli.exe

性能测试

1
2
redis-benchmark [option] [option value]
redis-benchmark -h localhost -p 6379 -c 100 -n 100000 -a password

Redis性能测试工具可选参数

序号 选项 描述 默认值
1 -h 指定服务器主机名 127.0.0.1
2 -p 指定服务器端口 6379
3 -s 指定服务器 socket
4 -c 指定并发连接数 50
5 -n 指定请求数 10000
6 -d 以字节的形式指定 SET/GET 值的数据大小 2
7 -k 1=keep alive 0=reconnect 1
8 -r SET/GET/INCR 使用随机 key, SADD 使用随机值
9 -P 通过管道传输 请求 1
10 -q 强制退出 redis。仅显示 query/sec 值
11 –csv 以 CSV 格式输出
12 -l 生成循环,永久执行测试
13 -t 仅运行以逗号分隔的测试命令列表。
14 -I Idle 模式。仅打开 N 个 idle 连接并等待。

命令

1.Redis每个数据库对外都是一个从0开始的递增数字命名,Redis默认支持16个数据库(可以通过配置文件支持更多,无上限),通过配置databases修改。客户端与Redis建立连接后会默认选择0号数据库,可以使用SELECT命令更换数据库。

command

1.启动Redis

1
redis-server redis.conf

2.连接Redis

1
redis-cli -h 127.0.0.1 -p 6379 [-a password]

3.关闭Redis

1
redis-cli [-a password] shutdown

1.测试连通性

1
2
127.0.0.1:6379> ping
PONG

2.选择数据库db_index

1
2
127.0.0.1:6379> select 3
OK

3.删除当前选定数据库的所有键

1
2
127.0.0.1:6379[1]> flushdb
OK

4.删除所有现有数据库的所有键

1
2
127.0.0.1:6379[1]> flushall
OK

Redis Key

1.获取该数据库所有键

1
2
127.0.0.1:6379[1]> keys *
1) "test"

2.返回当前数据库中的键总数

1
2
127.0.0.1:6379[1]> dbsize
(integer) 1

3.查询键是否存在

1
2
3
4
5
6
7
8
127.0.0.1:6379[1]> set name admin
OK
127.0.0.1:6379[1]> exists name
(integer) 1
127.0.0.1:6379[1]> exists username
(integer) 0
127.0.0.1:6379[1]> exists username name #查询多个返回存在的个数
(integer) 1

4.删除键

1
2
127.0.0.1:6379> del name     #删除多个返回删除成功的个数
(integer) 1

5.查询键类型

1
2
127.0.0.1:6379[2]> type name
string

6.移动键

1
2
3
4
5
6
7
8
127.0.0.1:6379[1]> move name 2
(integer) 1
127.0.0.1:6379[1]> keys *
(empty array)
127.0.0.1:6379[1]> select 2
OK
127.0.0.1:6379[2]> keys *
1) "name"

7.查询key的生命周期(秒)

秒:ttl key
毫秒:pttl key

-1:键永远不过期

-2:键不存在

1
2
127.0.0.1:6379[2]> ttl name
(integer) -1

8.设置过期时间

秒:expire key seconds
毫秒:pexpire key milliseconds

1:设置成功

0:当key不存在或者不能为key设置过期时间时

1
2
3
4
127.0.0.1:6379> expire name 60
(integer) 1
127.0.0.1:6379> ttl name
(integer) 45

9.设置永不过期

persist key

1:当key设置了过期时间,同时使用persist成功返回1。

0:key不存在或本身永久有效。

1
2
3
4
127.0.0.1:6379> expire name 60
(integer) 1
127.0.0.1:6379> persist name
(integer) 1

10.更改键名称

rename key newkey

1
2
127.0.0.1:6379> rename name username
OK
命令 描述
AUTH password 验证密码是否正确
ECHO message 打印字符串
PING 查看服务是否运行
QUIT 关闭当前连接
SELECT index 切换到指定的数据库

数据类型

Redis data types

Data Structures

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)

数据类型 简介
String(字符串) String 是 redis 最基本的类型,string 类型的值最大能存储 512MB,一个 key 对应一个 value。string 类型是二进制安全的,即 redis 的 string 可以包含任何数据,如jpg图片或者序列化的对象。
Hash(哈希) Hash 是一个键值(key=>value)对集合。Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 适合用于存储对象。
List(列表) List 是简单的字符串列表,允许重复的成员,按插入顺序排序。可添加一个元素到列表的头部(左边)或者尾部(右边)。
Set(集合) Set 是 string 类型的无序集合,不允许重复的成员。集合通过哈希表实现,即添加,删除,查找的复杂度均为 O(1)。
Zset(sorted set:有序集合) Zset 是 string 类型元素的集合,不允许重复的成员。每个元素都关联一个double类型分数。redis通过分数为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)可以重复。

String

One of the most versatile of Redis’ building blocks, Redis Strings is a binary-safe data structure. It can store any kind of data–a string, integer, floating point value, JPEG image, serialized Ruby object, or anything else you want it to carry. Operate on a whole string or parts, and increment or decrement integers and floats.

常用命令 描述
SET key value 设置指定 key 的值
GET key 获取指定 key 的值
GETRANGE key start end 返回 key 中字符串值的子字符
GETSET key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
GETBIT key offset 对 key 所储存的字符串值,获取指定偏移量上的位(bit)
MGET key1 key2.. 获取所有(一个或多个)给定 key 的值
SETBIT key offset value 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。位的设置或清除取决于 value 参数,当 value 是 0 时清除偏移量上的位,当 value 是 1 时设置偏移量上的位。当 key 不存在时,自动生成一个新的字符串值
SETEX key seconds value (set with expire)将键 key 的值设置为 value , 并将键 key 的生存时间设置为 seconds 秒钟
SETNX key value (set if not exist)只有在 key 不存在时设置 key 的值
SETRANGE key offset value 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始
STRLEN key 返回 key 所储存的字符串值的长度
MSET key value key value … 同时设置一个或多个 key-value 对
MSETNX key value key value … 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
PSETEX key milliseconds value 以毫秒为单位设置 key 的生存时间
INCR key 将 key 中储存的数字值增一
INCRBY key increment 将 key 所储存的值加上给定的增量值(increment)
INCRBYFLOAT key increment 将 key 所储存的值加上给定的浮点增量值(increment)
DECR key 将 key 中储存的数字值减一
DECRBY key decrement key 所储存的值减去给定的减量值(decrement)
APPEND key value 如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> set msg HelloRedis
OK
127.0.0.1:6379> getrange msg 4 7
"oRed"
127.0.0.1:6379> setrange msg 5 Jedis
(integer) 10
127.0.0.1:6379> get msg
"HelloJedis"
127.0.0.1:6379> getset msg HelloWorld
"HelloJedis"
127.0.0.1:6379> getrange msg 0 -1
"HelloWorld"
127.0.0.1:6379> setbit bit 10 1
(integer) 0
127.0.0.1:6379> getbit bit 10
(integer) 1
127.0.0.1:6379> getbit bit 9
(integer) 0
127.0.0.1:6379> setbit bit 10 0
(integer) 1
127.0.0.1:6379> getbit bit 10
(integer) 0
127.0.0.1:6379> mget msg k1
1) "HelloWorld"
2) "v1"
127.0.0.1:6379> setex username 60 Coco
OK
127.0.0.1:6379> ttl username
(integer) 53
127.0.0.1:6379> setnx name admin
(integer) 1
127.0.0.1:6379> setrange name 1 john
(integer) 5
127.0.0.1:6379> get name
"ajohn
127.0.0.1:6379> strlen name
(integer) 5
127.0.0.1:6379> mset key1 value1 key2 value2
OK
127.0.0.1:6379> mget key1 key2
1) "value1"
2) "value2"
127.0.0.1:6379> msetnx team1 Killer team2 player
(integer) 1
127.0.0.1:6379> mget team1 team2
1) "Killer"
2) "player"
127.0.0.1:6379> psetex team3 60000 noob
OK
127.0.0.1:6379> pttl team3
(integer) 58267
127.0.0.1:6379> set num1 45
OK
127.0.0.1:6379> get num1
"45"
127.0.0.1:6379> incr num1
(integer) 46
127.0.0.1:6379> incr num1
(integer) 47
127.0.0.1:6379> incrby num1 59
(integer) 106
127.0.0.1:6379> decr num1
(integer) 105
127.0.0.1:6379> decrby num1 65
(integer) 40
127.0.0.1:6379> append num1 isnum1
(integer) 8
127.0.0.1:6379> get num1
"40isnum1"
127.0.0.1:6379> set num2 16.45
OK
127.0.0.1:6379> incrbyfloat num2 1.97
"18.42"

List

Redis Lists holds collections of string elements sorted according to their order of insertion. Push or pop items from both ends, trim based on offsets, read individual or multiple items, or find or remove items by value and position. You can also make blocking calls for asynchronous message transfers.

命令 描述
BLPOP key1 [key2] timeout 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
BRPOP key1 [key2] timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
BRPOPLPUSH source destination timeout 从列表中取出最后一个元素,并插入到另外一个列表的头部,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
LINDEX key index 通过索引获取列表中的元素
LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素
LLEN key 获取列表长度
LPOP key 移出并获取列表的第一个元素
LPUSH key value1 [value2] 将一个或多个值插入到列表头部
LPUSHX key value 将一个值插入到已存在的列表头部
LRANGE key start stop 获取列表指定范围内的元素
LREM key count value 根据参数 count 的值,移除列表中与参数 value 相等的元素。count > 0:从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。count < 0:从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。count = 0:移除表中所有与 value 相等的值。
LSET key index value 将列表 key 下标为 index 的元素的值设置为 value
LTRIM key start stop 让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除
RPOP key 移除列表的最后一个元素,返回值为移除的元素
RPOPLPUSH source destination 将列表 source 中的最后一个元素(尾元素)弹出并返回,将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素
RPUSH key value1 [value2] 在列表尾部添加一个或多个值
RPUSHX key value 将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
127.0.0.1:6379> lpush team Mike
(integer) 1
127.0.0.1:6379> lpush team John
(integer) 2
127.0.0.1:6379> lpush team Peter
(integer) 3
127.0.0.1:6379> llen team
(integer) 3
127.0.0.1:6379> lrange team 0 2
1) "Peter"
2) "John"
3) "Mike"
127.0.0.1:6379> lrange team 0 -1
1) "Peter"
2) "John"
3) "Mike"
127.0.0.1:6379> blpop team 60
1) "team"
2) "Peter"
127.0.0.1:6379> brpop team 60
1) "team"
2) "Mike"
127.0.0.1:6379> lrange team 0 -1
1) "John"
127.0.0.1:6379> lpush team tim
(integer) 2
127.0.0.1:6379> lpush team Deft
(integer) 3
127.0.0.1:6379> lrange team 0 -1
1) "Deft"
2) "tim"
3) "John"
127.0.0.1:6379> brpoplpush team team1 60
"John"
127.0.0.1:6379> lrange team1 0 -1
1) "John"
127.0.0.1:6379> linsert team before tim Alex
(integer) 3
127.0.0.1:6379> lrange team 0 -1
1) "Deft"
2) "Alex"
3) "tim"
127.0.0.1:6379> lpop team
"Deft"
127.0.0.1:6379> lrange team 0 -1
1) "Alex"
2) "tim"
127.0.0.1:6379> lpushx team vk
(integer) 3
127.0.0.1:6379> lrange team 0 -1
1) "vk"
2) "Alex"
3) "tim"
127.0.0.1:6379> lrange team 0 -1
1) "vk"
2) "Alex"
3) "tim"
127.0.0.1:6379> lrem team 1 tim
(integer) 1
127.0.0.1:6379> lrange team 0 -1
1) "vk"
2) "Alex"
127.0.0.1:6379> lset team 1 Elex
OK
127.0.0.1:6379> lrange team 0 -1
1) "vk"
2) "Elex"
127.0.0.1:6379> lpush team Joker
(integer) 3
127.0.0.1:6379> lpush team Hoya
(integer) 4
127.0.0.1:6379> lpush team Flu
(integer) 5
127.0.0.1:6379> lrange team 0 -1
1) "Flu"
2) "Hoya"
3) "Joker"
4) "vk"
5) "Elex"
127.0.0.1:6379> ltrim team 1 3
OK
127.0.0.1:6379> lrange team 0 -1
1) "Hoya"
2) "Joker"
3) "vk"
127.0.0.1:6379> rpoplpush team team1
"vk"
127.0.0.1:6379> lrange team 0 -1
1) "Hoya"
2) "Joker"
127.0.0.1:6379> lrange team1 0 -1
1) "vk"
2) "John"
127.0.0.1:6379> rpush team One Ioer
(integer) 4
127.0.0.1:6379> lrange team 0 -1
1) "Hoya"
2) "Joker"
3) "One"
4) "Ioer"
127.0.0.1:6379> rpushx team Truth
(integer) 5
127.0.0.1:6379> lrange team 0 -1
1) "Hoya"
2) "Joker"
3) "One"
4) "Ioer"
5) "Truth"
127.0.0.1:6379> rpushx team2 Truth
(integer) 0

Set

Redis Sets data structure stores a unique set of members. With Sets, you can add, fetch, or remove members, check membership, or retrieve a random member. You can also perform set operations such as intersection, union, and set difference and compute set cardinality.

命令 描述
SADD key member1 [member2] 向集合添加一个或多个成员
SCARD key 获取集合的成员数
SDIFF key1 [key2] 返回第一个集合与其他集合之间的差异。
SDIFFSTORE destination key1 [key2] 返回给定所有集合的差集并存储在 destination 中
SINTER key1 [key2] 返回给定所有集合的交集
SINTERSTORE destination key1 [key2] 返回给定所有集合的交集并存储在 destination 中
SISMEMBER key member 判断 member 元素是否是集合 key 的成员
SMEMBERS key 返回集合中的所有成员
SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合
SPOP key 移除并返回集合中的一个随机元素
SRANDMEMBER key [coun] 只有key返回集合中的一个随机元素。如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
SREM key member1 [member2] 移除集合中一个或多个成员
SUNION key1 [key2] 返回所有给定集合的并集
SUNIONSTORE destination key1 [key2] 所有给定集合的并集存储在 destination 集合中
SSCAN key cursor [MATCH pattern] [COUNT count] 迭代集合中的元素,cursor:游标,pattern:匹配的模式,count:指定从数据集里返回多少元素,默认值为 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
127.0.0.1:6379> sadd team Alice
(integer) 1
127.0.0.1:6379> sadd team Bob
(integer) 1
127.0.0.1:6379> sadd team Cookie
(integer) 1
127.0.0.1:6379> scard team
(integer) 3
127.0.0.1:6379> sadd team1 Alice
(integer) 1
127.0.0.1:6379> sadd team1 Bob
(integer) 1
127.0.0.1:6379> sdiff team team1
1) "Cookie"
127.0.0.1:6379> sdiffstore team2 team team1
(integer) 1
127.0.0.1:6379> smembers team2
1) "Cookie"
127.0.0.1:6379> sinter team team1
1) "Bob"
2) "Alice"
127.0.0.1:6379> sinterstore team4 team team1
(integer) 2
127.0.0.1:6379> smembers team4
1) "Alice"
2) "Bob"
127.0.0.1:6379> sismember team Alice
(integer) 1
127.0.0.1:6379> sismember team alice
(integer) 0
127.0.0.1:6379> smembers team1
1) "Bob"
2) "Alice"
127.0.0.1:6379> smembers team2
1) "Cookie"
127.0.0.1:6379> smove team2 team1 "Cookie"
(integer) 1
127.0.0.1:6379> smembers team1
1) "Cookie"
2) "Bob"
3) "Alice"
127.0.0.1:6379> smembers team2
(empty array)
127.0.0.1:6379> spop team
"Cookie"
127.0.0.1:6379> spop team
"Alice"
127.0.0.1:6379> smembers team
1) "Bob"
127.0.0.1:6379> sadd team Xia
(integer) 1
127.0.0.1:6379> sadd team Zod
(integer) 1
127.0.0.1:6379> sadd team Nick
(integer) 1
127.0.0.1:6379> sadd team Hu
(integer) 1
127.0.0.1:6379> smembers team
1) "Xia"
2) "Zod"
3) "Nick"
4) "Bob"
5) "Hu"
127.0.0.1:6379> srandmember team 3
1) "Xia"
2) "Hu"
3) "Bob"
127.0.0.1:6379> srandmember team 3
1) "Zod"
2) "Bob"
3) "Nick"
127.0.0.1:6379> srandmember team 6
1) "Xia"
2) "Zod"
3) "Nick"
4) "Bob"
5) "Hu"
127.0.0.1:6379> srandmember team -4
1) "Zod"
2) "Nick"
3) "Nick"
4) "Bob"
127.0.0.1:6379> srem team Zod Xia
(integer) 2
127.0.0.1:6379> smembers team
1) "Hu"
2) "Bob"
3) "Nick"
127.0.0.1:6379> sunion team team1
1) "Hu"
2) "Nick"
3) "Bob"
4) "Cookie"
5) "Alice"
127.0.0.1:6379> sunionstore team3 team team1
(integer) 5
127.0.0.1:6379> smembers team3
1) "Hu"
2) "Nick"
3) "Bob"
4) "Cookie"
5) "Alice"

127.0.0.1:6379> sadd nums n1
(integer) 1
.......
127.0.0.1:6379> sadd nums n10
(integer) 1

127.0.0.1:6379> sscan nums 0 match n* count 3
1) "4"
2) 1) "n7"
2) "n8"
3) "n5"
127.0.0.1:6379> sscan nums 4 match n* count 3
1) "6"
2) 1) "n3"
2) "n4"
3) "n9"
127.0.0.1:6379> sscan nums 6 match n* count 3
1) "5"
2) 1) "n10"
2) "n1"
3) "n2"
127.0.0.1:6379> sscan nums 5 match n* count 3
1) "0"
2) 1) "n6"

Hash

Similar to documents in a document store or rows in a relational database, the Redis Hashes structure stores a set of field-value pairs. It provides the ability to add, fetch, or remove individual items and fetch the entire hash, or use one or more fields in the hash as a counter.

命令 描述
HDEL key field1 [field2] 删除一个或多个哈希表字段
HEXISTS key field 查看哈希表 key 中,指定的字段是否存在
HGET key field 获取存储在哈希表中指定字段的值
HGETALL key 获取在哈希表中指定 key 的所有字段和值
HINCRBY key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment
HINCRBYFLOAT key field increment 为哈希表 key 中的指定字段的浮点数值加上增量 increment
HKEYS key 获取所有哈希表中的字段
HLEN key 获取哈希表中字段的数量
HMGET key field1 [field2] 获取所有给定字段的值
HMSET key field1 value1 [field2 value2 ] 同时将多个 field-value (域-值)对设置到哈希表 key 中
HSET key field value 将哈希表 key 中的字段 field 的值设为 value
HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值
HVALS key 获取哈希表中所有值
HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对,返回的每个元素都是一个元组,每一个元组元素由一个字段(field) 和值(value)组成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
127.0.0.1:6379> hmset user username "Alice" password "12345678" phone "18589702341" age 25 likes 29
OK
127.0.0.1:6379> hgetall user
1) "username"
2) "Alice"
3) "password"
4) "12345678"
5) "phone"
6) "18589702341"
7) "age"
8) "25"
9) "likes"
10) "29"
127.0.0.1:6379> hkeys user
1) "username"
2) "password"
3) "phone"
4) "age"
5) "likes"
127.0.0.1:6379> hlen user
(integer) 5
127.0.0.1:6379> hdel user likes
(integer) 1
127.0.0.1:6379> hkeys user
1) "username"
2) "password"
3) "phone"
4) "age"
127.0.0.1:6379> hexists user phone
(integer) 1
127.0.0.1:6379> hexists user likes
(integer) 0
127.0.0.1:6379> hget user username
"Alice"
127.0.0.1:6379> hincrby user age 20
(integer) 45
127.0.0.1:6379> hmset user weight 50.81
OK
127.0.0.1:6379> hkeys user
1) "username"
2) "password"
3) "phone"
4) "age"
5) "weight"
127.0.0.1:6379> hincrbyfloat user weight 12.23
"63.04"
127.0.0.1:6379> hmget user username password
1) "Alice"
2) "12345678"
127.0.0.1:6379> hset user username Bob
(integer) 0
127.0.0.1:6379> hvals user
1) "Bob"
2) "12345678"
3) "18589702341"
4) "45"
5) "63.04"
127.0.0.1:6379> hsetnx user sex man
(integer) 1
127.0.0.1:6379> hvals user
1) "Bob"
2) "12345678"
3) "18589702341"
4) "45"
5) "63.04"
6) "man"

1
2
3
4
5
6
7
8
127.0.0.1:6379> hscan user 0 MATCH "p*" COUNT 1
1) "0"
2) 1) "password"
2) "12345678"
3) "phone"
4) "18589702341"
127.0.0.1:6379> object encoding user
"ziplist"

COUNT参数失效:

When iterating Sets encoded as intsets (small sets composed of just integers), or Hashes and Sorted Sets encoded as ziplists (small hashes and sets composed of small individual values), usually all the elements are returned in the first SCAN call regardless of the COUNT value.


Zset

Redis Sorted Sets contain a unique set of members ordered by floating-point scores. As with Sets, you can add, fetch, or remove individual members and perform set operations such as union, intersection, set difference, and compute cardinality. Furthermore, you can also query the set based on score or member value, aggregate, filter, and order results.

命令 描述
ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZCARD key 获取有序集合的成员数
ZCOUNT key min max 计算在有序集合中指定区间分数的成员数
ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment
ZINTERSTORE destination numkeys key [key …] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 destination 中,key 的数量必须以 numkeys 参数指定默认情况下,结果集中某个成员的分数值是所有给定集下该成员分数值之和。
ZLEXCOUNT key min max 在有序集合中计算指定字典区间内成员数量,min:有序集合中分数排名较小的成员,max:在有序集合中分数排名较大的成员,成员名称前需要加[符号作为开头, [符号与成员之间不能有空格,可以使用 -+ 表示得分最小值和最大值,minmax 不能反, max 放前面 min放后面会导致返回结果为0,计算成员之间的成员数量时,参数 minmax 的位置也计算在内。
ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合指定区间内的成员
ZRANGEBYLEX key min max [LIMIT offset count] 返回给定的有序集合键 key 中, 值介于 minmax 之间的成员。如果有序集合里面的成员带有不同的分值, 那么命令返回的结果是未指定的(unspecified)。LIMIT offset count 参数用于获取指定范围内的匹配元素。offset:返回结果起始位置
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员,maxmin参数前可以加 ( 符号作为开头表示小于, ( 符号与成员之间不能有空格,可以使用 +inf-inf 表示得分最大值和最小值,maxmin 不能反, max 放后面 min放前面会导致返回结果为空,计算成员之间的成员数量不加 ( 符号时,参数 minmax 的位置也计算在内。
ZRANK key member 返回有序集合中指定成员的索引
ZREM key member [member …] 移除有序集合中的一个或多个成员
ZREMRANGEBYLEX key min max 移除有序集合中给定的字典区间的所有成员
ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员
ZREMRANGEBYSCORE key min max 移除有序集合中给定的分数区间的所有成员
ZREVRANGE key start stop [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到低
ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序
ZREVRANK key member 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
ZSCORE key member 返回有序集中,成员的分数值
ZUNIONSTORE destination numkeys key [key …] 计算给定的一个或多个有序集的并集,并存储在新的 key 中
ZSCAN key cursor [MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
127.0.0.1:6379> zadd database 1 Oracle
(integer) 1
127.0.0.1:6379> zadd database 2 MySQL
(integer) 1
127.0.0.1:6379> zadd database 3 MongoDB
(integer) 1
127.0.0.1:6379> zadd database 3 "MS SQL Server"
(integer) 1
127.0.0.1:6379> zadd database 4 MySQL
(integer) 0
127.0.0.1:6379> zcard database
(integer) 4
127.0.0.1:6379> zcount database 1 3
(integer) 3
127.0.0.1:6379> zincrby database 8 Oracle
"9"
127.0.0.1:6379> zcount database 1 3
(integer) 2
127.0.0.1:6379> zadd udb 3 MongoDB 4 "MS SQL Server"
(integer) 2
127.0.0.1:6379> zinterstore db 2 database udb
(integer) 2
127.0.0.1:6379> zrange db 0 -1 WITHSCORES
1) "MongoDB"
2) "6"
3) "MS SQL Server"
4) "7"
127.0.0.1:6379> zlexcount database - +
(integer) 4
127.0.0.1:6379> zlexcount database [MongoDB [MySQL
(integer) 2
127.0.0.1:6379> zlexcount database ["MS SQL Server" [MySQL
(integer) 3
127.0.0.1:6379> zadd database 6 memcached
(integer) 1
127.0.0.1:6379> zrange database 0 -1 WITHSCORES
1) "MS SQL Server"
2) "3"
3) "MongoDB"
4) "3"
5) "MySQL"
6) "4"
7) "memcached"
8) "6"
9) "Oracle"
10) "9"
127.0.0.1:6379> zrange database 0 2 WITHSCORES
1) "MS SQL Server"
2) "3"
3) "MongoDB"
4) "3"
5) "MySQL"
6) "4"
127.0.0.1:6379> zrangebyscore database 3 4
1) "MS SQL Server"
2) "MongoDB"
3) "MySQL"
127.0.0.1:6379> zrangebyscore database 3 (4
1) "MS SQL Server"
2) "MongoDB"
127.0.0.1:6379> zrangebyscore database 3 4 WITHSCORES
1) "MS SQL Server"
2) "3"
3) "MongoDB"
4) "3"
5) "MySQL"
6) "4"
127.0.0.1:6379> zrangebyscore database 3 4 LIMIT 1 2
1) "MongoDB"
2) "MySQL"
127.0.0.1:6379> zrank database MySQL
(integer) 2
127.0.0.1:6379> zrem database Oracle
(integer) 1
127.0.0.1:6379> zrange database 0 -1
1) "MS SQL Server"
2) "MongoDB"
3) "MySQL"
4) "memcached"
127.0.0.1:6379> zremrangebyscore database 3 4
(integer) 3
127.0.0.1:6379> zrange database 0 -1
1) "memcached"
127.0.0.1:6379> zadd database 1 MySQL 2 Oracle 3 Redis 4 MongoDB
(integer) 4
127.0.0.1:6379> zrevrange database 0 4
1) "memcached"
2) "MongoDB"
3) "Redis"
4) "Oracle"
5) "MySQL"
127.0.0.1:6379> zrevrangebyscore database 4 1 WITHSCORES
1) "MongoDB"
2) "4"
3) "Redis"
4) "3"
5) "Oracle"
6) "2"
7) "MySQL"
8) "1"
127.0.0.1:6379> zrevrank database Redis
(integer) 2
127.0.0.1:6379> zscore database Redis
"3"
127.0.0.1:6379> zunionstore dbzset 2 database myzset
(integer) 7
127.0.0.1:6379> zrange dbzset 0 -1
1) "f"
2) "g"
3) "MySQL"
4) "Oracle"
5) "Redis"
6) "MongoDB"
7) "memcached"
127.0.0.1:6379> zscan dbzset 0 MATCH M*
1) "0"
2) 1) "MySQL"
2) "1"
3) "MongoDB"
4) "4"


127.0.0.1:6379> ZADD myzset 0 a 0 b 0 c 0 d 0 e 0 f 0 g
(integer) 7
127.0.0.1:6379> ZRANGEBYLEX myzset - +
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
7) "g"
127.0.0.1:6379> ZRANGEBYLEX myzset - (g
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
127.0.0.1:6379> ZRANGEBYLEX myzset [c (g
1) "c"
2) "d"
3) "e"
4) "f"
127.0.0.1:6379> ZRANGEBYLEX myzset (c (g
1) "d"
2) "e"
3) "f"
127.0.0.1:6379> ZRANGEBYLEX myzset - + LIMIT 0 4
1) "a"
2) "b"
3) "c"
4) "d"
127.0.0.1:6379> ZRANGEBYLEX myzset - + LIMIT 2 4
1) "c"
2) "d"
3) "e"
4) "f"
127.0.0.1:6379> zremrangebylex myset (a (d
(integer) 0
127.0.0.1:6379> zrange myset 0 -1
(empty array)
127.0.0.1:6379> zremrangebylex myzset (a (d
(integer) 2
127.0.0.1:6379> zrange myzset 0 -1
1) "a"
2) "d"
3) "e"
4) "f"
5) "g"
127.0.0.1:6379> zremrangebyrank myzset 0 2
(integer) 3
127.0.0.1:6379> zrange myzset 0 -1
1) "f"
2) "g"

1.有序集合的所有成员都具有相同的分值时, 有序集合的元素会根据成员的字典序(lexicographical ordering)来进行排序。

minmax 参数必须包含 ( 或者 [ , 其中 ( 表示开区间(指定的值不会被包含在范围之内), 而 [ 则表示闭区间(指定的值会被包含在范围之内)。特殊值 +-min 参数以及 max 参数中具有特殊的意义, 其中 + 表示正无限, 而 - 表示负无限。 因此, 向一个所有成员的分值都相同的有序集合发送命令 ZRANGEBYLEX <zset> - + , 命令将返回有序集合中的所有元素。



BitMap

可以将 Redis 中的 BitMap 想象为以 Bit 为单位的组,数组的每个单位只能存储0和1,数组的下标在BitMaps 中叫做偏移量,这样做的优势是节省大量内存空间,BitMap 底层是基于 Redis 的字符串类型实现的。

Redis Bitmaps is a compact data structure to store binary logic and states. It provides commands to fetch and set a bit value at a given position, and perform AND, OR, XOR, and NOT operations between multiple bitmap keys.

命令 描述
SETBIT key offset value 对 key 字符串设置或清除指定偏移量上的位(bit)。位的设置或清除取决于 value 参数,可以是 0 或1 。当 key 不存在时,自动生成一个新的字符串值。字符串会进行伸展(grown)以确保它可以将 value 保存在指定的偏移量上。当字符串值进行伸展时,空白位置以 0 填充。offset 参数必须大于或等于 0 ,小于 2^32
GETBIT key offset 对 key 字符串获取指定偏移量上的位(bit)。当 offset 比字符串值的长度大,或者 key 不存在时,返回 0 。
BITCOUNT key [ start] [end] key字符串中被设置为 1 的比特位的数量。一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在指定的字节或者位(默认为位)进行。不存在的 key 被当成是空字符串来处理,即一个不存在的 key 进行 BITCOUNT 结果为 0 。
BITFIELD key [GET encoding offset] [SET encoding offset value] [INCRBY encoding offset increment] [OVERFLOW WRAP | SAT | FAIL] BITFIELD将一个 Redis 字符串视为一个由二进制位组成的数组, 并对这个数组中储存的长度不同的整数进行访问 (被储存的整数无需进行对齐)
BITOP operation destkey key [key …] 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey
BITPOS key bit [start] [end] 返回位图中第一个值为 bit 的二进制位的位置start和end参数以Byte为单位。
BITFIELD_RO key GET encoding offset BITFIELD 命令的只读变体。 类似于原始的 BITFIELD,但只接受 GET 子命令,可以安全地用于只读副本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
127.0.0.1:6379> setbit bit 2 1
(integer) 0
127.0.0.1:6379> getbit bit 2
(integer) 1
127.0.0.1:6379> getbit bit 1
(integer) 0
127.0.0.1:6379> getbit bit 5
(integer) 0


127.0.0.1:6379> setbit bit 4 1
(integer) 0
127.0.0.1:6379> setbit bit 8 1
(integer) 0
127.0.0.1:6379> bitcount bit
(integer) 2
127.0.0.1:6379> bitcount bit 0 0
(integer) 1
127.0.0.1:6379> bitcount bit 0 1
(integer) 2

127.0.0.1:6379> setbit mybits 9 1
(integer) 0
127.0.0.1:6379> bitpos mybits 1
(integer) 9
127.0.0.1:6379> bitpos mybits 0
(integer) 0
127.0.0.1:6379> bitpos mybits 1 0 0
(integer) -1
127.0.0.1:6379> bitpos mybits 1 0 1
(integer) 9

127.0.0.1:6379> setbit bits:1 0 1
(integer) 0
127.0.0.1:6379> setbit bits:1 3 1
(integer) 0
127.0.0.1:6379> setbit bits:2 1 1
(integer) 0
127.0.0.1:6379> setbit bits:2 2 1
(integer) 0
127.0.0.1:6379> bitop OR or-result bits:1 bits:2
(integer) 1
127.0.0.1:6379> getbit or-result 0
(integer) 1
127.0.0.1:6379> getbit or-result 1
(integer) 1
127.0.0.1:6379> getbit or-result 2
(integer) 1
127.0.0.1:6379> getbit or-result 3
(integer) 1
127.0.0.1:6379> getbit or-result 4
(integer) 0

1.SETBIT操作中使用大的offset,内存分配可能造成 Redis 服务器被阻塞。

2.Redis7.0.0添加 BYTE|BIT 参数。

3.operation 可以是 ANDORNOTXOR 这四种操作中的任意一种:

  • BITOP AND destkey key [key ...] ,对一个或多个 key 求逻辑并,并将结果保存到 destkey

  • BITOP OR destkey key [key ...] ,对一个或多个 key 求逻辑或,并将结果保存到 destkey

  • BITOP XOR destkey key [key ...] ,对一个或多个 key 求逻辑异或,并将结果保存到 destkey

  • BITOP NOT destkey key ,对给定 key 求逻辑非,并将结果保存到 destkey

除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入。

当 BITOP处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0


Hyperloglog

1.Redis HyperLogLog 是用来做基数统计的算法。

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

Redis HyperLogLog is a probabilistic data structure used to count unique values (set cardinality) at a constant memory size. You can add and count a large number of unique items with memory efficiency, and merge two or more HyperLogLog data structures into one.

命令 描述
PFADD key element [element …] 添加指定元素到 HyperLogLog 中
PFCOUNT key [key …] 返回给定 HyperLogLog 的基数估算值
PFMERGE destkey sourcekey [sourcekey …] 将多个 HyperLogLog 合并为一个 HyperLogLog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
127.0.0.1:6379> pfadd TechStack SpringBoot
(integer) 1
127.0.0.1:6379> pfadd TechStack SpringMVC
(integer) 1
127.0.0.1:6379> pfadd TechStack MyBatis
(integer) 1
127.0.0.1:6379> pfadd TechStack Nginx
(integer) 1
127.0.0.1:6379> pfadd TechStack Redis
(integer) 1
127.0.0.1:6379> pfadd TechStack MySQL
(integer) 1
127.0.0.1:6379> pfcount TechStack
(integer) 6
127.0.0.1:6379> pfadd NewTechStack MongoDB
(integer) 1
127.0.0.1:6379> pfadd NewTechStack Docker
(integer) 1
127.0.0.1:6379> pfmerge tes TechStack NewTechStack
OK
127.0.0.1:6379> pfcount tes
(integer) 8

Geospatial index

Geospatial indexes provide an extremely efficient and simple way to manage and use geospatial data in Redis. You can add unique items with latitude and longitude, compute the distance between objects, and find members within a given radius range from a location.

命令 描述
GEOADD key longitude latitude member [longitude latitude member …] 将给定的空间元素(纬度、经度、名字)添加到指定的键里面。这些数据会以有序集合的形式被储存在键里面, GEOADD 命令以标准的 x,y 格式接受参数, 即必须先输入经度再输入纬度
GEOPOS key member [member …] 从键里面返回所有给定位置元素的位置(经度和纬度)
GEODIST key member1 member2 [unit] 返回两个给定位置之间的距离。如果两个位置之间的其中一个不存在,返回空值
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count] 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count] 找出位于指定范围内的元素, 中心点是给定的位置元素
GEOHASH key member [member …] 返回一个或多个位置元素的 Geohash 表示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
127.0.0.1:6379> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
127.0.0.1:6379> GEODIST Sicily Palermo Catania
"166274.1516"
127.0.0.1:6379> GEOPOS Sicily Palermo
1) 1) "13.36138933897018433"
2) "38.11555639549629859"
127.0.0.1:6379> GEOPOS Sicily Palermo Catania
1) 1) "13.36138933897018433"
2) "38.11555639549629859"
2) 1) "15.08726745843887329"
2) "37.50266842333162032"
127.0.0.1:6379> GEODIST Sicily Palermo Catania km
"166.2742"
127.0.0.1:6379> GEORADIUS Sicily 15 37 200 km WITHDIST
1) 1) "Palermo"
2) "190.4424"
2) 1) "Catania"
2) "56.4413"
127.0.0.1:6379> GEORADIUS Sicily 15 37 200 km WITHCOORD
1) 1) "Palermo"
2) 1) "13.36138933897018433"
2) "38.11555639549629859"
2) 1) "Catania"
2) 1) "15.08726745843887329"
2) "37.50266842333162032"
127.0.0.1:6379> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD
1) 1) "Palermo"
2) "190.4424"
3) 1) "13.36138933897018433"
2) "38.11555639549629859"
2) 1) "Catania"
2) "56.4413"
3) 1) "15.08726745843887329"
2) "37.50266842333162032"
127.0.0.1:6379> GEOADD Sicily 13.583333 37.316667 "Agrigento"
(integer) 1
127.0.0.1:6379> GEORADIUSBYMEMBER Sicily Agrigento 100 km
1) "Agrigento"
2) "Palermo"
127.0.0.1:6379> geohash Sicily Agrigento Palermo
1) "sq9sm1716e0"
2) "sqc8b49rny0"

1.GEOADD 能够记录的坐标是有限的: 非常接近两极的区域是无法被索引的。

精确的坐标限制由 EPSG:900913 / EPSG:3785 / OSGEO:41001 等坐标系统定义:

  • 有效的经度介于 -180 度至 180 度之间。
  • 有效的纬度介于 -85.05112878 度至 85.05112878 度之间。

当输入一个超出范围的经度或者纬度时, GEOADD 命令将返回一个错误。

2.GEODIST指定单位的参数 unit 必须是以下单位:

  • m :米。
  • km:千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

如果没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。GEODIST 命令在计算距离时会假设地球为完美的球形, 在极限情况下,该假设最大会造成 0.5% 的误差。

3.GEORADIUS以下可选项时命令会返回额外信息:

  • WITHDIST:在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
  • WITHCOORD:将位置元素的经度和维度也一并返回。
  • WITHHASH:以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值(底层应用和调试)。

命令默认返回未排序的位置元素。 以下两个参数可以指定被返回位置元素的排序方式:

  • ASC:根据中心的位置, 按照从近到远的方式返回位置元素。
  • DESC:根据中心的位置, 按照从远到近的方式返回位置元素。

在默认情况下, GEORADIUS 命令会返回所有匹配的位置元素。

虽然使用 COUNT <count> 选项去获取前 N 个匹配元素, 但命令内部可能会需要对所有被匹配的元素进行处理, 所以对一个非常大的区域进行搜索时, 即使只使用 COUNT 选项去获取少量元素,命令的执行速度也可能会非常慢。 但是从另一方面来说, 使用 COUNT 选项去减少需要返回的元素数量, 对于减少带宽来说仍然非常有用。


Stream

1.Redis Stream 主要用于消息队列(MQ,Message Queue),Redis 本身有 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃,即发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息。

2.Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,保证消息不丢失。

3.消费者组最早是由Kafka引入的。Redis用完全不同术语实现相似的概念,但目标是相同的:允许一组客户端相互配合来消费同一个Stream的不同部分的消息。

Redis Streams is an incredibly powerful data structure for managing high-velocity data streams (like a message queue). With out-of-the-box partitioning, replication, and persistence, it can capture and process millions of data points per second at sub-millisecond latency. Redis Streams is based on an efficient radix-tree implementation, which makes the range and lookup queries extremely fast. It connects producers and consumers with asynchronous calls and supports consumer groups.

命令 描述
XADD key ID field value [field value …] 向队列添加消息,如果指定的队列不存在,则创建一个队列。key:队列名称,如果不存在就创建。ID:消息id,* 表示由 redis 生成,自定义需要保证递增性。 field value:记录
XTRIM key MAXLEN | MINID [ = | ~] threshold [LIMIT count] 对流进行修剪,限制长度
XDEL key ID [ID …] 删除消息
XLEN key 获取流包含的元素数量,即消息长度
XRANGE key start end [COUNT count] 获取消息列表,自动过滤已经删除的消息
XREVRANGE key end start [COUNT count] 获取消息列表,自动过滤已经删除的消息
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key …] id [id …] 以阻塞或非阻塞方式获取消息列表
XGROUP [CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername] 创建消费者组
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key …] ID [ID …] 读取消费组中的消息

事务

1.Redis 事务(Redis Transactions)可以一次执行多个命令,以命令 MULTI、EXEC、DISCARD 和 WATCH 为中心, 并且有两个重要的保证:

  • 事务中的所有命令都会序列化、按顺序地执行。
  • 事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

2.单个 Redis 命令的执行是原子性的,但 Redis 事务上并没有维持原子性的机制,所以 Redis 事务的执行并非原子性的。

3.事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令中止执行。

4.一个事务从开始到执行经历三个阶段:

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

5.Redis不支持回滚(roll back)。

命令 描述
DISCARD 取消事务,放弃执行事务块内的所有命令
EXEC 执行所有事务块内的命令
MULTI 标记一个事务块的开始。
UNWATCH 取消 WATCH 命令对所有 key 的监视。
WATCH key [key …] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
127.0.0.1:6379> clear
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) "v2"
127.0.0.1:6379> keys *
1) "k1"
2) "k2"

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k2"

事务错误:

  • 事务在执行EXEC之前,入队的命令可能会出错。如命令可能会产生语法错误(参数名错误),或者内存不足等问题( Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用EXEC命令时,拒绝执行并自动放弃这个事务)。
  • 命令可能在EXEC调用之后失败。如事务中命令可能处理了错误类型的键,将列表命令用在字符串键上不会进行特别处理,即使事务中有某个/某些命令在执行时产生了错误,事务中的其他命令仍然会继续执行)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> keys *
1) "k1"
2) "k2"

127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> get k4
QUEUED
127.0.0.1:6379(TX)> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v4"

乐观锁

1.WATCH命令可以为 Redis 事务提供 check-and-set (CAS)行为。被WATCH的键会被监视,并会发觉这些键是否被改动过。 如果有至少一个被监视的键在 EXEC 执行之前被修改了,那么整个事务都会被取消, EXEC 返回nil-reply来表示事务已经失败。如果在 WATCH执行之后, EXEC 执行之前, 其他客户端修改WATCH监视键的值, 那么当前客户端事务会失败。 程序需要不断重试这个操作,直到没有发生碰撞为止。

2.WATCH 使得 EXEC 命令需要有条件地执行:事务只能在所有被监视键都没有被修改的前提下执行, 如果这个前提不能满足的话,事务就不会被执行。

3.WATCH命令可以被调用多次。

4.对键的监视从 WATCH 执行之后开始生效,直到调用 EXEC为止。当EXEC被调用时, 不管事务是否成功执行,对所有键的监视都会被取消。当客户端断开连接时,该客户端对键的监视也会被取消。

5.单个 WATCH 命令可以监视任意多个键。

6.使用无参数的 UNWATCH 命令可以手动取消对所有键的监视。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
127.0.0.1:6379> get money
"80"


127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 32
QUEUED

# 另一客户端
127.0.0.1:6379> incrby money 120
(integer) 200

127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379> get money
"200"


Jedis

Jedis is a Java client for Redis designed for performance and ease of use.


1
2
3
4
5
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.0</version>
</dependency>

1.连接Redis

  • JedisPool
1
2
3
4
5
6
JedisPool pool = new JedisPool(url, port,username,password); /* 无username即为null */
try (Jedis jedis = pool.getResource()) {
System.out.println(jedis.ping());
jedis.set("name", "Jedis");
System.out.println(jedis.get("name") );
}
  • JedisPooled
1
2
3
JedisPooled jedis = new JedisPooled(url, port,username,password);
jedis.set("Name","RedisTest");
System.out.println(jedis.get("Name"));
  • Jedis
1
2
3
4
5
Jedis jedis = new Jedis("106.52.67.217", 6379);
jedis.auth("ryuT?=0-U623");
System.out.println(jedis.ping());
jedis.set("team","Gaming");
System.out.println(jedis.get("team"));

1.Key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*1.选择数据库*/
System.out.println(jedis.select(2));
/*2.清空该数据库*/
System.out.println(jedis.flushDB());
/*3.查询键存在*/
System.out.println(jedis.exists("name"));
/*4.设置键值对*/
System.out.println(jedis.set("name","Admin"));
System.out.println(jedis.set("username","Jedis"));
System.out.println(jedis.set("score","12"));
/*5.查询该数据库所有键值对*/
Set<String> keys = jedis.keys("*");
System.out.println(keys);
/*6.删除键*/
System.out.println(jedis.del("username"));
/*7.查看对应键的值类型*/
System.out.println(jedis.type("name"));
/*8.当前数据库中随机返回一个key*/
System.out.println(jedis.randomKey());
/*9.重命名key*/
System.out.println(jedis.rename("name","usr"));
/*10.返回当前数据库key数目*/
System.out.println(jedis.dbSize());
/*11.清空所有数据库*/
System.out.println(jedis.flushAll());

2.String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*1.设置对应key的值*/
System.out.println(jedis.set("k1","v1"));
System.out.println(jedis.set("k2","v2"));
System.out.println(jedis.set("k3","v3"));
/*2.获取指定key的值*/
System.out.println(jedis.get("k1"));
/*3.删除指定key*/
System.out.println(jedis.del("k1"));
/*
4.为指定的key追加值
key已存在且是字符串,将value追加到key原来值的末尾
key 不存在,给定key设为value
*/
System.out.println(jedis.append("k2"," is value"));
/*5.设置多个键值对*/
System.out.println(jedis.mset("k4","v4","k5","v5"));
/*6.获取多个键值对*/
System.out.println(jedis.mget("k1","k2","k3","k4","k5"));
/*7.删除多个键值对*/
System.out.println(jedis.del("k1,k3"));
/*8.指定的key不存在时,为key设置指定的值*/
System.out.println(jedis.setnx("k4","val1"));
System.out.println(jedis.setnx("key1","value1"));
/*9.为指定的key设置值及过期时间。key已经存在,将替换旧值*/
System.out.println(jedis.setex("key3",3,"value3"));
System.out.println(jedis.get("key3"));
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(jedis.get("key3"));

/*10.设置指定key的值,返回key的旧值*/
System.out.println(jedis.getSet("key1","This is value1"));

/*11.获取存储在指定key中字符串的子字符串。字符串截取范围由start和end两个偏移量决定(包括start和end)*/
System.out.println(jedis.getrange("key1",2,9));

3.List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*1.将多个值插入列表头部*/
jedis.lpush("techStack","Java","MySQL","SpringBoot","MyBatis");
jedis.lpush("techStack","Redis","MongoDB","Java");
/*2.获取列表指定范围内的元素*/
System.out.println(jedis.lrange("techStack",0,-1));
/*3.移除列表中与参数 value 相等指定数量的元素*/
System.out.println(jedis.lrem("techStack",1,"Java"));
/*4.保留指定区间内的元素*/
System.out.println(jedis.ltrim("techStack",0,3));
/*5.移出并获取列表的第一个元素*/
System.out.println(jedis.lpop("techStack"));
/*6.在列表尾部添加一个或多个值*/
System.out.println(jedis.rpush("techStack","Shiro","Kafka"));
/*7.移除列表的最后一个元素*/
System.out.println(jedis.rpop("techStack"));
/*8.将列表 key下标为index的元素的值设置为value*/
System.out.println(jedis.lset("techStack",1,"Docker"));
/*9.获取列表长度*/
System.out.println(jedis.llen("techStack"));
/*10.获取列表对应下标的值*/
System.out.println(jedis.lindex("techStack",1));
/*11.返回或保存给定列表、集合、有序集合 key 中经过排序的元素。默认以数字作为对象,值被解释为双精度浮点数进行比较*/
System.out.println(jedis.lpush("num", "3", "6", "2", "4", "5", "7", "9"));
System.out.println(jedis.lrange("num",0,-1));
System.out.println(jedis.sort("num"));

4.Set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*1.集合中添加元素*/
System.out.println(jedis.sadd("mySet","v1","v2","v3","v4","v5"));
System.out.println(jedis.sadd("mySet","v7"));
System.out.println(jedis.sadd("mySet2","v3","v4","v5","v8","v9","v11"));
/*2.返回集合中的所有成员*/
System.out.println(jedis.smembers("mySet"));
/*3.移除集合中一个或多个成员*/
System.out.println(jedis.srem("mySet","v1"));
/*4.移除并返回集合中的一个随机元素*/
System.out.println(jedis.spop("mySet"));
/*5.获取集合的成员数*/
System.out.println(jedis.scard("mySet"));
/*6.判断元素是否是集合的成员*/
System.out.println(jedis.sismember("mySet","v2"));
/*7.将元素从集合移动到另一集合*/
System.out.println(jedis.smove("mySet2","mySet","v11"));
/*8.两集合的交集*/
System.out.println(jedis.sinter("mySet","mySet2"));
/*9.两集合的并集*/
System.out.println(jedis.sunion("mySet","mySet2"));
/*10.两集合的差集*/
System.out.println(jedis.sdiff("mySet","mySet2"));

5.Hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*1.将多个field-value对设置到哈希表中*/
jedis.hmset("myHash",map);
/*2.哈希表中的字段的设值*/
jedis.hset("myHash","k4","v4");
/*3.获取在哈希表中指定 key 的所有字段和值*/
System.out.println(jedis.hgetAll("myHash"));
/*4.获取所有哈希表中的字段*/
System.out.println(jedis.hkeys("myHash"));
/*5.获取哈希表中所有值*/
System.out.println(jedis.hvals("myHash"));
/*6.为哈希表的指定字段的整数值加上增量*/
System.out.println(jedis.hincrBy("myHash","k5",10));
/*7.为哈希表的指定字段的浮点数值加上增量*/
System.out.println(jedis.hincrByFloat("myHash","k6",1.24));
/*8.删除一个或多个哈希表字段*/
System.out.println(jedis.hdel("myHash","k1"));
/*9.获取哈希表中字段的数量*/
System.out.println(jedis.hlen("myHash"));
/*10.查看哈希表中指定的字段是否存在*/
System.out.println(jedis.hexists("myHash","k1"));
/*11.获取所有给定字段的值*/
System.out.println(jedis.hmget("myHash","k2","k3"));

5.Zset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*1.向有序集合添加一个或多个成员,或者更新已存在成员的分数*/
Map<String, Double> scoreMembers = new HashMap<>();
scoreMembers.put("member1",1.0);
scoreMembers.put("member2",2.0);
scoreMembers.put("member3",3.0);
System.out.println(jedis.zadd("myZest",scoreMembers));
/*2.获取有序集合的成员数*/
System.out.println(jedis.zcard("myZest"));
/*3.计算在有序集合中指定区间分数的成员数*/
System.out.println(jedis.zcount("myZest",1.0,2.0));
/*4.有序集合中对指定成员的分数加上增量*/
System.out.println(jedis.zincrby("myZest",3.6,"member1"));
/*5.返回有序集合中指定成员的索引*/
System.out.println(jedis.zrank("myZest","member1"));
/*6.移除有序集合中的一个或多个成员*/
System.out.println(jedis.zrem("myZest","member1"));
/*7.返回有序集中指定区间内的成员,通过索引,分数从高到低*/
System.out.println(jedis.zrevrange("myZest",0,4));
/*8.用于移除有序集中指定分数(score)区间内的所有成员*/
System.out.println(jedis.zremrangeByScore("myZest",0,1));
/*9.返回有序集中,成员的分数值*/
System.out.println(jedis.zscore("myZest","member3"));


1
2
3
4
5
6
7
8
9
10
11
12
13
Jedis jedis = new Jedis("106.52.67.217", 6379);
jedis.auth("ryuT?=0-U623");
Transaction multi = jedis.multi();
try {
multi.set("name","admin");
multi.set("phone","12458902");
int num= 1/0;
multi.set("id","10");
}catch (Exception e){
/*multi.discard();*/
e.printStackTrace();
}
multi.exec();

Lettuce

1.Lettuce是一个高性能基于Java编写的Redis驱动框架,底层集成了Project Reactor提供天然的反应式编程,通信框架集成了Netty使用了非阻塞IO,5.x版本之后融合了JDK1.8的异步编程特性,在保证高性能的同时提供了十分丰富易用的API。

  1. Spring Boot 2.X 默认集成了 Lettuce(spring-boot-starter-data-redis)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}

@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
/*
默认情况下RedisTemplat模板只能支持 RedisTemplate<String,String>只能进行字符串操作,
需要自定义 RedisTemplate并设置序列化器来操作实例对象,@ConditionalOnMissingBean默认RedisTemlate失效
*/
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@ConfigurationProperties(
prefix = "spring.redis"
)
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String username;
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Duration connectTimeout;
private String clientName;
private RedisProperties.ClientType clientType;
private RedisProperties.Sentinel sentinel;
private RedisProperties.Cluster cluster;
private final RedisProperties.Jedis jedis = new RedisProperties.Jedis();
private final RedisProperties.Lettuce lettuce = new RedisProperties.Lettuce();

......
public static class Lettuce {
private Duration shutdownTimeout = Duration.ofMillis(100L);
private final RedisProperties.Pool pool = new RedisProperties.Pool();
private final RedisProperties.Lettuce.Cluster cluster = new RedisProperties.Lettuce.Cluster();

.....

public static class Cluster {
private final RedisProperties.Lettuce.Cluster.Refresh refresh = new RedisProperties.Lettuce.Cluster.Refresh();

public Cluster() {
}

public RedisProperties.Lettuce.Cluster.Refresh getRefresh() {
return this.refresh;
}

public static class Refresh {
private boolean dynamicRefreshSources = true;
private Duration period;
private boolean adaptive;

......
}
}
}

public static class Jedis {
private final RedisProperties.Pool pool = new RedisProperties.Pool();

public Jedis() {
}

public RedisProperties.Pool getPool() {
return this.pool;
}
}

public static class Sentinel {
private String master;
private List<String> nodes;
private String password;
......
}

public static class Pool {
private Boolean enabled;
private int maxIdle = 8;
private int minIdle = 0;
private int maxActive = 8;
private Duration maxWait = Duration.ofMillis(-1L);
private Duration timeBetweenEvictionRuns;
......
}

public static enum ClientType {
LETTUCE,
JEDIS;
private ClientType() {
}
}
}

1
2
3
4
5
spring:
redis:
host:
port: 6379
password:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Autowired
@Qualifier("redisTemplate")
RedisTemplate redisTemplate;
/*
opsForValue String
opsForList List
opsForZSet ZSet
*/
@Test
void contextLoads(){
/*
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushDb();
*/
redisTemplate.opsForValue().set("Major","Computer Science");
System.out.println(redisTemplate.opsForValue().get("Major"));
}

测试后发现虽然值可以获取,但键在数据库中实际为\xAC\xED\x00\x05t\x00\x05major,由于spring-data-redis的RedisTemplate<K,V>模板类在操作redis时默认使用JdkSerializationRedisSerializer来进行序列化

  • 明确redisTemplate类型
1
2
@Autowired 
private RedisTemplate<String,String> redisTemplate
  • 编写Redis配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Configuration
public class CacheConfig {

@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

/*String序列化*/
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
/*JSON序列化*/
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);

/*Key采用String序列化*/
redisTemplate.setKeySerializer(stringRedisSerializer);
/*Hash Key采用String序列化*/
redisTemplate.setHashKeySerializer(stringRedisSerializer);
/*Value采用JSON(JackSon)序列化*/
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
/*Hash Value采用JSON序列化*/
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

redisTemplate.setConnectionFactory(cf);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
template.setConnectionFactory(factory);
/*key序列化*/
template.setKeySerializer(redisSerializer);
/*value序列化*/
template.setValueSerializer(redisSerializer);
//*value HashMap序列化*/
template.setHashValueSerializer(redisSerializer);
/*key HashMap序列化*/
template.setHashKeySerializer(redisSerializer);
return template;
}
}
  • 使用StringRedisTemplate类
1
2
@Autowired
StringRedisTemplate redisTemplate;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
private boolean enableTransactionSupport = false;
private boolean exposeConnection = false;
private boolean initialized = false;
private boolean enableDefaultSerializer = true;
@Nullable
private RedisSerializer<?> defaultSerializer;
@Nullable
private ClassLoader classLoader;
@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
@Nullable
private ScriptExecutor<K> scriptExecutor;
private final ValueOperations<K, V> valueOps = new DefaultValueOperations(this);
private final ListOperations<K, V> listOps = new DefaultListOperations(this);
private final SetOperations<K, V> setOps = new DefaultSetOperations(this);
private final StreamOperations<K, ?, ?> streamOps = new DefaultStreamOperations(this, ObjectHashMapper.getSharedInstance());
private final ZSetOperations<K, V> zSetOps = new DefaultZSetOperations(this);
private final GeoOperations<K, V> geoOps = new DefaultGeoOperations(this);
private final HyperLogLogOperations<K, V> hllOps = new DefaultHyperLogLogOperations(this);
private final ClusterOperations<K, V> clusterOps = new DefaultClusterOperations(this);

public RedisTemplate() {
}

public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}

if (this.enableDefaultSerializer) {
if (this.keySerializer == null) {
this.keySerializer = this.defaultSerializer;
defaultUsed = true;
}

if (this.valueSerializer == null) {
this.valueSerializer = this.defaultSerializer;
defaultUsed = true;
}

if (this.hashKeySerializer == null) {
this.hashKeySerializer = this.defaultSerializer;
defaultUsed = true;
}

if (this.hashValueSerializer == null) {
this.hashValueSerializer = this.defaultSerializer;
defaultUsed = true;
}
}

if (this.enableDefaultSerializer && defaultUsed) {
Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized");
}

if (this.scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor(this);
}

this.initialized = true;
}
...........
}

Redis工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
@Component
public final class RedisUtil {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

// =============================common============================

/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}

/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}

// ============================String=============================

/**
* 普通缓存获取
* @param key 键
* @return
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}

/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于 如果time小于等于 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 递增
* @param key 键
* @param delta 要增加几(大于)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于");
}
return redisTemplate.opsForValue().increment(key, delta);
}

/**
* 递减
* @param key 键
* @param delta 要减少几(小于)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于");
}
return redisTemplate.opsForValue().increment(key, -delta);
}

// ================================Map=================================

/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}

/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}

/**
* HashSet
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**

* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @param time 过期时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}


/**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}

/**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}

/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key 键
* @param item 项
* @param by 要增加几(大于)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}

/**
* hash递减
* @param key 键
* @param item 项
* @param by 要减少记(小于)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}

// ============================set=============================

/**
* 根据key获取Set中的所有值
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 根据value从一个set中查询,是否存在
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}


/**
* 将数据放入set缓存
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

/**
* 将set数据放入缓存
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

/**
* 获取set缓存的长度
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

// ===============================list=================================

/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 到 -代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}


/**
* 获取list缓存的长度
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}


/**
* 通过索引 获取list中的值
* @param key 键
* @param index 索引 index>=时, 表头, 第二个元素,依次类推;index<时,-,表尾,-倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}


/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}


/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 过期时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}


/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}


/**
* 将list放入缓存,并设置过期时间
* @param key 键
* @param value 值
* @param time 过期时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}


/**
* 根据索引修改list中的某条数据
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}


/**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}


Conf

配置 含义
include /path/to/local.conf 额外载入配置文件
bind 127.0.0.1 绑定redis服务器访问IP
protected-mode yes 默认开启,只允许本地客户端连接
port 6379 监听端口号,默认6379
tcp-backlog 511 TCP监听最大容纳数量
timeout 0 client 空闲多少秒关闭连接(0 永不关闭)
tcp-keepalive 300 单位秒,表示周期性使用SO_KEEPALIVE检测客户端是否还处于健康状态,避免服务器阻塞
daemonize yes 默认redis不作为守护进程运行,在后台运行改成 yes
pidfile /var/redis/run/redis_6379.pid 配置PID文件路径,当redis作为守护进程运行会把 pid 默认写到入/var/redis/run/redis_6379.pid 文件
loglevel notice 定义日志级别 debug(记录大量日志信息,适用于开发、测试阶段 verbose(较多日志信息)notice(适量日志信息,使用于生产环境) warning(仅有部分重要、关键信息才会被记录)
logfile /var/redis/log/redis_6379.log 日志文件位置,当指定为空字符串时,为标准输出,如果redis已守护进程模式运行,日志将输出到/dev/null
databases 16 设置数据库的数目 默认的数据库是DB 0
save 900 1 存DB到磁盘(持久化)
save <间隔时间(秒)> <写入次数>
根据时间间隔和写入次数将数据保存到磁盘
900 秒内如果至少有 1 个 key 的值变化,则保存
stop-writes-on-bgsave-error yes 如果用户开启RDB快照功能,那么redis持久化数据到磁盘时如果出现失败,默认情况下redis会停止接受所有的写请求,即用户知道内存中的数据和磁盘上的数据已经存在不一致。如果下一次RDB持久化成功,redis会自动恢复接受写请求
rdbcompression yes 存储到磁盘中的快照设置是否进行压缩存储
rdbchecksum yes 使用CRC64算法对快照进行数据校验
dir /var/redis/6379 设置快照文件的存放路径
requirepass foobared 设置redis连接密码
rename-command CONFIG “” 将命令重命名,可以将某些重要的、危险的命令重命名。
maxclients 10000 设置客户端最大并发连接数
maxmemory 指定Redis最大内存限制
maxmemory-policy noeviction 当内存使用达到最大值时,redis使用的清除策略
1.volatile-lru LRU算法移除设置过过期时间的key
2.allkeys-lru 利用LRU算法移除任何key
3.volatile-random 移除设置过过期时间的随机key
4.allkeys-random 移除随机key
5.volatile-ttl 移除即将过期的key(minor TTL)
6.noeviction noeviction 不移除任何key,只是返回一个写错误 ,默认
appendonly no 默认redis是RDB方式持久化,这种方式在redis如果中途宕机 会导致可能有几分钟的数据丢失,根据save来策略进行持久化,可使用Append Only File(AOF)持久化方式,Redis会把每次写入的数据在接收后都写入appendonly.aof文件, 每次启动时Redis都会先把这个文件的数据读入内存里。
appendfilename “appendonly.aof” aof文件名
appendfsync always aof持久化策略的配置
no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
always表示每次写入都执行fsync,以保证数据同步到磁盘。
everysec表示每秒执行一次fsync,可能会导致丢失这1s数据
cluster-enabled yes 集群开关,默认是不开启集群模式

持久化

Redis persistence

1.RDB (Redis Database): The RDB persistence performs point-in-time snapshots of your dataset at specified intervals.

2.AOF (Append Only File): The AOF persistence logs every write operation received by the server, that will be played again at server startup, reconstructing the original dataset. Commands are logged using the same format as the Redis protocol itself, in an append-only fashion. Redis is able to rewrite the log in the background when it gets too big.

3.No persistence: If you wish, you can disable persistence completely, if you want your data to just exist as long as the server is running.

4.RDB + AOF: It is possible to combine both AOF and RDB in the same instance. Notice that, in this case, when Redis restarts the AOF file will be used to reconstruct the original dataset since it is guaranteed to be the most complete.


Redis作为一个键值对内存数据库(NoSQL),数据都存储在内存当中,在处理客户端请求时,所有操作都在内存当中进行,存储在内存当中的数据,只要服务器宕机,内存中就会数据消失。如果Redis仅仅作为缓存来用影响不大,但如果业务数据存储在Redis,Redis的内存数据丢失影响则是毁灭性的。避免内存数据丢失,Redis提供对持久化操作,可以将数据从内存中保存到硬盘当中,使数据持久化保存。

RDB

1.RDB是一种快照(Snapshot)存储持久化方式,将Redis某一时刻的内存数据快照保存到磁盘中,默认保存文件名为dump.rdb,在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中恢复数据。

2,RDB持久化方案是按照指定时间间隔对你的数据集生成的时间点快照,是Redis数据库中数据的内存快照,是一个二进制文件,存储了文件生成时Redis数据库中所有的数据内容。可用于Redis的数据备份、转移与恢复。

1.save(同步)

当客户端向服务器发送save命令请求进行持久化时,服务器会阻塞save命令之后的其他客户端的请求,直到数据同步完成。如果数据量太大同步数据会执行很久,这期间Redis服务器也无法接收其他请求,最好不要在生产环境使用save命令。

2.bgsave(异步)

当客户端发出bgsave命令时,Redis服务器主进程会fork一个子进程来处理持久化,在将数据写入到临时rdb文件后,替换原来的快照文件,子进程会退出。与save命令相比,Redis服务器在处理bgsave采用子线程进行IO写入,而主进程仍然可以接收其他请求,但fork子进程是同步的,所以fork子进程时一样不能接收其他请求,即如果fork一个子进程花费的时间太久(一般很快),bgsave命令仍然有阻塞其他客户的请求的情况发生。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#redis.conf
save 60 5
dir ./

127.0.0.1:6379> config get dir # 备份与自动恢复目录
1) "dir"
2) "/root"
127.0.0.1:6379> save
OK
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> set k4 v4
OK
127.0.0.1:6379> set k5 v5
OK

[root@VM-8-3-centos ~]# ls
dump.rdb

1.RDB文件由主进程生成还是子进程来生成:

  • 生成临时rdb文件,并写入数据
  • 完成数据写入,用临时文代替代正式rdb文件
  • 删除原来的db文件

2.优点

  • 与AOF方式相比,通过rdb文件恢复数据比较快
  • rdb文件紧凑(二进制文件),适合于数据备份、全量复制
  • 通过RDB进行数据备份使用子进程生成时对Redis服务器性能影响较小
  • 对灾难恢复、数据迁移非常友好,rdb文件可转移至需要的地方重新加载

3.缺点

  • 服务器宕机会造成某个时段内数据的丢失(无法实时持久化)
  • save会造成服务器阻塞,直接数据同步完成才能接收后续请求
  • bgsave命令在fork子进程时,如果数据量太大forks的过程也会发生阻塞,另外forks进程会耗费内存

AOF

AOF(Append Only File)持久化功能,AOF持久化会把被执行的写命令写到AOF文件的末尾,记录数据的变化。默认情况下,Redis不开启AOF持久化,开启后每执行一条更改Redis数据命令,都会把该命令追加到AOF文件(appendonly.aof),虽然会降低Redis的性能,但大部分情况下能够接受,使用较快硬盘可以提高AOF的性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# redis.conf
appendonly yes
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"

redis-server /usr/local/redis/etc/redis.conf
> ls
appendonly.aof dump.rdb

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> set k4 v4
OK

# appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$2
k1
$2
v1
*3
$3
set
$2
k2
$2
v2
*3
$3
set
$2
k3
$2
v3
*3
$3
set
$2
k4
$2
v4

1.aof文件出错时会导致Redis无法启动,需要redis-check-aof来修复aof文件。

1
redis-check-aof --fix appendonly.aof

优点:

  • 数据更完整,安全性更高,秒级数据丢失(取决fsync策略)
  • AOF文件只进行追加,内容可读,适合误删紧急恢复

缺点:

  • 对于相同的数据集,AOF文件体积大于RDB文件,数据恢复比较慢
  • 根据所使用的fsync策略,AOF的速度可能会慢于RDB

订阅

  1. 发送者(发送信息的客户端)不是将信息直接发送给特定的接收者(接收信息的客户端), 而是将信息发送给频道(channel), 然后由频道将信息转发给所有对这个频道感兴趣的订阅者。

2.Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。

3.Redis 客户端可以订阅任意数量的频道。

4.底层是通过字典实现的,这个字典就用于保存订阅频道的信息:字典的键为正在被订阅的频道,而字典的值则是一个链表,链表中保存了所有订阅这个频道的客户端。当发送消息时首先根据 channel 定位到字典的键,然后将信息发送给字典值链表中的所有客户端。

命令 描述
PSUBSCRIBE pattern [pattern …] 订阅一个或多个符合给定模式的频道
PUBSUB subcommand [argument [argument …]] 查看订阅与发布系统状态
PUBLISH channel message 将信息发送到指定的频道
PUNSUBSCRIBE [pattern [pattern …]] 退订所有给定模式的频道
SUBSCRIBE channel [channel …] 订阅给定的一个或多个频道的信息
UNSUBSCRIBE [channel [channel …]] 指退订给定的频道
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 发送端
127.0.0.1:6379> publish Gaming CSGO
(integer) 1
127.0.0.1:6379> publish Gaming Apex
(integer) 1

# 接收端
127.0.0.1:6379> subscribe Gaming
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Gaming"
3) (integer) 1
1) "message"
2) "Gaming"
3) "CSGO"
1) "message"
2) "Gaming"
3) "Apex"


主从复制

1.主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

2.作用

  • 数据冗余
  • 故障恢复
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载,在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 高可用基石:主从复制是哨兵和集群能够实施的基础

3.保存主节点信息->主从建立socket连接->发送ping命令->权限验证->同步数据集->命令持续复制

命令 描述
SLAVEOF host port 将当前服务器转变为指定服务器的从属服务器或者停止对旧主服务器的同步转而对新主服务器进行同步
SLAVEOF NO ONE 使得该从属服务器关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据集不会被丢弃
ROLE 返回实例在复制中担任的角色(master 、slave或者sentinel)以及其他信息:主服务器将返回属下从服务器的 IP 地址和端口,从服务器将返回自己正在复制的主服务器的 IP 地址、端口、连接状态以及复制偏移量,Sentinel 将返回自己正在监视的主服务器列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# ls
redis.conf
# cp redis.conf redis79.conf
# cp redis.conf redis80.conf
# cp redis.conf redis81.conf

#redis80.conf
port 6380
replicaof <masterip> <masterport>
masterauth <master-password>
pidfile /var/run/redis_6380.pid
logfile "redis6380.log"
dbfilename dump6380.rdb

redis-server /usr/local/redis/etc/redis81.conf

# ps -ef|grep redis
root 32221 1 0 15:15 ? 00:00:00 redis-server *:6380
root 32280 1 0 15:15 ? 00:00:00 redis-server *:6381
root 32531 1 0 15:17 ? 00:00:00 redis-server *:6379
root 32542 31790 0 15:17 pts/1 00:00:00 grep --color=auto redis

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:85958e18b498d2498d7df202f153954361c237f2
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

# slaveof host port 无密码 一次性
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:42
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:9e9dcc696ee088ad017f9d8dae3a3cddb6d5b71c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=56,lag=0
master_failover_state:no-failover
master_replid:9e9dcc696ee088ad017f9d8dae3a3cddb6d5b71c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:56
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:56

127.0.0.1:6380> set name admin #从机无法设值,只能用于读取
(error) READONLY You can't write against a read only replica.
127.0.0.1:6379> set name admin
OK
127.0.0.1:6380> get name
"admin"

在Redis2.8以前,从节点向主节点发送sync命令请求同步数据,同步方式为全量复制,在Redis2.8及以后,从节点可以发送psync命令请求同步数据,根据主从节点当前状态的不同,同步方式可能是全量复制或部分复制。

类型 描述
全量复制 用于初次复制场景,把主节点全部数据一次性发送给从节点,当数据量较大时,会对主从节点和网络造成极大开销
部分复制 用于处理在主从复制中网络中断等原因造成的数据丢失,当从节点再次连上主节点后,如果条件允许,主节点会补发中断期间执行的写命令给从节点,与全量复制相比更加高效

哨兵模式

1.哨兵(sentinel)是Redis的一种运行模式,专注于对Redis实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个Redis系统的可用性。未配置哨兵之前,master宕机需要手动选择一个正常的slave成为新的主节点,效率低、故障感知滞后,不具备生产实用性。

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒/通知(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

​ Redis Sentinel 是一个分布式系统, 可以在一个架构中运行多个 Sentinel 进程(progress), 这些进程使用流言协议(gossip protocols)来接收关于主服务器是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。

2.sentinel可以让redis实现主从复制,当一个集群中的master失效之后,sentinel可以选举出一个新的master用于自动接替master的工作,集群中的其他redis服务器自动指向新的master同步数据。一般建议sentinel采取奇数台,防止某一台sentinel无法连接到master导致误切换。

3.哨兵通过PING命令实现对主从节点的故障检测。哨兵以集群方式工作,官方建议至少要有三个节点,每个节点都以相同的方式对主从节点进行监控与故障检测。单个哨兵对节点的故障检测可能无法代表其真实状态,为了降低误判,哨兵之间需要对节点的故障状态进行协商。

  • 主观宕机(Subjective Down, SDOWN):是指一个哨兵实例通过检测发现某个主节点发生故障的一种状态。

  • 客观宕机(Objective Down, ODOWN):是指哨兵检测到某个主节点发生故障,通过命令SENTINEL is-master-down-by-addr与其他哨兵节点协商,并且在指定时间内接收到指定数量的其他哨兵的确认反馈时的一种状态。

    SDOWN是该哨兵认为节点宕机,而ODOWN是不但该哨兵认为节点宕机,而且该哨兵与其他节点沟通后,达到一定数量的哨兵都认为节点宕机。

4.主节点客观宕机后,Sentinel Leader会从该主节点存活的从节点中选出一个新的主节点,之后让其他存活的从节点以该节点为主节点,然后向该节点发起主从复制。


1.配置哨兵配置文件

sentinel <选项的名字> <主服务器的名字> <选项的值>

1
2
3
4
sentinel monitor mymaster 127.0.0.1 6379 2  # 监视mymaster主服务器, 该主服务器的IP地址为 127.0.0.1 , 端口为 6379 ,将这个主服务器判断为失效至少需要2个Sentinel同意
sentinel down-after-milliseconds mymaster 60000 # 判断服务器已经断线所需的毫秒数
sentinel failover-timeout mymaster 180000 # 与故障转移超时的判断有关,用来判断其几个子阶段的超时,例如主节点晋升从节点时间超过timeout,或从节点向新的主节点发起复制操作的时间(不包括复制数据的时间)超过timeout,都会导致故障转移超时失败
sentinel parallel-syncs mymaster 1 # 在执行故障转移时,最多可以有多少个从服务器同时对新的主服务器进行同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# sentinel.conf
sentinel monitor myredis 127.0.0.1 6379 1
sentinel auth-pass myredis 123456 #连接master密码

> redis-sentinel /usr/local/redis/etc/sentinel.conf

28053:X 01 May 2022 21:33:10.035 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6381
28053:X 01 May 2022 21:33:10.035 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381
28053:X 01 May 2022 21:33:40.096 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381

127.0.0.1:6379> shutdown
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=9336,lag=0
master_failover_state:no-failover
master_replid:5ded70a045bbdfa227bc4e7f5f98e4f347ba3a54
master_replid2:aed8ad31232bf77921784659206a9a0a682b2367
master_repl_offset:9336
second_repl_offset:2852
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:9336

优点:

  1. 基于主从复制模式

  2. 故障自动转移,高可用

  3. 增强了系统的健壮性

缺点:

  1. 集群容量一旦到达上限,在线扩容困难
  2. 哨兵模式配置繁琐
  3. 始终只有一个Redis主机来接收和处理写请求,易受单机瓶颈影响
  4. 集群里所有节点保存的都是全量数据,没有实现分布式存储(Redis Sharding)

sentinel.conf


Redis缓存

缓存雪崩

1.如果缓存在某一个时刻出现大规模的key失效,那么就会导致大量的请求打在了数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机。这时候如果运维马上又重启数据库,马上又会有新的流量把数据库打死。这就是缓存雪崩。

2.应对方法:

  • 均匀过期:设置不同的过期时间,让缓存失效的时间尽量均匀
  • 分级缓存:第一级缓存失效的基础上,访问二级缓存,每一级缓存的失效时间都不同
  • 热点数据缓存永远不过期
  • 确保Redis缓存的高可用
  • 使用互斥锁
  • 使用熔断机制,限流降级

缓存击穿

1.缓存击穿跟缓存雪崩有点类似,缓存雪崩是大规模的key失效,而缓存击穿是某个热点的key失效,大并发集中对其进行请求,就会造成大量请求读缓存没读到数据,从而导致高并发访问数据库,引起数据库压力剧增。

2.应对方法

  • 在缓存失效后,通过互斥锁或者队列来控制读数据写缓存的线程数量,比如某个key只允许一个线程查询数据和写缓存,其他线程等待。这种方式会阻塞其他的线程,此时系统的吞吐量会下降

  • 热点数据缓存永远不过期

    • 物理不过期,针对热点key不设置过期时间
    • 逻辑过期,把过期时间存在key对应的value里,如果发现要过期,通过一个后台的异步线程进行缓存的构建

缓存穿透

1.缓存穿透是指用户请求的数据在缓存中不存在即没有命中,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍。如果有恶意攻击者不断请求系统中不存在的数据,会导致短时间大量请求落在数据库上,造成数据库压力过大,甚至导致数据库承受不住而宕机崩溃。

2.缓存穿透的关键在于在Redis中查不到key值,它和缓存击穿的根本区别在于传进来的key在Redis中是不存在的。假如有黑客传进大量的不存在的key,那么大量的请求打在数据库上是很致命的问题,所以在日常开发中要对参数做好校验,一些非法的参数,不可能存在的key就直接返回错误提示。

3.应对方法

  • 将无效的key存放进Redis中

当出现Redis查不到数据,数据库也查不到数据时,将key保存到Redis中,设置value为null,并设置其过期时间极短(无法应对不存在的Key值每次都是随机的情况)

  • 使用布隆过滤器(Bloom Filter,1970年由布隆提出的。实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难)

如果布隆过滤器判定某个 key 不存在布隆过滤器中,那么就一定不存在,如果判定某个 key 存在,那么很大可能是存在(存在一定的误判率)。在缓存之前再加一个布隆过滤器,将数据库中的所有key都存储在布隆过滤器中,在查询Redis前先去布隆过滤器查询 key 是否存在,如果不存在就直接返回,不让其访问数据库,从而避免了对底层存储系统的查询压力。


缓存预热

缓存预热是指系统上线后,提前将相关的缓存数据加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。


缓存降级

缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。在项目实战中通常会将部分热点数据缓存到服务的内存中,这样一旦缓存出现异常,可以直接使用服务的内存数据,从而避免数据库遭受巨大压力。



Redis使用规范

Key名称规范:

  1. 使用统一的命名规范。

    一般使用业务名(或数据库名)为前缀,用冒号分隔,如业务名:表名:id

  2. 控制key名称的长度。

    保证语义清晰的情况下,尽量减少Key的长度。常用单词可使用缩写,如user缩写为u,messages缩写为msg。

  3. 名称中不要包含特殊字符。

Value值规范:

  1. 避免大Key。

    大Key会带来网卡流量风暴和慢查询,一般string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。

  2. 选择合适的数据类型。

    比如存储用户的信息,可用使用多个key,使用set u:1:name "X"set u:1:age 20这样存储,也可以使用hash数据结构,存储成1个key,设置用户属性时使用hmset一次设置多个,同时这样存储也能节省内存。

  3. 设置合理的过期时间。

    最好是过期时间打散,不要集中在某个时间点过期。