目录
概述
第1章 初识Redis,带领读者进入Redis的世界,了解它的前世今生、众多特性、应用场景、安装配置、简单使用,最后对Redis发展过程中的重要版本进行说明,可以让读者对Redis有一个全面的认识。
第2章 API的理解和使用,全面介绍了Redis提供的5种数据结构字符串(string)、哈(hash)、列表(list)、集合(set)、有序集合(zset)的 数据模型、常用命令、典型应用场景,并且每个小节都会给出在Redis开发 过程可能要注意的坑和技巧。同时本章还会对Redis的单线程处理机制、键值管理做一个全面介绍,通过对这些原理的理解,读者可以在合适的应用场景选择合适的数据结构和命令进行开发,有效提高程序效率,降低可能产生的问题和隐患。
第3章 小功能大用处,除了5种数据结构外,Redis还提供了诸如慢查询、Redis Shell、Pipeline、Lua脚本、Bitmaps、HyperLogLog、发布订阅、GEO等附加功能,在这些功能的帮助下,Redis的应用场景更加丰富。
第4章 客户端,本章重点关注Redis客户端的开发,介绍了Redis的客户端通信协议、详细讲解了Java客户端Jedis的使用技巧,同时通过从原理角度剖析在开发运维中,客户端的监控和管理技巧,最后给出客户端开发中常见问题以及案例讲解。
第5章 持久化,Redis的持久化功能有效避免因进程退出造成的数据丢失问题,本章首先介绍RDB和AOF两种持久化配置和运行流程,其次对常见的持久化问题进行定位和优化,最后结合Redis常见的单机多实例部署场景进行优化。
第6章 复制,在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他机器,用于故障恢复和负载均衡等需求,Redis也是如此。它为我们提供了复制(replication)功能,实现了多个相同数据的Redis副本。复制功能是高可用Redis的基础,后面章节的哨兵和集群都是在复制
的基础上实现高可用。
第7章 Redis的噩梦:阻塞,Redis是典型的单线程架构,所有的读写操作都在一条主线程中完成的。当Redis用于高并发场景时这条线程就变成了它的生命线。如果出现阻塞哪怕是很短时间对于我们的应用来说都是噩梦。导致阻塞问题的场景大致分为内在原因和外在原因,本章将进行详细分
析。
第8章 理解内存,Redis所有的数据存在于内存中,如何高效利用Redis内存变得非常重要。高效利用Redis内存首先需要理解Redis内存消耗在哪里,如何管理内存,最后再深入到如何优化内存。掌握这些知识后相信读者能够实现用更少的内存存储更多的数据从而降低成本。
第9章 哨兵,Redis从2.8版本开始正式提供了Redis Sentinel,它有效解决了主从复制模式下故障转移的若干问题,为Redis提供了高可用功能。本章将一步步解析Redis Sentinel的相关概念、安装部署、配置、命令使用、原理解析,最后分析了Redis Sentinel运维中的一些问题。
第10章 集群,是本书的重头戏,Redis Cluster是Redis3提供的Redis分布式解决方案,有效解决了Redis分布式方面的需求,理解应用好RedisCluster将极大的解放我们对分布式Redis的需求,同时它也是学习分布式存储的绝佳案例。本章将针对RedisCluster的数据分布,搭建集群,节点通信,请求路由,集群伸缩,故障转移等方面进行分析说明。
第11章 缓存设计,缓存能够有效加速应用的读写速度,以及降低后端负载,对于开发人员进行日常应用的开发至关重要,但是将缓存加入应用架构后也会带来一些问题,本章将介绍缓存使用和设计中遇到的问题,具体包括:缓存的收益和成本、缓存更新策略、缓存粒度控制、穿透问题优化、无底洞问题优化、雪崩问题优化、热点key优化。
第12章 开发运维的“陷阱”,介绍Redis开发运维中的一些棘手问题,具体包括:Linux配置优化、flush误操作数据恢复、如何让Redis变得安全、bigkey问题、热点key问题。
第13章 Redis监控运维云平台CacheCloud,介绍笔者所在团队开源的Redis运维工CacheCloud,它有效解决了Redis监控和运维中的一些问题, 本章将按照快速部署、机器部署、接入应用、用户功能、运维功能多个维度 全面的介绍CacheCloud,相信在它的帮助下,读者可以更好的监控和运维好 Redis。
第14章 Redis配置统计字典,会对Redis的系统状态信息以及全部配置做一个全面的梳理,希望本章能够成为Redis配置统计字典,协助大家分析 和解决日常开发和运维中遇到的问题。
第1章 初识Redis
1.1 盛赞Redis
1.2 Redis特性
Redis的8个重要特性
1.速度快
- ·Redis的所有数据都是存放在内存中
- ·Redis是用C语言实现的
- ·Redis使用了单线程架构
2.基于键值对的数据结构服务器
Redis的全称是REmote Dictionary Server,它主要提供了5种数据结 构:字符串、哈希、列表、集合、有序集合,同时在字符串的基础之上演变出了位图(Bitmaps)和HyperLogLog两种神奇的“数据结构”,并且随着 LBS(Location Based Service,基于位置服务)的不断发展,Redis3.2版本中 加入有关GEO(地理信息定位)的功能。
3.丰富的功能
- ·提供了键过期功能,可以用来实现缓存。
- ·提供了发布订阅功能,可以用来实现消息系统。
- ·支持Lua脚本功能,可以利用Lua创造出新的Redis命令。
- ·提供了简单的事务功能,能在一定程度上保证事务特性。
- ·提供了流水线(Pipeline)功能,这样客户端能将一批命令一次性传到Redis,减少了网络的开销。
4.简单稳定
- Redis的源码很少
- Redis使用单线程模型
5.客户端语言多
Redis提供了简单的TCP通信协议
6.持久化
7.主从复制
Redis提供了复制功能,实现了多个相同数据的Redis副本(如图1-2所示),复制功能是分布式Redis的基础。
8.高可用和分布式
Redis从2.8版本正式提供了高可用实现Redis Sentinel,它能够保证Redis节点的故障发现和故障自动转移。Redis从3.0版本正式提供了分布式实现 Redis Cluster,它是Redis真正的分布式实现,提供了高可用、读写和容量的 扩展性。
1.3 配置、启动、操作、关闭Redis
1.启动Redis
有三种方法启动Redis:默认配置、运行配置、配置文件启动。
(1)默认配置 redis-server执行
(2)运行启动 redis-server加上要修改配置名和值(可以是多对),没有设置的配置将使用默认配置:
# redis-server --configKey1 configValue1 --configKey2 configValue2
#例如,如果要用6380作为端口启动Redis,那么可以执行:
# redis-server --port 6380
(3)配置文件启动(推荐,生产环境,默认模板修改)
2.Redis命令行客户端
·第一种是交互式方式:通过redis-cli-h{host}-p{port}的方式连接到Redis
服务,之后所有的操作都是通过交互的方式实现,不需要再执行redis-cli 了,例如:
redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
·第二种是命令方式:用redis-cli-h ip{host}-p{port}{command}就可以直接得到命令的返回结果,例如:
redis-cli -h 127.0.0.1 -p 6379 get hello
"world"
3.停止Redis服务
Redis提供了shutdown命令来停止Redis服务,例如要停掉127.0.0.1上6379端口上的Redis服务,可以执行如下操作。
$ redis-cli shutdown
注意:
1)Redis关闭的过程:断开与客户端的连接、持久化文件生成,是一种
相对优雅的关闭方式。
2)除了可以通过shutdown命令关闭Redis服务以外,还可以通过kill进程号的方式关闭掉Redis,但是不要粗暴地使用kill-9强制杀死Redis服务,不但 不会做持久化操作,还会造成缓冲区等资源不能被优雅关闭,极端情况会造 成AOF和复制丢失数据的情况。
3)shutdown还有一个参数,代表是否在关闭Redis前,生成持久化文件:
redis-cli shutdown nosave|save
第2章 API的理解和使用
2.1 预备
2.1.1 全局命令
1.查看所有键
keys *
2.键总数
dbsize
dbsize命令在计算键总数时不会遍历所有键,而是直接获取Redis内置的
键总数变量,所以dbsize命令的时间复杂度是O(1)。而keys命令会遍历所 有键,所以它的时间复杂度是O(n),当Redis保存了大量键时,线上环境 禁止使用。
3.检查键是否存在
exists key
如果键存在则返回1,不存在则返回0。
4.删除键
del key [key ...]
del是一个通用命令,无论值是什么数据结构类型,del命令都可以将其删除。返回结果为成功删除键的个数,假设删除一个不存在的键,就会返回0。
5.键过期
expire key seconds
Redis支持对键添加过期时间,当超过过期时间后,会自动删除键,
ttl命令会返回键的剩余过期时间,它有3种返回值:
·大于等于0的整数:键剩余的过期时间。
·-1:键没设置过期时间。
·-2:键不存在。
6.键的数据结构类型
type key
2.1.2 数据结构和内部编码
type命令实际返回的就是当前键的数据结构类型,它们分别是:
string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)。
object encoding命令查询内部编码,每种数据结构都有自己底层的内部编码实现。
Redis这样设计有两个好处:第一,可以改进内部编码,而对外的数据结构和命令没有影响,这样一旦开发出更优秀的内部编码,无需改动外部数 据结构和命令,例如Redis3.2提供了quicklist,结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现,而对外部用户来说基本感知不到。第二,多种内部编码实现可以在不同场景下发挥各自的优势,例如ziplist比较节省内存,但是在列表元素比较多的情况下,性能会有所下降,这时候Redis会根据配置选项将列表类型的内部实现转换为 linkedlist。
2.1.3 单线程架构
Redis使用了单线程架构和I/O多路复用模型来实现高性能的内存数据库服务。
1.引出单线程模型
每次客户端调用都经历了发送命令、执行命令、返回结果三个过程
Redis是单线程来处理命令的,所以一条命令从客户端达到服务端不会立刻被执行,所有命令都会进入一个队列中,然后逐个被执行。所以上面3个客户端命令的执行顺序是不确定的(如 图2-4所示),但是可以确定不会有两条命令被同时执行(如图2-5所示),
2.为什么单线程还能这么快
第一,纯内存访问。
第二,非阻塞I/O,Redis使用epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不 在网络I/O上浪费过多的时间,如图2-6所示。
第三,单线程避免了线程切换和竞态产生的消耗,Redis是面向快速执行场景的数据库。
2.2 字符串
字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字 (整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。
2.2.1 命令
(1)设置值
set key value [ex seconds] [px milliseconds] [nx|xx]
set命令有几个选项:
·ex seconds:为键设置秒级过期时间。
·px milliseconds:为键设置毫秒级过期时间。
·nx:键必须不存在,才可以设置成功,用于添加。
·xx:与nx相反,键必须存在,才可以设置成功,用于更新。
除了set选项,Redis还提供了setex和setnx两个命令,它们的作用和ex和nx选项是一样的:
setex key seconds value
setnx key value
因为键hello已存在,所以setnx失败,返回结果为0:
127.0.0.1:6379> setnx hello redis
(integer) 0
setnx和setxx在实际使用中有什么应用场景吗?以setnx命令为例子,由于Redis的单线程命令处理机制,如果有多个客户端同时执行setnx key value, 根据setnx的特性只有一个客户端能设置成功,setnx可以作为分布式锁的一种实现方案,Redis官方给出了使用setnx实现分布式锁的方法:Distributed Locks with Redis | Redis
(2)批量
mset key value [key value ...]
mget key [key ...]
使用mget命令后,要执行n次get命令操作只需要按照图2-9的方式来完
成,具体耗时如下: n次get时间 = 1次网络时间 + n次命令时间
学会使用批量操作,有助于提高业务处理效率,但是要注意的是每次批
量操作所发送的命令数不是无节制的,如果数量过多可能造成Redis阻塞或 者网络拥塞。
2.2.2 内部编码
2.2.3 典型使用场景
1.缓存功能
1)该函数用于获取用户的基础信息:
UserInfo getUserInfo(long id){
...
}
2)首先从Redis获取用户信息:
// 定义键
userRedisKey = "user:info:" + id;
// 从Redis获取值
value = redis.get(userRedisKey);
if (value != null) {
// 将值进行反序列化为UserInfo并返回结果
userInfo = deserialize(value);
return userInfo;
}
开发提示
Redis没有命令空间,比较推荐的方式是使用“业务名:对象 名:id:[属性]”作为键名(也可以不是分号)。
例如MySQL的数据库名为 vs,用户表名为user,
那么对应的键可以用"vs:user:1","vs:user:1: name"来表示,
如果当前Redis只被一个业务使用,甚至可以去掉“vs:”。
如果键名比较长,例如“user:{uid}:friends:messages:{mid}”,
可以在能描述键含义的前提下适当减少键的长度,
例如变为“u:{uid}:fr:m: {mid}”,从而减少由于键过长的内存浪费。
3)如果没有从Redis获取到用户信息,需要从MySQL中进行获取,并将
结果回写到Redis,添加1小时(3600秒)过期时间:
// 从MySQL获取用户信息
userInfo = mysql.get(id);
// 将userInfo序列化,并存入Redis
redis.setex(userRedisKey, 3600, serialize(userInfo));
// 返回结果
return userInfo
2.计数
3.共享Session
如图2-11所示,一个分布式Web服务将用户的Session信息(例如用户登录信息)保存在各自服务器中,这样会造成一个问题,出于负载均衡的考虑,分布式服务会将用户的访问均衡到不同服务器上,用户刷新一次访问可 能会发现需要重新登录,这个问题是用户无法容忍的。
为了解决这个问题,可以使用Redis将用户的Session进行集中管理,如图2-12所示,在这种模式下只要保证Redis是高可用和扩展性的,每次用户 更新或者查询登录信息都直接从Redis中集中取。
4.限速
为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率,例如一分钟不能超过5次.
2.3 哈希
2.3.1 命令
(1)基本操作
(1)设置值
hset key field value
下面为user:1添加一对field-value:
127.0.0.1:6379> hset user:1 name tom
(integer) 1
如果设置成功会返回1,反之会返回0。此外Redis提供了hsetnx命令,
它们的关系就像set和setnx命令一样,只不过作用域由键变为field。
(2)获取值
hget key field
例如,下面操作获取user:1的name域(属性)对应的值:
127.0.0.1:6379> hget user:1 name
"tom"
如果键或field不存在,会返回nil:
127.0.0.1:6379> hget user:2 name
(nil)
127.0.0.1:6379> hget user:1 age
(nil)
(3)删除field
hdel key field [field ...]
hdel会删除一个或多个field,返回结果为成功删除field的个数,例如:
127.0.0.1:6379> hdel user:1 name
(integer) 1
127.0.0.1:6379> hdel user:1 age
(integer) 0
(4)计算field个数
hlen key
(5)批量设置或获取field-value
hmget key field [field ...]
hmset key field value [field value ...]
hmset和hmget分别是批量设置和获取field-value,
hmset需要的参数是key 和多对field-value,
hmget需要的参数是key和多个field。例如:
127.0.0.1:6379> hmset user:1 name mike age 12 city tianjin
OK
127.0.0.1:6379> hmget user:1 name city
1) "mike"
2) "tianjin"
(6)判断field是否存在
hexists key field
(7)获取所有field
hkeys key
(8)获取所有value
hvals key
(9)获取所有的field-value
hgetall key
在使用hgetall时,如果哈希元素个数比较多,会存在阻塞Redis的可能。
如果开发人员只需要获取部分field,可以使用hmget,
如果一定要获取全部 field-value,可以使用hscan命令,该命令会渐进式遍历哈希类型
(10)hincrby hincrbyfloat
hincrby key field
hincrbyfloat key field
hincrby和hincrbyfloat,就像incrby和incrbyfloat命令一样,
但是它们的作用域是filed。
(11)计算value的字符串长度(需要Redis3.2以上)
hstrlen key field
例如hget user:1name的value是tom,那么hstrlen的返回结果是3:
127.0.0.1:6379> hstrlen user:1 name
(integer) 3
2.3.2 内部编码
object encoding hashkey
2.3.3 使用场景
2.4 列表
列表(list)类型是用来存储多个有序的字符串,列表中的每个字符串称为元素(element),一个列表最多可以存储2^32-1个元素。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。
2.4.1 命令
1.添加操作
(1)从右边插入元素
rpush key value [value ...]
(2)从左边插入元素
lpush key value [value ...]
(3)向某个元素前或者后插入元素
linsert key before|after pivot value
linsert命令会从列表中找到等于pivot的元素,在其前(before)
或者后(after)插入一个新的元素value,例如下面操作会在列表的元素b前插入 java:
127.0.0.1:6379> linsert listkey before b java
(integer) 4
返回结果为4,代表当前命令的长度,当前列表变为:
127.0.0.1:6379> lrange listkey 0 -1
1) "c"
2) "java"
3) "b"
4) "a"
2.查找
(1)获取指定范围内的元素列表
lrange key start end
lrange操作会获取列表指定索引范围所有的元素。索引下标有两个特点:
第一,索引下标从左到右分别是0到N-1,但是从右到左分别是-1到-N。
第二,lrange中的end选项包含了自身,这个和很多编程语言不包含end不太相同,
例如想获取列表的第2到第4个元素,可以执行如下操作:
127.0.0.1:6379> lrange listkey 1 3
1) "java"
2) "b"
3) "a"
(2)获取列表指定索引下标的元素
lindex key index
(3)获取列表长度
llen key
3.删除
(1)从列表左侧弹出元素
lpop key
(2)从列表右侧弹出
rpop key
(3)删除指定元素
lrem key count value
lrem命令会从列表中找到等于value的元素进行删除,
根据count的不同分为三种情况:
·count>0,从左到右,删除最多count个元素。
·count<0,从右到左,删除最多count绝对值个元素。
·count=0,删除所有。
(4)按照索引范围修剪列表
ltrim key start end
4.修改
修改指定索引下标的元素:
lset key index newValue
5.阻塞操作
阻塞式弹出如下:
blpop key [key ...] timeout
brpop key [key ...] timeout
blpop和brpop是lpop和rpop的阻塞版本,它们除了弹出方向不同,使用方法基本相同,所以下面以brpop命令进行说明,brpop命令包含两个参数:
·key[key...]:多个列表的键。
·timeout:阻塞时间(单位:秒)
1)列表为空:如果timeout=3,那么客户端要等到3秒后返回,
如果timeout=0,那么客户端一直阻塞等下去:
127.0.0.1:6379> brpop list:test 3
(nil)
(3.10s)
127.0.0.1:6379> brpop list:test 0
...阻塞...
如果此期间添加了数据element1,客户端立即返回:
127.0.0.1:6379> brpop list:test 3
1) "list:test"
2) "element1"
(2.06s)
2)列表不为空:客户端会立即返回。
127.0.0.1:6379> brpop list:test 0
1) "list:test"
2) "element1"
在使用brpop时,有两点需要注意。
第一点,如果是多个键,那么brpop会从左至右遍历键,一旦有一个键能弹出元素,客户端立即返回:
127.0.0.1:6379> brpop list:1 list:2 list:3 0
..阻塞..
此时另一个客户端分别向list:2和list:3插入元素:
client-lpush> lpush list:2 element2
(integer) 1
client-lpush> lpush list:3 element3
(integer) 1
客户端会立即返回list:2中的element2,因为list:2最先有可以弹出的元素:
127.0.0.1:6379> brpop list:1 list:2 list:3 0
1) "list:2"
2) "element2_1"
第二点,如果多个客户端对同一个键执行brpop,那么最先执行brpop命
令的客户端可以获取到弹出的值。
2.4.2 内部编码
2.4.3 使用场景
1.消息队列
2.文章列表
每个用户有属于自己的文章列表,现需要分页展示文章列表。此时可以考虑使用列表,因为列表不但是有序的,同时支持按照索引范围获取元素。
2.5 集合
2.5.1 命令
下面将按照集合内和集合间两个维度对集合的常用命令进行介绍。
1.集合内操作
(1)添加元素
sadd key element [element ...]
(2)删除元素
srem key element [element ...]
(3)计算元素个数
scard key
(4)判断元素是否在集合中
sismember key element
(5)随机从集合返回指定个数元素
srandmember key [count]
(6)从集合随机弹出元素
spop key
(7)获取所有元素
smembers key
smembers和lrange、hgetall都属于比较重的命令,
如果元素过多存在阻塞Redis的可能性,这时候可以使用sscan来完成
2.集合间操作
(1)求多个集合的交集
sinter key [key ...]
(2)求多个集合的并集
suinon key [key ...]
(3)求多个集合的差集
sdiff key [key ...]
(4)将交集、并集、差集的结果保存
sinterstore destination key [key ...]
suionstore destination key [key ...]
sdiffstore destination key [key ...]
三个命令(原命令+store)将集合间交集、并集、差集的结果保存在 destination key中
2.5.2 内部编码
2.5.3 使用场景
2.6 有序集合
2.6.1 命令
本节依旧按照集合内和集合外两个维度对有序集合的命令进行介绍。
1.集合内
(1)添加成员
zadd key score member [score member ...]
·有序集合相比集合提供了排序字段,但是也产生了代价,zadd的时间
复杂度为O(log(n)),sadd的时间复杂度为O(1)。
(2)计算成员个数
zcard key
(3)计算某个成员的分数
zscore key member
(4)计算成员的排名
zrank key member
zrevrank key member
(5)删除成员
zrem key member [member ...]
(6)增加成员的分数
zincrby key increment member
(7)返回指定排名范围的成员
zrange key start end [withscores]
zrevrange key start end [withscores]
(8)返回指定分数范围的成员
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores] [limit offset count]
withscores选项会同时返回每个 成员的分数。
[limit offset count]选项可以限制输出的起始位置和个数。
同时min和max还支持开区间(小括号)和闭区间(中括号),
-inf和 +inf分别代表无限小和无限大:zrangebyscore user:ranking (200 +inf withscores
(9)返回指定分数范围成员个数
zcount key min max
(10)删除指定排名内的升序元素
zremrangebyrank key start end
(11)删除指定分数范围的成员
zremrangebyscore key min max
2.集合间的操作
(1)交集
zinterstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
这个命令参数较多,下面分别进行说明:
·destination:交集计算结果保存到这个键。
·numkeys:需要做交集计算键的个数。
·key[key...]:需要做交集计算的键。
·weights weight[weight...]:每个键的权重,在做交集计算时,每个键中的每个member会将自己分数乘以这个权重,每个键的权重默认是1。
·aggregate sum|min|max:计算成员交集后,分值可以按照sum(和)、min(最小值)、max(最大值)做汇总,默认值是sum。
127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2
对user:ranking:1和user:ranking:2做交集,weights和aggregate使用了默认配置,
可以看到目标键user:ranking:1_inter_2对分值 做了sum操作
(2)并集
zunionstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
2.6.2 内部编码
2.6.3 使用场景
有序集合比较典型的使用场景就是排行榜系统。例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间、按照播 放数量、按照获得的赞数。本节使用赞数这个维度,记录每天用户上传视频的排行榜。主要需要实现以下4个功能。
(1)添加用户赞数
例如用户mike上传了一个视频,并获得了3个赞,可以使用有序集合的zadd和zincrby功能:
zadd user:ranking:2016_03_15 mike 3
如果之后再获得一个赞,可以使用zincrby:
zincrby user:ranking:2016_03_15 mike 1
(2)取消用户赞数
由于各种原因(例如用户注销、用户作弊)需要将用户删除,此时需要
将用户从榜单中删除掉,可以使用zrem。例如删除成员tom:
zrem user:ranking:2016_03_15 mike
(3)展示获取赞数最多的十个用户
此功能使用zrevrange命令实现:
zrevrangebyrank user:ranking:2016_03_15 0 9
(4)展示用户信息以及用户分数
此功能将用户名作为键后缀,将用户信息保存在哈希类型中,至于用户
的分数和排名可以使用zscore和zrank两个功能:
hgetall user:info:tom
zscore user:ranking:2016_03_15 mike
zrank user:ranking:2016_03_15 mike
2.7 键管理
2.7.1 单个键管理
1.键重命名
rename key newkey
Redis提供了renamenx命令,确保只有newKey不存在时候才被覆盖
2.随机返回一个键
randomkey
3.键过期
除了expire、ttl命令以外,Redis还提供了 expireat、pexpire、pexpireat、pttl、persist等一系列命令,下面分别进行说明:
·expire key seconds:键在seconds秒后过期。
·expireat key timestamp:键在秒级时间戳timestamp后过期。
ttl命令和pttl都可以查询键的剩余过期时间,但是pttl精度更高可以达到毫秒级别,有3种返回值:
·大于等于0的整数:键剩余的过期时间(ttl是秒,pttl是毫秒)。
·-1:键没有设置过期时间。
·-2:键不存在。
expireat命令可以设置键的秒级过期时间戳,
·pexpire key milliseconds:键在milliseconds毫秒后过期。
·pexpireat key milliseconds-timestamp键在毫秒级时间戳timestamp后过期。
在使用Redis相关过期命令时,需要注意以下几点:
1.如果过期时间为负值,键会立即被删除,犹如使用del命令一样
2.persist命令可以将键的过期时间清除
3.对于字符串类型键,执行set命令会去掉过期时间,这个问题很容易在开发中被忽视
4.setex命令作为set+expire的组合,不但是原子执行,同时减少了一次
网络通讯的时间。
4.迁移键
Redis发展历程中提 供了move、dump+restore、migrate三组迁移键的方法,
迁移键功能非常重要,因为有时候我们只想把部分数据由一个Redis迁移到另一个Redis(例如从生产环境迁移到测试环境)。
(1)move
move key db
move命令用于在Redis内部进行数据迁移,Redis内部可以有多个数据库,彼此在数据上是相互隔离的,move key db就是把指定的键从源数据库移动到目标数据库中。
(2)dump+restore
dump key
restore key ttl value
(3)migrate
migrate host port key|"" destination-db timeout [copy] [replace] [keys key [key ...]]
2.7.2 遍历键
1.全量遍历键
keys pattern
glob风格的通配符:
- ·*代表匹配任意字符。
- ·代表匹配一个字符。
- ·[]代表匹配部分字符,例如[1,3]代表匹配1,3,[1-10]代表匹配1到10的任意数字。
- ·\x用来做转义,例如要匹配星号、问号需要进行转义。
2.渐进式遍历
那么每次执行scan,可以想象成只扫描一个字典中的一部分键,直到将
字典中的所有键遍历完毕。scan的使用方法如下:scan cursor [match pattern] [count number]
注意:
渐进式遍历可以有效的解决keys命令可能产生的阻塞问题,但是scan并非完美无瑕,如果在scan的过程中如果有键的变化(增加、删除、修改),那么遍历效果可能会碰到如下问题:新增的键可能没有遍历到,遍历出了重复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键,这些是我们在开发时需要考虑的。
2.7.3 数据库管理
1.切换数据库
select dbIndex
Redis只是用数 字作为多个数据库的实现。Redis默认配置中是有16个数据库,默认使用的就是0号数据库
如果要使用多个数据库功能,完全可以在一台机器上部署多个,Redis实例,彼此用端口来做区分
2.flushdb/flushall
flushdb/flushall命令用于清除数据库,两者的区别的是
flushdb只清除当前数据库,
flushall会清除所有数据库。
2.8 本章重点回顾
1)Redis提供5种数据结构,每种数据结构都有多种内部编码实现。
2)纯内存存储、IO多路复用技术、单线程架构是造就Redis高性能的三个因素。
3)由于Redis的单线程架构,所以需要每个命令能被快速执行完,否则会存在阻塞Redis的可能,理解Redis单线程命令处理机制是开发和运维Redis 的核心之一。
4)批量操作(例如mget、mset、hmset等)能够有效提高命令执行的效率,但要注意每次批量操作的个数和字节数。5)了解每个命令的时间复杂度在开发中至关重要,例如在使用keys、hgetall、smembers、zrange等时间复杂度较高的命令时,需要考虑数据规模对于Redis的影响。
6)persist命令可以删除任意类型键的过期时间,但是set命令也会删除字符串类型键的过期时间,这在开发时容易被忽视。7)move、dump+restore、migrate是Redis发展过程中三种迁移键的方式,其中move命令基本废弃,migrate命令用原子性的方式实现了 dump+restore,并且支持批量操作,是Redis Cluster实现水平扩容的重要工具。
8)scan命令可以解决keys命令可能带来的阻塞问题,同时Redis还提供了hscan、sscan、zscan渐进式地遍历hash、set、zset。