Redis - 基本操作

背景信息

概念

Redis在内存中存储数据的中间件,用于作为数据库,数据缓存。主要应用于分布式系统中(因为单机系统可以将数据通过变量保存到内存中,而分布式系统中多个服务器无法直接共享内存中的变量,只能通过网络来同步数据)

特点

  1. 使用键值对格式在内存中存储数据,属于非关系型数据库,Key都是String,Value可以是字符串,哈希表,列表,集合这样的数据结构
  2. 可编程性:可以通过简单的交互式命令进行操作,也可使用一些脚本的方式进行批量操作
  3. 可扩展性:Redis提供了一组API,可以在Redis原有功能的基础上使用(C、C++、RUST)编写扩展。比如让Redis支持更多的数据结构,以及支持更多的命令
  4. 持久化:Redis不仅会将数据保存在内存中,还会在硬盘中保存一份备份数据。正常使用时,访问的都是内存中的数据,当服务器重启,内存中的数据丢失时。就会将硬盘中的数据加载到内存中,使Redis恢复到服务器重启之前的状态
  5. 支持集群/主从复制:可以引入多台主机,部署多个Redis节点,每个Redis节点存储数据的一部分。Redis自身也支持主从复制,从节点相当于主节点的备份
  6. 在网络角度上,使用了IO多路复用的方式
  7. 使用单线程模型,减少了不必要的线程之间的竞争开销(因为Redis的核心功能都是一些简单的逻辑,主要是操作内存中的数据结构,不会吃很多CPU,所以使用单线程结构)

使用场景

  1. 作为数据库:在广告搜索的业务中,由于对性能的高要求,所以会将需要检索的数据保存到Redis中
  2. 作为Cache:将关系型数据库中经常访问的数据,保存到Redis中。这些经常访问的数据,足以满足绝大部分的业务场景
  3. 会话保存:将用户登录信息保存到Redis中。解决在分布式应用服务器的情况下,同一用户的不同请求路由到不同服务器时,造成的登录信息不同步的问题

注意

​ redis的快,是相对于MySQL这种关系型数据库的。如果和直接在内存中操作变量相比,那就是慢了,因为存在网络上的开销。也就是说,当目前的业务采用分布式架构,或者希望内存中的数据可以持久化保存,才推荐使用Redis。盲目使用反倒会适得其反

Redis客户端

Redis是一个客户端-服务器结构的程序(服务器本体负责存储和管理数据,用于使用客户端通过网络来访问和操作服务器中的数据)。客户端可以有一个,也可存在多个

  1. 自带的命令行客户端

    复制代码
    redis -cli
  2. 图形化界面的客户端

  3. 基于redis的api自行开发客户端

通用命令

可以搭配任意一个数据结构来使用的命令

https://redis.io/docs/latest/commands

Keys

获取匹配的key,返回符合要求的key O(N)

redis 复制代码
Keys pattern

pattern

  1. ? 匹配任意一个字符
  2. * 匹配0个或多个任意字符
  3. abcd 只匹配a b c d其中一个
  4. \^e 排除e的任意一个
  5. a-c 只能匹配a b c

exists

判断某个key是否存在,返回key的个数 O(1)

复制代码
exists key [key...]

del

删除指定的key ,返回删除key的个数 O(1)

复制代码
del key [key...]

expire

为指定的key添加秒级的过期时间 返回1(成功) 0(失败) O(1)

复制代码
expire key seconds

ttl

复制代码
ttl key

获取指定key的过期时间,秒级。返回剩余过期时间,-1(没有过期时间),-2(表示Key不存在)

pexpire/pttl

使用毫秒为单位,具体使用方法与expire和ttl相同

定时器

在某个时间达到之后,执行指定任务

  1. 基于优先级队列/堆实现:队首元素为过期时间最早的元素,为定时器分配一个线程,让这个线程根据队首元素的过期时间来设置线程等待,避免频繁检查队首元素而造成CPU忙等。当新插入一个元素时,唤醒定时器进程,继续维护优先级队列

  2. 基于时间轮实现:将时间划分为一个个的小段,具体大小根据实际需求来确定。每一小段时间挂着一段链表,表示要处理的事件

    每隔100ms,时间轮转动一次,开始处理下一个时间段的事件

key的过期策略

一个redis中存在多个key,这些key大部分存在过期时间,此时redis就需要通过某种方法来知道哪些key已经过期

不能直接遍历所有的key,因为这样会消耗大量时间;而且redis是单线程程序,在这段时间,只能进行遍历,无法对外提供服务。

  1. 定期删除:每次抽取一部分,验证过期时间,保证这个抽取的过程足够快
  2. 惰性删除:当某个key已经到了过期时间,暂时不删除。当这个key再次被访问时,进行删除,并返回nil

type

返回key对应的value的类型 O(1)

复制代码
type key

数据类型与编码方式

数据类型

redis所有的key都是字符串,只有value的类型存在差别,这里讨论的也是value的类型

string

​ redis 的string类型就是直接按照二进制数据的方式进行存储,不会做任何的编码转化,存进去是什么编码,取出来就是什么编码。可以存储文本数据、整数、字符串、JSON、二进制数据(图片,视频)等等

set/get

  1. 不加nx/xx:key不存在则创建,存在则更新value
  2. 当更新value的值时:value的数据类型可能发生改变,原有的ttl实现
  3. 可以使用setnx、setex、psetex代替

mset/mget

一次操作多组键值对

incr/incrby/decr/decrby/incrbyfloat

  1. incr/incrby/decr/decrby:对应的n必须为整数
  2. incrbyfloat:对应的n可以为浮点数(redis内部使用string类型保存浮点数)
  3. 当操作的key不存在时,会先创建key - 0的键值对,然后对其进行操作

append

在key对应的value后面追加字符串,如果key不存在,则相当于set语句

getrange/setrange

  1. start/end允许传负数,表示倒数第几个元素
  2. setrange对于不存在的key也可以操作,会将offset前面的值都设为0X00

strlen

返回字符串的长度,单位是字节。对应的value类型不对,报错

hash

  1. hset/hget/hexists/hdel

  2. hkeys/hvals

  3. hgetall/hmget

  4. hlen/hsetnx/hincrby/hincrbyfloat

List

相当于数组或者顺序表,列表中的数据是有序的(将元素顺序调换,得到的新的List与之前的List不等价),可重复的

阻塞操作

  1. 如果队列为空,则阻塞一段时间,在这段时间redis可以执行其他命令。期间如果有元素插入,则立刻返回。如果没有,则在阻塞时间之后,返回nil
  2. 时间单位是秒,redis6之前,只能设置整数,redis6以及之后可以设置小数
  3. 如果命令中设置了多个键,那么会从左往右进行遍历,一旦有一个键可以弹出元素,命令立刻返回
  4. 如果多个客户端同时对一个键进行操作,那么最先执行该命令的客户端会得到弹出的元素

set

把一些有关联的数据,放到一起,作为集合。集合中元素是无序的,且不允许重复。一个集合中最多可以存储2^32-1个元素。元素只能是字符串类型

普通命令

集合操作:集合的交集、并集、差集

zset

有序集合,且元素不能重复。为每个元素引入一个score属性,存储的是浮点数。依据元素对应的score进行排序

  1. inf 可以表示无穷大,-inf表示无穷小
  2. 分数相同则按照字典序排序
  3. zpopmax 底层调用的通用删除函数(传下标),所以时间复杂度为O(log(N)*M)

阻塞操作

  1. 集合为空,则发生阻塞。若指定多个key,则删除第一个非空key的元素后,直接返回

集合操作

编码方式

value的各个数据类型内部实现不一定是标准的哈希,列表...。redis 会对其实现进行优化

  1. string
    1. raw:最基本的字符串编码方式,底层就是一个字符数组
    2. int:value为一个整数时,redis直接使用int来保存
    3. embstr:对短字符串进行的优化
  2. hash
    1. hashtable:最基本的哈希表
    2. ziplist压缩列表:当哈希表的元素比较少时,会使用列表存储,以节省空间
  3. list
    1. linkedlist:链表
    2. ziplist压缩列表:元素个数少时,使用压缩列表实现
    3. quicklist:从redis3.2开始,使用quicklist代替以上两种。quicklist是一个链表,每个元素是一个ziplist
  4. set
    1. hashtable:哈希表
    2. intset:集合里存放的都是整数
  5. zset
    1. skiplist跳表:每个节点上有多个指针域,巧妙搭配这些指针域的指向,就可以达到从跳表上查询元素的时间复杂度是O(logN)
    2. ziplist压缩列表

查看key对应的value的数据类型:

复制代码
object encoding key

使用场景

String

  1. 作为缓存:先查redis,如果存在则直接返回,不存在则查数据库,然后写入redis并返回。为防止redis内部的数据过多,可以设置过期时间

  2. 计数器:视频网站的播放次数统计就可以使用redis实现,每播放一次视频redis对应的key自增一

  3. 共享会话:使用redis同一管理session会话,以解决同一用户请求被路由到不同服务器时,session会话无法统一的问题

  4. 手机验证码:为验证码设置过期时间,或者设置单位时间内验证码的请求次数

hash

  1. 保存关系型数据库的信息

list

  1. 作为数组这样的结构,用来存储多个元素。比如,每个班级作为一个列表,列表中存储学生id来实现学生和班级的绑定

  2. 作为阻塞消息队列模型,实现类似"轮询"的效果

  3. 分频道阻塞消息队列模型,一个频道只有一个消费者能够抢到元素

  4. 模拟栈或队列

set

  1. 使用set保存用户的标签,计算共同好友等集合操作

zset

  1. 因为zset是天生有序并且数据唯一的。所以可以作为排行榜,分数实时变化,排名也可以实时变化

渐进式遍历

用于解决Keys pattern 命令导致的阻塞问题,渐进式遍历,可以指定每次遍历的key的个数

  1. match:匹配语法与keys命令的语法相同
  2. count:用于指定每次遍历的key个数。redis不一定完全按照count数去遍历,但是是一个邻近的值
  3. type:获取指定value类型的Key

Redis的单线程模型

只使用一个线程去处理所有命令请求(不包括网络请求,网络请求仍使用多线程进行处理)。所有的客户端请求都串行执行,不用考虑线程安全问题

为什么效率高

这里的效率高是与关系型数据库比较

  1. redis访问内存,且核心功能简单,只需保存对应的key和value或者根据key取出对应的value。没有复杂查询

  2. redis采取单线程模型,避免一些不必要的线程竞争开销。因为redis每个基本操作都是短平快的,即使使用多线程提升也不大,反而会增加额外的开销

  3. 网络IO多路复用:对于redis来说,同一时刻,只有少数的socket是活跃的。所以不需要为每个socket都安排一个线程来处理请求,可以使用一个线程来处理多个socket,这就是IO多路复用

    对于TCP协议,服务器每次要访问一个客户端,都需要给这个客户端安排一个socket。如果是网络游戏,或者直播平台这类频繁与服务器交互的场景,服务器会给每个socket安排单独的线程

数据库管理

redis默认提供了16个database,每个database之间相互隔离。默认使用0号database

redis客户端

Java客户端

创建maven项目,添加Jedis依赖

java 复制代码
JedisPool jedisPool=new JedisPool("tcp://jedis服务器地址");
try(Jedis jedis=jedisPool.getResource()){
		//在这里使用jedis敲redis代码
}

Spring客户端

配置文件:

yaml 复制代码
spring:
  data:
    redis:
      host: 127.0.0.1
      port: 8888

注入StringRedisTemplate:

java 复制代码
@Autowired
private StringRedisTemplate redisTemplate;
//执行某些数据类型特有的命令,Spring对Redis命令作了进一步封装
//要先获取对应的对象,然后执行操作逻辑
 redisTemplate.opsForValue().set("key1","1");
//执行数据库命令以及通用命令
redisTemplate.execute((RedisConnection connection)->{
            connection.serverCommands().flushAll();
            return null;
      });
相关推荐
青山木2 小时前
Redis 高可用的最后一公里:Cluster 分片、Gossip 与故障转移全流程
数据库·redis·后端·缓存
蓝胖的四次元口袋2 小时前
JavaString知识梳理
数据库·oracle
csdn_aspnet3 小时前
mysql 使用逗号拼接一列数据
数据库·mysql·group_concat
大师兄66684 小时前
HarmonyOS7 数据持久化 relationalStore:数据库实战
数据库·arkui·实战案例·harmonyos7
TDengine (老段)4 小时前
TDengine SMA 索引 — 块级/文件级统计索引
android·大数据·服务器·数据库·人工智能·时序数据库·tdengine
冰茶丿4 小时前
状态机在嵌入式系统中的应用:不只是switch-case这么简单
数据库·嵌入式硬件·mongodb
AIHR数智引擎4 小时前
15%对撞40%:WEF白皮书里的AI组织隐忧
数据库·人工智能·经验分享·职场和发展·aihr
记忆停留w4 小时前
Celery+Redis 分布式异步任务队列工程落地业务逻辑
大数据·人工智能·redis·分布式·缓存·架构·wpf
吴声子夜歌4 小时前
Reids 6.x——集群环境部署
java·redis