01 千里之行始于足下
文章内容基于redis6
安装与运行
无论你一名极客还是一名工程师,Redis安装我都推荐源码安装,请前往官方下载地址:http://redis.io/download 进行源码下载,偶数为稳定版 奇数为不稳定版。
如果你是类linux系统,使用wget命令直接远程下载源码 wget https://download.redis.io/releases/redis-{具体版本号}.tar.gz
编译与安装命令:make && make install
默认安装地址:/usr/local/bin
,准备好redis.conf配置文件,以下几个配置建议修改:
daemonize yes 后台启动
port xxxx 修改默认端口
requireoass xxxx 添加访问密码
bind 127.0.0.1 如果想其它服务器可以访问,注释掉
服务端启动运行命令: ./redis-server ../conf/redis.conf
服务端停止运行命令:pkill redis-server
或者使用客户端发出关闭命令: ./redis-cli shutdown
客户端链接命令:./redis-cli -h 服务器IP -p 端口 -u 用户 -a 密码
,建议更换默认端口,添加访问认证密码
redis版本号查看命令:redis-server -v
redis自带工具集
- redis-benchmark:性能测试工具,测试redis在你的系统及配置下的读写性能
- redis-check-aof:用于修复出问题的AOF
- redis-check-rdb:用于修复出问题的rdb
- redis-sentinel:redis的集群管理工具
两种线程模型
-
单线程模型:socket读写、解析数据、执行处理、返回数据等操作都是由一个主线程来完成的。通过对epoll函数的包装来做到。
单线程原因:瓶颈在内存和网络不在cpu、多线程可能不安全、复杂度增加、线程上下文切换性能损耗等
-
多线程模型:redis6开始支持I/O多线程,因为之前的瓶颈主要在I/O数据读写性能
高性能根本原因:抽象了一套事件模型,使用多路复用机制(epoll),使得I/O读写都是非阻塞的,从而具备高性能的网络处理能力;同时基于内存进行数据处理。
value存储形式
我们日常中所提到的String(字符串)、List(列表)、Hash(哈希)、Set(集合)和 Sorted Set(有序集合)都只是Redis 键值对中值的数据类型,也就是数据的保存形式。
严格来说并不是Redis数据结构,Redis底层数据结构实现其实一共有6种:分别是简单动态字符串(SDS)、双向链表、压缩列表、哈希表、跳表和整数数组。具体对应关系,后面介绍底层存储结构时会进一步介绍。
先理解和掌握value的5种基本存储形式
String
key是字符串,如果存在空格必须加上双引号,最大的容量是512M
类似Memcache的键值存储,
- 使用场景:缓存、计数、共享会话、限速
List
底层实现是链表
类似数据结构中的队列,不过支持双向操作。
- 使用场景:消息队列、文章列表
Hash
按hash的方式存放字符串
相当于关系数据的行数据
- 使用场景:存储类似于关系数据库的行
Set
是通过hashTable实现的
存储很多数据名单又不重复,判断某个元素是否存在相当方便,聚合运算效率也很高(存放好友,联系人,共同好友)
- 使用场景:标签,分类,社交
Zet
是通过散列表和跳跃表来实现的
在Set基础上增加了有序的特点,增加了一个double类型的分数作为权重,用于排序
- 使用场景:排行榜、社交点赞
存储基本结构
整体上看,无论是哪种存储形式,Redis都是键-值的形式,为了实现这种从键到值的快速访问,Redis使用了一个哈希表来保存所有的键值对。
一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。所以,我们常说,一个哈希表是由多个哈希桶组成的,每个哈希桶中保存了键值对数据。
每个键值对包含了键部分和值部分,键部分都是String类型,值部分由大家所熟知的(上面介绍的)5种存储结构组成,而底层用来实现的数据结构有6种,它们之间的对应关系如下:
上面这个哈希表保存了所有的键值对,也称作全局哈希表。当Hash表查询数据的时间复杂度是O(1),操作又是内存级别的,所以数据会非常快。
不过当redis存储的数据越来越大的时候,难免就会产生hash冲突,形成链式hash,hash链上的元素只能逐个查找,时间复杂度是O(n),所以效率就会下降。
当链式hash过程的时候,redis会进行rehash操作,保证元素分布均匀。rehahs的过程是一个渐进式的过程。可以分为3个步骤:
1 先构造一个更大的全局哈希表;
2 映射元素到新的全局哈希表;
3 释放掉旧的hash表。
这里的关键是第2步,redis不是一次性完成所有元素拷贝的,而是在某索引位置发生请求或空闲时,才进行拷贝。
巧妙的把一次性大量拷贝分摊到了多次请求的过程中,既不影响正常请求也能保证数据快速拷贝(后续AOF重写技术也利用了这个设计)。
熟悉了底层数据结构的实现,对我们如何使用redis提供了技术指导,操作的复杂度取决于数据结构的复杂度:
- 对Hash和Set的单元素操作,其实现是哈希表,复杂度都是O(1),它们现在也支持了多元素操作,M个元素复杂度就是O(M)
- 对List、Hash、Set遍历操作时其实现是通过链表或数组实现,复杂度一般为O(N),我们尽量避免这种操作
- 对集合类型元素统计操作(LLEN,SCARD),一般会有单独的字段来存储这个值,所以复杂度为O(1)
- 特别注意对与压缩列表和双向列表都会记录头尾指针偏移量,对与头尾类操作(LPOP,RPUSH),复杂度为O(1)
通用规则
- List、Hash、Set、Zset被称作容器型数据
- 不存在就创建原则:容器型数据不存在,就会自动创建一个,再进行操作
- 没有就释放原则:容器型数据中如果没有元素了,就删除掉并释放内存
- 能有过期时间:所有数据结构都可以设置过期时间
- 过期时间擦除:对有过期时间的字符串进行修改操作会抹除掉过期时间
过期机制
设计理念基于性能与效率的折中,采用定期删除与惰性删除机制
定期删除:Redis会在后台,默认每秒10次的执行如下操作: 随机 选取100个key校验是否过期,如果有25个以上的key过期了,立刻 额外随机选取下100个key(不计算在10次之内)。也就是说,如果过 期的key不多,Redis最多每秒回收200条左右,如果有超过25%的 key过期了,它就会做得更多,这样即使从不被访问的数据,过期 了也会被删除掉。
惰性删除:当client主动访问key时,会先对key进行超时判断,过 时的key会立刻删除
处理过期keys的相关命令:
- expire:设置过期时间,格式是expire key值 秒数
- expireat:设置过期时间,格式是expireat key值 到秒的时间戳
- ttl:查看还有多少秒过期,格式是ttl key值,-1表示永不过期,-2表示已 过期
- persist:设置成永不过期,格式是persist key值,删除key的过期设置;另 外使用set或者getset命令为键赋值的时候,也会清除键的过期时间
- pttl:查看还有多少毫秒过期,格式是pttl key值
- pexpire:设置过期时间,格式是pexpire key值 毫秒数
- pexpireat:设置过期时间,格式是pexpireat key值 到毫秒的时间戳