参考《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.事务过程
Redis事务将经历3个过程
1)开启事务
2)命令进入队列
3)执行事务事务命令
命令 | 说明 | 备注 |
---|---|---|
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操作