0%

前言

本文介绍 Redis 的五种数据类型,并结合 redis-cli 交互式命令行,以及 Node.js 应用演示如何与 Redis 的五种数据类型进行交互,在文章最后还有关于过期时间设置的介绍。相关代码已上传至 GitHub

  • Node.js 应用使用 Koa2 框架,与 Redis 的操作都使用 Restful API 的形式展现,使用 Postman 测试接口;
  • 使用的依赖为ioredis,执行 npm i ioredis 安装依赖。之前有尝试 noderedis,但是在服务器上部署运行有连接不上 Redis 的 Bug 出现。

数据类型

Redis 通常被称为数据结构服务器,因为它提供了五种数据类型用于数据存储,包括 String 字符串、Hash 哈希、List 列表、Set 集合、Zset 有序集合

String

Redis String 是 Redis 最基本的类型,用于保存字符串类型的 Key-Value 键值对。

1
2
3
4
5
6
127.0.0.1:6379> SET string:s1 hello
OK
127.0.0.1:6379> GET string:s1
"hello"
127.0.0.1:6379> DEL string:s1
(integer) 1

Node.js 应用中的 contollers/string.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const router = require('koa-router')()
const redis = require('../config/redis')

const setString = async (ctx) => {
ctx.body = await redis.set('string:s1', 'hello world')
}

const getString = async (ctx) => {
ctx.body = await redis.get('string:s1')
}

const delString = async (ctx) => {
ctx.body = await redis.del('string:s1')
}

router.post('/string', setString)
router.get('/string', getString)
router.delete('/string', delString)

module.exports = router
阅读全文 »

Preface

Redis 不仅可以基于内存进行高速的数据读写,还提供了两种持久化方式,即 RDB(or Redis Database) 和 AOF(or Append Only File),本文主要介绍这两种持久化方式的原理及区别

RDB

RDB 持久化可以在指定的时间间隔内生成数据集的快照(snapshot)

  • 我们可以在配置文件/etc/redis/redis.conf中查看 RDB 持久化的配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    ################################ SNAPSHOTTING  ################################           
    #
    # Save the DB on disk:
    #
    # save <seconds> <changes>
    #
    # Will save the DB if both the given number of seconds and the given
    # number of write operations against the DB occurred.
    #
    # In the example below the behaviour will be to save:
    # after 900 sec (15 min) if at least 1 key changed
    # after 300 sec (5 min) if at least 10 keys changed
    # after 60 sec if at least 10000 keys changed

    save 900 1
    save 300 10
    save 60 10000

    格式如下save <seconds> <changes>,比如save 900 1就表示 900秒内如果有超过一个键被修改就自动保存一次数据集

  • 另外两个重要信息就是快照写入的文件名以及文件夹路径

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # The filename where to dump the DB
    dbfilename dump.rdb

    # The working directory.
    #
    # The DB will be written inside this directory, with the filename specified
    # above using the 'dbfilename' configuration directive.
    #
    # The Append Only File will also be created inside this directory.
    #
    # Note that you must specify a directory here, not a file name.
    dir /var/lib/redis

    可以看到快照的文件名是dump.rdb,保存路径/var/lib/redisRDB 和 AOF 的持久化文件都保存在这个目录下

  • 另外也可以在redis-cli中查看配置

    1
    2
    3
    127.0.0.1:6379> CONFIG GET save
    1) "save"
    2) "900 1 300 10 60 10000"

    可以通过调用SAVEBGSAVE (background save),手动进行数据集保存

    1
    2
    127.0.0.1:6379> SAVE
    OK

RDB 的特性

  • RDB 是一个非常紧凑的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于备份

  • RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作

  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快

  • 如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。 虽然你可以通过设置来控制保存 RDB 文件的频率。但是,因为 RDB 文件需要保存整个数据集的状态,所以它并不是一个轻松的操作。因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下,一旦发生故障停机,你就可能会丢失好几分钟的数据

  • 每次保存 RDB 的时候,Redis 都要fork出一个子进程,并由子进程来进行实际的持久化工作。在数据集比较庞大时,fork可能会非常耗时,造成服务器在某某毫秒内停止处理客户端;如果数据集非常巨大,并且 CPU 时间非常紧张的话,甚至可能会停止长达整整一秒。虽然 AOF 重写也需要进行fork,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失

AOF

AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被 追加(append) 到文件的末尾。 Redis 还可以在后台对 AOF 文件进行 重写(rewrite) ,使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小

  • 在配置文件中查看 AOF 设置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    ############################## APPEND ONLY MODE ###############################

    # By default Redis asynchronously dumps the dataset on disk. This mode is
    # good enough in many applications, but an issue with the Redis process or
    # a power outage may result into a few minutes of writes lost (depending on
    # the configured save points).
    #
    # The Append Only File is an alternative persistence mode that provides
    # much better durability. For instance using the default data fsync policy
    # (see later in the config file) Redis can lose just one second of writes in a
    # dramatic event like a server power outage, or a single write if something
    # wrong with the Redis process itself happens, but the operating system is
    # still running correctly.
    #
    # AOF and RDB persistence can be enabled at the same time without problems.
    # If the AOF is enabled on startup Redis will load the AOF, that is the file
    # with the better durability guarantees.

    appendonly no

    # The name of the append only file (default: "appendonly.aof")

    appendfilename "appendonly.aof"

    # The fsync() call tells the Operating System to actually write data on disk
    # instead of waiting for more data in the output buffer. Some OS will really flush
    # data on disk, some other OS will just try to do it ASAP.
    #
    # Redis supports three different modes:
    #
    # no: don't fsync, just let the OS flush the data when it wants. Faster.
    # always: fsync after every write to the append only log. Slow, Safest.
    # everysec: fsync only one time every second. Compromise.
    #
    # The default is "everysec", as that's usually the right compromise between
    # speed and data safety. It's up to you to understand if you can relax this to
    # "no" that will let the operating system flush the output buffer when
    # it wants, for better performances (but if you can live with the idea of
    # some data loss consider the default persistence mode that's snapshotting),
    # or on the contrary, use "always" that's very slow but a bit safer than
    # everysec.
    #
    # If unsure, use "everysec".

    # appendfsync always
    appendfsync everysec
    # appendfsync no
  • 默认appendonly no,需要改为yes开启 AOF 功能,修改后重启redis-server,发现/var/lib/redis多了个appendonly.aof文件,即以 Redis 协议格式保存的一条条命令

    1
    2
    $ ls /var/lib/redis
    appendonly.aof dump.rdb
  • 我们打开redis-cli执行一些写操作

    1
    2
    3
    4
    5
    6
    7
    127.0.0.1:6379> set foo "foooo"
    OK
    127.0.0.1:6379> set bar "barrr"
    OK
    127.0.0.1:6379> keys *
    1) "foo"
    2) "bar"

    查看appendonly.aof,文件可读性很强,*3表示命令由 3 个短语构成,$3表示短语长度为 3,第一句SELECT 0表示选中第一个数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    *2
    $6
    SELECT
    $1
    0
    *3
    $3
    set
    $3
    foo
    $5
    foooo
    *3
    $3
    set
    $3
    bar
    $5
    barrr
  • 我们将上述文件 13 ~ 19 行删除,重启redis-server,发现第二个键已被我们人工删除。

    1
    2
    127.0.0.1:6379> keys *
    1) "foo"
阅读全文 »

Preface

Redis 是一个开源的使用 ANSI C 语言编写的高性能 Key-Value 数据库,是一个可基于内存亦可持久化的日志型NoSQL 数据库。它将数据保存在内存中,在项目中可用于保存编码表、Token 等少量数据存储,高速读写访问的场景

Installing

  • 我的服务器系统为 Ubuntu 16.04 ,直接使用 apt 安装。当然也可以在官网下载压缩包解压安装

    1
    sudo apt install -y redis-server
  • Redis 的命令主要有两个,一个是服务端命令redis-server,用于启动 Redis 。还有个客户端命令redis-cli,可用于访问 Redis

    1
    2
    redis-server -v    // 查看版本
    redis-server -h // 查看帮助
  • 安装完成后 redis-server 已经启动了,默认端口号 6379

    1
    2
    $ ps -ef | grep redis
    redis 14689 1 0 10:43 ? 00:00:00 /usr/bin/redis-server 127.0.0.1:6379
  • 注意到 Redis 默认启动时只允许本机(即 127.0.0.1)访问,如果要允许其他主机访问或使用 GUI 图形化管理工具,可以修改配置文件/etc/redis/redis.conf

    1
    bind 127.0.0.1 => bind 0.0.0.0

    如果只需某几个 IP 访问 Redis 服务器,可以修改为对应 IP 以增强安全性

  • 修改完成后重启 Redis

    1
    service redis-server restart

    使用service redis-server status命令可以查看 Redis 状态

redis-cli

redis-cli是客户端程序,可以使用一些命令操作 Redis 数据库,具体命令参考 Redis 官网,或者 Redis 命令参考

  • 直接输入redis-cli进入客户端,设置一个键值对

    1
    2
    3
    4
    5
    $ redis-cli
    127.0.0.1:6379> set foo "bar"
    OK
    127.0.0.1:6379> get foo
    "bar"
  • redis-cli也可查看 Redis 的配置,使用config get,命令大小写不敏感,可以使用TAB补全

    1
    2
    3
    127.0.0.1:6379> CONFIG GET bind
    1) "bind"
    2) "0.0.0.0"
  • 也可以修改配置,比如我们可以设置 Redis 数据库的密码,默认是不设密码的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    127.0.0.1:6379> CONFIG SET requirepass passwd
    OK

    127.0.0.1:6379> CONFIG GET requirepass
    (error) NOAUTH Authentication required.

    127.0.0.1:6379> AUTH passwd
    OK

    127.0.0.1:6379> CONFIG GET requirepass
    1) "requirepass"
    2) "passwd"

    修改密码的格式为CONFIG SET requirepass password,修改后需要先登录认证,命令为AUTH password

GUI

Mac 下我使用的 Redis 图形化管理工具是 Redis Desktop Manager

  • 输入配置信息连接 Redis 服务器
    99B23802-7EA6-4ECE-9A1F-A63F31624FA0

  • 连接上后查看我们刚才设置的键值对
    92EF26D6-3278-43C7-BB81-C34D0D6E6DC7

阅读全文 »

前言

我的上一篇博客Java 泛型之类型擦除只是对于 Java 泛型和类型擦除做了一个简要的介绍,本文主要是针对 Java 泛型做一些更深入的补充说明。

泛型的引入

在没有泛型之前,我们如果想向一个 Box 类中装入一个不知道类型的对象,只能使用 Object 这个顶级父类来操作盒子中的对象。

1
2
3
4
5
6
public class Box {
private Object object;

public void set(Object object) { this.object = object; }
public Object get() { return object; }
}

而引入了泛型之后,代码修改如下:

1
2
3
4
5
6
7
8
9
10
11
/**
* Generic version of the Box class.
* @param <T> the type of the value being boxed
*/
public class Box<T> {
// T stands for "Type"
private T t;

public void set(T t) { this.t = t; }
public T get() { return t; }
}

我们定义了一个装有 T 类型的 Box 类。在使用时可以随心所欲,既可创建一个专门装 String 的 Box ,也可创建一个装 Integer 的 Box。

1
2
Box<String> bs = new Box<String>();
Box<Integer> bi = new Box<Integer>();

甚至于我们在定义 Box 时可以更加苛刻,只允许这个盒子装入一个数字类型的对象。

阅读全文 »

前言

Java SE5 的重大变化之一就是加入了泛型,实现了参数化类型,使得代码可以适用于多种类型。但由于兼容旧版本的考虑,不得不作出了一些牺牲,比如擦除,使得泛型使用受到一定局限。本文简要介绍了 Java 泛型以及类型擦除的概念和使用

泛型

Java泛型 (Generics) 是 JDK5 引入的新特性,允许在定义类和接口的时候使用类型参数 (Type Parameter)。声明的类型参数在使用时用具体的类型来替换。泛型最主要的应用是在 JDK5 中的新集合类框架中。

通过泛型,大大提升了集合类的可复用性,你可以定义一个装字符串的 ArrayList,也可以定义一个装整型的 ArrayList

1
2
3
4
5
6
ArrayList<Stringals = new ArrayList<>();
als.add("aaa");

ArrayList<Integerali = new ArrayList<>();
ali.add(1);
ali.add("aaa"); // error

自定义泛型类可以参考我的上一篇博客,Java泛型之LinkedStack

使用泛型需要注意

泛型的引入可以解决之前的集合类框架在使用过程中经常出现的运行时类型错误,因为编译器可以在编译时就发现很多明显的错误。但无奈的是,为了保证与旧版本的兼容性,Java 泛型的实现上存在着一些不够优雅的地方。

开发人员在使用泛型的时候,很容易根据自己的直觉而犯一些错误

阅读全文 »