源文地址:https://mp.weixin.qq.com/s/4xdAWJHWg0vui8DRvNVHTQ
Redis7–ziplist的替代者listpack 一、ziplist简介 ziplist是一种连续内存空间并且有序的压缩链表,其主要的数据结构如下图:
结合以上数据结构的内存模型图,我们可以看出ziplist具有的一些优势与问题:
优势 问题 1. 具有极高的内存利用率 1. 每次增/删/改需要realloc连续内存空间并进行移动 2. 连锁更新问题 3. 查找时间复杂度过高的问题 (o(n)) 4. ziplist总内存大小限制(4G) 5. 元素个数超过65535时统计ziplist元素个数带来的遍历统计问题 什么时候可能会触发连锁更新呢?
向ziplist中增加、删除、修改数据内容、合并ziplist场景。
为什么会有连锁更新的问题?
从上图的数据结构中可以看到,在实际数据的内存结构前有prevlen与encoding字段,当其发生变化时可能会导致内存不连续,为了保证内存中数据的连续,所以可能会触发连锁更新。
二、listpack简介 由于ziplist存在不可避免的问题 – 连锁更新问题, 所以在Redis 5版本中,推出了ziplist替代版本listpack。
1. ziplist整体的结构与listpack的整体结构对比 从以上整体的对比图可以看到,其实两者结构相差不大,listpack相对于ziplist,没有了指向末尾节点地址的偏移量,这样也可以解决ziplist内存长度限制的问题。
2. entry结构对比 其数据结构对比如下图:
从以上结构中可以看到,listpack移除了prevlen,在data后新增了backlen,两者有着本质区别:
在ziplist中,prevlen代表前一个entry节点长度的偏移量; 在listpack中,backlen代表的是本entry节点的回朔起始地址长度的偏移量 Entry这样设计具有以下一些优势:
时间:
|
阅读:
2460 字 ~12分钟
原文地址:https://raw.githubusercontent.com/antirez/redis/7.0/00-RELEASENOTES
Introduction to the Redis 7.0 release redis7.0版本介绍
Redis 7.0 includes several new user-facing features, significant performance optimizations, and many other improvements. It also includes changes that potentially break backwards compatibility with older versions. We urge users to review the release notes carefully before upgrading.
Redis7.0引入了一些新的面向用户的特性,重要的性能优化和一些其他的提升。它还包括一些可能会破坏旧版本的兼容性的更改。我们建议用户在升级之前仔细阅读发布说明。
In particular, users should be aware of the following changes: > 用户应特别注意一下变更:
Redis 7 stores AOF as multiple files in a folder; see Multi-Part AOF below.
时间:
|
阅读:
719 字 ~4分钟
首先关于redisson的介绍,这里就不搬运了,贴一下github原地址:
概述 由于我这里只是简单使用了redisson的 分布式锁 的功能,这里仅记录下锁的简单使用。
官方文档:8.分布式锁和同步器
此次所用锁为可重入锁
8.1. 可重入锁(Reentrant Lock) 基于Redis的Redisson分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
RLock lock = redisson.getLock("anyLock"); // 最常见的使用方法 lock.lock(); 复制代码 大家都知道,如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
// 加锁以后10秒钟自动解锁 // 无需调用unlock方法手动解锁 lock.lock(10, TimeUnit.SECONDS); // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁 boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); if (res) { try { ... } finally { lock.unlock(); } } 复制代码 Redisson同时还为分布式锁提供了异步执行的相关方法:
RLock lock = redisson.getLock("anyLock"); lock.lockAsync(); lock.lockAsync(10, TimeUnit.SECONDS); Future<Boolean> res = lock.tryLockAsync(100, 10, TimeUnit.SECONDS); 复制代码 RLock对象完全符合Java的Lock规范。也就是说只有拥有锁的进程才能解锁,其他进程解锁则会抛出IllegalMonitorStateException错误。但是如果遇到需要其他进程也能解锁的情况,请使用分布式信号量Semaphore 对象.
时间:
|
阅读:
51 字 ~1分钟
Redis之VM机制 原文地址:https://www.codenong.com/cs106843764
该功能已经在redis2.6版本废弃了,此处仅作为相关了解,相关文档见:https://redis.io/docs/reference/internals/internals-vm/
Redis的VM(虚拟内存)机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过VM功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。Redis提高数据库容量的办法有两种:一种是可以将数据分割到多个Redis Server上;另一种是使用虚拟内存把那些不经常访问的数据交换到磁盘上。需要特别注意的是Redis并没有使用OS提供的Swap,而是自己实现。
Redis为了保证查找的速度,只会将value交换出去,而在内存中保留所有的Key。所以它非常适合Key很小,Value很大的存储结构。如果Key很大,value很小,那么vm可能还是无法满足需求。
(1)VM相关配置
通过在redis的redis.conf文件里,设置VM的相关参数来实现数据在内存和磁盘之间 换入和 换出操作。 相关配置如下:
#开启vm功能 vm-enabled yes #交换出来的value保存的文件路径 vm-swap-file /tmp/redis.swap #设置当内存消耗达到上限时开始将value交换出来 vm-max-memory 1000000 #设置单个页面的大小,单位是字节 vm-page-size 32 #设置最多能交换保存多少个页到磁盘 vm-pages 13417728 #设置完成交换动作的工作线程数,设置为0表示不使用工作线程而使用主线程,这会以阻塞的方式来运行。建议设置成CPU核个数 vm-max-threads 4 redis规定同一个数据页面只能保存一个对象,但一个对象可以保存在多个数据页面中。在redis使用的内存没超过vm-max-memory时,是不会交换任何value到磁盘上的。当超过最大内存限制后,redis会选择较老的对象(如果两个对象一样老会优先交换比较大的对象)将它从内存中移除,这样会更加节约内存。
对于Redis来说,一个数据页面只会保存一个对象,也就是一个Value值,所以应该将vm-page-size设置成大多数value可以保存进去。如果设置太小,一个value对象就会占用几个数据页面,如果设置太大,就会造成页面空闲空间浪费。
(2)VM的工作机制
redis的VM的工作机制分为两种:一种是vm-max-threads=0,一种是vm-max-threads>0。
第一种:vm-max-threads = 0
数据换出:主线程定期检查使用的内存大小,如果发现内存超出最大上限,会直接以阻塞的方式,将选中的对象 换出 到磁盘上(保存到文件中),并释放对象占用的内存,此过程会一直重复直到下面条件满足任意一条才结束: 数据换入:当有client请求key对应的value已被换出到磁盘中时,主线程会以阻塞的方式从换出文件中加载对应的value对象,加载时此时会阻塞所有client,然后再处理client的请求。这种方式会阻塞所有的client。 第二种:vm-max-threads > 0
数据换出:当主线程检测到使用内存超过最大上限,会将选中的要交换的数据放到一个队列中交由工作线程后台处理,主线程会继续处理client请求。 数据换入:当有client请求key的对应的value已被换出到磁盘中时,主线程先阻塞当前client,然后将加载对象的信息放到一个队列中,让工作线程去加载,此时进主线程继续处理其他client请求。加载完毕后工作线程通知主线程,主线程再执行被阻塞的client的命令。这种方式只阻塞单个client。 总结:Redis直接自己构建了VM 机制 ,不会像一般的系统会调用系统函数处理,会浪费一定的时间去 移动 和 请求,而Redis不存在。这也是Redis能够那么快的一个原因。
时间:
|
阅读:
140 字 ~1分钟
简介: 线上经常遇到用户想知道自己Redis实例内存使用情况,质疑内存占用量太高。为了不影响线上实例的使用,我们一般会采用bgsave生成dump.rdb文件,再结合redis-rdb-tools和sqlite来进行静态分析。
背景 线上经常遇到用户想知道自己Redis实例中数据的内存分布情况。 为了不影响线上实例的使用,我们一般会采用bgsave生成dump.rdb文件,再结合redis-rdb-tools和sqlite来进行静态分析。
创建备份 自建Redis可在客户端执行bgsave生成rdb文件。 阿里云数据库Redis版可以在控制台上可以进行数据备份和下载的操作,下载后的数据为rdb格式文件。 步骤详见下图:
生成内存快照 redis-rdb-tools是一个python的解析rdb文件工具, 主要有一下三个功能:
生成内存快照 转储成json格式 使用标准的diff工具比较两个dump文件 在分析内存的使后,我们主要用到它的生成内存快照功能。
redis-rdb-tools安装 redis-rdb-tools有两种安装方式,任选其一即可。 使用PYPI安装
pip install rdbtools 从源码安装
git clone https://github.com/sripathikrishnan/redis-rdb-tools cd redis-rdb-tools sudo python setup.py install 使用redis-rdb-tools生成内存快照 生成内存快照的命令为:
rdb -c memory dump.rdb > memory.csv 生成CSV格式的内存报告。包含的列有:数据库ID,数据类型,key,内存使用量(byte),编码。内存使用量包含key、value和其他值。 注意:内存使用量是理论上的近似值,在一般情况下,略低于实际值。 memory.csv例子:
$head memory.csv database,type,key,size_in_bytes,encoding,num_elements,len_largest_element 0,string,"orderAt:377671748",96,string,8,8 0,string,"orderAt:413052773",96,string,8,8 0,sortedset,"Artical:Comments:7386",81740,skiplist,479,41 0,sortedset,"pay:id:18029",2443,ziplist,84,16 0,string,"orderAt:452389458",96,string,8,8 分析内存快照 SQLite,是一款轻型的数据库。我们可以将前面生成的csv导入到数据库中之后,就可以利用sql语句很方便的对Redis的内存数据进行各种分析了。 导入方法:
sqlite3 memory.db sqlite> create table memory(database int,type varchar(128),key varchar(128),size_in_bytes int,encoding varchar(128),num_elements int,len_largest_element varchar(128)); sqlite>.
时间:
|
阅读:
799 字 ~4分钟
简介: 本文将对Redis内核单元测试框架进行基本的解析,并对如何编写测试用例进行基本的讲解。
在修改Redis内核之后,第一步我们需要做的就是添加或者对应的单元测试用例来进行基本的单元测试。本文将对Redis内核单元测试框架进行基本的解析,并对如何编写测试用例进行基本的讲解。
单元测试框架流程 Redis单元测试框架是基于tcl sh脚本实现的,其启动的方式为runtest [options]。 每一类的测试case写在单独的测试文件中,测试文件列表写入到test_server中all_tests列表中。 在启动测试时,会以server模式启动一个测试服务器,再启动多个测试客户端与之通信。由测试服务器会给空闲的测试服务端发送测试任务,参数为测试用例所在脚本文件名,由测试客户端执行对应的测试用例。详细的流程图如下:
1. processOptions 对选项进行解析,其中默认的模式是server模式,进入test_server_main函数; 若带了client选项则进入test_client_main函数.
2. test_server_main 内部维护了一系列当前的测试客户端状态列表; accept_test_clients 创建一个socket fd,侦听来自测试客户端的消息; 按照传入的参数,以runtest –client的方式启动n个测试client; 3. test_client_main 启动测试客户端,往测试服务器的fd上发送ready消息,开启客户端与服务端的交互流程;
4. 客户端与服务端的交互 客户端启动后,往测试服务器fd上发送ready消息; 服务端收到客户端ready或done事件后,检查所有的测试集,若还有测试任务未完成,则使用signal_idle_client方法往测试客户端发送测试任务,即”run 测试用例脚本文件名”消息; 客户端收到run消息后,调用execute_tests $data方法执行测试用例脚本文件; 客户端执行完测试case脚本后,往服务端发送done事件。再次进入第2步; 测试用例编写 1. 增加测试用例 新建一个测试用例文件,比如dummy.tcl,将之加入到test_helper.tcl的all_tests列表里
set ::all_tests { unit/auth ... unit/dummy ... } 这样启动测试的时候,会自动执行unit/dummy.tcl里面的测试用例;
2. 测试用例文件 每个测试用例文件里面可以包含多个start_server的部分,每个start_server都会启动一个redis实例。 每个start_server内部包含多个test函数模块,每个test函数对应一个测试用例。 例子:auth.tcl
start_server {tags {"auth"}} { test {AUTH fails if there is no password configured server side} { catch {r auth foo} err set _ $err } {ERR*no password*} } start_server {tags {"auth"} overrides {requirepass foobar}} { test {AUTH fails when a wrong password is given} { catch {r auth wrong!
时间:
|
阅读:
157 字 ~1分钟
1.Redis2.6
Redis2.6在2012年正是发布,经历了17个版本,到2.6.17版本,相对于Redis2.4,主要特性如下:
1)服务端支持Lua脚本。
2)去掉虚拟内存相关功能。
3)放开对客户端连接数的硬编码限制。
4)键的过期时间支持毫秒。
5)从节点支持只读功能。
6)两个新的位图命令:bitcount和bitop。
7)增强了redis-benchmark的功能:支持定制化的压测,CSV输出等功能。
8)基于浮点数自增命令:incrbyfloat和hincrbyfloat。
9)redis-cli可以使用–eval参数实现Lua脚本执行。
10)shutdown命令增强。
11)重构了大量的核心代码,所有集群相关的代码都去掉了,cluster功能将会是3.0版本最大的亮点。
12)info可以按照section输出,并且添加了一些统计项
13)sort命令优化
2.Redis2.8
Redis2.8在2013年11月22日正式发布,经历了24个版本,到2.8.24版本,相比于Redis2.6,主要特性如下:
1)添加部分主从复制的功能,在一定程度上降低了由于网络问题,造成频繁全量复制生成RDB对系统造成的压力。
2)尝试性的支持IPv6.
3)可以通过config set命令设置maxclients。
4)可以用bind命令绑定多个IP地址。
5)Redis设置了明显的进程名,方便使用ps命令查看系统进程。
6)config rewrite命令可以将config set持久化到Redis配置文件中。
7)发布订阅添加了pubsub。
8)Redis Sentinel第二版,相比于Redis2.6的Redis Sentinel,此版本已经变成生产可用。
3.Redis3.0(里程碑)
Redis3.0在2015年4月1日正式发布,相比于Redis2.8主要特性如下:
Redis最大的改动就是添加Redis的分布式实现Redis Cluster。
1)Redis Cluster:Redis的官方分布式实现。
2)全新的embedded string对象编码结果,优化小对象内存访问,在特定的工作负载下载速度大幅提升。
3)Iru算法大幅提升。
4)migrate连接缓存,大幅提升键迁移的速度。
5)migrate命令两个新的参数copy和replace。
6)新的client pause命令,在指定时间内停止处理客户端请求。
7)bitcount命令性能提升。
8)cinfig set设置maxmemory时候可以设置不同的单位(之前只能是字节)。
9)Redis日志小做调整:日志中会反应当前实例的角色(master或者slave)。
10)incr命令性能提升。
4.Redis3.2
Redis3.2在2016年5月6日正式发布,相比于Redis3.0主要特征如下:
1)添加GEO相关功能。
2)SDS在速度和节省空间上都做了优化。
3)支持用upstart或者systemd管理Redis进程。
4)新的List编码类型:quicklist。
5)从节点读取过期数据保证一致性。
6)添加了hstrlen命令。
7)增强了debug命令,支持了更多的参数。
8)Lua脚本功能增强。
9)添加了Lua Debugger。
10)config set 支持更多的配置参数。
11)优化了Redis崩溃后的相关报告。
12)新的RDB格式,但是仍然兼容旧的RDB。
13)加速RDB的加载速度。
14)spop命令支持个数参数。
15)cluster nodes命令得到加速。
时间:
|
阅读:
6 字 ~1分钟
Redis命令回溯方案 公司的缓存经常报CPU高的情况,在警报报出来的时刻,可能已经过了实例峰值执行的时间段了,再执行monitor命令去分析命令可能已经不够准确了,没法准确还原现场。另外monitor命令也不能长时间执行。
我有一个想法,就是做一个类似AOF机制的功能,每秒将执行的所有命令写入磁盘,只记录 【时间戳,命令,缓存Key,值长度】这几个字段的值,这样儿可以确保日志记录文件不会很大,也不需要aof重写。日志文件保留24小时,每天执行定时任务进行清除。
这个方案的缺点是需要修改Redis的源码,增加类似AOF的执行功能或者新增Redis Module,难度较高,另外对于已存在的应用,去做相应改造的话,需要涉及到集群的重建和迁移,工作量较大,迁移操作过程可能会对现有业务有所影响。