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
架构性键-值储存
主机式服务
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算法的存储
多数据库
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.启动
1 2 # 启动失败 redis-server.exe redis.windows.conf
性能测试 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
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 > pingPONG
2.选择数据库db_index
1 2 127.0.0.1:6379 > select 3 OK
3.删除当前选定数据库的所有键
1 2 127.0.0.1:6379 [1] > flushdbOK
4.删除所有现有数据库的所有键
1 2 127.0.0.1:6379 [1] > flushallOK
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 adminOK 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 namestring
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 usernameOK
命令
描述
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 v1OK 127.0.0.1:6379> get k1"v1" 127.0.0.1:6379> set msg HelloRedisOK 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 k11 ) "HelloWorld" 2 ) "v1" 127.0.0.1:6379> setex username 60 CocoOK 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 ElexOK 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 team11 ) "Cookie" 127.0.0.1:6379 > sdiffstore team2 team team1(integer) 1 127.0.0.1:6379 > smembers team21 ) "Cookie" 127.0.0.1:6379 > sinter team team11 ) "Bob" 2 ) "Alice" 127.0.0.1:6379 > sinterstore team4 team team1(integer) 2 127.0.0.1:6379 > smembers team41 ) "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 team11 ) "Bob" 2 ) "Alice" 127.0.0.1:6379 > smembers team21 ) "Cookie" 127.0.0.1:6379 > smove team2 team1 "Cookie" (integer) 1 127.0.0.1:6379 > smembers team11 ) "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 team1 ) "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 team1 ) "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 team1 ) "Hu" 2 ) "Bob" 3 ) "Nick" 127.0.0.1:6379 > sunion team team11 ) "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 team31 ) "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 password1 ) "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:在有序集合中分数排名较大的成员,成员名称前需要加[
符号作为开头, [
符号与成员之间不能有空格,可以使用 -
和 +
表示得分最小值和最大值,min
和 max
不能反, max
放前面 min
放后面会导致返回结果为0,计算成员之间的成员数量时,参数 min
和 max
的位置也计算在内。
ZRANGE key start stop [WITHSCORES]
通过索引区间返回有序集合指定区间内的成员
ZRANGEBYLEX key min max [LIMIT offset count]
返回给定的有序集合键 key
中, 值介于 min
和 max
之间的成员。如果有序集合里面的成员带有不同的分值, 那么命令返回的结果是未指定的(unspecified)。LIMIT offset count
参数用于获取指定范围内的匹配元素。offset:返回结果起始位置
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT]
通过分数返回有序集合指定区间内的成员,max
和 min
参数前可以加 (
符号作为开头表示小于, (
符号与成员之间不能有空格,可以使用 +inf
和 -inf
表示得分最大值和最小值,max
和 min
不能反, max
放后面 min
放前面会导致返回结果为空,计算成员之间的成员数量不加 (
符号时,参数 min
和 max
的位置也计算在内。
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 WITHSCORES1 ) "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 WITHSCORES1 ) "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 WITHSCORES1 ) "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 - (g1 ) "a" 2 ) "b" 3 ) "c" 4 ) "d" 5 ) "e" 6 ) "f" 127.0.0.1:6379> ZRANGEBYLEX myzset [c (g1 ) "c" 2 ) "d" 3 ) "e" 4 ) "f" 127.0.0.1:6379> ZRANGEBYLEX myzset (c (g1 ) "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)来进行排序。
min
和 max
参数必须包含 (
或者 [
, 其中 (
表示开区间(指定的值不会被包含在范围之内), 而 [
则表示闭区间(指定的值会被包含在范围之内)。特殊值 +
和 -
在 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
可以是 AND
、 OR
、 NOT
、 XOR
这四种操作中的任意一种:
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 NewTechStackOK 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 Palermo1 ) 1 ) "13.36138933897018433" 2 ) "38.11555639549629859" 127.0.0.1:6379 > GEOPOS Sicily Palermo Catania1 ) 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 WITHDIST1 ) 1 ) "Palermo" 2 ) "190.4424" 2 ) 1 ) "Catania" 2 ) "56.4413" 127.0.0.1:6379 > GEORADIUS Sicily 15 37 200 km WITHCOORD1 ) 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 WITHCOORD1 ) 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 km1 ) "Agrigento" 2 ) "Palermo" 127.0.0.1:6379 > geohash Sicily Agrigento Palermo1 ) "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 > clear127 .0 .0 .1 :6379 > multiOK 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)> exec1 ) OK2 ) OK3 ) "v2" 127 .0 .0 .1 :6379 > keys *1 ) "k1" 2 ) "k2" 127 .0 .0 .1 :6379 > multiOK 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)> discardOK 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 > multiOK 127.0.0.1:6379 (TX)> set k3 v3QUEUED 127.0.0.1:6379 (TX)> set k4 v4QUEUED 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 v5QUEUED 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 v3OK 127.0.0.1:6379 > multiOK 127.0.0.1:6379 (TX)> incr k1QUEUED 127.0.0.1:6379 (TX)> set k4 v4QUEUED 127.0.0.1:6379 (TX)> set k5 v5QUEUED 127.0.0.1:6379 (TX)> get k4QUEUED 127.0.0.1:6379 (TX)> exec1 ) (error) ERR value is not an integer or out of range2 ) OK3 ) OK4 ) "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 moneyOK 127.0.0.1:6379 > multiOK 127.0.0.1:6379 (TX)> decrby money 20 QUEUED 127.0.0.1:6379 (TX)> exec1 ) (integer) 80 127.0.0.1:6379 > get money"80" 127.0.0.1:6379 > watch moneyOK 127.0.0.1:6379 > multiOK 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
1 2 3 4 5 6 JedisPool pool = new JedisPool(url, port,username,password); try (Jedis jedis = pool.getResource()) { System.out.println(jedis.ping()); jedis.set("name" , "Jedis" ); System.out.println(jedis.get("name" ) ); }
1 2 3 JedisPooled jedis = new JedisPooled(url, port,username,password); jedis.set("Name" ,"RedisTest" ); System.out.println(jedis.get("Name" ));
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 System.out.println(jedis.select(2 )); System.out.println(jedis.flushDB()); System.out.println(jedis.exists("name" )); System.out.println(jedis.set("name" ,"Admin" )); System.out.println(jedis.set("username" ,"Jedis" )); System.out.println(jedis.set("score" ,"12" )); Set<String> keys = jedis.keys("*" ); System.out.println(keys); System.out.println(jedis.del("username" )); System.out.println(jedis.type("name" )); System.out.println(jedis.randomKey()); System.out.println(jedis.rename("name" ,"usr" )); System.out.println(jedis.dbSize()); 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 System.out.println(jedis.set("k1" ,"v1" )); System.out.println(jedis.set("k2" ,"v2" )); System.out.println(jedis.set("k3" ,"v3" )); System.out.println(jedis.get("k1" )); System.out.println(jedis.del("k1" )); System.out.println(jedis.append("k2" ," is value" )); System.out.println(jedis.mset("k4" ,"v4" ,"k5" ,"v5" )); System.out.println(jedis.mget("k1" ,"k2" ,"k3" ,"k4" ,"k5" )); System.out.println(jedis.del("k1,k3" )); System.out.println(jedis.setnx("k4" ,"val1" )); System.out.println(jedis.setnx("key1" ,"value1" )); 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" )); System.out.println(jedis.getSet("key1" ,"This is value1" )); 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 jedis.lpush("techStack" ,"Java" ,"MySQL" ,"SpringBoot" ,"MyBatis" ); jedis.lpush("techStack" ,"Redis" ,"MongoDB" ,"Java" ); System.out.println(jedis.lrange("techStack" ,0 ,-1 )); System.out.println(jedis.lrem("techStack" ,1 ,"Java" )); System.out.println(jedis.ltrim("techStack" ,0 ,3 )); System.out.println(jedis.lpop("techStack" )); System.out.println(jedis.rpush("techStack" ,"Shiro" ,"Kafka" )); System.out.println(jedis.rpop("techStack" )); System.out.println(jedis.lset("techStack" ,1 ,"Docker" )); System.out.println(jedis.llen("techStack" )); System.out.println(jedis.lindex("techStack" ,1 )); 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 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" )); System.out.println(jedis.smembers("mySet" )); System.out.println(jedis.srem("mySet" ,"v1" )); System.out.println(jedis.spop("mySet" )); System.out.println(jedis.scard("mySet" )); System.out.println(jedis.sismember("mySet" ,"v2" )); System.out.println(jedis.smove("mySet2" ,"mySet" ,"v11" )); System.out.println(jedis.sinter("mySet" ,"mySet2" )); System.out.println(jedis.sunion("mySet" ,"mySet2" )); 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 jedis.hmset("myHash" ,map); jedis.hset("myHash" ,"k4" ,"v4" ); System.out.println(jedis.hgetAll("myHash" )); System.out.println(jedis.hkeys("myHash" )); System.out.println(jedis.hvals("myHash" )); System.out.println(jedis.hincrBy("myHash" ,"k5" ,10 )); System.out.println(jedis.hincrByFloat("myHash" ,"k6" ,1.24 )); System.out.println(jedis.hdel("myHash" ,"k1" )); System.out.println(jedis.hlen("myHash" )); System.out.println(jedis.hexists("myHash" ,"k1" )); 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 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)); System.out.println(jedis.zcard("myZest" )); System.out.println(jedis.zcount("myZest" ,1.0 ,2.0 )); System.out.println(jedis.zincrby("myZest" ,3.6 ,"member1" )); System.out.println(jedis.zrank("myZest" ,"member1" )); System.out.println(jedis.zrem("myZest" ,"member1" )); System.out.println(jedis.zrevrange("myZest" ,0 ,4 )); System.out.println(jedis.zremrangeByScore("myZest" ,0 ,1 )); 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){ e.printStackTrace(); } multi.exec();
Lettuce
1.Lettuce是一个高性能基于Java编写的Redis驱动框架,底层集成了Project Reactor提供天然的反应式编程,通信框架集成了Netty使用了非阻塞IO,5.x版本之后融合了JDK1.8的异步编程特性,在保证高性能的同时提供了十分丰富易用的API。
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"} ) @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; @Test void contextLoads () { 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
来进行序列化
1 2 @Autowired private RedisTemplate<String,String> 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 @Configuration public class CacheConfig { @Bean public RedisTemplate<String,Object> redisTemplate (RedisConnectionFactory cf) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); 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); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); 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); template.setKeySerializer(redisSerializer); template.setValueSerializer(redisSerializer); template.setHashValueSerializer(redisSerializer); template.setHashKeySerializer(redisSerializer); return template; } }
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; 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 ; } } public long getExpire (String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } public boolean hasKey (String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false ; } } @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)); } } } public Object get (String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } public boolean set (String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true ; } catch (Exception e) { e.printStackTrace(); return 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 ; } } public long incr (String key, long delta) { if (delta < 0 ) { throw new RuntimeException("递增因子必须大于" ); } return redisTemplate.opsForValue().increment(key, delta); } public long decr (String key, long delta) { if (delta < 0 ) { throw new RuntimeException("递减因子必须大于" ); } return redisTemplate.opsForValue().increment(key, -delta); } public Object hget (String key, String item) { return redisTemplate.opsForHash().get(key, item); } public Map<Object, Object> hmget (String key) { return redisTemplate.opsForHash().entries(key); } public boolean hmset (String key, Map<String, Object> map) { try { redisTemplate.opsForHash().putAll(key, map); return true ; } catch (Exception e) { e.printStackTrace(); return 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 ; } } 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 ; } } 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 ; } } public void hdel (String key, Object... item) { redisTemplate.opsForHash().delete(key, item); } public boolean hHasKey (String key, String item) { return redisTemplate.opsForHash().hasKey(key, item); } public double hincr (String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, by); } public double hdecr (String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, -by); } public Set<Object> sGet (String key) { try { return redisTemplate.opsForSet().members(key); } catch (Exception e) { e.printStackTrace(); return null ; } } public boolean sHasKey (String key, Object value) { try { return redisTemplate.opsForSet().isMember(key, value); } catch (Exception e) { e.printStackTrace(); return false ; } } public long sSet (String key, Object... values) { try { return redisTemplate.opsForSet().add(key, values); } catch (Exception e) { e.printStackTrace(); return 0 ; } } 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 ; } } public long sGetSetSize (String key) { try { return redisTemplate.opsForSet().size(key); } catch (Exception e) { e.printStackTrace(); return 0 ; } } public long setRemove (String key, Object... values) { try { Long count = redisTemplate.opsForSet().remove(key, values); return count; } catch (Exception e) { e.printStackTrace(); return 0 ; } } 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 ; } } public long lGetListSize (String key) { try { return redisTemplate.opsForList().size(key); } catch (Exception e) { e.printStackTrace(); return 0 ; } } public Object lGetIndex (String key, long index) { try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { e.printStackTrace(); return null ; } } public boolean lSet (String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true ; } catch (Exception e) { e.printStackTrace(); return false ; } } 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 ; } } public boolean lSet (String key, List<Object> value) { try { redisTemplate.opsForList().rightPushAll(key, value); return true ; } catch (Exception e) { e.printStackTrace(); return false ; } } 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 ; } } 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 ; } } 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 > saveOK 127.0.0.1:6379 > set k1 v1OK 127.0.0.1:6379 > set k2 v2OK 127.0.0.1:6379 > set k3 v3OK 127.0.0.1:6379 > set k4 v4OK 127.0.0.1:6379 > set k5 v5OK [root@VM-8-3-centos ~] # lsdump.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 appendonly yes 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 *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
订阅
发送者(发送信息的客户端)不是将信息直接发送给特定的接收者(接收信息的客户端), 而是将信息发送给频道(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 GamingReading 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 redis.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 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 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 127.0.0.1:6380> info 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 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 sentinel down-after-milliseconds mymaster 60000 sentinel failover-timeout mymaster 180000 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 > shutdown127.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:5d ed70a045bbdfa227bc4e7f5f98e4f347ba3a54 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
优点:
基于主从复制模式
故障自动转移,高可用
增强了系统的健壮性
缺点:
集群容量一旦到达上限,在线扩容困难
哨兵模式配置繁琐
始终只有一个Redis主机来接收和处理写请求,易受单机瓶颈影响
集群里所有节点保存的都是全量数据,没有实现分布式存储(Redis Sharding)
sentinel.conf
Redis缓存 缓存雪崩
1.如果缓存在某一个时刻出现大规模的key失效,那么就会导致大量的请求打在了数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机。这时候如果运维马上又重启数据库,马上又会有新的流量把数据库打死。这就是缓存雪崩。
2.应对方法:
均匀过期:设置不同的过期时间,让缓存失效的时间尽量均匀
分级缓存:第一级缓存失效的基础上,访问二级缓存,每一级缓存的失效时间都不同
热点数据缓存永远不过期
确保Redis缓存的高可用
使用互斥锁
使用熔断机制,限流降级
缓存击穿
1.缓存击穿跟缓存雪崩有点类似,缓存雪崩是大规模的key失效,而缓存击穿是某个热点的key失效,大并发集中对其进行请求,就会造成大量请求读缓存没读到数据,从而导致高并发访问数据库,引起数据库压力剧增。
2.应对方法
缓存穿透
1.缓存穿透是指用户请求的数据在缓存中不存在即没有命中,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍。如果有恶意攻击者不断请求系统中不存在的数据,会导致短时间大量请求落在数据库上,造成数据库压力过大,甚至导致数据库承受不住而宕机崩溃。
2.缓存穿透的关键在于在Redis中查不到key值,它和缓存击穿的根本区别在于传进来的key在Redis中是不存在的。假如有黑客传进大量的不存在的key,那么大量的请求打在数据库上是很致命的问题,所以在日常开发中要对参数做好校验,一些非法的参数,不可能存在的key就直接返回错误提示。
3.应对方法
当出现Redis查不到数据,数据库也查不到数据时,将key保存到Redis中,设置value为null,并设置其过期时间极短(无法应对不存在的Key值每次都是随机的情况)
使用布隆过滤器(Bloom Filter,1970年由布隆提出的。实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难)
如果布隆过滤器判定某个 key 不存在布隆过滤器中,那么就一定不存在,如果判定某个 key 存在,那么很大可能是存在(存在一定的误判率)。在缓存之前再加一个布隆过滤器,将数据库中的所有key都存储在布隆过滤器中,在查询Redis前先去布隆过滤器查询 key 是否存在,如果不存在就直接返回,不让其访问数据库,从而避免了对底层存储系统的查询压力。
缓存预热
缓存预热是指系统上线后,提前将相关的缓存数据加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。
缓存降级
缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。在项目实战中通常会将部分热点数据缓存到服务的内存中,这样一旦缓存出现异常,可以直接使用服务的内存数据,从而避免数据库遭受巨大压力。
Redis使用规范
Key名称规范:
使用统一的命名规范。
一般使用业务名(或数据库名)为前缀,用冒号分隔,如业务名:表名:id
。
控制key名称的长度。
保证语义清晰的情况下,尽量减少Key的长度。常用单词可使用缩写,如user缩写为u,messages缩写为msg。
名称中不要包含特殊字符。
Value值规范:
避免大Key。
大Key会带来网卡流量风暴和慢查询,一般string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。
选择合适的数据类型。
比如存储用户的信息,可用使用多个key,使用set u:1:name "X"
、set u:1:age 20
这样存储,也可以使用hash数据结构,存储成1个key,设置用户属性时使用hmset一次设置多个,同时这样存储也能节省内存。
设置合理的过期时间。
最好是过期时间打散,不要集中在某个时间点过期。