redis 简介
redis 介绍
redis 一款开源的,基于内存操作的高性能 key-value 数据结构存储,可以用来作为数据库、缓存和消息队列、分布式锁和分布式序列等等。
redis 为什么很快
- 基于内存操作,操作不需要跟磁盘交互
- 基于k-v的数据结构,类似与hashMap,所以查询速度接近O(1)
- redis 有自己独特的数据结构支持,比如跳表、SDS
- 命令执行是单线程 (但是数据刷盘、数据同步、定时调度等会fork子进程来完成。redis 6 以后,支持了多线程,可以选择性使用)
- 使用lO多路复用技术来处理客户端的并发请求,提高redis的网络IO处理能力
redis 为什么采用单线程
- CPU处理速度很快,使用多线程模型带来的性能提升并不能抵消多线程上下文切换所带来的性能损耗
redis 常见的数据类型及常见命令
详细说明可参考官网:
redis 常用命令
set k v
get k v
flushall #清空实例下的所有数据(慎用)
flushdb #清空当前数据库
keys * #查看所有键(慎用,因为是单线程的,请求量过多导致线程阻)
expire k 10
pexpire k 10000 #单位是毫秒
exists k
del k
type k
string
说明
- 可以存浮点,整型,字符串类型,最大长度是512M
- redis中Key也是基于String类型存储,所以最大大小也是512M
- redis中的字符串自己定义了数据结构,包含了data+长度,可以用来存音频、视频等,不会失真
常用命令
set k v
get k
mset k v1 k2 v2 #批量插入
mget k k1 #批量获取
strlen k
append k aa #追加
getrange k 0 5 #截取指定范围
incr intkey #自增,要求value是整数
incrby intkey 100
set f 2.6
incrbyfloat f 2.3
# EX − 设置指定的到期时间(以秒为单位)。
# PX - 设置指定的到期时间(以毫秒为单
# NX - 仅在键不存在时设置键。
# XX - 只有在键已存在时才设置。
# 用于做分部署锁
set lock1 1 EX 10 NX
setnx k1 1
使用场景
- 缓存相关场景:数据缓存、 token(可以设置过期时间)
- 线程安全的计数场景 :软限流、分布式ID
hash
说明
hash 其实也是一个 key - value 的形式,不同的是hash的 value 是多个 key-value 的集合,可以单独对小key进行增删改查操作。
常用命令
hset h1 f 6
hmset h1 a 1 b 2 c 3 d 4
hget h1 a
hmget h1 a b c d
hkeys h1
hvals h1
hgetall h1
hincrby h1 a 10 #给某个字段添加值
hexists h1 y #查询key中file是否存在 h
del h1 a
hlen h1
使用场景
- 存储对象类的数据,比如一个对象下有多个字段
- 统计类的数据 我可以对单个统计数据进行单独操作
list
说明
存储有序的字符串列表,元素可以重复。列表的最大长度为 2^32 - 1 个元素(4294967295,每个列表超过 40 亿个元素)。
常用命令
#压栈,存数据
lpush queue a
lpush queue b c
rpush queue d e
#弹栈,取数据
blpop queue 10 #弹出时如果 queue 里面为空,就会阻塞设置的超时时间秒,如果设置的是0,则一致阻塞
brpop queue 10
lindex queue 0
lrange queue 0 -1 #负数代表从index的末尾数第几位
linsert queue BEFORE "World" "There" #在某个值之前插入元素
lpushx queue v
使用场景
- 按序操作的列表:(时间计划)因为list是有序的 所以我们可以先进先出 先进后出的列表都可以做
- 消息队列:List提供了两个阻塞的弹出操作:BLPOP/BRPOP,可以设置超时时间。但是有数据丢失的风险,不建议用redis做消息队列。
set
说明
String类型的无序集合,最大存储数量 2^32-1(40 亿左右)。 添加、删除元素效率较高,时间复杂度是O(1)。
常用命令
#添加元素
sadd myset a b c d e f g
#获取元素
smembers myset #获取所有元素
scard myset #获取所有元素个数
srandmember myset 3 #随机获取3个元素,3可以不填,默认为1(set 中的元素不会减少)
spop myset 2 #随弹出2个元素,2可以不填,默认为1(set 中的元素会减少)
srem myset g a #弹出指定元素
sismember myset e #查看元素是否存在
sdiff myset myset1 #获取前一个集合有而后面1个集合没有的
sinter myset myset1 #获取交集
sunion myset myset1 #获取并集
使用场景
- 抽奖 :spop跟srandmember 随机弹出或者获取元素
- 点赞、签到等 sadd 集合存储
- 交集并集:朋友圈关注等场景
sorted set
说明
有序的set,每个元素有个score。 score相同时,按照key的ASCII码排序
常用命令
zadd zset 1 a 2 b 3 c 4 d 5 e
zrange zset 0 -1 withscores #根据score从低到高
zrevrange zset 0 -1 withscores #根据score从高到低
zrangebyscore zset 1 5 #根据score范围取值
zcard zset
zcount zset 2 3 # score 在 2-3 内的元素个数
zdiff 3 zset zset1 zset2
zincrby zset 2 a #给zset 中的元素a的score加2,返回值是增加以后的score
zrank zset a #返回元素a在zet中的顺序,顺序由score从第到高排列,不过不存在则返回nil
zscore zset d #返回元素的score
使用场景
- 排行榜:销售榜、热搜榜、游戏评分排行
BitMap
说明
位图不是实际的数据类型,而是String类型中定义的一种面向位的操作,所以这个位图的最大长度是512M。可以容纳最少2^32 不同的位。可以在不同的位置设置0或者1
常用命令
setbit permission 5 1 //把位5设置为1
getbit permission 5 //得到位5的值
bitcount permission //获取位为1的总数
bitpos permission 1 //获取 permission位为1的第一个位置
bitop AND hbit bitkey permission //获取bitkey与permission 的&运算 并且赋值给hbit,如果bitkey和permission 是字符串,则按将对应字符的ascii码转成二进制,然后再进行二进制操作
使用场景
- 实时的统计数据: 比如学生上课次数,到一次就将对应的位置1,统计1的用 bitcount 计算1的个数即可
- 用户权限: 权限位,不同的位代表不同的权限,如果有权限设置为1 否则设置为0 这样我们就能很高效的拿到用户是否有相关权限。
扩充知识
IO多路复用
一个服务端进程可以同时处理多个套接字描述符。
- 多路:多个客户端连接(连接就是套接字描述符)
- 复用:使用单进程就能够实现同时处理多个客户端的连接
目前unix上主要有三种IO多路复用技术,分别是select-、poll、epoll其发展可以分select->poll→epoll三个阶段。
- select:通过遍历监听的连接,取出需要进行IO的连接进行IO操作(速度慢,能监听的连接数有限,通常为1024)
- poll:采用了新的数据结构pollfd,使用链表的形式来保存自己监控的fd信息,不再有长度限制;实现方式上也是和select一样采用轮询
// pollfd 模型
ypedef struct pollfd{
int fd; // 需要被检测或选择的文件描述符
short events; // 对文件描述符fd上感兴趣的事件
short revents; // 文件描述符fd上当前实际发生的事件
} pollfd_t;
- epoll: 使用内核事件表(B+树结构)来保存监听的连接,事件表中的每个连接需要绑定回调函数,当事件激活后主动回调callback函数事件加到一个活跃事件队列里,epoll_wait调用结束时会把队列里的连接和事件类型返回给应用进程。
poll相对于select优势:
- 采用链表保存数据,没有长度限制
- select 调用返回的fd_set是只包含了上次返回的活跃事件的fd_set集合,下一次调用select又需要把这几个fd_set清空,重新添加上自己感兴趣的fd和事件类型,而poll采用的pollfd 保存着对应fd需要监控的事件集合,也保存了一个当返回于激活事件的fd集合。 所以重新发请求时不需要重置感兴趣的事件类型参数。
epoll优势:
- 零拷贝
- epoll并不是像select一样去遍历事件列表,然后逐个轮询的监控fd的事件状态,而是事先就建立了fd与之对应的回调函数,当事件激活后主动回调callback函数,这也就避免了遍历事件列表的这个操作,所以epoll并不会像select和poll一样随着监控的fd变多而效率降低,这种事件机制也是epoll要比select和poll高效的主要原因。
具体可参考: IO多路复用、redis 中IO多路复用的使用
本文参考了: IO多路复用、redis 中IO多路复用的使用这两篇文章,感谢总结~