Java经典框架之Redis

Redis

Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。

课程内容的介绍

  1. Redis介绍与安装
  2. Redis基本数据类型介绍
  3. Redis高级应用介绍

一、Redis介绍与安装

1.NoSQL数据库

NoSQL,泛指非关系型的数据库,随着互联网的发展传统的关系型数据库面对持续增长的数据处理起来显得越来越力不从心,此时非关系型数据库应运而生。

1.1四种类型

目前大家基本认同将NoSQL数据库分为四大类:键值存储数据库文档型数据库列存储数据库图形数据库,其中每一种类型的数据库都能够解决关系型数据不能解决的问题。在实际应用中,NoSQL数据库的分类界限其实没有那么明显,往往会是多种类型的组合体。

1.2 MongoDB

MongoDB 是一个高性能,开源,无模式的文档型数据库,开发语言是C++。它在许多场景下可用于替代统的关系型数据库或键/值存储方式。

1.2.1 MongoDB特点

所用语言:C++
特点:保留了SQL一些友好的特性(查询,索引)。
使用许可: AGPL(发起者: Apache)
协议: Custom, binary( BSON)
Master/slave复制(支持自动错误恢复,使用 sets 复制)
内建分片机制
支持 javascript表达式查询
可在服务器端执行任意的 javascript函数
update-in-place支持比CouchDB更好
在数据存储时采用内存到文件映射
对性能的关注超过对功能的要求
建议最好打开日志功能(参数 --journal)
在32位操作系统上,数据库大小限制在约2.5Gb
空数据库大约占 192Mb
采用 GridFS存储大数据或元数据(不是真正的文件系统)

1.2.2 MongoDB优点
  1. 更高的写负载,MongoDB拥有更高的插入速度。
  2. 处理很大的规模的单表,当数据表太大的时候可以很容易的分割表。
  3. 高可用性,设置M-S不仅方便而且很快,MongoDB还可以快速、安全及自动化的实现节点 (数据中心)故障转移。
  4. 快速的查询,MongoDB支持二维空间索引,比如管道,因此可以快速及精确的从指定位置 获取数据。MongoDB在启动后会将数据库中的数据以文件映射的方式加载到内存中。如果内 存资源相当丰富的话,这将极大地提高数据库的查询速度。
  5. 非结构化数据的爆发增长,增加列在有些情况下可能锁定整个数据库,或者增加负载从而 导致性能下降,由于MongoDB的弱数据结构模式,添加1个新字段不会对旧表格有任何影响, 整个过程会非常快速。
1.2.3 MongoDB缺点:
  1. 不支持事务。
  2. MongoDB占用空间过大 。
  3. MongoDB没有成熟的维护工具。
1.2.4 MongoDB应用场景
  1. 适用于实时的插入、更新与查询的需求,并具备应用程序实时数据存储所需的复制及高度伸缩性;
  2. 非常适合文档化格式的存储及查询;
  3. 高伸缩性的场景:MongoDB 非常适合由数十或者数百台服务器组成的数据库。
  4. 对性能的关注超过对功能的要求。
1.3 HBase

HBase 是 Apache Hadoop 中的一个子项目,属于 bigtable 的开源版本,所实现的语言为Java(故依赖 Java SDK)。HBase 依托于 Hadoop 的 HDFS(分布式文件系统)作为最基本存储基础单元。

1.3.1.HBase 特点:

所用语言: Java
特点:支持数十亿行X上百万列
使用许可: Apache
协议:HTTP/REST (支持 Thrift,见编注4)
在 BigTable之后建模
采用分布式架构 Map/reduce
对实时查询进行优化
高性能 Thrift网关
通过在server端扫描及过滤实现对查询操作预判
支持 XML, Protobuf, 和binary的HTTP
Cascading, hive, and pig source and sink modules
基于 Jruby( JIRB)的shell
对配置改变和较小的升级都会重新回滚
不会出现单点故障
堪比MySQL的随机访问性能

1.3.2 HBase 优点
  1. 存储容量大,一个表可以容纳上亿行,上百万列;
  2. 可通过版本进行检索,能搜到所需的历史版本数据;
  3. 负载高时,可通过简单的添加机器来实现水平切分扩展,跟Hadoop的无缝集成保障了其数据可靠性(HDFS)和海量数据分析的高性能(MapReduce);
  4. 在第3点的基础上可有效避免单点故障的发生。
1.3.3 HBase 缺点
  1. 基于Java语言实现及Hadoop架构意味着其API更适用于Java项目;
  2. node开发环境下所需依赖项较多、配置麻烦(或不知如何配置,如持久化配置),缺乏文档;
  3. 占用内存很大,且鉴于建立在为批量分析而优化的HDFS上,导致读取性能不高;
  4. API相比其它 NoSql 的相对笨拙。
1.3.4 HBase 适用场景
  1. bigtable类型的数据存储;
  2. 对数据有版本查询需求;
  3. 应对超大数据量要求扩展简单的需求。
1.4 Redis

Redis 是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的API。目前由VMware主持开发工作。

1.4.1 Redis 特点:

所用语言:C/C++
特点:运行异常快
使用许可: BSD
协议:类 Telnet
有硬盘存储支持的内存数据库,
但自2.0版本以后可以将数据交换到硬盘(注意, 2.4以后版本不支持该特性!)
Master-slave复制(见编注3)
虽然采用简单数据或以键值索引的哈希表,但也支持复杂操作,例如 ZREVRANGEBYSCORE。
INCR & co (适合计算极限值或统计数据)
支持 sets(同时也支持 union/diff/inter)
支持列表(同时也支持队列;阻塞式 pop操作)
支持哈希表(带有多个域的对象)
支持排序 sets(高得分表,适用于范围查询)
Redis支持事务
支持将数据设置成过期数据(类似快速缓冲区设计)
Pub/Sub允许用户实现消息机制

1.4.2 Redis 优势
  1. 非常丰富的数据结构;
  2. Redis提供了事务的功能,可以保证一串 命令的原子性,中间不会被任何操作打断;
  3. 数据存在内存中,读写非常的高速,可以达到10w/s的频率。
1.4.3 Redis 缺点
  1. Redis3.0后才出来官方的集群方案,但仍存在一些架构上的问题;
  2. 持久化功能体验不佳------通过快照方法实现的话,需要每隔一段时间将整个数据库的数据写到磁盘上,代价非常高;而aof方法只追踪变化的数据,类似于mysql的binlog方法,但追加log可能过
    大,同时所有操作均要重新执行一遍,恢复速度慢;
  3. 由于是内存数据库,所以,单台机器,存储的数据量,跟机器本身的内存大小。虽然redis本身有key过期策略,但是还是需要提前预估和节约内存。如果内存增长过快,需要定期删除数据。
1.4.4 Redis 应用场景:

最佳应用场景:适用于数据变化快且数据库大小可遇见(适合内存容量)的应用程序。
例如:微博、数据分析、实时数据搜集、实时通讯等。

2.Redis的介绍

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构。

3.Redis安装

官网下载地址:http://www.redis.cn
各个历史版本下载地址:http://download.redis.io/releases/

3.1 安装软件下载

通过wget命令下载,将软件下载到/opt目录下

html 复制代码
http://download.redis.io/releases/redis-4.0.14.tar.gz
3.2 解压缩
html 复制代码
[root@bobo01 opt]# tar -zxvf redis-4.0.14.tar.gz

删除源文件就重命名文件夹。

3.3 编译安装

因为Redis是C语音编写的,所以我们需要编译安装,首先进入redis目录,然后安装依赖环境。

html 复制代码
yum install -y wget gcc make tcl

然后执行

html 复制代码
make MALLOC=libc


进入src目录下执行编译安装命令

html 复制代码
make install


表示安装成功!

3.4 启动Redis服务
html 复制代码
./src/redis-server


通过客户端程序连接接口。

html 复制代码
./src/redis-cli


表示客户端连接成功。

3.5 启动配置

如果我们直接通过redis-server来启动,会以一个单独的会话进程来启动,当我们关闭该会话的时候,Redis服务也被关闭了,这时我们可以通过修改src同级目录下redis.conf文件来配置Redis守护进程启动。

启动Redis服务

html 复制代码
[root@bobo01 redis]# vim redis.conf
[root@bobo01 redis]# ./src/redis-server redis.conf
2248:C 24 Feb 14:32:48.796 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
2248:C 24 Feb 14:32:48.796 # Redis version=4.0.14, bits=64, commit=00000000,modified=0, pid=2248, just started
2248:C 24 Feb 14:32:48.796 # Configuration loaded

当我们关闭当前的会话窗口,然后新开一个窗口通过redis-cli连接服务器也是成功的。

html 复制代码
[root@bobo01 redis]# ./src/redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>

关闭服务可以通过shutdown来实现,退出Redis会话可以通过exit命令来实现。

html 复制代码
[root@bobo01 redis]# ./src/redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> exit
[root@bobo01 redis]# ./src/redis-cli
127.0.0.1:6379> SHUTDOWN
not connected> exit
[root@bobo01 redis]# ./src/redis-cli
Could not connect to Redis at 127.0.0.1:6379: Connection refused
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected> exit
[root@bobo01 redis]#

二、Redis数据类型介绍

Redis中的数据都是key/value对,这里的数据类型指的是value的值的类型。

启动redis服务并登录客户端。

1. 通用命令

五种数据类型的数据的结构差异所以命令也不尽相同,但是还是有一些相通的命令。所以此处先介绍下通用命令。
创建一个简单的k/v对

html 复制代码
127.0.0.1:6379> set name dpb
OK
12

在redis中,默认一共有16个数据库,编号为0-15,正常情况下,用户登录成功后,首先看到的是0号库,可以手动切换为其他库,使用。
SELECT

html 复制代码
127.0.0.1:6380> set name zhangsan
OK
127.0.0.1:6380> select 4
OK
127.0.0.1:6380[4]> get name
(nil)
127.0.0.1:6380[4]> select 0
OK
127.0.0.1:6380> get name
"zhangsan"
12345678910

DEL命令
该命令用于在 key 存在时删除 key。

html 复制代码
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> del name
(integer) 0
1234

返回数字表示删除的记录数。
DUMP命令
序列化给定 key ,并返回被序列化的值。

html 复制代码
127.0.0.1:6379> dump name
"\x00\x03dpb\t\x00\xe5\xa2\xc4\xd1\xfc\xb2\xd9\xcb"
127.0.0.1:6379> dump name1
(nil)
1234

如果key不存在返回nil。
EXISTS命令
检查key是否存在。

html 复制代码
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists name1
(integer) 0
1234

1表示存在,0表示不存在。
TTL命令
以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。

html 复制代码
127.0.0.1:6379> ttl name
(integer) -1
127.0.0.1:6379> ttl name1
(integer) -2
1234

-2表示key不存在,-1表示没有设置有效时间,不会过期,我们可以通过EXPIRE key 设置有效时间。
EXPIRE
是指有效时间,单位秒。

html 复制代码
127.0.0.1:6379> expire name 120
(integer) 1
127.0.0.1:6379> ttl name
(integer) 112
127.0.0.1:6379> ttl name
(integer) 10
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> ttl name
(integer) -2
12345678910

112是剩余的有效时间。
PEXPIRE命令
同样是设置过期时间,只是单位是毫秒。

html 复制代码
127.0.0.1:6379> pexpire name 100
(integer) 1
127.0.0.1:6379> ttl name
(integer) -2
1234

100毫秒比较短暂,-2表示已经不存在了。
PTTL
和ttl命令一样,只是返回单位是毫秒。

html 复制代码
127.0.0.1:6379> pexpire name 100000
(integer) 1
127.0.0.1:6379> ttl name
(integer) 91
127.0.0.1:6379> pttl name
(integer) 84601
123456

KEYS
通过正则表达式查找符合条件的key。

html 复制代码
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> set sex 男
OK
127.0.0.1:6379> set address 深圳
OK
127.0.0.1:6379> keys *
1) "age"
2) "address"
3) "sex"
12345678910

keys * 查询所有的key。
RANDOMKEY
从当前数据库中随机返回一个key。

html 复制代码
127.0.0.1:6379> randomkey
"address"
127.0.0.1:6379> randomkey
"age"
127.0.0.1:6379> randomkey
"sex"
127.0.0.1:6379> randomkey
"age"
12345678

TYPE命令
返回key对应的数据类型。

html 复制代码
127.0.0.1:6379> type name
none
127.0.0.1:6379> type age
string
127.0.0.1:6379> type sex
string
123456

不存在返回的是none。
RENAME
修改key的名称。

html 复制代码
127.0.0.1:6379> rename age age1
OK
127.0.0.1:6379> exists age
(integer) 0
127.0.0.1:6379> exists age1
(integer) 1
123456

flushdb
清空当前库。

html 复制代码
127.0.0.1:6380> keys *
1) "name"
127.0.0.1:6380> flushdb
OK
127.0.0.1:6380> keys *
(empty list or set)
127.0.0.1:6380>
1234567

flushall
清空所有库。

2. String类型


Set命令
设置指定 key 的值。

html 复制代码
127.0.0.1:6379> set name abc
OK
12

Get命令
返回 key 的值,如果 key 不存在时,返回 nil。 如果 key 不是字符串类型,那么返回一个错误。

html 复制代码
127.0.0.1:6379> get name
"abc"
127.0.0.1:6379> get aaa
(nil)
1234

StrLen命令
获取value的长度。

html 复制代码
127.0.0.1:6379> strlen name
(integer) 10
12

Append
如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾,如果key不存在则报错。

html 复制代码
127.0.0.1:6379> append name 123
(integer) 6
127.0.0.1:6379> get name
"abc123"
127.0.0.1:6379> append aaa
(error) ERR wrong number of arguments for 'append' command
123456

Incr命令
将 key 中储存的数字值增一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

html 复制代码
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> get age
"18"
127.0.0.1:6379> incr age
(integer) 19 #正常数字增1
127.0.0.1:6379> incr aaa
(integer) 1 # 不存在 初始为0增1
127.0.0.1:6379> incr name # 类型不匹配报错
(error) ERR value is not an integer or out of range
12345678910

IncrBy命令
将 key 所储存的值加上特定的值。

html 复制代码
127.0.0.1:6379> incrby age 5
(integer) 24
127.0.0.1:6379> incrby age 5
(integer) 29
1234

IncrByFloat命令
将 key 所储存的值加上给定的浮点增量值。

html 复制代码
127.0.0.1:6379> incrbyfloat age 2.3
"31.3"
127.0.0.1:6379> incrbyfloat age 2.3
"33.6"
1234

Decr
将 key 中储存的数字值减一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

html 复制代码
127.0.0.1:6379> get age
"18"
127.0.0.1:6379> decr age
(integer) 17
1234

DecrBy
将key中存储的数字减固定的值。

html 复制代码
27.0.0.1:6379> decrby age 8
(integer) 9
127.0.0.1:6379> decrby age 8
(integer) 1
1234

GetRange
获取value值的子集。
命令 getrange key start end。

html 复制代码
127.0.0.1:6379> getrange name 2 5
"c123"
127.0.0.1:6379> getrange name 4 6
"23"
127.0.0.1:6379> getrange name 2 6
"c123"
123456

SetRange命令
用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。

html 复制代码
127.0.0.1:6379> set name helloworld
OK
127.0.0.1:6379> get name
"helloworld"
127.0.0.1:6379> setrange name 5 redis
(integer) 10
127.0.0.1:6379> get name
"helloredis"
12345678

GetSet命令
获取设置key的值并返回原来的旧值。

html 复制代码
127.0.0.1:6379> getset name 波波烤鸭
"abc123"
127.0.0.1:6379> get name
"\xe6\xb3\xa2\xe6\xb3\xa2\xe7\x83\xa4\xe9\xb8\xad"
1234

MGet命令
批量获取值。

html 复制代码
127.0.0.1:6379> mget name age address
1) "abc"
2) "1"
3) "sz"
1234

MSet命令
批量设置值。

html 复制代码
127.0.0.1:6379> mset a1 a1 a2 a2 a3 a3 a4 a4
OK
127.0.0.1:6379> mget a1 a2 a3 a4
1) "a1"
2) "a2"
3) "a3"
4) "a4"
1234567

SetEx命令
设置key对应的value,同时设置过期时间,单位是秒。

html 复制代码
127.0.0.1:6379> setex name1 20 test
OK
127.0.0.1:6379> ttl name1
(integer) 16
127.0.0.1:6379> ttl name1
(integer) 6
127.0.0.1:6379> ttl name1
(integer) -2
12345678

PSetEx命令
这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。

html 复制代码
127.0.0.1:6379> psetex name2 20000 aaa
OK
127.0.0.1:6379> ttl name2
(integer) 17
127.0.0.1:6379> pttl name2
(integer) 4020
127.0.0.1:6379> pttl name2
(integer) 381
12345678

SetNx命令
只有在 key 不存在时设置 key 的值,set if n ot exists。

html 复制代码
127.0.0.1:6379> setnx name aaa
(integer) 0
127.0.0.1:6379> get name
"abc"
127.0.0.1:6379> setnx name3 aaa
(integer) 1
127.0.0.1:6379> get name3
"aaa"
12345678

MSetNx命令
兼具了mset和setnx的特性,但是批量设置中只要有一个key存在,则所有的操作都失效。

html 复制代码
127.0.0.1:6379> msetnx b1 bb1 b2 bb2 b3 bb3
(integer) 1
127.0.0.1:6379> mget b1 b2 b3
1) "bb1"
2) "bb2"
3) "bb3"
127.0.0.1:6379> msetnx a1 aaa b5 bbb b6 bbb
(integer) 0
127.0.0.1:6379> mget a1 b5 b6
1) "a1"
2) (nil)
3) (nil)
3.String中bit命令

Redis中字符串的存储方式都是以二进制的方式存储的。
比如:

html 复制代码
127.0.0.1:6379> set k1 a
OK
12

k1的值为a,对应的ascii码是97,转换为二进制就是01100001。BIT命令就是对这个二进制数据进行操作的。

GetBit命令
用于对 key 所储存的字符串值,获取指定偏移量上的位(bit)。

html 复制代码
127.0.0.1:6379> set k1 a
OK
127.0.0.1:6379> getbit k1 0
(integer) 0
127.0.0.1:6379> getbit k1 1
(integer) 1
127.0.0.1:6379> getbit k1 2
(integer) 1
127.0.0.1:6379> getbit k1 3
(integer) 0
127.0.0.1:6379> getbit k1 4
(integer) 0
127.0.0.1:6379> getbit k1 5
(integer) 0
127.0.0.1:6379> getbit k1 6
(integer) 0
127.0.0.1:6379> getbit k1 7
(integer) 1
123456789101112131415161718

当偏移量 OFFSET 比字符串值的长度大,或者 key 不存在时,返回 0 。
SetBit命令
用于对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit),返回的结果是该位上原来的bit值。
比如我们设置 k1为a,k2为c,a对应的ASCII码为97(01100001),c对应的ASCII码为99(01100011),相差了一个1,如此我们就可以通过setbit来调整。

html 复制代码
127.0.0.1:6379> set k2 c
OK
127.0.0.1:6379> get k1
"a"
127.0.0.1:6379> get k2
"c"
127.0.0.1:6379> setbit k1 6 1
(integer) 0
127.0.0.1:6379> get k1
"c"
12345678910

如此我们就将k1的值快速调整为了c。
BitCount命令
用来统计二进制中为1的个数,比如a(97 01100001)为3,c(99 01100011)为4。

html 复制代码
127.0.0.1:6379> get k1
"a"
127.0.0.1:6379> get k2
"c"
127.0.0.1:6379> bitcount k1
(integer) 3
127.0.0.1:6379> bitcount k2
(integer) 4
12345678

实际使用官网给出了例子,统计在线人数。
假设现在我们希望记录自己网站上的用户的上线频率,比如说,计算用户 A 上线了多少天,用户 B 上线了多少天,诸如此类,以此作为数据,从而决定让哪些用户参加 beta 测试等活动 ------ 这个模式可以使用 SETBIT 和 BITCOUNT 来实现。
比如说,每当用户在某一天上线的时候,我们就使用 SETBIT ,以用户名作为 key ,将那天所代表的网站的上线日作为 offset 参数,并将这个 offset 上的为设置为 1 。
举个例子,如果今天是网站上线的第 100 天,而用户 peter 在今天阅览过网站,那么执行命令SETBIT peter 100 1 ;如果明天 peter 也继续阅览网站,那么执行命令 SETBIT peter 101 1 ,以此类推。
当要计算 peter 总共以来的上线次数时,就使用 BITCOUNT 命令:执行 BITCOUNT peter ,得出的结果就是 peter 上线的总天数。
性能
前面的上线次数统计例子,即使运行 10 年,占用的空间也只是每个用户 10*365 比特位(bit),也即是每个用户 456 字节。对于这种大小的数据来说, BITCOUNT 的处理速度就像 GET 和 INCR 这种 O(1) 复杂度的操作一样快。
BitOp命令
对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。BITOP 命令支持AND(与) 、 OR(或) 、 NOT(非) 、 XOR(异或)运算。比如a 01100001和c 01100011进行相关运算。

html 复制代码
127.0.0.1:6379> get k1
"a"
127.0.0.1:6379> get k2
"c"
127.0.0.1:6379> bitop and k3 k1 k2
(integer) 1
127.0.0.1:6379> get k3
"a"
127.0.0.1:6379> bitop or k4 k1 k2
(integer) 1
127.0.0.1:6379> get k4
"c"
127.0.0.1:6379> bitop xor k5 k1 k2
(integer) 1
127.0.0.1:6379> get k5
"\x02"
12345678910111213141516

not运算注意参数个数。

html 复制代码
127.0.0.1:6379> bitop not k6 k1
(integer) 1
127.0.0.1:6379> get k6
"\x9e"
1234

BitPos命令
返回字符串里面第一个被设置为1或者0的bit位。
返回一个位置,把字符串当做一个从左到右的字节数组,第一个符合条件的在位置0,其次在位置8,等等。还是以a为例01100001。

html 复制代码
127.0.0.1:6379> bitpos k1 1
(integer) 1
127.0.0.1:6379> bitpos k1 0
(integer) 0
4.Hash介绍

在实际开发过程中我们肯定会碰到很多需要存储对象的需求,此时hash就比较合适了。hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)。

HSet
HSet命令用来设置指定的key中的字段的值。

html 复制代码
127.0.0.1:6379> hset user1 name zhangsan
(integer) 1
12

HGet
HGet命令获取指定的key中的hash值。

html 复制代码
127.0.0.1:6379> hget user1 name
"zhangsan"
12

HMSet
HMGet命令用来批量设置对应的key中的字段的值。

html 复制代码
127.0.0.1:6379> hmset user2 name zhangsan age 18 sex nan address sz
OK
12

HMGet
HMGet命令用来批量获取key中的字段所对应的值。

html 复制代码
127.0.0.1:6379> hmget user2 name age sex address
1) "zhangsan"
2) "18"
3) "nan"
4) "sz"
12345

HDel
HDel命令可以移除指定的key中对应的字段,如果字段不存在则忽略。

html 复制代码
127.0.0.1:6379> hdel user2 address a1
(integer) 1 # 移除成功
127.0.0.1:6379> hmget user2 name age sex address
1) "zhangsan"
2) "18"
3) "nan"
4) (nil) # 不存在了
1234567

HSetNx
如果设置的字段不存在就设置值,如果存在就忽略。

html 复制代码
127.0.0.1:6379> hsetnx user2 name lisi
(integer) 0 # name字段存在,不起作用
127.0.0.1:6379> hsetnx user2 id 1001
(integer) 1 # id不存在,设置成功
127.0.0.1:6379> hmget user2 name age id
1) "zhangsan"
2) "18"
3) "1001"
12345678

HVals
HVals可以返回指定的key中所有字段的值。

html 复制代码
127.0.0.1:6379> hvals user2
1) "zhangsan"
2) "18"
3) "nan"
4) "1001"
12345

HKeys
HKeys命令可以获取指定的key中的所有的字段信息。

html 复制代码
127.0.0.1:6379> hkeys user2
1) "name"
2) "age"
3) "sex"
4) "id"
12345

HGetAll
HGetAll可以获取指定key中的所有的字段和对应的值,返回的形式是先返回字段然后是对应的值,所以返回的数据的长度是本身长度的两倍,如下:

html 复制代码
127.0.0.1:6379> hgetall user2
1) "name"
2) "zhangsan"
3) "age"
4) "18"
5) "sex"
6) "nan"
7) "id"
8) "1001"
123456789

HExists
HExists用来判断指定的key中是否还有某字段,有返回1,没有返回0,如下:

html 复制代码
127.0.0.1:6379> hexists user2 name
(integer) 1
127.0.0.1:6379> hexists user2 ids
(integer) 0
1234

HIncrBy
HIncrBy用来增加指定的key中的某字段的值,如果字段不存在,则会创建字段与key关联,默认值为0然后增加相关数据。如果字段对应的值不是数字则报错。

html 复制代码
127.0.0.1:6379> hget user2 age
"18"
127.0.0.1:6379> hincrby user2 age 2
(integer) 20 # 增加2 并返回增加后的结果
127.0.0.1:6379> hincrby user2 age1 10
(integer) 10 # 关联字段 初始0 再加10
127.0.0.1:6379> hincrby user2 name 10
(error) ERR hash value is not an integer # 类型不匹配,报错
12345678

HIncrByFloat
该命令和HIncrBy类似,增加的是float类型的数据。
HLen
HLen命令获取的是指定的key下的字段的个数。

html 复制代码
127.0.0.1:6379> hlen user2
(integer) 5
12

HStrLen
HStrLen获取指定key下的字段的值的长度。

html 复制代码
127.0.0.1:6379> hstrlen user2 name
(integer) 8
127.0.0.1:6379> hstrlen user2 address
(integer) 0 #不存在返回0
127.0.0.1:6379> hstrlen user2 sex
(integer) 3
5. List类型

列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

LPush
LPush将所有指定的值插入到存于 key 的列表的头部。如果 key 不存在,那么在进行 push 操作前会创建一个空列表。 如果 key 对应的值不是一个 list 的话,那么会返回一个错误。

html 复制代码
127.0.0.1:6379> lpush games lol dnf cs cf
(integer) 4
127.0.0.1:6379> lpush user2 a b c
(error) WRONGTYPE Operation against a key holding the wrong kind of value
1234

返回结果是插入的数据的个数。
LRange
从列表中获取指定的元素。start 和 end 偏移量都是基于0的下标,即list的第一个元素下标是0(list的表头),第二个元素下标是1,以此类推。
偏移量也可以是负数,表示偏移量是从list尾部开始计数。 例如,-1 表示列表的最后一个元素,-2 是倒数第二个,以此类推。

html 复制代码
127.0.0.1:6379> lrange games 0 -1
1) "cf"
2) "cs"
3) "dnf"
4) "lol"
127.0.0.1:6379> lrange games 0 3
1) "cf"
2) "cs"
3) "dnf"
4) "lol"
127.0.0.1:6379> lrange games 0 1
1) "cf"
2) "cs"
12345678910111213

RPush
功能和LPush一致,只是从列表的右侧(尾部)添加。

html 复制代码
127.0.0.1:6379> lpush games a1 a2
(integer) 6
127.0.0.1:6379> rpush games b1 b2
(integer) 8
127.0.0.1:6379> lrange games 0 -1
1) "a2"
2) "a1"
3) "cf"
4) "cs"
5) "dnf"
6) "lol"
7) "b1"
8) "b2"
12345678910111213

llen
获取列表的长度。

html 复制代码
127.0.0.1:6379> llen games
(integer) 3
12

lset
通过下标设置列表的值。

html 复制代码
127.0.0.1:6379> lset games 1 aaa
OK
127.0.0.1:6379> lrange games 0 -1
1) "a1"
2) "aaa"
3) "a3"
123456

RPop
移除列表尾部的元素并返回。

html 复制代码
127.0.0.1:6379> rpop games
"b2" # 获取到了最后一个
127.0.0.1:6379> lrange games 0 -1
1) "a2"
2) "a1"
3) "cf"
4) "cs"
5) "dnf"
6) "lol"
7) "b1" # b2被移除了
12345678910

LPop
和RPop类似,只是移除的是头部的元素。

html 复制代码
127.0.0.1:6379> lpop games
"a2" # 移除了头部是a2并返回了
127.0.0.1:6379> lrange games 0 -1
1) "a1" # a2被移除了
2) "cf"
3) "cs"
4) "dnf"
5) "lol"
6) "b1"
123456789

LIndex
Lindex命令返回key中对应的index的下标的值。0表示第一个,-1表示最后一个。

html 复制代码
127.0.0.1:6379> lindex games 0
"a1" #第一个
127.0.0.1:6379> lindex games -1
"b1" # 最后一个
127.0.0.1:6379> lindex games 3
"dnf" # 第四个
123456

LTrim
LTrim截取列表中的部分元素。0第一个,-1最后一个。

html 复制代码
127.0.0.1:6379> lrange games 0 -1
1) "a1"
2) "cf"
3) "cs"
4) "dnf"
5) "lol"
6) "b1"
127.0.0.1:6379> ltrim games 0 3
OK
127.0.0.1:6379> lrange games 0 -1
1) "a1"
2) "cf"
3) "cs"
4) "dnf"
1234567891011121314

BLPop
BLPOP 是阻塞式列表的弹出原语。 它是命令 LPOP 的阻塞版本,这是因为当给定列表内没有任何元素可供弹出的时候, 连接将被 BLPOP 命令阻塞。 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。同时在使用此命令的时候也需要指定过期时间,单位是秒。返回的接口是key和列表元素值。

html 复制代码
127.0.0.1:6379> blpop games 20
1) "games"
2) "cf"
127.0.0.1:6379> blpop games 20
1) "games"
2) "cs"
127.0.0.1:6379> blpop games 20
1) "games"
2) "dnf"
127.0.0.1:6379> blpop games 20
(nil) # 列表为空了,所以阻塞了20秒
(20.01s)
127.0.0.1:6379>
12345678910111213

BRPop
命令和BLPop类似,RPop的阻塞版,阻塞的是返回尾部不为空的元素。

html 复制代码
127.0.0.1:6379> lrange games 0 -1
1) "cs"
2) "dnf"
3) "lol"
127.0.0.1:6379> brpop games 5
1) "games"
2) "lol"
127.0.0.1:6379> brpop games 5
1) "games"
2) "dnf"
127.0.0.1:6379> brpop games 5
1) "games"
2) "cs"
127.0.0.1:6379> brpop games 5
(nil)
(5.04s)
12345678910111213141516

RPopLPush
原子性地返回并移除存储在 source 的列表的最后一个元素(列表尾部元素), 并把该元素放入存储在 destination 的列表的第一个元素位置(列表头部)。

html 复制代码
127.0.0.1:6379> rpush games a1 a2 a3
(integer) 3
127.0.0.1:6379> lrange games 0 -1
1) "a1"
2) "a2"
3) "a3"
127.0.0.1:6379> rpoplpush games mygame
"a3"
127.0.0.1:6379> lrange games 0 -1
1) "a1"
2) "a2"
127.0.0.1:6379> lrange mygame 0 -1
1) "a3"
127.0.0.1:6379> rpoplpush games mygame
"a2"
127.0.0.1:6379> rpoplpush games mygame
"a1"
127.0.0.1:6379> rpoplpush games mygame
(nil)
127.0.0.1:6379> lrange games 0 -1
(empty list or set)
127.0.0.1:6379> lrange mygame 0 -1
1) "a1"
2) "a2"
3) "a3"
12345678910111213141516171819202122232425

BRPopLPush
BRPOPLPUSH是RPOPLPUSH的阻塞版,如果没有移除的数据就会阻塞到有数据或者timeout。

html 复制代码
127.0.0.1:6379> brpoplpush mygame games 5
"a3"
127.0.0.1:6379> brpoplpush mygame games 5
"a2"
127.0.0.1:6379> brpoplpush mygame games 5
"a1"
127.0.0.1:6379> brpoplpush mygame games 5
(nil) #阻塞到了timeout
(5.03s)
6.set类型

对比list集合是有序可以保持重复记录的列表,set集合是无序的,不可以存储重复的记录集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。

SADD
添加一个或多个元素到集合中,如果集合中存在该元素则忽略。

html 复制代码
127.0.0.1:6379> sadd stu zhangsan lisi wangwu lisi zhangsan
(integer) 3
12

添加了5个元素,但只成功了3个,因为有两个是相同的。
SCard
返回set集合中元素的个数。

html 复制代码
127.0.0.1:6379> scard stu
(integer) 3
12

SIsMember
判断集合中是否含有某元素。

html 复制代码
127.0.0.1:6379> sismember stu zhangsan
(integer) 1 # 含有zhangsan
127.0.0.1:6379> sismember stu zhangsan1
(integer) 0 # 不含有zhangsan1
1234

SMembers
获取集合中的所有的元素。

html 复制代码
127.0.0.1:6379> smembers stu
1) "lisi"
2) "zhangsan"
3) "wangwu"
1234

SRem
删除集合中指定的元素,如果集合中不含有该元素,则忽略,返回0。

html 复制代码
127.0.0.1:6379> srem stu lisi
(integer) 1
127.0.0.1:6379> srem stu aa
(integer) 0 # 集合中不含有 aa 返回0
127.0.0.1:6379> smembers stu
1) "zhangsan"
2) "wangwu"
1234567

SRandMember
随机返回集合中的元素,redis2.6之后可以在命令后加一个count参数,指定随机返回的元素的个数,如果count大于集合中的个数,则返回所有的元素。负数的话取绝对值。

html 复制代码
127.0.0.1:6379> srandmember stu
"zhangsan" # 取一个随机值
127.0.0.1:6379> srandmember stu
"bobo"
127.0.0.1:6379> srandmember stu
"dpb"
127.0.0.1:6379> srandmember stu 3
1) "bobo" #取三个随机值
2) "dpb"
3) "wangwu"
127.0.0.1:6379> srandmember stu 3
1) "bobo"
2) "dpb"
3) "lisi"
127.0.0.1:6379> srandmember stu 100
1) "lisi" # 大于总的个数,去所有值
2) "dpb"
3) "bobo"
4) "zhangsan"
5) "wangwu"
127.0.0.1:6379> srandmember stu -2
1) "dpb" # 负数取绝对值 两个
2) "zhangsan"
127.0.0.1:6379> srandmember stu -2
1) "lisi"
2) "dpb"
1234567891011121314151617181920212223242526

Spop
和srandmember类似,只是spop会将获取的元素移除而srandmember不会移除元素。

html 复制代码
127.0.0.1:6379> smembers stu
1) "bobo"
2) "zhangsan"
3) "dpb"
4) "lisi"
5) "wangwu"
127.0.0.1:6379> spop stu 2
1) "zhangsan" # 随机获取两个元素并移除
2) "dpb"
127.0.0.1:6379> smembers stu
1) "bobo"
2) "lisi"
3) "wangwu"
12345678910111213

SMove
将元素从一个集合移动到另一个集合中。

html 复制代码
127.0.0.1:6379> smembers stu
1) "bobo"
2) "lisi"
3) "wangwu"
127.0.0.1:6379>
127.0.0.1:6379> smove stu stu1 bobo
(integer) 1
127.0.0.1:6379> smembers stu
1) "lisi"
2) "wangwu"
127.0.0.1:6379> smembers stu1
1) "bobo"
123456789101112

SDiff
返回两个集合的差集。

html 复制代码
127.0.0.1:6379> smembers stu
1) "lisi"
2) "wangwu"
127.0.0.1:6379> smembers stu1
1) "bobo"
127.0.0.1:6379> sdiff stu stu1
1) "lisi"
2) "wangwu"
127.0.0.1:6379> sdiff stu1 stu
1) "bobo"
12345678910

SDiffStore
和sdiff类似,不同的是会将差集结果保存起来。

html 复制代码
127.0.0.1:6379> sdiffstore stu2 stu1 stu
(integer) 1
127.0.0.1:6379> smembers stu2
1) "bobo"
1234

SInter
获取两个集合的交集。

html 复制代码
127.0.0.1:6379> smembers stu
1) "lisi"
2) "wangwu"
127.0.0.1:6379> smembers stu2
1) "lisi"
2) "bobo"
127.0.0.1:6379> sinter stu stu2
1) "lisi" #两个集合中都有lisi
12345678

SInterStore
获取两个集合的交接并将结果保存起来了。

html 复制代码
127.0.0.1:6379> sinterstore stu3 stu stu2
(integer) 1
127.0.0.1:6379> smembers stu3
1) "lisi"
1234

SUnion
获取两个集合的并集。

html 复制代码
127.0.0.1:6379> sunion stu stu2
1) "bobo"
2) "lisi"
3) "wangwu"
1234

SunionStore
获取两个集合的并集并保存起来。

html 复制代码
127.0.0.1:6379> sunionstore stu3 stu stu2
(integer) 3
127.0.0.1:6379> smembers stu3
1) "bobo"
2) "lisi"
3) "wangwu"
7.zset类型

有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。

ZAdd
向有序集合中添加一个或者多个元素(分数/元素),如果元素已经存在,则更新该元素的分数,并调整到对应的位置。按分数从小到大排列。

html 复制代码
127.0.0.1:6379> zadd student 60 a1 70 a2 80 a3 90 a4
(integer) 4
12

ZScore
获取有序集合中元素对应的分数值。

html 复制代码
127.0.0.1:6379> zscore student a1
"60"
127.0.0.1:6379> zscore student a4
"90"
1234

ZRange
获取集合中指定的元素信息,如果加上withscores参数则会连同分数一并返回。

html 复制代码
127.0.0.1:6379> zrange student 0 -1
1) "a1"
2) "a2"
3) "a3"
4) "a4"
127.0.0.1:6379> zrange student 0 -1 withscores
1) "a1"
2) "60"
3) "a2"
4) "70"
5) "a3"
6) "80"
7) "a4"
8) "90"
1234567891011121314

ZRevRange
和zrange命令类似,只是结果倒序显示。

html 复制代码
127.0.0.1:6379> zrevrange student 0 3
1) "a4"
2) "a3"
3) "a2"
4) "a1"
127.0.0.1:6379> zrevrange student 0 3 withscores
1) "a4"
2) "90"
3) "a3"
4) "80"
5) "a2"
6) "70"
7) "a1"
8) "60"
1234567891011121314

ZCard
返回集合中元素的个数。

html 复制代码
127.0.0.1:6379> zcard student
(integer) 4
12

ZCount
统计集合中元素的分数在min和max之间的个数,如果不需要保持min或者max,在其前面加(即可,如下。

html 复制代码
127.0.0.1:6379> zcount student 60 90
(integer) 4
127.0.0.1:6379> zcount student 60 (90
(integer) 3
127.0.0.1:6379> zcount student (60 90
(integer) 3
127.0.0.1:6379> zcount student (60 (90
(integer) 2
12345678

ZRangeByScore
可以根据score范围来查找集合中的元素,加上withscores也可以一并查询出对应的分数。

html 复制代码
127.0.0.1:6379> zrangebyscore student 60 90
1) "a1"
2) "a2"
3) "a3"
4) "a4"
127.0.0.1:6379> zrangebyscore student 60 90 withscores
1) "a1"
2) "60"
3) "a2"
4) "70"
5) "a3"
6) "80"
7) "a4"
8) "90"
127.0.0.1:6379> zrangebyscore student (60 (90 withscores
1) "a2"
2) "70"
3) "a3"
4) "80"
12345678910111213141516171819

ZRank
获取元素在集合中的排名,从小到大排序,最小的排名是0,不存在的返回 nil。

html 复制代码
127.0.0.1:6379> zrank student a3
(integer) 2
127.0.0.1:6379> zrank student a66
(nil)
127.0.0.1:6379> zrank student a1
(integer) 0
123456

ZRevRank
获取元素在集合中的排名,从大到小排名,和ZRank命令刚好相反。

html 复制代码
127.0.0.1:6379> zrevrank student a3
(integer) 1
127.0.0.1:6379> zrevrank student a1
(integer) 3
1234

ZIncrBy
给集合中的元素增加分数,如果元素不存在则新建元素,并设置分数初始为0然后在增加设置的分数。

html 复制代码
127.0.0.1:6379> zrange student 0 -1 withscores
1) "a1"
2) "60"
3) "a2"
4) "70"
5) "a3"
6) "80"
7) "a4"
8) "90"
127.0.0.1:6379> zincrby student 5 a1
"65"
127.0.0.1:6379> zrange student 0 -1 withscores
1) "a1"
2) "65"
3) "a2"
4) "70"
5) "a3"
6) "80"
7) "a4"
8) "90"
127.0.0.1:6379> zincrby student 5 aa
"5"
127.0.0.1:6379> zrange student 0 -1 withscores
1) "aa"
2) "5"
3) "a1"
4) "65"
5) "a2"
6) "70"
7) "a3"
8) "80"
9) "a4"
10) "90"

三、Redis高级内容

1.Redis的发布和订阅

https://dpb-bobokaoya-sm.blog.csdn.net/article/details/87483479
发送者(pub)发送消息的。
订阅者(sub)接收消息的。

1.1 订阅消息

客户端可以订阅任意数量的频。

html 复制代码
127.0.0.1:6379> SUBSCRIBE c1 c2 c3
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "c1"
3) (integer) 1
1) "subscribe"
2) "c2"
3) (integer) 2
1) "subscribe"
2) "c3"
3) (integer) 3
1.2 发送消息

给某个频道发送消息。

html 复制代码
127.0.0.1:6379> PUBLISH c1 hello
(integer) 1
127.0.0.1:6379>

客户端可以同时接收到相关的消息。

1.3 模式订阅消息

客户端在订阅消息的时候不是订阅某个具体的频道,而是通过模式匹配的方式订阅,具体如下。

html 复制代码
127.0.0.1:6379> PSUBSCRIBE c*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "c*"
3) (integer) 1
1) "pmessage"
2) "c*"
3) "cc"
4) "okokok"

发送消息就没有什么变化了。

2.事务管理

Redis是NOSQL数据库,也存在事务管理,只是在Redis中的事务和关系型数据库中的事务(ACID)是有区别的。
Redis中的事务使用其实是非常简单的,通过MULTI命令即可实现。
进入队列之前发生错误。

html 复制代码
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> sets k4 v4
(error) ERR unknown command `sets`, with args beginning with: `k4`, `v4`,127.0.0.1:6379> set k5 v5 s5
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

整个队列没法提交成功。
进入队列之后发生错误。

html 复制代码
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set k4 v4 s4
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) OK
4) (error) ERR syntax error
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379> get k4
(nil)

这种情况命令都会执行,但是不会保证都成功或者都失败
不同与关系型数据库,Redis中的事务是没有回滚操作的,官方的解释是:
Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

3. Watch

Watch命令可以监控一个或多个key。一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到exec命令(事务中的命令是在exec之后才执行的,所以在multi命令后可以修改watch监控的键值)。假设我们通过watch命令在事务执行之前监控了多个Keys,倘若在watch之后有任何Key的值发生了变化,exec命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务执行失败。

html 复制代码
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> WATCH k1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 kkkk
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK
127.0.0.1:6379> get k1
"kkkk"
127.0.0.1:6379> WATCH k1
OK
127.0.0.1:6379> set k1 okokok
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 bbbb
QUEUED
127.0.0.1:6379> set user zhang
QUEUED
127.0.0.1:6379> EXEC

exec之后会自动执行unwatch,撤销监控,当然你也可以显示的通过 unwatch 命令来撤销监控。

4. Redis的持久化

所谓的持久化就是保持我们的数据不丢失,将我们的数据保存在硬盘中,在Redis中持久化的的方式有两种,一种是快照持久化,一种是AOF持久化,各有个的优缺点。我们都会给大家一一介绍。

4.1 快照持久化

也叫RDB持久化,就是通过拍摄快照的方式来实现持久化,本质是将某个时间点的内存中的数据存储在一个rdb文件中,在Redis重启的时候会加载rdb中的文件数据。
配置快照持久化
Redis中的快照持久化默认是开启的,在redis.conf配置文件中有相关的配置信息。

html 复制代码
################################ 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
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""

save 900 1 # 900秒之后至少有一个key被改变就执行一次快照
save 300 10 # 300秒之后至少有10个1key被改变就执行一次快照
save 60 10000 # 60秒之后至少有10000个1key被改变就执行一次快照


按照上面的设置规则,我们改变的key没有触发快照的条件,但是当我们shutdownRedis服务后,我们发现在当前目录下面会帮忙我新建dump.rdb文件。这个的原因是

shutdown命令默认会执行 save 命令,本质上快照就是执行了 save 命令,在Redis运行的时候我们可以显示的发送一条save命令来实现拍摄快照。
bgsave命令
bgsave命令也是立即拍摄快照,有别与save命令,bgsave命令并不是一条阻塞的命令,而是fork了一个子线程,专门负责备份操作。而父进程继续处理用户的请求。这样就不会造成阻塞了。

优缺点
优点:1.RDB是一个非常简洁的文件。保存了某个时间点的数据,很适合做数据的备份。
2.RDB很适合用于灾备。单文件很方便就能传输到网络中。
3.RDB文件的性能很好。持久化的时候可以通过bgsave命令可以fork一个新的线程来单独处理备份。
缺点:容易造成数据丢失。拍摄快照的频率。
RDB文件比较大的情况下,持久化会比较花费时间,容易造成系统卡顿。

4.2 AOF持久化

与快照持久化通过直接保存 Redis 的键值对数据不同,AOF 持久化是通过保存 Redis 执行的写命令来记录 Redis 的内存数据。理论上说,只要我们保存了所有可能修改 Redis 内存数据的命令(也就是写命令),那么根据这些保存的写命令,我们可以重新恢复 Redis 的内存状态。AOF 持久化正是利用这个原理来实现数据的持久化与数据的恢复的。
开启AOF
Redis中的AOF默认是关闭的。我们需要通过修改配置文件来开启AOF。


RDB快照关闭。

然后当我们执行写数据的命令。就会出现aof文件。

aof文件中保存的也是我们刚刚执行的写操作的命令。

随着Redis写命令执行的越来越多,会有很多的写命令被记录到AOF文件中,AOF文件会越来越大,造成的后果就是还原操作的时间就会越来越长,为了解决这个问题可以向Redis发送 BGREWRITEAOF ,该命令会移除AOF中冗余的命令。使AOF文件体积变小。



AOF的优缺点
AOF优点

  1. AOF数据安全性更高,提供了多种频率来同步。
  2. AOF通过命令追加的方式来构造,在运行过程中同步数据效率更高。
  3. AOF文件格式的可读性更高。即使我们不小心执行了flushAll命令,在重写还没进行之前我们可以通过AOF文件来恢复数据。
    AOF缺点
  4. 对于具有相同数据的Redis,AOF的体积会更大。
  5. 虽然提供了多种同步频率,每秒同步的频率也具有较高的性能,但是在Redis负载较高的情况下,RDB比AOF具有更好的性能保障。
    持久化方案的比较
    1.如果redis仅仅是用来做为缓存服务器的话,我们可以不使用任何的持久化。
    2.一般情况下我们会将两种持久化的方式都开启。redis优先加载AOF文件来回复数据。RDB的好处是快速。
    3.在主从节点中,RDB作为我们的备份数据,只在salve(从节点)上启动,同步时间可以设置的长一点,只留(save 900 1)这条规则就可以了。
    4.开启AOF的情况下,主从同步是时候必然会带来IO的性能影响,此时我们可以调大auto-aof-rewrite-min-size的值,比如5GB。来减少IO的频率。
    5.不开启AOF的情况下,可以节省IO的性能影响,这是主从建通过RDB持久化同步,但如果主从都挂掉,影响较大~
5. 主从复制

​​​​​​​主从复制可以扩展Redis的性能,比单机版的Redis性能更加的稳定,Redis的主从复制和关系型数据库的主从复制差不多。从机能精确的从主机复制信息。
主从复制的优点:

  1. 实现读写分离
  2. 降低master的压力
  3. 实现数据的备份
    实现主从配置
    主从关系图
    一个主节点两个从节点

    实例配置
    简化操作,我们直接在一个Redis服务中配置三个Redis实例。
html 复制代码
192.168.100.120:6379
192.168.100.120:6380
192.168.100.120:6381

创建三个配置文件。
直接将redis.conf复制3份即可。

分别修改三个配置文件。
先修改63791的配置文件,修改如下的信息。

html 复制代码
port 6379
pidfile /var/run/redis6379.pid
logfile "6379.rdb"
dbfilename dump6379.rdb
appendfilename "appendonly6379.aof"

同理将另外两个文件修改即可。
接下来分别启动这三个redis实例。

html 复制代码
[root@bobo01 redis]# ./src/redis-server redis6379.conf
[root@bobo01 redis]# ./src/redis-server redis6380.conf
[root@bobo01 redis]# ./src/redis-server redis6381.conf

通过客户端命令分别连接这三个实例。



到此我们成功的配置了三个Redis实例,但是这三个实例是没有任何关系的。
配置主从关系
我们把6379的服务设置为master,6380和6381的服务设置为salve。如此我们只需要在6380和6381上分别执行如下命令即可。

html 复制代码
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
OK
127.0.0.1:6380>
html 复制代码
127.0.0.1:6381> SLAVEOF 127.0.0.1 6379
OK
127.0.0.1:6381>

查看主从关系
执行info replication命令即可
主节点查看

从节点查看

测试主从节点操作。
我们在主节点写入数据。

在从节点可以立马看到。

默认是本允许在从节点写入数据的。

主从复制要注意的地方

  1. 如果master运行了一段时间,slave才连接上来,此时slave会对master的所有数据进行同步,而不是从连接上的时间点同步
  2. master节点可读可写,但是slave节点只读不可写。
  3. 在当前的主从节点中,如果master挂掉后,重启后依然是master节点,主从操作依然可以使用。
6. 主从复制原理
6.1.全量复制

Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:

  1. 从服务器连接主服务器,发送SYNC命令;
  2. 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
  3. 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
  4. 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
  5. 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
  6. 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;
    完成上面几个步骤后就完成了从服务器数据初始化的所有操作,从服务器此时可以接收来自用户的读请求。
6.2.增量复制

Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。 增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
复制偏移量
执行复制的双方------主服务器和从服务器会分别维护一个复制偏移量:

  1. 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N;
  2. 从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N;
    注意只要是slave重新连接master都会自动执行一个全量复制。
7. 主从的其他结构

主从结构搭建的时候,整体的结构是任意,只要合理即可。

实现的方式也很简单。

html 复制代码
127.0.0.1:6381> SLAVEOF 127.0.0.1 6380
OK
127.0.0.1:6381>

master节点

6380slave节点

6381slave节点

结构搞定,数据操作没有问题。

8.哨兵模式

主从模式有个特点是master挂点后,slave节点中不会自定选举新的master节点,那么整个Redis环境其实就瘫痪了,那么为了解决这个问题,我们可以通过哨兵模式来实现。主从模式我们还是切换回一主两从的结构。

哨兵模式的配置
在redis.conf配置文件的同级目录下有一个 sentinel.conf 文件就是哨兵模式的配置文件。

mymster 是要监控的主机名, 可以自定义
最后的1 表示的是哨兵投票的最低票数。
启动哨兵模式
先关闭主从关系,然后开启哨兵模式

分别启动三个redis实例,设置好主从关系。
然后测试关闭master节点,我们查看是否会重写选举一个master节点。

等待一会就会选举新的master节点出来。

原来的master重新启动后会变为slave节点。

相关推荐
paopaokaka_luck4 分钟前
基于Spring Boot+Vue的多媒体素材管理系统的设计与实现
java·数据库·vue.js·spring boot·后端·算法
奋飞安全11 分钟前
初试js反混淆
开发语言·javascript·ecmascript
guoruijun_2012_411 分钟前
fastadmin多个表crud连表操作步骤
android·java·开发语言
浪里个浪的102413 分钟前
【C语言】计算3x3矩阵每行的最大值并存入第四列
c语言·开发语言·矩阵
@东辰21 分钟前
【golang-技巧】-自定义k8s-operator-by kubebuilder
开发语言·golang·kubernetes
Hello-Brand21 分钟前
Java核心知识体系10-线程管理
java·高并发·多线程·并发·多线程模型·线程管理
乐悠小码27 分钟前
数据结构------队列(Java语言描述)
java·开发语言·数据结构·链表·队列
史努比.29 分钟前
Pod控制器
java·开发语言
2的n次方_32 分钟前
二维费用背包问题
java·算法·动态规划
皮皮林55132 分钟前
警惕!List.of() vs Arrays.asList():这些隐藏差异可能让你的代码崩溃!
java