在 Node.js 中操作 Redis

news2025/1/17 16:34:17

在 Node.js 中操作 Redis

Node.js 中可以操作 Redis 的软件包推荐列表:https://redis.io/clients#nodejs。

推荐下面两个:

  • node-redis
  • ioredis

这里主要以 ioredis 为例。

ioredis 是功能强大的 Redis 客户端,已被世界上最大的在线商务公司阿里巴巴和许多其他了不起的公司所使用。

ioredis 特点:

  • 功能齐全。它支持集群,前哨,流,流水线,当然还支持Lua脚本和发布/订阅(具有二进制消息的支持)。
  • 高性能
  • 令人愉快的 API。它的异步 API 支持回调函数与 Promise
  • 命令参数和返回值的转换
  • 透明键前缀
  • Lua脚本的抽象,允许您定义自定义命令。
  • 支持二进制数据
  • 支持 TLS
  • 支持脱机队列和就绪检查
  • 支持ES6类型,例如 Map 和 Set
  • 支持GEO命令(Redis 3.2不稳定)
  • 复杂的错误处理策略
  • 支持NAT映射
  • 支持自动流水线

相关链接:

  • API 参考文档
  • 更新日志
  • 从 node_redis 迁移

基本使用

npm install ioredis
const Redis = require("ioredis");
const redis = new Redis(); // uses defaults unless given configuration object

// ioredis supports all Redis commands:
redis.set("foo", "bar"); // returns promise which resolves to string, "OK"

// the format is: redis[SOME_REDIS_COMMAND_IN_LOWERCASE](ARGUMENTS_ARE_JOINED_INTO_COMMAND_STRING)
// the js: ` redis.set("mykey", "Hello") ` is equivalent to the cli: ` redis> SET mykey "Hello" `

// ioredis supports the node.js callback style
redis.get("foo", function (err, result) {
  if (err) {
    console.error(err);
  } else {
    console.log(result); // Promise resolves to "bar"
  }
});

// Or ioredis returns a promise if the last argument isn't a function
redis.get("foo").then(function (result) {
  console.log(result); // Prints "bar"
});

// Most responses are strings, or arrays of strings
redis.zadd("sortedSet", 1, "one", 2, "dos", 4, "quatro", 3, "three");
redis.zrange("sortedSet", 0, 2, "WITHSCORES").then((res) => console.log(res)); // Promise resolves to ["one", "1", "dos", "2", "three", "3"] as if the command was ` redis> ZRANGE sortedSet 0 2 WITHSCORES `

// All arguments are passed directly to the redis server:
redis.set("key", 100, "EX", 10);

有关更多实例,可以参考这里:https://github.com/luin/ioredis/tree/master/examples。

Pipelining

如果要发送一批命令(例如> 5),则可以使用流水线将命令在内存中排队,然后将它们一次全部发送到 Redis。这样,性能提高了50%〜300%(请参阅基准测试部分)。

redis.pipeline() 创建一个 Pipeline 实例。您可以像 Redis 实例一样在其上调用任何 Redis 命令。这些命令在内存中排队,并通过调用 exec 方法刷新到 Redis:

const pipeline = redis.pipeline();
pipeline.set("foo", "bar");
pipeline.del("cc");
pipeline.exec((err, results) => {
  // `err` is always null, and `results` is an array of responses
  // corresponding to the sequence of queued commands.
  // Each response follows the format `[err, result]`.
});

// You can even chain the commands:
redis
  .pipeline()
  .set("foo", "bar")
  .del("cc")
  .exec((err, results) => {});

// `exec` also returns a Promise:
const promise = redis.pipeline().set("foo", "bar").get("foo").exec();
promise.then((result) => {
  // result === [[null, 'OK'], [null, 'bar']]
});

每个链接的命令还可以具有一个回调,该回调将在命令得到答复时被调用:

redis
  .pipeline()
  .set("foo", "bar")
  .get("foo", (err, result) => {
    // result === 'bar'
  })
  .exec((err, result) => {
    // result[1][1] === 'bar'
  });

除了将命令分别添加到管道队列之外,您还可以将命令和参数数组传递给构造函数:

redis
  .pipeline([
    ["set", "foo", "bar"],
    ["get", "foo"],
  ])
  .exec(() => {
    /* ... */
  });

#length 属性显示管道中有多少个命令:

const length = redis.pipeline().set("foo", "bar").get("foo").length;
// length === 2

事务

大多数时候,事务命令 multi&exec 与管道一起使用。因此,在调用 multi 时,默认情况下会自动创建 Pipeline 实例,因此您可以像使用管道一样使用 multi:

redis
  .multi()
  .set("foo", "bar")
  .get("foo")
  .exec((err, results) => {
    // results === [[null, 'OK'], [null, 'bar']]
  });

如果事务的命令链中存在语法错误(例如,错误的参数数量,错误的命令名称等),则不会执行任何命令,并返回错误:

redis
  .multi()
  .set("foo")
  .set("foo", "new value")
  .exec((err, results) => {
    // err:
    //  { [ReplyError: EXECABORT Transaction discarded because of previous errors.]
    //    name: 'ReplyError',
    //    message: 'EXECABORT Transaction discarded because of previous errors.',
    //    command: { name: 'exec', args: [] },
    //    previousErrors:
    //     [ { [ReplyError: ERR wrong number of arguments for 'set' command]
    //         name: 'ReplyError',
    //         message: 'ERR wrong number of arguments for \'set\' command',
    //         command: [Object] } ] }
  });

就接口而言,multi 与管道的区别在于,当为每个链接的命令指定回调时,排队状态将传递给回调,而不是命令的结果:

redis
  .multi()
  .set("foo", "bar", (err, result) => {
    // result === 'QUEUED'
  })
  .exec(/* ... */);

如果要使用不带管道的事务,请将 { pipeline: false } 传递给 multi,每个命令将立即发送到 Redis,而无需等待 exec 调用:

redis.multi({ pipeline: false });
redis.set("foo", "bar");
redis.get("foo");
redis.exec((err, result) => {
  // result === [[null, 'OK'], [null, 'bar']]
});

multi 的构造函数还接受一批命令:

redis
  .multi([
    ["set", "foo", "bar"],
    ["get", "foo"],
  ])
  .exec(() => {
    /* ... */
  });

管道支持内联事务,这意味着您可以将管道中的命令子集分组为一个事务:

redis
  .pipeline()
  .get("foo")
  .multi()
  .set("foo", "bar")
  .get("foo")
  .exec()
  .get("foo")
  .exec();

错误处理

Redis服务器返回的所有错误都是 ReplyError 的实例,可以通过 Redis 进行访问:

const Redis = require("ioredis");
const redis = new Redis();
// This command causes a reply error since the SET command requires two arguments.
redis.set("foo", (err) => {
  err instanceof Redis.ReplyError;
});

默认情况下,错误堆栈没有任何意义,因为整个堆栈都发生在 ioredis 模块本身而不是代码中。因此,要找出错误在代码中的位置并不容易。 ioredis 提供了一个选项 showFriendlyErrorStack 来解决该问题。启用 showFriendlyErrorStack 时,ioredis 将为您优化错误堆栈:

const Redis = require("ioredis");
const redis = new Redis({ showFriendlyErrorStack: true });
redis.set("foo");

Redis 集群

对于一个小型项目来说,使用一台 Redis 服务器已经非常足够了,然后现实中的项目通常需要若干台 Redis 服务器的支持:

  • 从结构上,单个 Redis 服务器会发生单点故障,同时一台服务器需要承受所有的请求负载。这就需要为数据生成多个副本并分配在不同的服务器上;
  • 从容量上,单个 Redis 服务器的内存非常容易成为存储瓶颈,所有需要进行数据分片

同时拥有多个 Redis 服务器后就会面临如何管理集群的问题,包括如何增加节点、故障恢复等。

为此,我们将依次介绍 Redis 中的复制、哨兵(sentinel)和集群的使用和原理。

复制

通过持久化功能,Redis 保证了即使在服务器重启的情况下也不会损失(或少量损失)数据。但是由于数据是存储在一台服务器上的,如果这台服务器出现硬盘故障等问题,也会导致数据丢失。为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其它服务器依然可以继续提供服务。为此,Redis 提供了复制功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其它数据库上。

配置

在复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库(slave)。

  • 主数据库可以进行读写操作
  • 当写操作导致数据变化时会自动将数据同步给从数据库
  • 而从数据库一般是只读的,并接受主数据库同步过来的数据
  • 一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库
    在这里插入图片描述

在 Redis 中使用复制功能非常容易,只需要在从数据库的配置文件中加入下面的配置即可,主数据库无需进行任何配置。

slaveof 主数据库地址 主数据库端口

(1)开启两个 Redis 服务进程

为了能够更直观的展示复制的流程,下面将实现一个最简化的复制系统。我们要在一台服务器上启动两个 Redis 实例,监听不同端口,其中一个作为主数据库,另一个作为从数据库。首先我们不加任何参数来启动一个 Redis 实例作为主数据库:

redis-server

该实例默认监听 6379 端口。然后加上 slaveof 参数启动另一个 Redis 实例作为从数据库,并让其监听 6380 端口;

redis-server --port 6380 --slaveof 127.0.0.1 6379

此时在主数据库中的任何数据变化都会自动地同步到从数据库中。

(2)查看复制信息

INFO replication

(3)测试数据同步效果

在实例 A 中写入数据,会被自动同步到实例 B 中。

(4)从数据库中的数据是只读的

默认情况下,从数据库是只读的,如果直接修改从数据库的数据会出现错误:

INFO replication

可以通过设置从数据库的配置文件中的 slave-read-only 为 no 以使从数据库可写,但是因为对从数据库的任何更改都不会同步给任何其它数据库,并且一旦主数据库中更新了对应的数据就会覆盖从数据库中的改动,所有通常的场景下不应该设置从数据库可写,以免导致被忽略的潜在应用逻辑错误。

配置多台从数据库的方法也一样,在所有的从数据库的配置文件中都加上 slaveof 参数指向同一个主数据库即可。

除了通过配置文件或命令行参数设置 slaveof 参数,还可以在运行时使用 SLAVEOF 命令修改。

Redis 设置远程连接

注意:为了保护数据安全,开放远程连接需谨慎操作。
Redis 默认是不允许远程连接的,通过下面的配置可以开启远程连接。
将 redis.conf 配置文件的 bind 和 protected-mode 修改如下:

# 绑定的端口号
bind 0.0.0.0

# 关闭保护模式
protected-mode no


# RDB 持久化
save 900 1 # 每 900 秒至少有 1 个 key 变化了,则写入快照
save 300 10 # 每 300 秒至少有 10 个 key 变化了,则写入快照
save 60 10000 # 每 60 秒至少有 10000 个 key 变化了,则写入快照
dbfilename dump.rdb # 快照保存的文件名称
dir ./ # 快照文件保存路径



# 开启 AOF 方式的持久化
appendonly yes
# 同步的文件名称
appendfilename "appendonly.aof"

除此之外还需要检查服务器防火墙是否开放了 Redis 服务占用的端口号。
修改之后重启 Redis 服务即可生效。

执行

redis-server 配置文件路径

在docker上使用到的相关命令行

# Liunx命令
cd /etc
rm -rf 6379.conf 
cp /app/redis-6.0.9/redis.conf ./6379.conf
cd redis
mv ../6379.conf ./
vim 6379.conf
#bash: vi: command not found
apt-get install vim
apt-get update
apt-get install vim
vim 6379.conf
#/6397 回车查找  按n下一个 把port改为1234
#/daemonize 回车查找 把no 改为 yes (在后台运行)
#按esc :wq回车 保存退出
#redis-server 6379.conf
#redis-cli
#select 1 切换到1号数据库

项目地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/49100.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MyBatis详解(一)

MyBatis简单介绍 【1】MyBatis是一个持久层的ORM框架【Object Relational Mapping,对象关系映射】,使用简单,学习成本较低。可以执行自己手写的SQL语句,比较灵活。但是MyBatis的自动化程度不高,移植性也不高&#xff…

数据库索引

目录 🐇今日良言:志在山顶的人,不会贪念山腰的风景。 🐉数据库索引 🐳1.概念 🐳2.使用 🐳3.在mysql中的数据结构 🐇今日良言:志在山顶的人,不会贪念山腰的风景。 &#x1f409…

Java基础

抽象类和普通类的区别抽象类不能被实例化。-- 抽象类只在分配了在栈中的引用,没有分配堆中的内存抽象类可以有构造函数,被继承时子类必须实现(调用)父类一个构造方法 — 《因为子类会继承父类的构造方法,如果父类方法中的构造函数是带有参数的…

乾元通多卡聚合路由设备海上通信应用解决方案

随着海上通信的发展,在各种应用需求的推动下,海上通信正在向着定制化的需求迈进。 海上通信通信建设目的是要构建一个战时、巡逻和应急情况下移动通信枢纽,为战时、巡逻和应急状态下提供多种通信保障手段。 乾元通智能融合通信设备可配合装…

Graalvm安装配置与springboot3.0尝鲜

Graalvm安装配置与springboot3.0尝鲜 Spring 团队一直致力于 Spring 应用程序的原生映像支持已有一段时间了。经过3 年的孵化春季原生Spring Boot 2 的实验性项目,原生支持将在 Spring Framework 6 和 Spring Boot 3 中正式发布! 安装Graalvm 由于spri…

别再问我Python打包成exe了!(终极版)

那今天我就给大家全面总结一下:Python如何打包成exe,以及如何打得足够小。 一、标准打包 目前比较常见的打包exe方法都是通过Pyinstaller来实现的,本文也将使用这种常规方法。如果对这块已经很熟悉的小伙伴,可以直接下滑到本文下…

Javaweb的AJAX及Axios框架使用(封装AJAX)

概念: AJAX(Asynchronous JavaScript And XML):异步的JS和XML。 作用: 与服务器进行数据交换 :通过AJAX可以给服务器发送请求。使用AJAX和服务器进行通信,就可以使用HTMLAJAX来替换JSP页面。异步交互 :可以在不重新加载整个页面的情况下,与服务器交互…

[附源码]SSM计算机毕业设计学生宿舍管理系统JAVA

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

永磁同步电机转子位置估算专题——正交锁相环

前言 在电机转子位置估算算法中,常常用到锁相环获取磁链信号或反电势信号中的转子位置信息,考虑到包含转子位置信息的磁链信号或反电势信号多被分解在α,β\alpha,\betaα,β轴,以下内容侧重分析适用于该工况的正交锁相环。 1、正交锁相环原…

线性代数学习笔记10-3:奇异值分解SVD(从四个子空间角度理解)

从四个子空间角度理解SVD AUmmΣmnVnnTA U_{m \times m}\Sigma_{m \times n}V_{n \times n}^TAUmm​Σmn​VnnT​ 将A\mathbf {A}A视为线性变换,并将整个Rn\mathbf R^nRn空间拆分为两部分,即A\mathbf {A}A的行空间(维数rrr)和零空…

cisco asa学习笔记

cisco asa学习笔记一、网络模拟实验中的问题调试记录1、ASA自身接口地址ping不通(从远端路由过来的主机)2、同安全级别的接口默认不能通信&#xff0c;怎么才能通信&#xff1f;3、一个asa连接3个路由器&#xff0c;互联互通解决方案<1>方案1 全局开启policy-map inspect…

C++习题2

指针调用数组元素 指针调用数组元素 int a[3][4] {{1,4,7,10},{2,5,8,11},{3,6,9,12}};int* pa[3] { a[0],a[1],a[2] };int(*pb)[4]; pb a;int** q pa;cout << *(a[2] 3)<<endl;cout << *(*(pa 2) 3)<<endl;cout << pb[2][3]<<en…

springboot + dubbo 整合Sentinel限流

一、前言 限流对一个生产环境的系统来说&#xff0c;具有重要的意义&#xff0c;限流的目的是为了保护系统中的某些核心业务资源不被瞬间的大并发流量冲垮而采取的一种措施&#xff0c;因此一个成熟的架构设计方案&#xff0c;限流也需要纳入到架构设计和规划中。 二、常用的限…

【linux】进程控制详述

进程控制一、进程创建1.1 fork 的补充1.2 写时拷贝1.3 fork 的使用场景二、进程终止2.1 退出码2.2 退出情况2.3 退出方式三、进程等待3.1 进程等待的原因3.2 进程等待的方法3.2.1 status 位图结构3.2.2 阻塞等待和非阻塞等待四、进程程序替换4.1 程序替换的原理4.2 程序替换函数…

[Spring]第五篇:AOP面向切面编程概念

AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展,往往用于实现 日志处理,权限控制,性能检测,事务控制等 AOP实现的原理就是动态代理,在有接口的情况下,使用JDK动态代理,在没有接口的情况下使用cglib动态代理 为Dao层所有的add方法添加一个性能记录功…

jupyter中配置多种虚拟环境

Microsoft Windows [版本 10.0.19044.2251] © Microsoft Corporation。保留所有权利。 C:\Users\ThinkStation>conda activate pytorch(pytorch) C:\Users\ThinkStation>conda install ipykernelCollecting package metadata (current_repodata.json): done Solvin…

CAS:956748-40-6|Biotin-PEG-Azide|Biotin-PEG-N3|生物素PEG叠氮供应

由抗生物素或链霉亲和素结合PEG衍生物化学试剂Biotin-PEG-Azide&#xff08;Biotin-PEG-N3&#xff09;&#xff0c;其中文名为生物素-聚乙二醇-叠氮&#xff0c;它所属分类为Azide PEG Biotin PEG。CAS编号为956748-40-6。 该PEG试剂的分子量均可定制&#xff0c;有&#xff…

POSIX线程(一)

目录 一、前言 二、What Is a Thread? 三、Advantages and Drawbacks of Threads 四、A First Threads Program 1、A Simple Threaded Program&#xff08;thread1.c&#xff09; 五、Simultaneous Execution 1、Simultaneous Execution of Two Threads&#xff08;thre…

Caffeine 源码、架构、原理(史上最全,10W超级字长文)

文章很长&#xff0c;而且持续更新&#xff0c;建议收藏起来&#xff0c;慢慢读&#xff01;疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 &#xff1a; 免费赠送 :《尼恩Java面试宝典》 持续更新 史上最全 面试必备 2000页 面试必备 大厂必备 涨薪必备 免费赠送 经典…

IbBBX24–IbTOE3–IbPRX17模块通过清除甘薯中的活性氧来增强甘薯对非生物胁迫耐受性

文章信息 题目&#xff1a;The IbBBX24–IbTOE3–IbPRX17 module enhances abiotic stress tolerance by scavenging reactive oxygen species in sweet potato 刊名&#xff1a;New Phytologist 作者&#xff1a;Huan Zhang&#xff0c;Shaozhen He et al. 单位&#xff1…