参考《Java EE 互联网轻量级框架整合开发》相关章节
一.Redis概述
1.适用场景
- 传统关系型数据库无法满足高并发场景(如商品抢购、主页瞬间大量访问)的需要
- Redis适用于缓存场景,现实场景中读操作远多于写操作,80%的读取集中在20%的数据
- Redis适用于高速读/写场景,将高速读/写的数据缓存到Redis,满足一定条件后再写入数据库
2.Redis性能优势
- 基于ANSI C语言编写,接近于汇编语言的机器语言,运行速度快
- 基于内存的读/写,比磁盘IO快
- 数据库结构比较简单(只有6种数据类型),规则较少
3.NoSQL与传统数据库
- NoSQL使用内存存储数据,传统数据库使用磁盘
- NoSQL数据结构比较简单,功能有限,不如传统数据库的SQL语句强大
- NoSQL基于内存,持久化能力受外部条件影响,不如基于磁盘的传统数据库
- NoSQL数据完整性、事务能力、安全性、可靠性及可扩展性都远不及传统数据库
- NoSQL短期内难以取代传统数据库,但能够作为提高互联网应用性能的辅助工具
二.Redis数据结构和常用命令
1.字符串
- Redis最基本的数据结构,以键值对的形式存储于Redis内部,Redis通过键查找值
- Redis字符串基本命令
命令 |
说明 |
备注 |
set key value |
设置键值对 |
|
get key |
通过键获取值 |
|
del key |
通过键删除键值对 |
返回删除的键值对个数 |
strlen key |
求key指向字符串的长度 |
返回长度 |
getset key value |
修改原key对应的值,并返回原值 |
若原值为空则返回空 |
getrange key start end |
获取子串 |
返回(start,end)截取的字符串 |
append key value |
将新value加入到key指向的value末尾 |
返回拼接后value的长度 |
- 如果字符串是数字(整数或浮点数),Redis还能支持简单的加减运算
命令 |
说明 |
备注 |
incr key |
原字段加1 |
限整数 |
incrby key increment |
原字段加整数(increment) |
限整数 |
decr key |
原字段减1 |
限整数 |
decrby key decrement |
原字段减整数(decrement) |
限整数 |
incrbyfloat key increment |
原字段加浮点数(increment) |
整数或浮点数 |
2.哈希
- Redis中hash是一个String类型的field和value的映射表,一个哈希里有许多键值对,适合存储对象
- Redis哈希常用命令
命令 |
说明 |
备注 |
hdel key field1 [field2...] |
删除hash结构中的某字段 |
可同时删除多个字段 |
hexists key field |
判断hash结构中是否存在field字段 |
存在返回1,否则返回0 |
hgetall key |
获取hash结构中所有键值 |
|
hincrby key field increment |
hash结构的指定字段加上一个整数 |
要求该字段本身为整数字符串 |
hincrby float key field increment |
hash结构的指定字段加上一个浮点数 |
要求该字段本身为数字型字符串 |
hkeys key |
返回hash中所有键 |
|
hvals key |
返回hash中所有值 |
|
hlen key |
返回hash中键值对的数量 |
|
hmget key field1 [field2 ...] |
返回hash中指定键的值,可以是多个键 |
依次返回值 |
hmset key field1 value1 [field2 value2 ...] |
hash设置多个键值对 |
|
hset key field value |
hash设置单个键值对 |
|
hsetnx key field vaule |
当hash结构中不存在对应的键,才设置值 |
|
3.链表(linked-list)
- Redis链表底层是双向链表
- 便于增删但不便于查询
- 因为是双向链表结构,所以Redis链表命令分为左操作和右操作两种,常见链表命令如下
命令 |
说明 |
备注 |
lpush key node1 [node2...] |
把node1加到链表最左边 |
|
rpush key node1 [node2...] |
把node1加到链表最右边 |
|
lindex key index |
读取下标为index的节点 |
返回节点字符串 |
llen key |
求链表的长度 |
返回链表节点数 |
lpop key |
删除左边第一个节点,并将其返回 |
|
rpop key |
删除右边第一个节点,并将其返回 |
|
insert key before/after pivot node |
在节点pivot的前/后插入node节点 |
如果list不存在,则报错;如果没有值为pivot的节点,将插入失败返回-1 |
lpushx list node |
如果存在key为list的链表,则从左边插入节点node |
如果list不存在,则失败 |
rpushx list node |
如果存在key为list的链表,则从右边插入节点node |
如果list不存在,则失败 |
Irange list start end |
获取链表list从start下标到end下标的节点值 |
包含start和end下标 |
lrem list count value |
如果count为0,则删除所有值等于value的节点;若count不为0,则从左到右删除不大于count绝对值数目值为value的节点 |
|
lset key index node |
设置列表下标为index的节点的值为node |
|
ltrim key start stop |
修剪链表,只保留从start到stop区间的节点 |
包含start和end下标 |
- 上述命令未对Redis的链表加锁,在多个Redis客户端同时操作同一链表时将造成并发数据安全和一致性问题,Redis提供链表的阻塞命令以保障链表操作的安全性
命令 |
说明 |
备注 |
blpop key timeout |
移出并获取链表的第一个元素,如果链表没有元素会阻塞链表直到等待超时或发现可弹出元素为止 |
相对于lpop命令线程安全 |
brpop key timeout |
移出并获取链表的最后一个元素,如果链表没有元素会阻塞链表直到等待超时或发现可弹出元素为止 |
相对于rpop命令线程安全 |
rpoplpush key src dest |
将原链表最右边一个元素移除,并插入目标链表最左边 |
不能设置超时时间 |
brpoplpush key src dest timeout |
将原链表最右边一个元素移除,并插入目标链表最左边,可以设置超时时间 |
|
4.集合
- Redis集合是一个哈希表结构,插入、删除和查找的复杂度都是O(1)
- 集合是无序、不可重复的,集合的每一个元素都是String数据结构类型
- Redis集合可对两个或多个集合求交集、差集和并集,常用命令如下
命令 |
说明 |
备注 |
sadd key member1 [member2 member3 ...] |
给集合key增加成员 |
可以同时增加多个 |
srem key member1 [member2 ...] |
移除集合key的元素 |
|
scard key |
统计集合key的成员数 |
|
sdiff key1 [key2] |
找出两个集合的差集 |
参数如果只有一个集合,则返回这个集合的所有元素 |
sdiffstore des key1 [key2] |
找出两个集合的差集,并保存在集合des中 |
|
sinter key1 [key2] |
求集合key1和集合key2的交集 |
参数如果只有一个集合,则返回这个集合的所有元素 |
sinterstore des key1 key2 |
求集合key1和集合key2的交集并保存到des |
|
sismember key member |
判断member是否为集合key的成员 |
是返回1,否返回0 |
smembers key |
返回集合key的所有成员 |
数据量大时需考虑迭代遍历 |
smove src des member |
将成员member从集合src迁移到集合des |
|
spop key |
随机弹出集合key的一个元素 |
|
srandmember key[count] |
随机返回集合key的一个或多个元素 |
count为整数,不填默认为1,负数取绝对值,count大于集合元素总数时返回整个集合 |
sunion key1 [key2] |
求集合key1和集合key2的并集 |
|
sunionstore des key1 key2 |
求并集并将结果保存到键为des的集合 |
|
5.有序集合
- Redis有序集合和集合类似,通过哈希表实现,添加、删除、查找的时间复杂度均为O(1)
- 区别在于每个元素除了值之外,还多一个分数,Redis根据分数进行排序
命令 |
说明 |
备注 |
zadd key score1 value1 [score2 value2 ...] |
给有序集合key增加成员 |
如果不存在有序集合key,则自动创建 |
zcard key |
统计有序集合key的成员数 |
|
zcount key min max |
根据分数返回对应的成员列表 |
分数在最小值min与最大值max间 |
zincrby key increment member |
给有序集合成员值为member的分数增加increment |
|
zinterstore desKey numkeys key1 [key2 key3 ...] |
求多个有序集合的交集并存入desKey |
|
zlexcount key min max |
求有序集合key成员值在min和max的范围集合 |
|
zrange key start stop [withscores] |
按照分值的大小从小到大返回成员,start和stop参数可截取某一段返回,若输入可选项withscores则连同分数一起返回 |
|
zrank key member |
按从小到大求有序集合的排行 |
|
zrangebylex key min max [limit offset count] |
根据值的大小,从小到大排序,min和max为最小/最大值,Redis求出范围集合后根据偏移量offset和限定返回数count返回对应的成员 |
|
zrangebyscore key min max [withscore] [limit offset count] |
根据分数的大小,从小到大排序,min和max为最小/最大值,Redis求出范围集合后根据偏移量offset和限定返回数count返回对应的成员 |
|
zremrangebyscore key start stop |
根据分数区间进行删除 |
|
zremrangebyrank key start stop |
按照分数排行从小到大的顺序删除 |
zremrangebylex key min max |
按照值得分布进行删除 |
|
zrevrange key start stop [withscores] |
从大到小按分数排序,参数参照zrange |
|
zrevrangebyscore key max min [withscores] |
从大到小按分数排序,参数参见zrangebyscore |
|
zrevrank key member |
按从大到小顺序求元素的排行 |
|
zscore key member |
返回成员的分数值 |
|
zunionstore desKey numKeys key1 [key2 key3 key4...] |
求多个有序集合的并集 |
numKeys是有序集合的个数 |
6.基数(HyperLogLog)
- 基数并不存储元素,存储元素需要较大内存空间
- 基数用于给某个有重复元素的数据集合评估需要的空间单元数(即数据集合中不重复的元素数量)
三. Redis事务
1.概述
- 在多个客户端同时向Redis系统发送命令的时候,同一个数据同时可能被不同线程操纵,产生并发下的数据一致性问题
- Redis通过事务解决并发场景的数据安全问题,事务使用Multi-Exec命令组合
1)事务是一个被隔离的操作,事务中的方法都会被Redis进行序列化并按顺序执行,事务在执行过程中不会被其他客户端发出的命令所打断
2)事务是一个原子性操作,要么全部执行,要么全部不执行
2.事务过程
命令 |
说明 |
备注 |
multi |
开启事务,之后的命令进入队列,但不会立刻执行 |
|
watch key1 [key2...] |
监听某些键,当被监听的键在事务执行前被修改,则事务将被回滚 |
乐观锁机制 |
unwatch key1 [key2...] |
取消监听的键 |
|
exec |
执行事务,但如果被监听的键发生改变,则执行回滚 |
执行事务队列存储的命令前,Redis会检测被监听的键值对有没有发生变化 |
discard |
回滚事务 |
回滚后的事务不能再提交 |
时刻 |
客户端1 |
客户端2 |
说明 |
T1 |
set key1 value1 |
|
客户端1:返回OK |
T2 |
watch key1 |
|
客户端1:监控key1 |
T3 |
multi |
|
客户端1:开启事务 |
T4 |
set key2 value2 |
|
客户端1:事务命令入列 |
T5 |
- |
set key1 value1 |
客户端2:修改key1的值 |
T6 |
exec |
- |
客户端1:执行事务,执行前检测到key1的值被其他命令修改过,所以将进行回滚 |
3.流水线(pipelined)
- Redis通过事务提供队列,作为一个可以批量执行任务的队列,但使用事务会检测对应的锁和序列化命令,存在系统开销
- Redis提供流水线技术,用来在没有任何附加条件的场景下使用队列批量执行一系列的命令,从而提高系统性能
- 应用场景
1)Redis执行读/写速度非常快,系统的瓶颈往往是网络通信的延时
2)使用Redis的流水线(本质是一种通信协议),可以有效提高性能
3)使用流水线产生的返回对象,可能占用服务器上较多的内存空间,导致OOM异常
4.发布订阅
- 场景
使用银行卡消费时,银行通过微信、短信或邮件通知用户该笔交易信息
- 观察者模式
1)记账系统是消息源,收到交易指令,成功记账后,就会发布消息
2)要有消息渠道,记账系统通过消息渠道向订阅者发布消息
3)要有订阅者(微信、短信、邮件等系统)订阅消息渠道的消息
四. 超时命令——Redis内存回收
1. Redis键值对的超时
命令 |
说明 |
备注 |
persist key |
持久化key,取消超时时间 |
|
ttl key |
查看key的超时时间 |
以秒计算,-1代表没有超时时间,-2代表不存在key或key已超时 |
expire key seconds |
设置超时时间戳 |
以秒为单位 |
expireat key timestamp |
设置超时时间点 |
用unix时间戳确定 |
pptl key milliseconds |
查看key的超时时间戳 |
以毫秒为单位 |
pexpire key |
设置键值超时的时间 |
以毫秒为单位 |
pexpireat key stamptimes |
设置超时时间点 |
以毫秒为单位的unix时间戳 |
- Redis的key超时不会被自动回收,而是被标识为已经超时
1)好处:避免因很大的键值对超时的回收造成卡顿
2)坏处:被标记为超时而未回收的键值对会占用空间
2. Redis回收机制
- 定时回收:在某个确定的时间统一回收超时的键值对
1)可以完全回收超时的键值对
2)一次性回收的键值对较多时,Redis会停顿影响业务,故定时回收一般在没有业务发生的时刻触发
- 惰性回收:再次执行访问(get命令)超时的键时回收该键
1)优点是可以指定回收超时的键值对
2)缺点是通过get命令指定回收,需要额外执行get操作