列举Redis基础知识,参考 《edis开发与运维 - 付磊 & 张益军》
redis的三个基本特征:
- 内存数据库
- 单进程高并发
- I/O优化
这三件事是高度相关的。
之所以作为单进程数据库,还能支撑高并发,就是因为它是内存数据库。 操作的响应速度远快于硬盘。 不需要多个进程切换来面对硬盘读写。在本机的速度问题解决之后,重要的就是网络,也就是I/O优化, Redis使用epoll实现,不在网络上浪费时间。在I/O问题解决之后,单线程的一个大优势就是不用做线程切换。
说了这么多好处,有没有问题? 最严重的问题就是如果一个命令执行时间过长,后面的命令就会阻塞,严重影响并发。
常用命令
set (ex:在key已经存在的时候生效,同update,nx:在key不存在的时候生效,同insert)
get
multil操作: 意义是减少I/O.
数据结构
类型 | 简介 | 特性 | 场景 |
---|---|---|---|
string(字符串) | 二进制安全 | 可以包含任何数据,比如jpg图片或者序列化对象 | --- |
Hash(字典) | 键值对集合,即编程语言中的map类型 | 适合存储对象,并且可以像数据库中的update一个属性一样只修改某一项属性值 | 存储、读取、修改用户属性 |
List(列表) | 链表(双向链表) | 增删快,提供了操作某一元素的api | 最新消息排行;消息队列 |
set(集合) | hash表实现,元素不重复 | 添加、删除、查找的复杂度都是O(1),提供了求交集、并集、差集的操作 | 共同好友;利用唯一性,统计访问网站的所有Ip |
sorted set(有序集合) | 将set中的元素增加一个权重参数score,元素按score有序排列 | 数据插入集合时,已经进行了天然排序 | 排行榜;带权重的消息队列 |
编码方式
编码方式是说这些数据结构是怎么存储的。 实际上能有的存储方式并不多, 不外乎连续存储(数组),链表, 哈希, 图(一般是树)。 每个方式都各有优缺点。 Redis的优势是随着数据的变化(单个数据大小,数据量),会自动地切换编码方式,达到比较好的存储效果。
zipList: 紧凑的连续的存储,能看到大多数数据结构在小而少的时候都使用ziplist
hashtable: hash多起来以后使用,目的是优化读写到O(1)
string
set key value
最常见的形式
哈希
结构:key --> field
field是一组哈希k:v值
- 命令:在常用命令前+h
hset
hexist
使用场景:对标关系型数据库
列表:list
- 类似python的list, 有序,可以使用下标随机访问。也可以lpush,lpop,rpush,rpop作为队列使用
- 一个列表最多存贮2^32-1个元素
命令
rpush key value
lpush key value
lrange key start end (包含end)
集合
无序,唯一
命令
sadd key element
使用场景
tag
有序集合
相比集合,加入了一个score变量,可以依据score变量排序
使用场景:排行榜系统
HyperLogLog
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。 代价是一定程度上不准确。
常见功能
慢查询
slowlog-log-slower-than = 正数
记录所有执行时间比这个大的命令
Pipeline
打包命令一次性发给server,主要解决网络延迟问题
lua事务
之所以要有lua事务,是因为原生事务不支持原子性。 单条命令当然是原子性的(单线程保证),但是多条命令中如果有一条出错,数据库不会回滚。
使用lua脚本,Redis保证脚本执行是原子性的。
- 应用: 使用Redis做分布式锁的时候, 解锁的两步操作要写lua脚本。
持久化
RDB
压缩后写入DISK,需要阻塞Redis。
优劣
-
优势: 由于压缩,占用DISK的Redis体积比占用内存小。
-
劣势: 持久化总是需要阻塞一段时间,不能做到每秒都持久化。
操作
save: 本进程,阻塞数据库进行持久化,已经废弃
bdsave:fork子进程持久化,只有fork的过程会阻塞。
fork过程复制父进程页表, redis的页表经常不小,导致fork过程慢
写时复制问题:如果在fork过程中, 新的指令写redis, (父进程),此时触发写时复制,操作系统需要重新为子进程分配物理地址,很可能造成长时间的阻塞。
AOF: append only file
原理:保存Redis建立以来的每一句命令
每次持久化的时候append在AOF缓存之后,AOF缓存依据配置写入文件系统。
优劣
- 优势:每秒持久化
- 劣势:大
操作
- AOF缓冲区同步到文件系统的策略:
配置 | 效果 |
---|---|
always | 每次写入缓冲区之后都调用fsync,fsync阻塞到磁盘写入完成。非常影响高并发 |
everysec | 每次写入缓冲区之后都调用write,操作系统什么时候sync不知道,有专门的线程每秒调用一次fsync,常用 |
no | 每次写入缓冲区之后都调用write,操作系统什么时候sync随它去 |
- 重写
手动或者自动,重写的时候剔除冗余部分。 AOF文件变小。
超时文件, 无效命令,合并命令
-
everysec阻塞:
虽然是另外一个线程fsync,但是主线程会检查上次sync时间,大于2秒,主线程会阻塞
复制
复制是集群化布置的核心功能。
通过slaveof命令指定主从关系,一个主可以有多个从。
-
复制原理
- 全量复制: 发RDB,造成网络和硬件压力。
- 部分复制:发AOF
-
心跳:主从之间分别伪装成client定时发送Ping-Pong以确认连接有效
-
异步复制:命令在主上完成后就返回,不在乎主从同步
-
应用:读写分离:因为应用里读远远多,可以把读分布到从数据库里
- 读只能在主数据库里
- 异步同步延迟,原则上无法避免,可以使用monitor防止偏移量过大