Redis,作为一个开源的、内存中的数据结构存储系统,以其卓越的性能和灵活的数据结构而受到广泛的欢迎和使用。然而,尽管Redis的使用相对直观,但要充分利用其潜力并优化其性能,就需要深入理解其工作原理和最佳实践。
本博客搜集网上的内容提供一份详尽的Redis使用规范,无论你是刚接触Redis的新手,还是希望进一步提升Redis使用技巧的老手,都能在这里找到有价值的信息。帮助你避免在实际使用中遇到的陷阱。
Redis的Key命名规范可以根据实际需求和个人偏好进行定义,但以下是一些常见的命名规范和最佳实践:
简洁明了:Key应该简洁明了,能够清晰表达其含义。避免使用过长或过于复杂的Key名称,以减少存储空间和提高性能。
使用命名空间:为了避免Key之间的冲突,可以使用命名空间来对Key进行分类或分组。例如,可以使用"namespace:key"的形式,如"user:123"和"order:456"。
使用冒号分隔层级:使用冒号作为层级分隔符,以便在Key命名中表示层级关系。例如,可以使用"category:books"和"category:movies"来表示不同类别下的数据。
避免使用特殊字符:避免在Key命名中使用特殊字符,如空格、制表符或换行符等,以免引起解析或处理上的困扰。
使用一致的命名风格:选择一种命名风格,并在整个应用程序中保持一致。例如,可以选择使用全小写字母、使用下划线作为单词分隔符,或者使用驼峰式命名。
可读性和可维护性:选择具有可读性和可维护性的Key名称,以便其他开发人员能够轻松理解和操作。避免使用过于简化或缩写的Key名称,除非在特定情况下确实需要。
规避命名冲突:确保Key名称与其他数据存储系统或数据库中的其他命名不冲突,以免造成混淆和错误。
总之,好的Key命名应该简洁、明了、具有可读性和可维护性,并且能够清晰地表示其含义和关系。根据具体情况,可以根据以上建议进行命名,也可以根据团队或项目的规范进行定制化命名。
确实,避免使用"bigkey"是一个良好的实践。在Redis中,"bigkey"指的是占用大量内存空间的Key,这可能会对性能和资源消耗产生负面影响。
内存消耗:"bigkey"占用大量内存,特别是当使用Redis的RDB持久化或AOF日志持久化时。如果大量的内存被用于存储单个Key,会导致Redis实例的内存不足,影响其他Key的存储和读取。
读写性能:对"bigkey"的读写操作需要更多的时间和资源。当需要读取或更新一个大的Key时,Redis需要花费更多的时间来序列化和反序列化数据,以及在网络传输中传递更多的字节。
过期处理:如果一个"bigkey"设置了过期时间,当该Key过期时,Redis可能会阻塞一段时间来删除它,从而影响其他操作的执行。
将大的数据拆分成更小的片段,存储在多个Key中。这样可以均匀地分散数据,减少单个Key的大小。
是的,数据分片是一种常用的方法,用于将大的数据拆分成更小的片段,并将它们存储在多个Key中。这样可以均匀地分散数据,减少单个Key的大小,从而提高Redis的性能和资源利用率。
数据分片的指导原则:
分片策略:选择合适的分片策略是非常重要的。一种常见的方法是使用哈希函数,将数据的某个唯一标识(如ID或属性)映射到不同的Key上。这样可以确保相同标识的数据总是存储在同一个分片中。
分片数量:决定分片数量时需要权衡。较少的分片数量可以减少管理和维护的复杂性,但可能导致某些分片过于庞大,仍然存在"bigkey"的问题。较多的分片数量可以更均匀地分散数据,但会增加一些额外的开销和复杂性。
分片映射:维护一个分片映射表,用于记录数据的唯一标识与对应分片的映射关系。这样可以根据标识快速定位到正确的分片,并进行读取和操作。
一致性哈希:一致性哈希算法是一种常用的数据分片算法,它可以在增加或减少分片时最小化数据迁移的量。一致性哈希可以提供较好的负载均衡和容错性。
对于大的数据,可以在存储之前进行压缩,以减少内存占用和网络传输的开销。Redis提供了一些压缩算法,如LZF和Snappy。
确实,使用高效的序列化方法和压缩方法可以在Redis中存储和传输数据时提高性能和节省存储空间。以下是一些相关的规范和建议:
选择高效的序列化方法可以将数据转换为字节序列,并在存储和传输过程中提供更高的效率。在Redis中,常见的序列化方法包括JSON、MessagePack、Protocol Buffers等。根据数据的特性和需求,选择适合的序列化方法。不同的序列化方法,在序列化速度和数据序列化后的占用内存空间这两个方面,效果是不一样的。比如说,protostuff 和 kryo 这两种序列化方法,就要比 Java 内置的序列化方法(java-build-in-serializer)效率更高。
常见的序列化方法
JSON适用于数据结构相对简单,对可读性和跨平台兼容性要求较高的场景。
MessagePack是一种高效的二进制序列化格式,可以将数据紧凑地序列化为字节序列。它具有较高的序列化和反序列化性能,并支持多种编程语言。MessagePack序列化方法在Redis中可以节省存储空间和提高性能,但可读性较差。它适用于对存储空间和传输效率要求较高的场景。
Protocol Buffers是一种语言无关、平台无关的序列化格式,由Google开发。它使用描述性的接口定义语言(IDL)来定义数据结构,并生成相应的代码库,以实现高效的序列化和反序列化操作。Protocol
Buffers在Redis中可以提供较高的性能和较小的序列化尺寸,但使用它需要额外的代码生成步骤。Protocol
Buffers适用于对性能和存储空间都有较高要求的场景。
三种序列化方式的比较:
序列化方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
JSON | - 易于理解和阅读 - 跨平台兼容性好 |
- 序列化和存储开销较大 - 可读性较好 |
数据结构相对简单,对可读性和兼容性要求较高的场景 |
MessagePack | - 序列化和反序列化性能高 - 存储空间小 |
- 可读性较差 - 不支持所有编程语言 |
对存储空间和传输效率要求较高的场景 |
Protocol Buffers | - 高性能 - 小序列化尺寸 |
- 需要额外的代码生成步骤 - 学习曲线较陡 |
对性能和存储空间有较高要求的场景 |
可以根据数据的特性和需求选择适合的序列化方式。如果可读性和兼容性是关键因素,可以选择JSON。如果存储空间和传输效率是优先考虑的,可以选择MessagePack。如果需要高性能和小序列化尺寸,并且愿意接受额外的代码生成步骤,可以选择Protocol Buffers。
对于大量重复或冗余的数据,使用压缩方法可以减少存储空间和网络传输的开销。Redis提供了一些内置的压缩算法,如LZF压缩算法和Snappy压缩算法。可以根据数据的特点选择合适的压缩方法。
当处理包含大量重复或冗余数据的场景时,使用压缩方法可以显著减少存储空间和网络传输开销。以下是两个内置的压缩算法示例,可以在Redis中使用:
1. LZF压缩算法:
LZF是一种快速的无损压缩算法,适用于需要高压缩速度和较低压缩率的场景。它在压缩和解压缩过程中消耗的CPU资源相对较少。LZF算法在Redis中被广泛使用,可以通过开启zstd
选项来启用。
假设在Redis中存储了大量的重复文本数据,例如日志消息,这些消息可能存在大量的重复行。使用LZF压缩算法可以显著减少存储空间和传输开销。
代码示例
pom.xml文件中添加以下依赖:
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>com.ninggroupId>
<artifactId>compress-lzfartifactId>
<version>1.0.3version>
dependency>
使用LZFEncoder.encode()
方法对字节数组进行压缩,然后将压缩后的字节数组保存到Redis中。 使用LZFDecoder.decode()
方法对其进行解压缩。
import redis.clients.jedis.Jedis;
import com.ning.compress.lzf.LZFEncoder;
import com.ning.compress.lzf.LZFDecoder;
public class RedisLZFExample {
public static void main(String[] args) throws Exception {
// Create a connection to Redis
Jedis jedis = new Jedis("localhost");
String str = "日志数据: This is a repeated message.";
byte[] original = str.getBytes();
// Compress the bytes
byte[] compressed = LZFEncoder.encode(original);
// Save the compressed data in Redis
jedis.set("myKey".getBytes(), compressed);
// Retrieve the compressed data from Redis
byte[] retrieved = jedis.get("myKey".getBytes());
// Decompress the bytes
byte[] decompressed = LZFDecoder.decode(retrieved);
// Convert the decompressed bytes back to a String
String result = new String(decompressed, "UTF-8");
System.out.println("Original: " + str);
System.out.println("Decompressed:" + result);
jedis.close();
}
}
2. Snappy压缩算法:
Snappy是一种快速压缩算法,适用于需要高压缩率和较低压缩速度的场景。它在压缩和解压缩过程中相对较快,但相对于LZF算法,压缩率较高。Snappy算法在Redis中也可用,可以通过开启snappy
选项来启用。设在Redis中存储了大量的图像文件,这些文件可能存在相似的图像块或者元数据。使用Snappy压缩算法可以大幅度减少存储空间和网络传输开销。
在选择压缩方法时,需要根据数据的特点和应用需求进行评估。如果对压缩速度要求较高且可以容忍较低的压缩率,可以选择LZF算法。如果需要更高的压缩率且可以接受稍微较慢的压缩速度,则可以选择Snappy算法。
需要注意的是,压缩算法并非适用于所有类型的数据。对于已经高度压缩的数据(如已经使用了其他压缩算法的数据)或者具有较低的冗余性的数据,压缩可能并不会产生显著的效果。因此,在应用压缩算法之前,应仔细评估数据的特点和预期的效果。
我觉得这个规范意义不是很大。Redis使用了一种名为整数对象共享池的技术来优化内存使用。这种技术的主要想法是将常用的小整数预先创建并存储在一个内部池中,当需要创建这些整数时,就直接从池中获取,而不是每次都创建新的对象。
这种技术在Redis的许多功能中都有使用,例如在计数器、哈希表的大小和列表的长度等地方。
在Redis源码中,这个整数对象共享池被定义为一个大小为10000的数组,数组的索引即为对应的整数值,数组的元素为对应的整数对象。
当需要一个整数对象时,Redis会首先检查这个整数是否在共享池的范围内(0-9999),如果在范围内就直接从共享池中获取,否则才创建新的整数对象。
这种技术对内存优化并减少了对象创建的次数有很大的帮助,但是对于开发者来说是透明的,开发者无需关心这个过程,Redis会自动处理。这种优化技术只适用于小整数,对于大整数或者浮点数,Redis并没有提供共享池的机制。
Redis是一个内存数据库,读写速度非常快,适合存储访问频繁的热数据。例如,可以将用户的会话信息、热门商品的信息等存储在Redis中,以提高系统的响应速度。
例如,对于一个电商网站,热门商品的销售数据是经常需要查询的热数据。将这些数据存储在Redis中,当用户查询这些商品的销售数据时,可以直接从Redis中获取,而不需要去数据库中查询,大大提高了响应速度。
为了保证数据的独立性和安全性,不同的业务数据应该存储在不同的Redis实例中。这样,即使某个实例出现问题,也不会影响到其他的业务数据。
例如,一个公司可能有多个业务线,如用户管理、订单管理、商品管理等,每个业务线的数据应该存储在不同的Redis实例中。不同业务线的数据隔离,可以降低数据泄露的风险,避免数据混淆。
Redis的数据都是存储在内存中的,如果数据量过大,可能会导致内存溢出。因此,在保存数据时,应该设置一个合理的过期时间,以自动清理不再需要的数据。同时,设置过期时间也可以防止数据过期,保证数据的实时性。
例如,对于用户的登陆信息,我们可以设置其在Redis中的过期时间为30分钟。这样,如果用户在30分钟内没有任何操作,Redis会自动删除这些数据,释放内存。同时,也可以避免用户的登陆信息被他人利用
每个Redis实例都有其最大的内存容量,超过这个容量,Redis可能会出现问题。因此,应该定期监控Redis实例的内存使用情况,及时清理不需要的数据,控制Redis实例的容量。
将Redis单实例的内存大小设置在2~6GB之间,可以在保证性能的同时,避免在进行RDB快照或主从集群数据同步时产生过大的延迟。这样的设置可以确保Redis在处理正常请求时,不会因为数据备份或同步而产生阻塞,从而提高系统的整体性能和稳定性。
比如,假设我们的服务器内存为16GB,为了保证Redis的正常运行,我们应当将Redis实例的最大内存限制设置在12GB以下,以防止内存溢出。我们还应当定期检查Redis实例的内存占用情况,如果内存占用接近最大限制,就应当及时清理不必要的数据。
在线上环境中,有些Redis命令可能会对系统的稳定性和性能产生影响,因此我们应当禁用这些命令。例如,我们可以禁用FLUSHDB和FLUSHALL命令,以防止误操作导致的数据丢失。另外,我们也可以禁用DEBUG和CONFIG命令,以防止未授权的用户修改系统配置。
MONITOR命令可以用来实时监控Redis服务器的所有请求,但是这个命令会消耗大量的CPU和网络资源。因此,在线上环境中,我们应当慎用MONITOR命令。如果我们需要监控Redis服务器的性能,可以使用INFO命令或者使用专门的监控工具。
全量操作命令,如KEYS、SMEMBERS等,会返回大量的数据,可能会导致网络延迟和CPU占用过高。在大数据量的情况下,这些命令可能会阻塞Redis服务器,影响其他命令的执行。因此,我们应当慎用全量操作命令。如果我们需要获取大量的数据,可以使用SCAN、SSCAN等命令,这些命令可以分批次获取数据,避免阻塞服务器。
下面是一位大牛整理的规范,我做了一些说明。
key 的长度尽量短:这个是因为 Redis 中的 key 是利用字典数据结构存储的,key 的大小会直接影响 Redis 的内存使用量,过长的 key 会占用更多的内存空间。
避免 bigkey:bigkey 是指大小超过 10kb 或者列表、集合、有序集合、哈希元素个数超过 5000 的 key。Redis 是单线程的,处理 bigkey 需要消耗很多 CPU 时间,会导致其他命令的阻塞。
4.0+版本建议开启 lazy-free:对内存回收策略进行优化,当删除大量数据时,Redis 会将内存回收操作放在后台线程中进行,避免主线程被阻塞。
把 Redis 当作缓存使用,设置过期时间:这样可以防止内存被大量数据占满,导致服务无法正常使用。
不使用复杂度过高的命令:这些命令的时间复杂度往往比较高,会消耗大量的 CPU 时间,影响 Redis 的性能。
查询数据尽量不一次性查询全量,写入大量数据建议分多批写入:这样可以防止一次性操作大量数据导致的 Redis 阻塞。
批量操作建议 MGET/MSET 替代 GET/SET,HMGET/HMSET 替代 HGET/HSET:批量操作可以减少网络延迟,提高 Redis 性能。
禁止使用 KEYS/FLUSHALL/FLUSHDB 命令:这些命令会阻塞 Redis,影响服务的性能。
避免集中过期 key:如果大量的 key 集中在某一时间过期,会导致 Redis 阻塞。
根据业务场景选择合适的淘汰策略:比如 volatile-lru(从设置了过期时间的 key 中选择最少使用的 key 淘汰)等。
按业务线部署实例:避免多个业务线混合部署,在出问题时,影响范围更小。
保证机器有足够的 CPU、内存、带宽、磁盘资源:这些都是 Redis 正常运行的基础。
建议部署主从集群,并分布在不同机器上:保证数据的稳定性,同时减少单点故障风险。
主从节点所部署的机器各自独立,尽量避免交叉部署:以免一个节点出问题,影响到其他节点。
推荐部署哨兵集群实现故障自动切换:自动切换主从,保证系统的高可用。
提前做好容量规划,防止主从全量同步时,实例使用内存突增导致内存不足:避免因内存不足导致的服务故障。
做好机器 CPU、内存、带宽、磁盘监控:及时发现问题,防止服务故障。
实例设置最大连接数:防止过多客户端连接导致实例负载过高,影响服务性能。
单个实例内存建议控制在 10G 以下:大实例在主从全量同步、备份时有阻塞风险。
设置合理的 slowlog 阈值,并对其进行监控:slowlog 过多可能意味着存在性能问题。
设置合理的 repl-backlog,降低主从全量同步的概率:主从全量同步往往会消耗大量资源,影响服务性能。
设置合理的 slave client-output-buffer-limit,避免主从复制中断情况发生。
推荐在从节点上备份,不影响主节点性能:备份任务往往会消耗大量资源,影响服务性能。
不开启 AOF 或开启 AOF 配置为每秒刷盘,避免磁盘 IO 拖慢 Redis 性能:AOF 会增加磁盘 IO,影响服务性能。
调整 maxmemory 时,注意主从节点的调整顺序,顺序错误会导致主从数据不一致:保证数据的一致性。
对实例部署监控,采集 INFO 信息时采用长连接,避免频繁的短连接:频繁的网络连接会消耗大量资源,影响服务性能。
做好实例运行时监控,重点关注 expired_keys、evicted_keys、latest_fork_usec:这些指标反映了 Redis 的运行状况,短时突增可能会有阻塞风险。
扫描线上实例时,记得设置休眠时间,避免过高 OPS 产生性能抖动:扫描操作往往会消耗大量资源,影响服务性能。