Redis内存碎片问题
内存碎片问题其实,内存碎片的形成有内因和外因两个层面的原因。简单来说,内因是操作系统的内存分配机制,外因是 Redis 的负载特征。
内因:内存分配器的分配策略内存分配器的分配策略就决定了操作系统无法做到“按需分配”。这是因为,内存分配器一般是按固定大小来分配内存,而不是完全按照应用程序申请的内存空间大小给程序分配。
这样的分配方式本身是为了减少分配次数。但是,如果 Redis 每次向分配器申请的内存空间大小不一样,这种分配方式就会有形成碎片的风险,而这正好来源于 Redis 的外因了。
外因:键值对大小不一样和删改操作Redis 通常作为共用的缓存系统或键值数据库对外提供服务,所以,不同业务应用的数据都可能保存在 Redis 中,这就会带来不同大小的键值对。
具体来说,一方面,如果修改后的键值对变大或变小了,就需要占用额外的空间或者释放不用的空间。另一方面,删除的键值对就不再需要内存空间了,此时,就会把空间释放出来,形成空闲空间。
如何判断是否有内存碎片?Redis 是内存数据库,内存利用率的高低直接关系到 Redis 运行效率的高低。为了让用户能监控到实时的内存使用情况,Redis ...
如何应对变慢的Redis?
如何应对变慢的Redis?如何判断 Redis 是不是真的变慢了?查看 Redis 的响应延迟当你发现 Redis 命令的执行时间突然就增长到了几秒,基本就可以认定 Redis 变慢了。
这种方法是看 Redis 延迟的绝对值,但是,在不同的软硬件环境下,Redis 本身的绝对性能并不相同。所以需要基于当前环境下的 Redis 基线性能做判断。
所谓的基线性能呢,也就是一个系统在低压力、无干扰下的基本性能,这个性能只由当前的软硬件配置决定。
从 2.8.7 版本开始,redis-cli 命令提供了–intrinsic-latency 选项,可以用来监测和统计测试期间内的最大延迟,这个延迟可以作为 Redis 的基线性能。其中,测试时长可以用–intrinsic-latency 选项的参数来指定。
例如:./redis-cli --intrinsic-latency 120该命令会打印 120 秒内监测到的最大延迟。
如果你想了解网络对 Redis 性能的影响,一个简单的方法是用 iPerf 这样的工具,测量从 Redis 客户端到服务器端的网络延迟。
如何应对 Redis 变慢?R ...
Redis如何避免单线程模型的阻塞
如何避免单线程模型的阻塞?Redis 实例有哪些阻塞点?和客户端交互时的阻塞点第一个阻塞点:集合全量查询和聚合操作。
键值对的增删改查操作是 Redis 和客户端交互的主要部分,也是 Redis 主线程执行的主要任务。所以,复杂度高的增删改查操作肯定会阻塞 Redis。
第二个阻塞点:集合元素的删除。
释放内存只是第一步,为了更加高效地管理内存空间,在应用程序释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配。如果一下子释放了大量内存,空闲内存块链表操作时间就会增加,相应地就会造成 Redis 主线程的阻塞。
第三个阻塞点:清空数据库。
既然频繁删除键值对都是潜在的阻塞点了,那么,在 Redis 的数据库级别操作中,清空数据库(例如 FLUSHDB 和 FLUSHALL 操作)必然也是一个潜在的阻塞风险,因为它涉及到删除和释放所有的键值对。
和磁盘交互时的阻塞点第四个阻塞点:AOF 日志同步写。
Redis 直接记录 AOF 日志时,会根据不同的写回策略对数据做落盘保存。如果有大量的写操作需要记录在 AOF 日志中,并同步写回的话,就会阻塞主线 ...
Redis实现消息队列
Redis实现消息队列概述消息队列在存取消息时,消息队列必须满足的三个需求:
消息保序
虽然消费者是异步处理消息,但是,消费者仍然需要按照生产者发送消息的顺序来处理消息,避免后发送的消息被先处理了。
重复消息处理
消费者从消息队列读取消息时,有时会因为网络堵塞而出现消息重传的情况。此时,消费者可能会收到多条重复的消息。对于重复的消息,消费者如果多次处理的话,就可能造成一个业务逻辑被多次执行,如果业务逻辑正好是要修改数据,那就会出现数据被多次修改的问题了。
消息可靠性保证
另外,消费者在处理消息的时候,还可能出现因为故障或宕机导致消息没有处理完成的情况。此时,消息队列需要能提供消息可靠性的保证,也就是说,当消费者重启后,可以重新读取消息再次进行处理,否则,就会出现消息漏处理的问题了。
redis实现方案List实现
消息保序
List 本身就是按先进先出的顺序对数据进行存取的,所以,如果使用 List 作为消息队列保存消息的话,就已经能满足消息保序的需求了。(即 LPUSH 和RPOP的结合)
为了即使没有新消息写入 List,消费者也要不停地调用 RPOP 命令,这就会导致消 ...
Redis实现时间序列数据
时间序列数据例如需要周期性地统计近万台设备的实时状态,包括设备 ID、压力、温度、湿度,以及对应的时间戳。
这些与发生时间相关的一组数据,就是时间序列数据。这些数据的特点是没有严格的关系模型,记录的信息可以表示成键和值的关系(例如,一个设备 ID 对应一条记录),所以,并不需要专门用关系型数据库(例如 MySQL)来保存。
基于 Hash 和 Sorted Set 保存时间序列数据
我们可以把时间戳作为 Hash 集合的 key,把记录的设备状态值作为 Hash 集合的 value。但是,Hash 类型有个短板:它并不支持对数据进行范围查询。
为了能同时支持按时间戳范围的查询,可以用 Sorted Set 来保存时间序列数据,因为它能够根据元素的权重分数来排序。我们可以把时间戳作为 Sorted Set 集合的元素分数,把时间点上记录的数据作为元素本身。
但是我们又会面临一个新的问题,也就是我们要解答的第二个问题:如何保证写入 Hash 和 Sorted Set 是一个原子性的操作呢?
这里可以使用redis事务,当多个命令及其参数本身无误时,MULTI 和 EXEC 命令可以保 ...
Redis的常见的四种统计模式
集合类型常见的四种统计模式聚合统计概念:所谓的聚合统计,就是指统计多个集合元素的聚合结果,包括:统计多个集合的共有元素(交集统计);把两个集合相比,统计其中一个集合独有的元素(差集统计);统计多个集合的所有元素(并集统计)。
例子:统计手机 App 每天的新增用户数和第二天的留存用户数,正好对应了聚合统计。
要完成这个统计任务,我们可以用一个集合记录所有登录过 App 的用户 ID,同时,用另一个集合记录每一天登录过 App 的用户 ID。然后,再对这两个集合做聚合统计。
新增用户:两个集合求差集
留存用户:两个集合求交集
Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞。所以,我给你分享一个小建议:你可以从主从集群中选择一个从库,让它专门负责聚合计算,或者是把数据读取到客户端,在客户端来完成聚合统计,这样就可以规避阻塞主库实例和其他从库实例的风险了。
排序统计概念:以在电商网站上提供最新评论列表的场景为例。
最新评论列表包含了所有评论中的最新留言,这就要求集合类型能对元素保序,也就是说,集合中的元素可以按序排 ...
Redis的RDB日志
RDB日志AOF日志记录了每条操作的命令,也正因为记录的是操作命令,而不是实际的数据,所以,用 AOF 方法进行故障恢复的时候,需要逐一把操作日志都执行一遍。也正是这个原因,RDB日志内存快照诞生了。
和 AOF 相比,RDB 记录的是某一时刻的数据,执行的是全量快照,并不是操作,所以,在做数据恢复时,我们可以直接把 RDB 文件读入内存,很快地完成恢复。
Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave。
save:在主线程中执行,会导致阻塞;
bgsave:创建一个子进程,专门用于写入 RDB 文件,避免了主线程的阻塞,这也是 Redis RDB 文件生成的默认配置。
写时复制技术:利用写时复制技术,在执行快照的同时,正常处理写操作。
原理就是如果主线程需要对数据进行修改,则会复制一份副本对副本进行修改
小结AOF日志与RDB日志都有各自的优缺点,没办法做到完全完美。所以Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。
这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响。而且,AOF 日志也只用 ...
Redis的AOF日志
AOF日志Redis 的持久化主要有两大机制,即 AOF(Append Only File)日志和 RDB 快照。AOF是写后日志,先执行命令再记录日志。这样就不会存在命令错误和阻塞当前写操作了。
风险:
如果刚执行完一个命令,还没有来得及记日志就宕机了,那么这个命令和相应的数据就有丢失的风险。
AOF 虽然避免了对当前命令的阻塞,但可能会给下一个操作带来阻塞风险。
写回磁盘策略:
Always:同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
Everysec:每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
No:操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
AOF重写机制:AOF会随着命令越来越多而变大,这个时候就需要对AOF进行重写。重写机制具有“多变一”功能。旧日志文件中的多条命令,在重写后的新日志中变成了一条命令。
和 AOF 日志由主线程写回不同,重写过程是由后台子进程 bgrewriteaof 来完成的,这也是为了避 ...
Redis的IO模型
Redis的IO 模型Redis 采用了多路复用机制,使其在网络 IO 操作中能并发处理大量的客户端请求,实现高吞吐率。但是由于是单线程如果线程被阻塞了,就无法进行多路复用了。
多路复用的高性能 I/O 模型Redis是基于多路复用的高性能 I/O 模型,该机制允许内核中,同时存在多个监听套接字和已连接套接字(使用的非阻塞模式)。并且为了在请求到达时能通知到 Redis 线程,select/epoll 提供了基于事件的回调机制,即针对不同事件的发生,调用相应的处理函数。这些事件会被放进一个事件队列,Redis 单线程对该事件队列不断进行处理。这样一来,Redis 无需一直轮询是否有请求实际发生,这就可以避免造成 CPU 资源浪费。
Redis6.0新特性随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 IO 的处理上,也就是说,单个主线程处理网络请求的速度跟不上底层网络硬件的速度。
单线程结构:
在之前Redis 一直被大家熟知的就是它的单线程架构,虽然有些命令操作可以用后台线程或子进程执行(比如数据删除、快照生成、AOF 重写),但是,从网络 IO 处理到实际的读 ...
MySQL用户权限
用户权限创建用户
create user 'ua'@'%' identified by 'pa';
这条语句的逻辑是创建一个用户’ua’@’%’,密码是pa。(%表示可以从任意ip登录,可以指定ip)
这条命令做了两个动作:
磁盘上,往mysql.user表里插入一行,由于没有指定权限,所以这行数据上所有表示权限的字段的值都是N;
内存里,往数组acl_users里插入一个acl_user对象,这个对象的access字段值为0。
权限操作
赋予权限:grant 权限列表 on 数据库.[.表名] to 用户名 [with grant option];(with grant option表示可以传递给其他用户)
取消权限:revoke 权限列表 on 数据库.[.表名] from 用户名;
全局权限:
全局权限,作用于整个 MySQL 实例用于管理MySQL服务器的操作,这些权限信息保存在 mysql 库的 user 表里,只能用*.*赋予。
revoke命令与grant命令,做了如下两个动作:
磁盘上,将mysql.user表 ...