Redis - 基本操作

📅 2026/7/13 15:18:21
Redis - 基本操作
背景信息概念Redis在内存中存储数据的中间件用于作为数据库数据缓存。主要应用于分布式系统中因为单机系统可以将数据通过变量保存到内存中而分布式系统中多个服务器无法直接共享内存中的变量只能通过网络来同步数据特点使用键值对格式在内存中存储数据属于非关系型数据库Key都是StringValue可以是字符串哈希表列表集合这样的数据结构可编程性可以通过简单的交互式命令进行操作也可使用一些脚本的方式进行批量操作可扩展性Redis提供了一组API可以在Redis原有功能的基础上使用C、C、RUST编写扩展。比如让Redis支持更多的数据结构以及支持更多的命令持久化Redis不仅会将数据保存在内存中还会在硬盘中保存一份备份数据。正常使用时访问的都是内存中的数据当服务器重启内存中的数据丢失时。就会将硬盘中的数据加载到内存中使Redis恢复到服务器重启之前的状态支持集群/主从复制可以引入多台主机部署多个Redis节点每个Redis节点存储数据的一部分。Redis自身也支持主从复制从节点相当于主节点的备份在网络角度上使用了IO多路复用的方式使用单线程模型减少了不必要的线程之间的竞争开销因为Redis的核心功能都是一些简单的逻辑主要是操作内存中的数据结构不会吃很多CPU所以使用单线程结构使用场景作为数据库在广告搜索的业务中由于对性能的高要求所以会将需要检索的数据保存到Redis中作为Cache将关系型数据库中经常访问的数据保存到Redis中。这些经常访问的数据足以满足绝大部分的业务场景会话保存将用户登录信息保存到Redis中。解决在分布式应用服务器的情况下同一用户的不同请求路由到不同服务器时造成的登录信息不同步的问题注意​ redis的快是相对于MySQL这种关系型数据库的。如果和直接在内存中操作变量相比那就是慢了因为存在网络上的开销。也就是说当目前的业务采用分布式架构或者希望内存中的数据可以持久化保存才推荐使用Redis。盲目使用反倒会适得其反Redis客户端Redis是一个客户端-服务器结构的程序服务器本体负责存储和管理数据用于使用客户端通过网络来访问和操作服务器中的数据。客户端可以有一个也可存在多个自带的命令行客户端redis -cli图形化界面的客户端基于redis的api自行开发客户端通用命令可以搭配任意一个数据结构来使用的命令https://redis.io/docs/latest/commandsKeys获取匹配的key返回符合要求的key O(N)Keys patternpattern? 匹配任意一个字符* 匹配0个或多个任意字符[abcd] 只匹配a b c d其中一个[^e] 排除e的任意一个[a-c] 只能匹配a b cexists判断某个key是否存在返回key的个数 O(1)exists key [key...]del删除指定的key 返回删除key的个数 O(1)del key [key...]expire为指定的key添加秒级的过期时间 返回1(成功) 0(失败) O(1)expire key secondsttlttl key获取指定key的过期时间秒级。返回剩余过期时间-1(没有过期时间)-2(表示Key不存在)pexpire/pttl使用毫秒为单位具体使用方法与expire和ttl相同定时器在某个时间达到之后执行指定任务基于优先级队列/堆实现队首元素为过期时间最早的元素为定时器分配一个线程让这个线程根据队首元素的过期时间来设置线程等待避免频繁检查队首元素而造成CPU忙等。当新插入一个元素时唤醒定时器进程继续维护优先级队列基于时间轮实现将时间划分为一个个的小段具体大小根据实际需求来确定。每一小段时间挂着一段链表表示要处理的事件每隔100ms时间轮转动一次开始处理下一个时间段的事件key的过期策略一个redis中存在多个key这些key大部分存在过期时间此时redis就需要通过某种方法来知道哪些key已经过期不能直接遍历所有的key因为这样会消耗大量时间而且redis是单线程程序在这段时间只能进行遍历无法对外提供服务。定期删除每次抽取一部分验证过期时间保证这个抽取的过程足够快惰性删除当某个key已经到了过期时间暂时不删除。当这个key再次被访问时进行删除并返回niltype返回key对应的value的类型 O(1)type key数据类型与编码方式数据类型redis所有的key都是字符串只有value的类型存在差别这里讨论的也是value的类型string​ redis 的string类型就是直接按照二进制数据的方式进行存储不会做任何的编码转化存进去是什么编码取出来就是什么编码。可以存储文本数据、整数、字符串、JSON、二进制数据图片视频等等set/get不加nx/xxkey不存在则创建存在则更新value当更新value的值时value的数据类型可能发生改变原有的ttl实现可以使用setnx、setex、psetex代替mset/mget一次操作多组键值对incr/incrby/decr/decrby/incrbyfloatincr/incrby/decr/decrby对应的n必须为整数incrbyfloat对应的n可以为浮点数redis内部使用string类型保存浮点数当操作的key不存在时会先创建key - 0的键值对然后对其进行操作append在key对应的value后面追加字符串如果key不存在则相当于set语句getrange/setrangestart/end允许传负数表示倒数第几个元素setrange对于不存在的key也可以操作会将offset前面的值都设为0X00strlen返回字符串的长度单位是字节。对应的value类型不对报错hashhset/hget/hexists/hdelhkeys/hvalshgetall/hmgethlen/hsetnx/hincrby/hincrbyfloatList相当于数组或者顺序表列表中的数据是有序的将元素顺序调换得到的新的List与之前的List不等价可重复的阻塞操作如果队列为空则阻塞一段时间在这段时间redis可以执行其他命令。期间如果有元素插入则立刻返回。如果没有则在阻塞时间之后返回nil时间单位是秒redis6之前只能设置整数redis6以及之后可以设置小数如果命令中设置了多个键那么会从左往右进行遍历一旦有一个键可以弹出元素命令立刻返回如果多个客户端同时对一个键进行操作那么最先执行该命令的客户端会得到弹出的元素set把一些有关联的数据放到一起作为集合。集合中元素是无序的且不允许重复。一个集合中最多可以存储2^32-1个元素。元素只能是字符串类型普通命令集合操作集合的交集、并集、差集zset有序集合且元素不能重复。为每个元素引入一个score属性存储的是浮点数。依据元素对应的score进行排序inf 可以表示无穷大-inf表示无穷小分数相同则按照字典序排序zpopmax 底层调用的通用删除函数传下标所以时间复杂度为O(log(N)*M)阻塞操作集合为空则发生阻塞。若指定多个key则删除第一个非空key的元素后直接返回集合操作编码方式value的各个数据类型内部实现不一定是标准的哈希列表…。redis 会对其实现进行优化stringraw最基本的字符串编码方式底层就是一个字符数组intvalue为一个整数时redis直接使用int来保存embstr对短字符串进行的优化hashhashtable最基本的哈希表ziplist压缩列表当哈希表的元素比较少时会使用列表存储以节省空间listlinkedlist链表ziplist压缩列表元素个数少时使用压缩列表实现quicklist从redis3.2开始使用quicklist代替以上两种。quicklist是一个链表每个元素是一个ziplistsethashtable哈希表intset集合里存放的都是整数zsetskiplist跳表每个节点上有多个指针域巧妙搭配这些指针域的指向就可以达到从跳表上查询元素的时间复杂度是O(logN)ziplist压缩列表查看key对应的value的数据类型object encoding key使用场景String作为缓存先查redis如果存在则直接返回不存在则查数据库然后写入redis并返回。为防止redis内部的数据过多可以设置过期时间计数器视频网站的播放次数统计就可以使用redis实现每播放一次视频redis对应的key自增一共享会话使用redis同一管理session会话以解决同一用户请求被路由到不同服务器时session会话无法统一的问题手机验证码为验证码设置过期时间或者设置单位时间内验证码的请求次数hash保存关系型数据库的信息list作为数组这样的结构用来存储多个元素。比如每个班级作为一个列表列表中存储学生id来实现学生和班级的绑定作为阻塞消息队列模型实现类似“轮询”的效果分频道阻塞消息队列模型一个频道只有一个消费者能够抢到元素模拟栈或队列set使用set保存用户的标签计算共同好友等集合操作zset因为zset是天生有序并且数据唯一的。所以可以作为排行榜分数实时变化排名也可以实时变化渐进式遍历用于解决Keys pattern 命令导致的阻塞问题渐进式遍历可以指定每次遍历的key的个数match匹配语法与keys命令的语法相同count用于指定每次遍历的key个数。redis不一定完全按照count数去遍历但是是一个邻近的值type获取指定value类型的KeyRedis的单线程模型只使用一个线程去处理所有命令请求不包括网络请求网络请求仍使用多线程进行处理。所有的客户端请求都串行执行不用考虑线程安全问题为什么效率高这里的效率高是与关系型数据库比较redis访问内存且核心功能简单只需保存对应的key和value或者根据key取出对应的value。没有复杂查询redis采取单线程模型避免一些不必要的线程竞争开销。因为redis每个基本操作都是短平快的即使使用多线程提升也不大反而会增加额外的开销网络IO多路复用对于redis来说同一时刻只有少数的socket是活跃的。所以不需要为每个socket都安排一个线程来处理请求可以使用一个线程来处理多个socket这就是IO多路复用对于TCP协议服务器每次要访问一个客户端都需要给这个客户端安排一个socket。如果是网络游戏或者直播平台这类频繁与服务器交互的场景服务器会给每个socket安排单独的线程数据库管理redis默认提供了16个database每个database之间相互隔离。默认使用0号databaseredis客户端Java客户端创建maven项目添加Jedis依赖JedisPooljedisPoolnewJedisPool(tcp://jedis服务器地址);try(JedisjedisjedisPool.getResource()){//在这里使用jedis敲redis代码}Spring客户端配置文件spring:data:redis:host:127.0.0.1port:8888注入StringRedisTemplateAutowiredprivateStringRedisTemplateredisTemplate;//执行某些数据类型特有的命令Spring对Redis命令作了进一步封装//要先获取对应的对象然后执行操作逻辑redisTemplate.opsForValue().set(key1,1);//执行数据库命令以及通用命令redisTemplate.execute((RedisConnectionconnection)-{connection.serverCommands().flushAll();returnnull;});