解锁Redis知识点,成为专家必备!

发表时间: 2022-11-28 10:24

目录

  • Redis的介绍、优缺点、使用场景
  • Linux中的安装
  • 常用命令
  • Redis各个数据类型及其使用场景
  • Redis字符串(String)
  • Redis哈希(Hash)
  • Redis列表(List)
  • Redis集合(Set)
  • Redis有序集合(sorted set)
  • Redis - 瑞士军刀
  • 慢查询
  • pipeline流水线
  • 发布订阅
  • bitmap
  • HyperLogLog算法
  • GEO
  • Redis持久化,数据备份与恢复
  • RDB
  • AOF
  • SpringBoot + Jedis + 1主2从3哨兵 实现Redis的高可用
  • SpringBoot + Jedis + Redis Cluster代码案例
  • 高可用
  • 主从复制
  • Redis事务

Redis的介绍、优缺点、使用场景

  • Redis是什么: 开源的,基于键值的存储服务系统,支持多种数据类型,性能高,功能丰富

特性(主要有8个特性):

  • 速度快:官方给出的结果是10W OPS,每秒10W的读写(为什么是10W,因为内存的相应时间是100纳秒-10万分之一秒)。数据存储在内存中;使用C语言开发;Redis使用单线程,减少上下文切换。本质原因是计算机存储介质的速度,内存比硬盘优几个数量级)。MemoryCache可以使用多核,性能上优于Redis。
  • 持久化:Redis所有的数据保持在内存中,对数据的更新将异步地保存到磁盘上。断掉,宕机? RDB快照/AOF日志模式来确保。MemoryCache不提供持久化
  • 多种数据结构:Redis提供字符串,HashTable, 链表,集合,有序集合;另外新版本的redis提供BitMaps位图,HyperLogLog超小内存唯一值计数,GEORedis3.2提供的地理位置定位。相比memocache只提供字符串的key-value结构
  • 支持多种编程语言:Java,PHP,Ruby,Lua,Node
  • 功能丰富: 发布订阅,支持Lua脚本,支持简单事务,支持pipline来提高客户端的并发效率
  • 简单:单机核心代码23000行,让开发者容易吃透和定制化;不依赖外部库;单线程模型
  • 主从复制:主服务器的数据可以同步到从服务器上,为高可用提供可能
  • 高可用、分布式:2.8版本后提供Redis-Sentinel支持高可用;3.0版本支持分布式
  • 典型应用场景:
  • 缓存系统:缓存一些数据减少对数据库的访问,提高响应速度
  • 计数器:类似微博的转发数,评论数,incr/decr的操作是原子性的不会出错
  • 消息队列系统:发布订阅的模型,消息队列不是很强
  • 排行版: 提供的有序集合能提供排行版的功能,例如粉丝数,关注数
  • 实时系统:利用位图实现布隆过滤器,秒杀等

安装

  • Linux中安装
wget http://download.redis.io/releases/redis-5.0.7.tar.gztar -zxvf redis-5.0.7.tar.gzmv redis-5.0.7 /usr/local/redis 不需要先创建/usr/local.redis文件夹cd /usr/local/redismakemake installvi redis.conf* bind 0.0.0.0 开发访问* daemonize yes 设置后台运行redis-server ./redis.conf 启动redis-cli 进入命令行,进行简单的命令操作vi redis.conf> requirepass password 修改密码redis-cli 再次进入cmd> shutdown save 关闭redis,同时持久化当前数据redis-server ./redis.conf 再次启动redisredis-cli 进入命令行> auth password将redis配置成系统服务,redis/utils中自带命令,我们只需修改参数/usr/local/redis/utils/./install_server.sh[root~ utils]# ./install_server.shWelcome to the redis service installerPlease select the redis port for this instance: [6379] 默认端口不管Selecting default: 6379Please select the redis config file name [/etc/redis/6379.conf] /usr/local/redis/redis.conf 修改配置文件路径Please select the redis log file name [/var/log/redis_6379.log] /usr/local/redis/redis.log 修改日志文件路径Please select the data directory for this instance [/var/lib/redis/6379] /usr/local/redis/data 修改数据存储路径Please select the redis executable path [/usr/local/bin/redis-server]Selected config:Port           : 6379Config file    : /usr/local/redis/redis.confLog file       : /usr/local/redis/redis.logData dir       : /usr/local/redis/dataExecutable     : /usr/local/bin/redis-serverCli Executable : /usr/local/bin/redis-clichkconfig --list | grep redis 查看redis服务配置项redis_6379      0:off   1:off   2:on    3:on    4:on    5:on    6:off服务名是redis_6379复制代码

可执行文件说明

  • redis-server: Redis服务器,启动Redis的
  • redis-cli: Redis命令行客户端连接
  • redis-benchmark: 对Redis做性能测试
  • redis-check-aof: AOF文件修复工具
  • redis-check-dump: RDB文件检查工具
  • redis-sentinel: Sentinel服务器(2.8以后)
  • 启动方式
  • redis-server: 最简单的默认启动,使用redis的默认参数
  • 动态参数启动:redis-server –port yourorderpoint
  • 配置文件的方式: redis-server configpath
  • 比较:
  • 生产环境选择配置启动;单机多实例配置文件可以选择配置文件分开
  • Redis客户端返回值
  • 状态回复:ping->pong
  • 错误恢复:执行错误的回复
  • 整数回复:例如incr会返回一个整数
  • 字符串回复: get
  • 多行字符串回复:mget
  • 常用配置
  • daemonize: 是否是守护进程(y/n)
  • port端口:默认是6379
  • logfile:Redis系统日志
  • dir:Redis工作目录
  • 常用命令:在线练习try.redis.io/
redis-cli -h x.x.x.x -p x 连接auth "password" 验证密码redis-cli --raw可以避免中文乱码exit 退出select index 切换到指定的数据库keys * 显示所有key,如果键值对多不建议使用,keys会遍历所有key,可以在从节点使用;时间复杂度O(N)dbsize 算出所有的key的数量,只是数量;时间复杂度O(1)exists key key是否存在,存在返回1,不存在返回0;时间复杂度O(1)incr key 将key的值加一,是原子操作decr key 将key的值加一,会出现复数,是原子操作del key 删除key,删除成功返回1,失败返回0;时间复杂度O(1)expire key seconds 设置过期时间,过期之后就不存在了;时间复杂度O(1)ttl key 查看key剩余的过期时间,key不存在返回-2;key存在没设置过期时间返回-1;(TTL Time To Live)persist key 去掉key的过期时间,再查看ttl key,返回值是-1,表示key存在并且没有设置过期时间type key 查看类型;时间复杂度O(1)config get * 获取配置信息set key value插入值sadd myset 1 2 3 4 插入setget key获取值del key删除keycat redis.conf | grep -v "#" | grep -v "^$" 查看配置文件,去除所有的#,去除所有的空格setnx key value #key不存在,才设置set key value xx #可以存在,才设置set key value [exporation EX seconds | PX milliseconds] [NX|EX]mget key1 key2 key3 批量获取 1次mget=1次网络时间+n次命令时间;时间复杂度O(n)mset key1 value1 key2 value2 批量插入;时间复杂度O(n)n次get = n次网络时间 + n次命令时间,mget一次就能完成,省去大量的网络时间getset key newvalue # set key newvalue并返回旧的valueappend key value #将value追加到旧的valuestrlen key #获取value的长度,中文占2个字节incrbyfloat key 3.5 #增加key对应的值set/get/del, incr(自增1)/decr(自减1)/incrby(incrby key n自增n)/decrbygetrange key start end #获取value从start到end的值setrange key index value #设置指定下标为一个新的值hset key field value #给key的field设置值hget key field #获取key的field的值hdel key field #删除key的field的值hgetall key #获取key的所有值hexists key field # 判断key的field是否存在hlen key #获取key field的数量hmset key field1 value1 field2 value2hmget key field1 field2hsetnx/hincrby/hdecry/hincrbyfloatlpush key value1 value2...valueN #从左边插入rpush key value1 value2...valueN #从右边插入linsert key before|after value newValuerinsert key before|after value newValuelpop key #从左边弹出一个itemrpop key #从右边弹出一个itemlrem key count value #若count等于0或者不填,表示删除所有的value值相等的item;若count>0,表示从左到右删除最多count个value相等的item;若count<0,表示从右到左,删除最多Math.abs(count)个value相等的项ltrim key start end #按照索引范围修剪列表,可以用来慢删除,因为全删除可能会阻塞redislrang key start end #获取key中从start到end的值lindex key index #取第index的值llen key #算出列表的长度lset key index newValue #修改index的值为newValueblpop key timeout #lpop阻塞版本,timeout是阻塞时间,timeout=0表示死等,lpop会立马返回,有时候数据更新不那么及时,或者消息队列中消息未及时处理,我们可以使用这个brpop key timeoutlpush + LPOP = STACKlpush + RPOP = QUEUElpush  + ltrim = 有序的集合lpush + rpop = 消息队列sadd key value #不支持插入重复元素,失败返回0srem key element #删除集合中的element元素smembers key #查看集合元素sinter key1 key2 #取出相同:交集sdiff key1 key2 #取出key1中key2没有的元素:差集sunion key1 key2 #取出二者所有的元素:并集sdiff|sinter|sunion store key #将结果存到key中,有时候计算一次耗时scard key #计算集合大小sismember key element #判断element是否在集合中srandmember #返回所有元素,结果是无序的,小心使用,可能结果很大smembers key #获取集合中的所有元素spop key #从集合中随机弹出一个元素scanSADD = TaggingSPOP/SRANDMEMBER = Random itemSADD + SINTER = Social Graphzadd key score element #添加score和element O(logN): 使用xx和跳表的数据结构zrem key element #删除元素zscore key element #返回元素的分数zincrby key increScore element #增加或减少元素分数zcard key #返回元素的总个数zrank key element #获取element的排名zrange key start end [withscores] #返回指定索引范围内的升序元素zrangebyscore key minScore maxScore [withscore] #返回分数在minScore和maxScore之间的元素zcount key minScore maxScore #返回有序集合内在指定分数范围内的个数zremrangebyrank key start end #删除指定排名内的元素zremrangebyscore key minScore maxScore #删除指定分数内的元素zrevrang/zrevrange/集合间的操作zsetunioninfo replication 查看分片,能够获取到主从的数量和状态config get databases 获取所有数据库复制代码

数据结构和内部编码

  • Reids支持5中存储的数据格式: String, Hash, List, Set, Sorted Set

String

  • redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象,最大能存储 512MB。
  • 使用场景:缓存/计数器/分布式锁/Web集群session共享/分布式系统全局序号(不用每次都拿,一次拿1000个放到内存中)…
  • 常用命令:
  • 实战:实现分布式的id生成器,可以使用incr的思路,但是实际中会比这复杂

hash

  • 是一个键值(key=>value)对集合。Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
  • 实战:统计用户主页的访问量, hincrby user:1:info pageview count
  • Redis集群架构下不太适合

list

  • Redis 列表是简单的字符串列表,按照插入顺序排序。列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。
  • 实战:微博按时间顺序展示消息

set

  • 是 string 类型的无序集合,不允许插入重复元素,插入重复元素失败返回0。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
  • 实战:抽奖系统(量不是很大的时候);like,star可以放到集合中;标签tag

zset

  • 有序集合:有序且无重复元素,和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
  • 实战:排行榜

Redis客户端: Java的Jedis(Socket通信),Python的redis-py

瑞士军刀

慢查询

  • 生命周期

两点说明:

  1. 慢查询发生在第3阶段,比如keys *等这些需要扫描全表的操作
  2. 客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素

两个配置

  • slowlog-log-slower-than=n(微秒):命令执行时间超过x微秒,会被丢到一个固定长度的慢查询queue中;n<0表示不配置
  • slowlog-max-len: 先进先出的队列,固定长度,保存在内存中(重启redis会消失)

配置方法

默认值

  • config get slowlog-max-len=128
  • config get slowlog-log-slower-than=10000

修改配置文件重启

动态配置

  • config set slowlog-max-len 1000
  • config set slowlog-log-slower-than 1000

常用命令

  • slowlog get [n]:获取慢查询队列
  • slowlog len: 获取慢查询队列的长度
  • slowlog reset: 清空慢查询队列

运维经验

  • slowlog-max-len不要设置过大,默认10ms,通常设置1ms,根据QPS来设置
  • slowlog-log-slower-than不要设置过小,通常设置1000左右
  • 定期持久化慢查询

pipeline流水线(批量操作)

当遇到批量网络命令的时候,n次时间=n次网络时间+n次命令时间。举个例子,北京到上海的距离是1300公里,光速是3万公里/秒,假设光纤传输速度是光速的2/3,也就是万公里/秒,那么一次命令的传输时间是 1300/20000*2(来回)=13毫秒, 什么是pipeline流水线,1次pipeline(n条命令)=1次网络时间+n次命令时间;pipeline命令在redis服务端会被拆分,因此pipeline命令不是一个原子的命令。注意每次pipeline携带数据量;pipeline每次只能作用在一个Redis节点上;M操作和pipeline的区别,M(mset)操作是redis的原生命令,是原子操作,pipeline不是原子操作

for(int i = 0; i < 10000; i++>) {    jedis.hset(key, field, value); //1万次hset差不多要50秒for(0->100) {  Pipeline pipeline = jedis.pipelined();  for(0->100) {    pipeline.hset(key,field,value);  }  pipeline.syncAndReturnAll(); //拆分100次,每次100个命令,大概需要0.7秒}复制代码

发布订阅:类似生产者消费者模型

  • 角色:发布者(publisher),频道(channel),订阅者(subscriber); 发布者将消息发布到频道中,订阅者订阅相关的频道;
  • API: publish/subscribe/unsubscribe
  • publish channel message : publish sohu:tv “hello world”
  • subscribe sohu:tv
  • unsubscribe [channel]
  • psubscribe [pattern] #订阅模式 sohu*

bitmap:位图:数据量很大的时候节省存储内存,数据量小了,不节省

hyperloglog(算法,数据结构):

  • 极小空间完成独立数量统计,本质是个string
  • api: pfadd key element[s]:向hyperloglog添加元素 pfcount key[s]:计算hyperloglog的独立总数 pfmerge key1 key2合并

GEO: 3.2提供的用于计算地理位置信息;数据类型是zset,可以使用zset的删除命令

  • 使用场景:微信摇一摇看附近好友
  • api:
  • geo key longitude latitude member #增加地理位置信息
  • geopos key member[n] #获取地理位置信息
  • geodist key member1 membe2 [unit] m米 km千米 mi英里 ft尺 获取两地位置的距离
  • georadius #算出指定范围内的地址位置信息的集合,语法复杂了点
  • 总结下Redis数据结构和类型的常见用法







类型

简介

特性

使用场景

String

二进制安全

可以包含任何数据,比如JPG图片或者序列化的对象,一个键最大能存储512M


Hash

键值对集合,即编程中的Map

适合存储对象,并且可以向数据库中那样update一个属性(Memcache中需要将字符串反序列化成对象之后再修改属性,然后序列化回去)

存取/读取/修改 用户信息

List

双向链表

增删快,提供了操作某一元段元素的API

1. 最新消息,按照时间线显示2. 消息队列

Set

哈希表实现,元素不重复

添加/删除/修改的复杂度都是O(1),为集合提供求交集/并集/差集的操作

1. 打label/tag,如文章2. 查找共同好友3. 抽奖系统

Zset

将Set中的元素增加一个double类型的权重score,按照score排序

数据插入集合就好序了

排行榜

Hyperloglog

本质是string

极小空间完成独立数据量统计

统计基数,不完全正确

GEO

数据类型是zset

存储地理位置信息,并提供计算距离等操作

微信摇一摇查看附近好友

Bitmap

位图

数据量很大的时候节省存储内存,数据量小了不节省


  • String 简单动态字符串 Simple Dynamic String, SDS
Redis没有直接使用C语言的传统字符串表示,而是自己构建了一种名为简单动态字符串(Simple Dynamic String, SDS)的抽象类型,并将SDS用作Redis的默认字符串表示。每个sds.h/sdshdr结构表示一个SDS值:struct sdshdr {  int len; // 记录buf数组中已经使用的字节数量  int free; // 记录buf数组中未使用字节的数量  char buf[]; // 字节数组,用于保存字符串。SDS遵循C字符串以空字符结尾的惯例}复制代码

Redis持久化

  • 持久化的作用:redis所有数据保存在内存中,对数据的更新将异步地保存到磁盘上。
  • 主流数据库持久化实现方式:快照(MySQL Dump/Redis RDB),写日志(MySQL Binlog/Redis AOF)

RDB:

  • 创建RDB文件(二进制文件)到硬盘中,启动后载入RDB文件到内存

三种触发机制

  • save(同步) - 会产生阻塞
  • 文件策略:如存在老的RDB文件,新的替换老的,新的会先生成到一个临时文件
  • bgsave(异步) - 不会阻塞
  • 客户端执行bgsave之后,redis会使用linux的一个fork()命令生成主进程的一个子进程(fork的操作会执行一个内存页的拷贝,使用copy-on-write策略),子进程会创建RDB文件,创建完毕后将成功的消息返回给redis。fork()出来的子进程执行快的话不会阻塞主进程,否则也会阻塞redis,阻塞的实际点就是生成出来这个子进程。由于是异步,在创建的过程中还有其他命令在执行,如何保证RDB文件是最新的呢?在数据量大的时候bgsave才能突出优点。

命令savebgsaveIO类型同步异步阻塞是是(阻塞发生在fork子进程复杂度O(n)O(n)优点不会消耗额外内存不阻塞客户端命令缺点阻塞客户端命令需要fork,消耗内存

  • 自动触发:多少秒内有多少changes会异步(bgsave)生成一个RDB文件,如60秒内有1W条changes,默认的规则,可以改;不是很好吧,无法控制频率;另外两条是900秒内有1条changes, 300秒内有10条changes;

配置

  • dbfilename dump.rdb
  • dir ./
  • stop-writes-on-bgsave-error yes 当bgsave发生错误是停止写RDB文件
  • rdbcompression yes 采用压缩格式
  • rdbchecksum yes 采用校验和

其他不能忽视的点:

全量复制;debug reload;shutdown save会执行rdb文件的生成

AOF:

  • RDB现存问题:耗时,耗性能(fork,IO),不可控(突然宕机)
  • AOF:redis中的cmd会先刷新到缓冲区,然后更具配置AOF的策略,异步存追加到AOF文件中,发生宕机后,可以通过- AOF恢复,基本上数据是完整的
  • AOF的三种策略(配置的三种属性)
  • always:来一条命令写一条;不丢失数据,IO开销较大
  • everysec:每秒把缓冲区fsync到AOF文件;丢1秒数据
  • no:操作系统决定什么时候把缓冲区同步到AOF就什么时候追加;不用配置,但是不可控,取决于操作系统

AOF重写

  • 如果AOF文件很大的话,恢复会很慢,AOF的重写是优化一些命名,使其变成1条,对于过期数据没必要Log,本质是把过期的没有用的,重复的过滤掉,以此减少磁盘占用量,加速恢复。极端的例子,1亿次incr,实际只需要set counter n就够了
  • 重写的两种方式
  • bgrewriteaof:异步执行,redis fork出一个子进程,然后进行AOF重写
  • AOF重写配置
  • auto-aof-rewrite-min-size: AOF文件到达多大的时候才开始重写
  • auto-aof-rewrite-percentage: AOF文件的增长率到达了多大才开始重写

统计

  • aof_current_size AOF当前尺寸 字节
  • aof_base_size AOF上次重启和重写的尺寸 字节,方便自动重写判断

重写触发机制(同时满足如下两条)

  • aof_current_size > auto-aof-rewrite-min-size
  • (aof_current_size - aof_base_size) / aof_base_size > auto-aof-rewrite-percentage

其他配置

  • appendonly yes
  • appendfilename “”
  • appendfsync everysec
  • dir /xx
  • no-appendfsync-on-rewrite yes AOF在重启之后恢复,要权衡是否开启AOF日志追加的功能,这个时候IO很大,如果设置为yes,也就意味着在恢复之前的日志数据会丢失

RDB & AOF最佳策略:RDB优先于AOF先启用

  • RDB:建议关掉,集中管理,在从节点开RDB
  • AOF:建议开启,每秒刷盘
  • 最佳策略:小分片(log文件分片)

常见问题

  • fork操作:是一个同步操作,做一个内存页的拷贝;与内存量息息相关,内存越大,耗时越长;执行info命令,有个latest_fork_usec的值,看下上次fork执行耗时
  • 进程外开销:
  • CPU:RDB AOF文件生成,属于CPU密集型操作(不要和CPU密集型应用部署在一起,减少RDB AOF频率);内存:fork内存开销;硬盘:IO开销大,选用SSD磁盘
  • AOF追加阻塞:主线程将命令刷到AOF缓冲区,同步线程同步命令到硬盘,同时主线程会对比上次fsync的时间,如果大于2秒就阻塞主线程,否则不阻塞,主线程这么做是为了达到每秒刷盘的目的,让子线程完成AOF,以此来达到数据同步。AOF发生阻塞怎么定位:redis日志/info persistence(aof_delayed_fsync累计阻塞次数,是累计,不好分清什么时候发生阻塞)
  • 单机多实例部署

高可用

Redis主从复制

  • 主从复制:单机故障/容量瓶颈/QPS瓶颈;一个master可以有多个slave,一个slave只能有一个master,数据必须是单流向,从master流向slave

复制的配置:

  • 使用slaeof命令,在从redis中执行slave masterip:port使其成为master的从服务器,就能从master拉取数据了;执行slaveof no one清除掉不成为从节点,但是数据不清楚;
  • 修改配置, slaveof ip port / slave-read-only yes(从节点只做都操作);配置要更改的话,要重启,所以选择的时候谨慎

全量复制

  • run_id(使用info server可以看到run_id),重启之后run_id就没有了,当从服务器去复制主服务器,主服务器run_id会在从服务器上做一个标识,当从服务器发现主服务器的run_id发生了变化,说明主服务器发生了变化(重启或者什么的),那么从服务器就要把主服务器的数据都同步过来
  • 偏移量:部分复制中的一个依据,后面说
  • 解析下上面的全量复制的过程,slave向master发送psync的命令要去master全量复制数据(PSYNC ,其中?表示我不知道master的runId啊,第一次连嘛,-1表示我都要,这时候slava咱啥也不知道),master大人收到了小弟的请求之后,大方的把自己的runId/offset发了过去,小弟收到后先存下来;在master大人把自个的信息发给小弟之后,立马投入了创建快照RDB的工作,一个bgsave命令立马开工,RDB生产了就发给slave;咦,细心的我们发现你这不对啊,你master创建快照到创建完成这之间新增的数据咋办,master吭吭了两声,我在开始快照的那一刻,后期的所有写命令都额外往buffer中存了一份,来保证我给你的是完整的,当我发送完RDB之后,立马给你发buffer;slave小弟内心对master大人产生了膜拜之情,收到了RDB/buffer之后,先把自己的老数据flush掉,然后load RDB,把最新的buffer刷一遍,分分钟让自己向master看齐。
  • 开销:bgsave时间, RDB文件网络传输时间,从节点清空数据时间,从节点加载RDB的时间,可能的AOF重写时间
  • 解释下上面的部分复制的过程,当遇到网络抖动,那这段时间内数据在slave上就会发生丢失,那么这些数据slave是不知道的,在2.8之前redis会重新做一次全量复制,但是很显然这样做开销很大,2.8之后提出部分复制的功能;当matster发现slave连接不上的时候,master在进行写操作的时候,也会往缓冲区写,等到下一次slave连上之后,slave会发送一条pysnc {offset}{runId}的命令,其中offset是slave自己的,相当于告诉master我的偏移量是多少,master判断slave的offset在缓冲区内(缓冲区有start/end offset)就向slave发送continue命令,然后把这部分数据发送给slave;当master发现slave这个offset偏移量很大的时候,也就意味着slave丢失了很多数据,那么就进行一次全量复制

故障处理:

  • master/slave宕机的情况,主从模式没有实现故障的完全自动转移
  • 常见问题:
  • 读写分离:读流量分摊到从节点,可能遇到复制数据延迟,也可能读到过期的数据,从节点故障怎么办
  • 主从配置不一致:主从maxmemory不一致,可能会丢失数据;主从内存不一致
  • 规避全量复制:第一次不可避免;小主节点,低峰处理(夜间);主节点重启后runId发生了变化
    规避复制风暴
  • 单机主节点复制风暴,如果是1主N从,当master重启之后,所有的slave都会发生全量复制,可想而知这样非常容易造成redis服务的不可用

Redis事务

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
  • Redis事务从开始到执行会经历以下三个阶段:开始事务 -> 命令入队 -> 执行事务。单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。这是官网上的说明 From redis docs on transactions: It’s important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands.
  • Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:
  • 首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。
  • 然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
  • 然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送
  • Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。管道技术最显著的优势是提高了 redis 服务的性能。

Redis 分区

  • 分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。

分区的优势:

  • 通过利用多台计算机内存的和值,允许我们构造更大的数据库。
  • 通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽。

分区的不足:

  • 涉及多个key的操作通常是不被支持的。举例来说,当两个set映射到不同的redis实例上时,你就不能对这两个set执行交集操作。
  • 涉及多个key的redis事务不能使用。
  • 当使用分区时,数据处理较为复杂,比如你需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件。
  • 增加或删除容量也比较复杂。redis集群大多数支持在运行时增加、删除节点的透明数据平衡的能力,但是类似于客户端分区、代理等其他系统则不支持这项特性。然而,一种叫做presharding的技术对此是有帮助的。
  • 分区类型:Redis 有两种类型分区。 假设有4个Redis实例 R0,R1,R2,R3,和类似user:1,user:2这样的表示用户的多个key,对既定的key有多种不同方式来选择这个key存放在哪个实例中。也就是说,有不同的系统来映射某个key到某个Redis服务,关注+转发后,私信【Redis】获取300多页的Redis实战学习笔记。

范围分区

  • 最简单的分区方式是按范围分区,就是映射一定范围的对象到特定的Redis实例。比如,ID从0到10000的用户会保存到实例R0,ID从10001到 20000的用户会保存到R1,以此类推。这种方式是可行的,并且在实际中使用,不足就是要有一个区间范围到实例的映射表。这个表要被管理,同时还需要各 种对象的映射表,通常对Redis来说并非是好的方法。

哈希分区

  • 另外一种分区方法是hash分区。这对任何key都适用,也无需是object_name:这种形式,像下面描述的一样简单:用一个hash函数将key转换为一个数字,比如使用crc32 hash函数。对key foobar执行crc32(foobar)会输出类似93024922的整数。对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个Redis实例中的一个了。93024922 % 4 = 2,就是说key foobar应该被存到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。