前言
读书笔记而已,加点自己的理解。来源:
Redis开发与运维 - 付磊 & 张益军
Redis核心技术与实战
Redis概述
Redis是一种基于键值对(key-value)的NoSQL数据库,键值对中的值可以是由String(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)、Bitmaps(位图)、HyperLogLog、GEO(地理信息定位)等多种数据结构和算法组成。
Redis的特点
-
速度快
读写性能可以达到10万/秒,为什么这么快? 1、Redis的所有数据都是存放在内存中的,这是主要原因。 2、C语言开发。面向过程编程性能比较好这个应该没问题。 3、高效的数据结构,简单动态字符串、双向链表、压缩列表、哈希表、跳表和整数数组 数据结构不是数据类型,那五种只是数据类型。 4、单线程架构,IO多路复用。 为什么在Redis里单线程就快?不该是合理的多线程会更好? 首先,要知道的是:Redis中的单线程指的是网络 IO和键值对读写是由一个线程来完成的, 但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。 单线程的优点可以减少上下文切换造成的消耗以及减少并发控制(请求进入队列中(FIFO),排队执行) IO多路复用: 这里我直接说个人理解,官方一点的话可以看别人的。 基本的IO处理中会有很多步骤,比如建立连接;读取客户端请求等 在这个过程中,如果连接的建立一直不成功或者读取请求但一直没到,这个时候就会发生阻塞(就是在等这个请求) 在单线程的情况下这是很致命的,所以慢慢就有了IO多路复用(多路指多个请求;复用指一个线程复用) ps:虽然如此,如果在redis中某条命令执行的时间很长,那也是会造成其他命令阻塞的 实现的效果类比: 去银行办理业务,刚进去的时候,大堂经理就问办什么请求是吧,拿号然后填表,你那些办业务的基础事项 都准备好了,你再去柜台。 一样的:Redis运行的时候,内核会有一种机制,监听进来的请求,当请求连接完成且请求也进来之后,才 交给Redis去运行,这样就不存在上面的阻塞问题,和办业务相比,这里没有取号,内核会有专门的机制去 轮询什么请求已经好了,然后交给redis处理。 这里的机制有:select;poll;epoll 这里不展开说,贴两个看到的文章: https://cloud.tencent.com/developer/article/1680732 https://www.cnblogs.com/flashsun/p/14591563.html
-
基于键值对
redis键值对中的值有5种数据结构:字符串、哈希、列表、集合、有序集合。可以应对许多不同场景的 应用开发。
-
功能丰富
1、键过期功能,可以实现缓存。 2、订阅功能,可以实现消息系统。(但是还是比不上专业的消息中间件的,看需求使用) 3、Lua脚本功能,利用Lua创造出新的Redis命令。 4、简单的事务功能,能在一定程度上保证事务特性。(Redis的事务理解和sql的不太一样,先不说) 5、流水线(Pipeline)功能,客户端能将一批命令一次性传到Redis,减少了网络的开销。 (这个主要是如果一条命令就建立一次连接,那么成千上百条都这样呢? 一次两次无所谓,但都这样这开销就大了,所以在某种机制下将命令统一批次传入以减少开销)
-
持久化
数据放在内存中是不安全的,所以此Redis提供了两种持久化方式: RDB和AOF,可以用两种策略将内存的数据保存到硬盘中。
-
主从复制
复制功能;感觉和MySQL的差不多,只是一个是数据库,一个是缓存。复制功能是分布式Redis的基础
-
高可用和分布式
这个自己意会吧
最后刻下个小问题:
既然Redis快是因为是基于内存的,为什么数据不直接放在内存呢?
nmmd,校招面试的惨痛回忆,那么简单不好好思考
Redis使用场景
1、缓存
这个不用说啦,做缓存可以减小数据库的压力,速度也更快。
2、排行榜系统
Redis提供了列表和有序集合数据结构,合理使用的话也是挺方便的
3、计数器应用
Redis天然支持计数功能而且计数的性能也非常好
4、社交网络
社交网站访问量通常比较大,而且传统的关系型数据不太适合保存诸如点赞;推送等的类型数据
5、消息队列系统
Redis虽然和专业的消息队列比还不够足够强大,但是对于一般的消息队列功能基本可以满足。
Redis的数据类型与命令
全局通用命令
1、查看所有键
key *
2、查看当前数据库中键的总数
dbsize
3、检查键是否存在
exists key
4、删除键
del key [key ...]
支持删除多个键
5、设置键的过期时间
expire key seconds
当超过过期时间后,会自动删除键
6、查看键还有多少秒过期
ttl key
-1表示永不过期,-2表示已过期
7、查看键的数据类型
type key
数据类型对应的数据结构目前我只能复现这点,以后有机会再补充
String
字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。
命令
set key value 设置key值
get key 查询key值
append key value 将给定的value追加到原值末尾
strlen key 获取值的长度
setnx key value 只有在key不存在的时候,设置key值
incr key 将key值存储的数字增1,只对数字值操作,如果为空,新增值为1
decr key 将key值存储的数字减1,只对数字值操作,如果为空,新增值为1
incrby/decrby key <步长> 将key值存储的数字增减如步长
mset key value key value.. 同时设置一个或者多个key-value
mget key key... 同时获取一个或多个value
msetnx key value key value.. 同时设置一个或者多个key-value.当且仅当所有给定key都不存在
getrange key <起始位置> <结束位置> 获取key的起始位置和结束位置的值
setrange key <起始位置> value 将value的值覆盖起始位置开始
setex key <> value 设置键值的同时,设置过期时间
getset key value 用新值换旧值
底层数据结构
1、int:8个字节的长整型。
2、embstr:小于等于44个字节的字符串。
3、raw:大于44个字节的字符串。
Redis会根据当前值的类型和长度决定使用哪一种实现
使用场景
1、缓存
2、计数
3、共享session
一个分布式Web服务将用户的Session信息(例如用户登录信息)保存在各自服务器中,
这样会造成一个问题,出于负载均衡的考虑,分布式服务会将用户的访问均衡到不同服务器上,
用户刷新一次访问可能会发现需要重新登录,这时候进行登录信息共享可以解决
4、限速
比如验证码2分钟内只能获取一次等操作
Hash
须知:hash中的值是一对键值对
常用命令
设置值
hset key field value (field value 指的就是键值对)
获取值
hget key field
删除hash中的值
hdel key field [field ...] 可以删除多个值
计算hash中值的个数
hlen key
批量设置/获取值
hmget key field [field ...]
hmset key field value [field value ...]
判断hash中值是否存在
hexists key field
获取所有值的键
hkeys key
获取所有的值的值
hvals key
获取所有的键值对
hgetall key
底层数据结构
ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。
hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现
List
一个列表最多可以存储2^32-1个元素,元素有序且可重复。在Redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色。
常用命令
从左或者右插入一个或者多个值(头插与尾插)
lpush/rpush key value value...
向某个元素前或者后插入元素
linsert key before|after pivot value
获取指定范围内的元素列表
lrange key start end
lrange key 0 -1 获取所有值
获取指定下标的值
lindex key index
获取列表长度
llen key
从左或者右弹出一个或者多个值(值在键在,值都没,键都没)
lpop/rpop key
删除指定元素
lrem key count value
count小于0,从右边开始删count的绝对值个;大于0,从左边开始删;等于0,删除所有
修改指定索引下标的值
lset key index newValue
底层数据结构
quicklist,它是以一个ziplist为节点的linkedlist(双向链表),它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现
使用场景
1、消息队列
2、文章列表
lpush+lpop=Stack(栈)
lpush+rpop=Queue(队列)
lpsh+ltrim=Capped Collection(有限集合)
lpush+brpop=Message Queue(消息队列)
Set
Set和列表类型的差别是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。
一个集合最多可以存储2^32-1个元素。Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。
常用命令
添加元素
sadd key element [element ...] 返回结果为添加成功的元素个数
删除元素
srem key element [element ...] 返回结果为删除成功的元素个数
计算元素个数
scard key
判断元素是否在集合中
sismember key element
随机返回指定的元素个数,不写默认为1
srandmember key [count]
随机弹出元素
spop key [count]
获取所有元素
smembers key
求多个集合交集
sinter key [key ...]
求多个集合并集
suinon key [key ...]
求多个集合差集
sdiff key [key ...] (第一个key有别的没有的)
底层数据结构
hashtable(哈希表),书里说的应该还有个整数数组,我没复现出来
使用场景
标签(例如CSDN那些标签,你可以看到和你选择了一样标签或者差不多的人)
ZSet
ZSet保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。
有序集合中的元素不能重复,但是score可以重复,就和一个班里的同学学号不能重复,但是考试成绩可以相同。
常用命令
添加成员
zadd key score member [score member ...] 返回结果代表添加成功的个数
计算成员个数
zcard key
计算某个成员的分数
zscore key member
计算成员排名
zrank key member
zrevrank key member
zrank是从分数从低到高返回排名,zrevrank反之。
删除成员
zrem key member [member ...] 返回结果为删除成功的个数
增加成员分数
zincrby key increment member
好多好繁琐,不想记这些了
贴一篇博客
https://blog.csdn.net/weixin_47872288/article/details/118410080
底层数据结构
ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplistentries配置(默认128个),同时每个元素的值都小于zset-max-ziplistvalue配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用。
skiplist(跳表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现。跳表和二分查找很像,只不过是按索引跳而已。
使用场景
排行榜系统
Pipeline
Redis客户端执行一条命令分为如下四个过程:
发送命令
命令排队
命令执行
返回结果
其中发送命令时间和返回结果时间加起来称为Round Trip Time(RTT,往返时间)
Redis提供了批量操作命令(例如mget、mset等),有效地节约RTT。但大部分命令是不支持批量操作的,例如要执行n次hgetall命令。简单点说就是来一条命令就送一次和命令攒着然后一起送的区别。
Pipeline虽然可以模拟出批量操作的效果,但是与原生批量命令还是有区别的:
原生批量命令是原子的,Pipeline是非原子的。
原生批量命令是一个命令对应多个key,Pipeline支持多个命令。
原生批量命令是Redis服务端支持实现的,而Pipeline需要服务端和客户端的共同实现。
新数据类型
Bitmaps
Bitmaps本身不是一种数据结构,实际上就是字符串。但是它可以对字符串的位进行操作。可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量。合理使用操作位可以有效地提高内存使用率和开发使用率
常用命令
设置值
setbit key offset value
获取值
gitbit key offset
获取指定范围内数值为1的个数
bitcount [start][end]
复合操作,它可以做多个Bitmaps的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在destkey中
bitop and(or/not/xor)destkey key
底层实现以及使用场景
前面说bitmaps只是基于redis的字符串类型的.。而一个字符串类型最多存储512M。所以只要将512M转换为bit就能知道bitmaps的最大长度。
8 * 1024 * 1024 * 512 = 2^32
至于使用场景:书中给出的例子是用户活跃问题即有没有登录。可以延伸一下,bitmaps的值只有0和1,那么就是对于只有两种可能的场景都可以尝试一下。
HyperLogLog
用于统计网页中页面访问量等(估计涉及统计的都可以试试)
其只会根据输入元素来计算基数,而不会储存输入元素本身,不能像集合那样,返回输入的各个元素
基数估计是存在误差的(可接受的范围内)快速计算(不重复元素的结算)
常用命令
添加指定的元素到hyperloglog中
pfadd key element
计算key的近似基数
pfcount key
一个或多个key合并后的结果存在另一个key
pfmerge destkey sourcekey sourcekey
Geographic
提供经纬度设置,查询范围,距离查询等
发布订阅
发布消息
publish channel message
订阅消息
subscribe channel [channel ...]
订阅者可以订阅一个或多个频道
取消订阅
unsubscribe [channel [channel ...]]
注意事项:
客户端在执行订阅命令之后进入了订阅状态,只能接收subscribe、
psubscribe、unsubscribe、punsubscribe的四个命令。
新开启的订阅客户端,无法收到该频道之前的消息,因为Redis不会对发布的消息进行持久化。