Redis 简介
-
什么是 Redis
Redis 是一款内存高速缓存数据库,全称为:Remote Dictionary Server(远程数据服务),使用C语言编写
Redis 是一种支持 key-value 等多种数据结构的存储系统,提供字符串,哈希,列表,队列,集合结构直接存取,基于内存,可持久化。可用于缓存,事件发布或订阅,高速队列等场景
-
Redis 的优势
性能高:每秒万级写操作,十万级读操作
数据类型丰富:对开发人员常见数据类型提供了原生支持
原子性:Redis 的所有操作都是原子性的,同时还可保证几个操作合并执行的原子性
丰富的特性:设置过期/发布订阅/可持久化/分布式集群等
-
Redis 快的原因
基于内存
基于简单的数据结构
基于 C 语言实现 IO 多路复用
基于单线程,避免了线程切换的开销
Redis 执行命令的工作线程是单线程,但是也有其他线程,如 IO 线程
事实证明,Redis 的单机性能的瓶颈是网络速度和网卡性能,而非 CPU
-
Redis IO 模型
Redis IO 是基于 epoll 的多路复用模型
# IO 1. 进程分配的内存分为用户空间和内核空间 2. IO 操作包括 IO 调用和 IO 执行,IO 调用由程序发起,IO 执行是内核的工作 3. IO 流程: 程序发起 IO 调用-> 内核读取 IO 数据,存放内核缓冲区 -> 拷贝数据到用户缓存区 # BIO 1. BIO:程序发起 IO 调用后,数据没有准备好,就一直等待 2. 常见的应用:阻塞 socket、Java BIO # NIO 1. NIO:通过轮询的方式请求数据状态而不用阻塞 2. IO 多路复用: select 函数:同时监控多个文件描述符 fd,通过遍历 fdset,找到就绪的 fd epoll 函数:使用监听事件回调代替遍历文件描述符
基础数据类型
String
String 是 Redis 中最基本的数据类型,一个 key 对应一个 value
-
Redis 保证 String 类型是二进制安全的,可以包含任何数据,最多能存储 512M 字节
如数字,字符串,图片或者序列化的对象
-
命令
# 设置 key value 键值对 SET <key> <value> # key 不存在才设置 SET <key> <value> NX # key 存在才设置 SET <key> <value> XX # 获取 key 对应的 value GET <key> # 删除 key value 键值对 DEL <key> # value 加/减 1 INCR <key> DECR <key> # value 加/减指定数 INCRBY <key> <num> DECRBY <key> <num>
-
常用场景
缓存数据
计数器
List
基于双端链表实现
-
列表可以包含重复数据
-
命令
# 将 value 插入到链表的左(右)端 LPUSH <name> <value> RPUSH ... # 弹出链表的左(右)端的值并返回 LPUSH <name> RPUSH ... # 获取指定范围内的数据,索引值可以从 -1 开始 LRANGE <name> <begin> <end> # 获取指定索引值的数据,索引值可以从 -1 开始 LINDEX <name> <idx> # 删除与 value 相等的元素 # 数量为 count 的绝对值,count 为负就从右开始,为 0 就删除全部 LREM <name> <count> <value> # 依次访问多个 list,弹出第一个有值的 list 的最后一个值 # 若没有获取到就会阻塞,阻塞时间为 0 则一直阻塞 # 若获取到值,则返回对应的 [key value] BLPOP <name1> <name2> ... <timeout> BRPOP ...
-
常用场景
实现队列和栈
实现消息队列(LPUSH + BRPOP)
Set
Set 是 String 类型的无序集合
-
Set 基于哈希表实现,元素不能重复,添加/删除/查找的复杂度都是 O(1)
-
命令
# 添加元素 SADD <name> <value> # 查看总元素个数 SCARD <name> # 随机删除一个元素 SPOP <name> # 删除指定元素 SREM <name> <value> # 返回所有元素 SMEMBER <name> # 判断是否包含指定元素 SISMEMBER <name> <value> # 随机返回一个或多个元素 SRANDMEMBER <name> <count> # 取交集 SINTER <name1> <name2>
-
常用场景
随机内容
取交集
Hash
String 类型的 key 和 value 的映射表
-
hash 结构适合存储对象
-
命令
# 设置 k-v 到一个哈希表 HSET <name> <key> <value> # 获取指定哈希表的指定 key 的 value HGET <name> <key> # 获取哈希表所有 key HGETALL <name> # 删除指定的 key HDEL <name> <key>
-
常用场景
缓存对象信息(比 String 节约空间)
ZSet
String 类型元素的集合,且不允许重复的成员
-
每个元素可以关联一个 double 类型的分数,用来排序,分数可以重复
-
命令
# 添加一个 value 并指定 score ZADD <name> <score> <value> # 获取指定索引范围内的元素(有序集合) ZRANGE <name> <begin> <end> # 获取指定分数范围内的元素 ZCOUNT <name> <min> <max> # 删除指定元素 ZREM <name> <value>
-
常用场景
排行
特殊数据类型
HyperLogLogs
Redis 2.8.9 开始支持 HyperLogLogs
-
HyperLogLogs 用于基数统计,基数是集合中不同元素的个数
-
命令
# 添加元素到集合 PFADD <name> <value1> <value2> ... # 统计数量 PFCOUNT <name> # 合并 name2、name3 到 name 1 PFMERGE <name1> <name2> <name3>
-
常用场景
计数(节省内存)
Bitmap
位图,用二进制进行记录,基于 String 类型实现
-
适合两种状态的统计
-
命令
# 设置位图的指定位置是 0 还是 1 SETBIT <name> <index> <0 | 1> # 获取指定位的数值 GETBIT <name> <index> # 获取是 1 的记录条数 BITCOUNT <name>
-
常用场景
签到:每个用户维护一个 Bitmap,日期作为索引,0/1 代表状态(数据太多可根据时间分片)
统计活跃个数:不同时间使用不同的 Bitmap,用户 id 作为索引
查询在线状态:使用一个 Bitmap,用户 id 作为索引
geospatial
Redis 3.2 版本支持,用于推算两地距离,基于 ZSet 实现
-
geospatial 将指定的地理空间位置(纬度、经度、名称)添加到指定的 key 中,基于 ZSet
-
命令
# 添加经纬度、地名 GEOADD <name> <longitude> <latitude> <address>... # 计算两地距离 GEODIST <name> <address1> <address2> [m|km|ft|mi] # 获取指定坐标为中心、指定半径内的成员 GEORADIUS <name> <longitude> <latitude> <radius> [m|km|ft|mi] # 获取指定成员为中心、指定半径内的成员 GEORADIUSBYMEMBER <name> <address> <radius> [m|km|ft|mi]
-
常用场景
附近内容推荐
Redis 持久化
Redis 是基于内存的数据库,内存是断电即失的,所以需要持久化技术保证数据不丢失
Redis 支持的常用的持久化技术有 RDB 和 AOF
RDB
-
RDB 是 Redis Database 的缩写,是一种将数据快照保存到磁盘的技术
-
触发方式:手动触发、自动触发
-
手动触发的命令
# 阻塞 Redis 直到完成快照(不推荐) SAVE # fork 一个子线程,异步进行快照(fork 过程是阻塞的) BGSAVE
-
自动触发的场景
配置文件配置了自动触发
主从复制发生时
执行 debug reload 命令重加载 Redis 时
执行 shutdown 命令时(没有开启 AOF 持久化)
-
配置自动触发
Redis.conf
# 默认执行快照的生效条件 # 900 秒内有 1 条 key 变化 save 900 1 # 300 秒内有 10 条 key 变化 save 300 10 # 60 秒内有 10000 条 key 变化 save 60 10000 # 关闭 RDB 快照 # save "" # 文件名称 dbfilename dump.rdb # 文件保存路径 dir ~/redis/data/ # 如果持久化出错,主进程是否停止写入 stop-writes-on-bgsave-error yes # 是否压缩 rdbcompression yes # 导入时是否检查(损失性能) rdbchecksum yes
-
RDB 优缺点
优点:文件体积小,恢复数据快
缺点:实时性不足,fork 线程成本高,快照生成慢
AOF
-
AOF 是以文本形式记录执行命令的日志,每次执行先写内存,后写日志
-
写后日志可以避免语句检查开销,但存在潜在风险
-
AOF 配置
# 开启 AOF 持久化 appendonly yes # 持久化的文件名,默认是 appendonly.aof appendfilename "appendonly.aof" # 持久化文件的保存位置 dir ./ # 同步策略,always/everysec appendfsync everysec
appendfsync
用来决定 Redis 将数据刷到磁盘的时机,选择 no 则由系统自行调度,Redis 默认选择了 everysec,每秒写入一次 -
重写是对 AOF 文件的精简,通过 fork 子线程执行 bgrewriteaof 来完成
# 重写期间是否同步 no-appendfsync-on-rewrite no # 重写触发的百分比,超过该百分比就触发重写 auto-aof-rewrite-percentage 100 # 重写触发的最小文件大小 auto-aof-rewrite-min-size 64mb
no-appendfsync-on-rewrite
表示是否在新命令执行时,如果 AOF 没有重写完毕,主线程是否继续写入 AOF。设置为 yes 表示重写完毕再写入新命令,避免磁盘 IO 冲突