60分钟

第7章 NoSQL数据库

【学习目标】

1.知识目标

了解NoSQL数据库优点。

了解NoSQL数据库的类型。

理解Memcached数据库的特点。

掌握Memcached数据库的安装与配置。

掌握Memcached数据库的基本操作。

理解Redis数据库的特点。

掌握Redis数据库的安装与配置。

掌握Redis数据库的基本操作。

2.技能目标

Memcached数据库的安装与配置。

Memcached数据库的基本操作。

Redis数据库的安装与配置。

Redis数据库的基本操作。

【认证考点】

掌握Memcached安装、状态查看、查询数据等操作。

能掌握Redis安装、连接、查询数据、状态监测等操作。

项目引导:NoSQL数据库的部署与操作

【项目描述】

随着大数据时代的到来,应用系统中的数据库拥有的信息越来越多,随着时间的推移数据库就会越来越慢,即使是为支持许多并发请求而精心设计的数据库管理系统也将最终达到极限。通常的解决方案是采用NoSQL中的缓存数据库来处理这些性能问题,缓存数据库将数据库查询的结果保存在更快、更容易访问的位置,一般情况下,直接将数据存储在内存中。由于内存的读写速度远快于硬盘,这样就直接减少了查询响应时间,降低了数据库负载从而降低了数据库的成本。

知识储备

7.1 NoSQL数据库概述

现代计算系统每天在网络上都会产生庞大的数据量。这些数据有很大一部分是由关系型数据库管理系统来处理,其严谨成熟的数学理论基础使得数据建模和应用程序编程更加简单。但随着信息化的浪潮和互联网的兴起,传统的关系数据库系统在一些业务上开始出现问题。首先,对数据库存储的容量要求越来越高,单机无法满足需求,很多时候需要用集群来解决问题,而关系数据库系统由于要支持join、union等操作,一般不支持分布式集群。其次,在大数据大行其道的今天,很多的数据都频繁读和增加,不频繁修改,而关系数据库系统对所有操作一视同仁,这就带来了优化的空间。另外,互联网时代业务的不确定性导致数据库的存储模式也需要频繁变更,不自由的存储模式增大了运维的复杂性和扩展的难度。

7.1.1 NoSQL简介

NoSQL(Not Only SQL)指的是非关系型的数据库,是对不同于传统的关系型数据库的数据库管理系统的统称。

NoSQL是一项全新的数据库革命性运动,早期就有人提出,到2009年NoSQL的发展趋势越发高涨。它主要有非关系型、分布式、开源、水平可扩展等特点,最初目的是为了满足大规模WEB应用。

1.NoSQL的发展

NoSQL一词最早出现于1998年,是Carlo Strozzi开发的一个轻量、开源、不提供SQL功能的关系数据库。到2009年,发起了一次关于分布式开源数据库的讨论,这时的NoSQL主要指非关系型、分布式、不提供ACID的数据库设计模式。

2.NoSQL的优点

易扩展。NoSQL数据库种类繁多,但是有一个共同的特点,都是去掉了关系型数据库的关系型特性,数据之间无关系,这样就非常容易扩展,为架构方面带来可扩展的能力。

海量数据,高性能。NoSQL数据库都具有非常高的读写性能,尤其在海量的数据下,表现尤其优秀,这个主要是源于它数据的无关系性和数据库的结构简单的特点。

灵活的数据模型。NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式,而在关系型数据库里,增删字段是一件非常麻烦的事情。

高可用。NoSQL在不太影响性能的情况下,就可以方便地实现高可用的架构。比如Cassandra、HBase模型,通过复制模型也能实现高可用。

3.NoSQL的缺点

没有标准。目前NoSQL数据库定义还没有标准。

没有存储过程。NoSQL数据库中大多没有存储过程。

不支持SQL。NoSQL大多不提供对SQL的支持,如果不支持SQL这样的工业标准,将会对用户产生一定的学习和应用迁移上的成本。

7.1.2 NoSQL数据库分类

NoSQL数据库的分类主要有列存储、文档存储、Key-Value存储、图存储、对象存储和XML数据库等类型。

1.列存储

列存储,顾名思义,是按列存储数据的。最大的特点是能存储结构化和半结构化数据,有利于数据进行压缩,在某一列或者某几列的查询时IO的优势比较明显。典型的列存储数据库有Hbase、Cassandra和Hypertable。

2.文档存储

文档存储一般采用类似json的格式存储,存储的内容是文档型的。这样可以实现对某些字段建立索引,实现关系数据库的某些功能。典型的文档存储的数据库有MongoDB、CouchDB。

3.Key-Value存储

Key-Value存储这一类数据库主要会使用到哈希表,在这个表中有一个特定的键和一个指针指向特定的数据。Key-Value模型对于IT系统来说优势在于简单、易部署。典型的Key-Value数据库有MemcacheDB、Redis等。

4.图存储

图形结构的数据库同其它行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。典型图存储数据库有Neo4J、InfoGrid、InfiniteGraph等。

5.对象存储

对象存储数据库是通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。典型的数据库有Db4o、Versant等。

6.XML数据库

XML数据库存储XML数据,并支持XML的内部查询语法。典型的数据库有Xquery、Xpath等。

项目实施

应用系统中的数据库随着时间推移,存储的信息越来越大,在对数据库进行读写时,就变得越来越慢,数据库的性能受到严重的挑战。通常的解决方案是采用NoSQL中的缓存数据库(如Redis、Memcached等)将数据库查询的结果保存到内存中,通过内存来直接读写数据,这样就可以减少数据读写的响应时间,降低数据库的负载,提高了数据库读写的性能。

需要完成的任务:

  • Memcached分布式缓存数据库部署与操作。
  • Redis数据库的部署与操作。

7.2 任务1:Memcached分布式缓存数据库部署与操作

Memcached是一个自由开源的,高性能,分布式内存对象缓存系统。它是Danga Interactive公司开发的一款软件。Memcached是一种基于内存的Key-Value存储结构,用来存储小块的任意数据(字符串、对象)。这些数据可以是数据库调用、API调用或者是页面渲染的结果。

Memcached是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的Hashmap。其守护进程(daemon)是用C写的,但是客户端可以用任何语言来编写,并通过Memcached协议与守护进程通信。

7.2.1 Memcached简介

Memcached简洁而强大。它的简洁设计便于快速开发,减轻开发难度,解决了大数据量缓存的很多问题。它的API兼容大部分流行的开发语言。本质上,它是一个简洁的Key-Value存储系统。

1.作用

Memcached的作用是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。

2.特征

Memcached作为高速运行的分布式缓存服务器,它的特点主要有协议简单、基于Libevent的事件处理、内置内存存储方式、Memcached不互相通信的分布式等。

(1)协议简单:Memcached的服务器客户端通信并不使用复杂的MXL等格式,而是使用简单的基于文本的协议。

(2)基于Libevent的事件处理:Libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等时间处理功能封装成统一的接口。Memcached使用这个Libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。

(3)内置内存存储方式:为了提高性能,Memcached中保存的数据都存储在Memcached内置的内存存储空间中。由于数据仅存在于内存中,因此Memcached重启操作系统会导致全部数据消失。另外,内容容量达到指定的值之后Memcached就自动删除不适用的缓存。

(4)Memcached互不通信的分布式:Memcached尽管是分布式缓存服务器,但服务器端并没有分布式功能。各个Memcached不会互相通信以共享信息。他的分布式主要是通过客户端实现的。

3.支持的语言

Memcached能支持的语言有Perl、PHP、Python、Ruby、C#、C/C++、Lua等。

4.Memcached的内存管理

Memcached默认情况下采用了名为Slab Allocatoion的机制分配,管理内存。在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。但是这种方式会导致内存碎片,加重操作系统内存管理器的负担。

Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块,以完全解决内存碎片问题。Slab Allocation的原理相当简单。将分配的内存分割成各种尺寸的块(chucnk),并把尺寸相同的块分成组(chucnk的集合)。

7.2.2 Memcached与配置

Memcached支持许多平台如Linux、FreeBSD、Solaris、Mac OS等,也可以安装在Windows上。

1.在Linux系统安装Memcached

(1)安装libevent库

在Linux系统安装Memcached,首先要先安装libevent库,在CentOS系统下安装libevent库,如图7-2-1所示。代码如下:

sudo apt-get install libevent ibevent-dev   #自动下载安装(Ubuntu/Debian)
yum install libevent libevent-devel   # 自动下载安装(Redhat/Fedora/Centos)                 
图7-2-1 在CentOS系统下安装libevent库

(2)自动安装Memcached

在Ubuntu/Debian系统中自动安装Memcached,代码如下:

sudo apt-get install memcached

在Redhat/Fedora/Centos系统中可以自动安装Memcached数据库,如Centos系统中安装Memcached数据库,如图7-2-2所示,代码如下:

yum install memcached
图7-2-2 在Centos下安装Memcached数据库

(3)源码安装

从官方网站(http://memcached.org)下载Memcached最新版本。源码安装代码如下:

wget http://memcached.org/latest          #下载最新版本
tar -zxvf memcached-1.x.x.tar.gz          # 解压源码
cd memcached-1.x.x                 #进入目录
./configure --prefix=/usr/local/memcached      # 配置
make && make test                 # 编译
sudo make install                 # 安装

(4)查看安装的路径

安装完后可以使用whereis查看命令的路径,如图7-2-3所示。

图7-2-3 查看Memcached安装路径

在图7-2-3中Memcached的安装路径为memcached: /usr/bin/memcached /usr/share/man/man1/。

(5)Memcached运行

Memcached运行,命令格式为“安装路径 memecached –h”,如图7-2-4所示。代码如下:

图7-2- 4 Memcached运行命令

启动选项有:

-d是启动一个守护进程;

-m是分配给Memcache使用的内存数量,单位是MB;

-u是运行Memcache的用户;

-l是监听的服务器IP地址,可以有多个地址;

-p是设置Memcache监听的端口,最好是1024以上的端口;

-c是最大运行的并发连接数,默认是1024;

-P是设置保存Memcache的pid文件。

7.2.3 Memcached数据库操作

一、Memcached的基本操作

1.Memcached的启动

(1)作为前台启动Memcached

在Memcashed安装目录下输入“安装路径/ memcached -m 64 -p 11211 -u nobody –vv”命令启动Memcached,如图7-2-5所示,代码如下:

图7-2- 5 Memcached的启动

代码中-m 64表示内存64M,T监听TCP端口为11211,-vv表示标准输出。

(2)作为后台服务程序运行

如果想让Memcache在后台运行,只需要加-d选项即可,代码如下:

安装路径/ memcached -m 64 -p 11211 -u nobody -d

2.Memcached连接

(1)安装xinetd服务

Telnet服务要依靠xinetd服务启动,所以要先安装xinetd服务,再安装telnet-server。

①检测xinetd是否安装,代码如下:

rpm -qa xinetd

②安装xinetd服务。如果xinetd未安装,则安装xinetd,如图7-2-6所示。代码如下:

图7-2- 6安装xinetd

(2)安装Telnet服务

Telnet分为telnet-client和telnet-server。telnet-client系统一般默认已经安装。telnet-server需要单独安装。注意安装Telnet服务需要在root下安装,命令如下:

yum list telnet*       #列出telnet相关的安装包
yum install telnet-server    #安装telnet服务
yum install telnet.*     #安装telnet客户端

用rpm -qa telnet-server命令,检查telenet-server服务是否安装成功,命令如下:

rpm -qa telnet-server
#或者
rpm -qa | grep telnet

安装好Telnet服务后,需要重启服务。在CentOS 7中,命令如下:

systemctl restart sshd
#或者
systemctl restart xinetd.service

查看服务启动状况,使用ps -a或者grep xinetd或者grep tftp命令。

(3)telnet命令连接Memcached

Memcached连接可以通过telnet命令并指定主机IP和端口来连接Memcached服务。语法如下:

telnet HOST PORT

命令中的 HOST 和 PORT 为运行Memcached服务的IP和端口。

在CentOS中连接Memcached,输入命令“telnet localhost 11211”,如果执行命令时出现报错,如图7-2-7所示。

图7-2-7 在Centos系统telnet报错

报错的原因是在CentOS系统中没有安装Telnet服务,需要安装Telnet服务。 安装好了Telnet服务后,用telnet localhost 11211命令重新连接Memcached,如下图7-2-8所示。代码如下:

图7-2-8 连接Memcached

使用stats命令查看连接状态,如图7-2-9所示。

图7-2- 9 stats查看状态

(4)Memcached中简单的set和get命令

Memcached的简单set和get命令,用法如图7-2-10所示。

图7-2-10 set和get命令

(5)退出telnet连接

退出telnet连接使用quit命令,如图7-2-11所示。

图7-2- 11 quit退出telnet

3.Memcached 存储命令

(1)set 命令

set 命令用于将 value(数据值)存储在指定的 key(键)中。如果set的key已经存在,该命令可以更新该key所对应的原来的数据,也就是实现更新的作用。语法格式:

set key flags exptime bytes [noreply] 
value 

参数说明:

key-键值Key-Value结构中的key,key是缓存名,用于查找缓存值。每个缓存有一个独特的名字和存储空间,key是操作数据的唯一标识,key可以250个字节以内(不能有空格和控制字符),在新版开发计划中提到key可能会扩充到65535个字节。

flags-flag是“标志”的意思,可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息。Memcached存储的数据形式只能是字符串,那么如果要存储“hello”和array('hello','world')怎么办?对于字符串,直接存5个字符即可,对于array,则需要序列化。在取出数据时,字符串取回直接用,数组则需要反序列化成数组。flag的取值范围为0~2^16-1。

exptime-在缓存中保存键值对的时间长度(以秒为单位,0表示永远)。

bytes-在缓存中存储的字节数。

noreply(可选)-该参数告知服务器不需要返回数据。

value-存储的值(始终位于第二行)(可直接理解为Key-Value结构中的value)。

【实例7-2-1】设置key →runoob,flag→0,exptime→900(以秒为单位),bytes→9(数据存储的字节数),value→memcached。代码如下:

set runoob 0 900 9
memcached
STORED
get runoob
VALUE runoob 0 9
memcached
END

如果数据设置成功,则输出STORED,表示保存成功后输出。如果数据设置失败,则输出ERROR,表示在保存失败后输出。

(2)add命令

Memcached add命令用于将value(数据值))存储在指定的key(键)中。如果add的key已经存在,则不会更新数据(过期的key会更新),之前的值将仍然保持相同,还会获得响应NOT_STORED。语法格式:

add key flags exptime bytes [noreply]
value

【实例7-2-2】设置key→ new_key,flag → 0,exptime → 900 (以秒为单位),bytes → 10(数据存储的字节数),value → data_value。代码如下:

add new_key 0 900 10
data_value
STORED

如果数据设置成功,则输出STORED,表示保存成功后输出。如果数据设置失败,则输出NOT_STORED,表示在保存失败后输出。

(3)replace命令

Memcached replace命令用于替换已存在的key的value。如果key不存在,则替换失败,响应为NOT_STORED。语法结构如下:

replace key flags exptime bytes [noreply]
value

【实例7-2-3】设置key → mykey,flag → 0,exptime → 900 (以秒为单位),bytes → 10 (数据存储的字节数),value → data_value。代码如下:

add mykey 0 900 10
data_value
STORED
get mykey
VALUE mykey 0 10
data_value
END
replace mykey 0 900 16
some_other_value
get mykey
VALUE mykey 0 16
some_other_value
END

(4)append命令

Memcached append命令用于向已存在key的value后面追加数据。语法结构如下:

append key flags exptime bytes [noreply]
value

【实例7-2-4】在Memcached中存储一个键runoob,其值为 memcached,使用get命令检索该值,使用append命令在键为runoob的值后面追加“redis”,再使用get命令检索该值。代码如下:

set runoob 0 900 9
memcached
STORED
get runoob
VALUE runoob 0 9
memcached
END
append runoob 0 900 5
redis
STORED
get runoob
VALUE runoob 0 14
memcachedredis
END

如果数据添加成功,则输出STORED表示保存成功后输出,如果输出为NOT_STORED表示该键在Memcached上不存在,如果输出为CLIENT_ERROR表示执行错误。

(5)prepend命令

prepend 命令用于向已存在key的value前面追加数据。语法结构如下:

prepend key flags exptime bytes [noreply]
value

【实例7-2-5】在Memcached中存储一个键 runoob,其值为memcached,get 命令检索该值,使用 prepend 命令在键为runoob的值前面追加 "redis",然后用get命令检索该值。代码如下:

set runoob 0 900 9
memcached
STORED
get runoob
VALUE runoob 0 9
memcached
END
prepend runoob 0 900 5
redis
STORED
get runoob
VALUE runoob 0 14
redismemcached
END

如果数据添加成功,则输出STORED表示保存成功后输出,如果输出为NOT_STORED表示该键在Memcached上不存在,如果输出为CLIENT_ERROR表示执行错误。

(6)CAS命令

CAS(Check-And-Set或Compare-And-Swap)命令用于执行一个“检查并设置”的操作。它仅在当前客户端最后一次取值后,该key对应的值没有被其他客户端修改的情况下,才能够将值写入。检查是通过cas_token参数进行的,这个参数是Memcach指定给已经存在的元素的一个唯一的64位值。语法格式:

cas key flags exptime bytes unique_cas_token [noreply]
value

使用CAS命令,需要通过gets命令获取令牌(token)。gets命令的功能类似于基本的get命令。两个命令之间的差异在于gets返回的信息稍微多一些,64位的整型值非常像名称/值对的“版本”标识符。

【实例7-2-6】添加键值对,通过gets命令获取唯一令牌,使用cas命令更新数据,使用get命令查看数据是否更新。代码如下:

cas tp 0 900 9
ERROR      #缺少 token
cas tp 0 900 9 2
memcached
NOT_FOUND    #键 tp 不存在
set tp 0 900 9
memcached
STORED
gets tp
VALUE tp 0 9 1
memcached
END
cas tp 0 900 5 1
redis
STORED
get tp
VALUE tp 0 5
redis
END

如果没有设置唯一令牌,则CAS命令执行错误,如果键key不存在,执行失败。

4. Memcached查找命令

(1)get命令

get命令获取存储在key中的value,如果key不存在,则返回空,用get命令获取key的值,如图7-2-12所示,语法格式:

get key
#多个key使用空格隔开
get key1 key2 key3
图7-2-12 get命令

(2) gets命令

gets命令获取带有CAS令牌存的value,如果key不存在,则返回空,运用gets获取令牌,如图7-2-13所示,语法格式:

gets key
#多个key使用空格隔开
gets key1 key2 key3
图7-2- 13 gets命令

在图7-2-13中使用gets命令的输出结果中,在最后一列的数字5代表了key为mykey的CAS令牌。

(3)delete命令

delete命令用于删除已存在的key,语法格式:

delete key [noreply]

【实例7-2-7】创建key1键,用get获取该键的值,用delete删除该键,再用get测试。代码如下:

set key1 0 900 5 
hello
STORED
get keys
END
get key1
VALUE key1 0 5
hello
END
delete key1
DELETED
get key1
END

输出信息,如果输出为DELETED则删除成功,如果输出为ERROR则语法错误或删除失败,如果输出为NOT_FOUND则key是不存在的。

(4)incr与decr命令

incr与decr命令用于对已存在的key的数字值进行自增或自减操作。incr与decr命令操作的数据必须是十进制的32位无符号整数。如果key不存在返回NOT_FOUND,如果键的值不为数字,则返回CLIENT_ERROR,其他错误返回ERROR。语法格式:

incr key increment_value #incr语法
decr key decrement_value #decr语法

【实例7-2-8】使用visitors作为key,初始值为10,进行减5操作,再进行加10操作。代码如下:

set visitor 0 900 2
10
STORED
get visitor
VALUE visitor 0 2
10
END
decr visitor 5
5
get visitor
VALUE visitor 0 1
5
END
incr key1 10
15
get key1
VALUE key1 0 2
15
END

5.Memcached统计命令

(1)stats命令

stats命令用于返回统计信息例如PID(进程号)、版本号、连接数等。命令格式如下:

stats

部分状态信息有

pid:Memcache服务器进程ID。

uptime:服务器已运行秒数。

time:服务器当前Unix时间戳。

version:Memcache版本。

pointer_size:操作系统指针大小。

rusage_user:进程累计用户时间。

rusage_system:进程累计系统时间。

curr_connections:当前连接数量。

total_connections:Memcached运行以来连接总数。

connection_structures:Memcached分配的连接结构数量。

(2)stats items命令

stats items命令用于显示各个slab中item的数目和存储时长。用stats cachedump命令列出所有的key,如图7-2-14所示。

图7-2- 14 stats items

(3)stats slabs命令

stats slabs命令用于显示各个slab的信息,包括chunk的大小、数目、使用情况等。命令格式如下:

stats slabs

(4)tats sizes命令

stats sizes命令用于显示所有item的大小和个数。该信息返回两列,第一列是item的大小,第二列是item的个数。命令格式如下:

stats sizes

(5)flush_all命令

flush_all 命令用于清理缓存中的所有key=>value对。该命令提供了一个可选参数time,用于在制定的时间后执行清理缓存操作。语法结构如下:

flush_all [time] [noreply]

【实例7-2-9】清理缓存。

get key2
VALUE key2 0 10
heool11111
END
flush_all
OK
get keys2
END

二、Memcached的内存存储机制

Memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存。在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。但是,这种方式会导致内存碎片,加重操作系统内存管理器的负担,最坏的情况下,会导致操作系统比Memcached进程本身还慢。Slab Allocator就是为解决该问题而诞生的。

1.Slab Allocator的基本原理

Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块,以完全解决内存碎片问题。Slab Allocation 的原理相当简单,将分配的内存分割成各种尺寸的块(chunk),并把尺寸相同的块分成组(chunk 的集合)如图7-2-15所示。

图7-2-15 Slab Allocator结构图

2.Slab Allocator缓存原理

Memcached根据收到的数据的大小,选择最适合数据大小的slab。Memcached中保存着slab内空闲chunk的列表,根据该列表选择chunk,然后将数据缓存于其中,如图7-2-16所示。

图7-2-16 Slab Allocator缓存原理

3.Slab Allocator的缺点

chunks为固定大小,造成浪费,这个问题不能克服,只能缓解。

三、数据过期与删除机制

1.Memcached数据过期

当某个存储在Memcached中的值过期了,只是让用户看不到这个缓存的数据而已,并没有在过期的瞬间立即从内存中删除,而是什么时候需要用到这个位置的时候再删除,这称之为惰性失效机制(lazy expiration)。它的好处在于节省了CPU时间和检测的成本。具体表现在:

(1)当某个值过期后,并没有从内存删除,因此stats统计时,curr_item有信息。

(2)不管之前有没有用get取其值,都不会自动删除。当某个新值去占用它的位置的时,就相当与是chunk来占用。

(3)当get取其值时,如果过期,则返回空,并清空,所以curr_item就减少了。

2.Memcached删除机制

Memcached删除机制的原理是当某个单元被请求时,维护一个计数器通过计数器来判断最近谁最少被使用,就把谁踢出。

7.3 任务2:Redis数据库的部署与操作

Redis(Remote Dictionary Server)是一个开源的、高性能的、基于键值对的缓存存储系统,它提供了多种键值数据类型来适应不同场景下的缓存与存储需求,同时Redis的很多高级功能完成消息队列、任务队列等不同的角色。

7.3.1 Redis简介

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

1.存储结构

Redis数据库是以字典结构存储数据,如dict“key”=“value”中,其中dict就是一个字典结构,字符串“key”表示键名,字符串“value”表示键值,在字典结构中通过键名来获取键值。在Redis数据库中,字典的键值可以设置为字符串,也可以设置为其他数据类型,目前Redis支持的键值的数据类型可以设置为字符串、散列、列表、集合和有序集合等类型。

如在程序中使用post来存储一篇文章的数据(包括标题、正文、阅读量和标签)。

post[“title”]= “hello”;
post[“content”]= “新年好新年好”;
post[“views]= 5;
post[“tags”]= [“php”, “HTML”, “Node.js”, “HTML”];

将这篇文章的数据存入数据库中,并要求通过标签检索文章。如果使用关系数据库来存储,会将标题、正文、阅读量存储到一个表中,将标签存储到另一个表中,然后通过第三个表将标签和文章连接。在查询数据时,将3个表进行连接,查询数据时不是很直观。采用Redis数据库存储数据,采用字典结构存储数据,直接将文章中的数据直接映射到Redis中,如采用集合类型直接存储文章标签。这样数据在Redis中的存储方式与程序中的存储方式相近,可以为程序提供非常方便的操作。

2.内存存储与持久化

Redis数据库中的所有数据都存储在内存中,内存的访问速度要远高于硬盘,因此Redis在性能上比基于硬盘存储的数据库中在读写数据速度上有明显的优势。一台普通的电脑,Redis可以在1秒读写超过10万个键值。

将数据存在内存也会存在如程序退出后内存中的数据就会丢失的危险,Redis数据库提供了持久化的存储,可以同步将内存中的数据异步写入硬盘中,不影响后续的服务。

3.功能丰富

Redis虽然是作为数据库进行开发,但其提供了丰富的功能,越来越多的人将其用于缓存、队列系统等。

Redis为每一个键设置了生存时间,生存时间到期后,键就会自动被删除,这一功能使得Redis可以作为缓存系统来使用。作为存储系统,Redis可以限定数据占用的最大的内存空间,当数据在空间上受限制后可以按照一定的规则自动淘汰不需要的键。Redis的列表类型键可以用来实现消息队列,并且支持阻塞式读取,可以很容易实现一个高性能的优先队列,Redis还支持“发布/订阅”的消息模式。

4.使用简单

Redis直观的存储结构使得通过程序与Redis交互十分容易,在Redis中使用命令来读写数据。如在Redis中读取键名为post:1的散列类型键的title字段值,可以使用如下命令:

HGET post:1 title

HGET是一个读取命令。Redis提供了100多个命令,但是常用的命令却只有十几个。

7.3.2 Redis的安装与配置

安装Redis是开始学习Redis的第一步。Redis兼容大部分的POSIX,如Linux、OS X和BSD等,在这些系统中,推荐直接下载Redis源代码编译安装以获得最新的稳定版本,下载源代码可以直接到官网下载。

一、在Centos中安装Redis

1.安装Redis

在Centos系统中安装Redis,安装的Redis版本为6.0.6版本,用“wget”命令下载,下载后用“tar xzf”命令解压,用“cd”进入“redis-6.0.6”目录,用“make”进行编译。代码如下:

$ wget http://download.redis.io/releases/redis-6.0.6.tar.gz
$ tar xzf redis-6.0.6.tar.gz
$ cd redis-6.0.6
$ make

Redis数据库下载过程,如下图7-3-1所示。

图7-3-1下载Redis

下载完成后,用“tar xzf”命令来解压,用“ls”命令查看解压后的文件夹“redis-6.0.6”,如图7-3-2所示。

图7-3- 2 解压Redis安装包

用命令“cd”,进入“redis-6.0.6”文件夹,用“make”进行编译。在“make”前用“yum”命令安装GCC编译,yum安装的GCC是4.8.5版本。“yum”安装GCC命令如下:

$ yum -y install gcc gcc-c++

在“make”编译过程中由于GCC版本过低会导致编译不成功,因为yum安装的GCC是4.8.5的。因此需要升级GCC,升级命令如下:

yum -y install Centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
#这句是临时的
scl enable devtoolset-9 bash
#修改环境变量
echo "source /opt/rh/devtoolset-9/enable" >> /etc/profile
gcc -v

GCC升级后,把解压后的“redis-6.0.6”的文件通过“rm -rf ” 命令删除。删除后用“$ tar xzf redis-6.0.6.tar.gz”重新解压,重新“make”编译,这时Redis数据库就安装成功了。在“redis-6.0.6”文件夹的src目录下存放着Redis服务程序redis-server,还有用于测试的客户端程序redis-cli,如图7-3-3所示。

图7-3-3 Redis数据库安装成功

2.启动Redis服务

安装好Redis后,就可以启动它。先了解Redis的可执行文件,见表7-3-1所示。

表7-3-1 Redis可执行文件

在表7-3-1中的可执行文件中,最常用的两个程序为redis-server和redis-cli,其中redis-server是Redis服务器,启动Redis服务就需要运行redis-server程序,redis-cli是自带的客户端命令行工具,是学习Redis数据库重要的工具。

进入安装目录下的src文件夹,执行redis-server可执行文件,就可以启动Redis服务了,命令如下:

$ cd src
$ ./redis-server

启动Redis服务,界面如图7-3-4所示。

图7-3-4 启动Redis服务

【注意】这种方式启动redis 使用的是默认配置。也可以通过启动参数告诉Redis使用指定配置文件,命令如下:

$ cd src
$ ./redis-server ../redis.conf

redis.conf 是一个默认的配置文件。可以根据需要使用自己的配置文件。

3.启动Redis的客户端程序

启动Redis服务进程后,就可以使用测试客户端程序redis-cli,代码如下:

$ cd src
$ ./redis-cli

进入src目录,键入“./redis-cli”就可以使用客户端程序,如图7-3-5所示。其中服务器地址为127.0.0.1,默认端口号为6379,通过-h和-p参数可以自定义地址和端口号。

图7-3- 5 启动客户端程序

4.停止Redis

在停止Redis服务时,需要将内存中的数据同步到硬盘中,强行终止Redis进程可能会导致数据丢失,正确停止Redis的方式为先向Redis发送“SHUTDOWN”命令,命令如下:

$redis-cli SHUTDOWN

当Redis收到“SHUTDOWN”命令后,会先断开所有客户端连接,如图7-3-6所示。然后根据配置执行持久化,最后完成退出。

图7-3-6停止Redis服务

二、Redis的配置

Redis的配置文件位于Redis安装目录下,文件名为 redis.conf(Windows 名为 redis.windows.conf)。可以通过 CONFIG 命令查看或设置配置项,Redis CONFIG命令格式如下:

redis 127.0.0.1:6379> CONFIG GET CONFIG_SETTING_NAME

【实例7-3-1】获取loglevel配置项的值,代码如下:

127.0.0.1:6379> CONFIG GET loglevel
1) "loglevel"
2) "notice"

使用 * 号获取所有配置项,代码如下:

127.0.0.1:6379> CONFIG GET *

通过修改redis.conf文件或使用 CONFIG set 命令来修改配置。 CONFIG set语法如下:

127.0.0.1:6379> CONFIG SET CONFIG_SETTING_NAME NEW_CONFIG_VALUE

【实例7-3-2】修改loglevel配置项的值为“notice”,代码如下:

127.0.0.1:6379> CONFIG SET loglevel "notice"

7.3.3 Redis数据库基本操作

Redis数据库的基本操作主要包括Redis的键名、数据类型、事务、分布订阅等内容。

一、Redis键名

1. 获得符合规则的键名列表

KEYS patten

patten支持glob风格统配符格式,见表7-3-2所示。

表7-3- 2 glob风格通配符规则

第一次启动redis-cli客户端,Redis数据库是空空的,先使用SET命令,创建一个名为bar的键,代码如下:

127.0.0.1:6379> set bar 1
OK

使用KEYS 就能获得Redis中所有的键,也可以使用KEYS ba或者KEYS bar等命令获得键,代码如下:

127.0.0.1:6379> keys *
1) "bar"

【注意】Redis不区分命令的大小写。

【注意】KEYS命令是遍历Redis中所有的键,当键的数量较多时会影响性能。

2.判断一个键是否存在

EXISTS key

如果键存在则返回1,否则返回0,代码如下:

127.0.0.1:6379> exists bar
(integer) 1
127.0.0.1:6379> exists baa
(integer) 0

3.删除键

DEL key[key…]

删除键时,可以删除一个或多个,返回值的删除的键的个数,代码如下:

127.0.0.1:6379> del bar
(integer) 1

4.获取键值的数据类型

获取键值的数据类型,采用type命令,返回值可能为String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(Sorted Set:有序集合)。代码如下:

127.0.0.1:6379> type bar
string
127.0.0.1:6379> type car
Hash

二、Redis数据类型

Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(Sorted Set:有序集合)。

1.字符串类型

字符串是Redis中最基本的数据类型,它可以存储任何形式的字符串,包含二进制数据。可以存储用户的邮箱、Json化的对象、图片。一个字符串类型键最大允许存储的数据容量为512M。

(1)字符串的赋值与取值

SET key value
GET key

SET和GET是Redis中较简单的两个命令,实现的功能为读写变量,如当键不存在时,在返回为空。代码如下:

127.0.0.1:6379> get f1
"1"
127.0.0.1:6379> set f2 3
OK
127.0.0.1:6379> get f3
(nil)
127.0.0.1:6379> set ke hello
OK
127.0.0.1:6379> get ke
"hello"

代码中“(nil)”表示返回为空。

(2)速增数字

INCR key

字符型类型可以存储任何形式的字符串,当存储字符串为整数形式时,Redis提供了incr命令,让其键值递增,并返回递增后的值。代码如下:

127.0.0.1:6379> incr f2
(integer) 4
127.0.0.1:6379> incr f2
(integer) 5

如操作键不存在时会默认为0,第一次递增后会为1。当键值不是整数时,会提示错误。代码如下:

127.0.0.1:6379> set k1 hello
OK
127.0.0.1:6379> incr k1
(error) ERR value is not an integer or out of range

(3)减少指定整数

DECR key
decr命令与incr命令用法相同,代码如下:
127.0.0.1:6379> get f2
"5"
127.0.0.1:6379> decr f2
(integer) 4

(4)获取字符串的长度

STRLEN key

strlen命令是返回键值的长度,如果键不存在则返回为0。代码如下:

127.0.0.1:6379> get b1
"helloword"
127.0.0.1:6379> strlen b1
(integer) 9

(5)同时设置/获取多个值

MEST key value [key value…]

mget/mset与get/set命令相似,不过mget/mset可以获得/设置多个键的值。代码如下:

127.0.0.1:6379> mset p1 12 p3 13 p4 15
OK
127.0.0.1:6379> mget p1 p3 p4
1) "12"
2) "13"
3) "15"

2.散列类型

散列类型(Hash)的键值是一种字典结构,其存储字段和字段值的映射,但字段值只能是字符串,不支持其他数据类型,换句话说散列类型不能嵌套其他数据类型。一个散列类型值可以包含232-1个字段。

【注意】除了散列类型,Redis的其他数据类型同样不支持数据类型嵌套,如集合类型,每一个元素只能是字符串,不能是另一个集合或散列表等。

散列类型适合存储对象,使用对象和ID构成键名,使用字段表示对象的属性,而字段值则存储属性值。如ID为1的汽车对象,它的属性有color、name、price,其属性值分别为白色、奥迪、90万,如图7-3-7所示。

图7-3-7 散列类型存储汽车对象结构图

(1)赋值与取值

hset命令给字段赋值,用hget命令来获取字段的值,代码如下:

127.0.0.1:6379> hset car:2 color red
(integer) 1
127.0.0.1:6379> hset car:2 name BMW
(integer) 1
127.0.0.1:6379> hset car:2 price 500
(integer) 1
127.0.0.1:6379> hget car:2 name
"BMW"

hmset命令可以同时给多个字段赋值,hmget命令可以同时获得多个字段的值,语法格式如下:

HMSET key fileds1 value1 fileds2 value2
HMGET key fileds1 fileds2

采用hmget命令同时获取多个字段的值,代码如下:

127.0.0.1:6379> hmget car:2 name color price
1) "BMW"
2) "red"
3) "500"

hgetall命令获取所有字段和字段值,代码如下:

127.0.0.1:6379> hgetall car:2
1) "color"
2) "red"
3) "name"
4) "BMW"
5) "price"
6) "500"

(2)判断字段是否存在

hexists命令判断一个字段是否存在,如果存在在返回1,不存在则返回为0,代码如下:

127.0.0.1:6379> hexists car:2 color
(integer) 1
127.0.0.1:6379> hexists car:2 col
(integer) 0

(3)增加数字

在散列数据类型中增加数字采用hincrby命令,hincrby命令可以使字段增加指定的整数,通过hincrby key field 1来实现,代码如下:

127.0.0.1:6379> hincrby person score 60
(integer)60

如果person键不存在,使用hincrby命令会自动建立该键并默认score字段在执行命令前的值为“0”,命令后返回值为增值后的字段值。

(4)删除字段

hdel命令可以删除一个或多个字段,返回值是被删除字段的个数。代码如下:

127.0.0.1:6379> hdel car:1 price
(integer) 1

(5)获取字段名或字段值

hkeys命令是获取字段名,hvals命令是获取字段值。代码如下:

127.0.0.1:6379> hkeys car:2
1) "color"
2) "name"
3) "price"
127.0.0.1:6379> hvals car:2
1) "red"
2) "BMW"
3) "500"
(6)获取字段数量
hlen命令获取字段的数量。代码如下:
127.0.0.1:6379> hlen car:2
(integer) 3

3.列表类型

列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获取列表的某一个片段。

列表类型内部是使用双向链表来实现的,所以向两端添加元素的时间复杂度为O(1),获得越接近亮度的元素速度越快,这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是十分快的。不过使用链表通过索引访问元素是比较慢的,但通过在链表尾部插入数据,是比较快的。

这种特性使列表类型能非常迅速完成关系数据库难以应付的场景,如社交网站的新鲜事,只关心最新的内容,用链表类型来存储,即使新鲜事有几千万个,获取其中最新鲜100条数据也是极快的,两端插入记录时间复杂度为O(1),列表类型也适合用来记录日志,保证加入的新日志不会受到已有日志数量的影响。

(1)lpush、rpush向列表两端增加元素。

LPUSH key value[value…]
RPUSH key value[value…]

lpush命令用来向列表左边增加运算,返回增加元素后列表的长度。rpush是向列表右边添加元素,返回增加元素后列表的长度。代码如下:

127.0.0.1:6379> lpush num 1 2
(integer) 2
127.0.0.1:6379> rpush num 5 6
(integer) 4

lpush会先向列表左边追加“1”,然后再加入“2”,如图7-3-8所示。

图7-3-8 LPUSH增加元素结构图

(2)lpop、rpop从列表两边弹出元素。

LPOP key
RPOP key

lpop命令是从列表左边弹出一个元素,rpop是从列表右边弹出一个元素。代码如下:

127.0.0.1:6379> lpop num
"2"
127.0.0.1:6379> rpop num
"6"

(3)blpop移出并获取列表第一个元素。

BLPOP key1 [key2 ] timeout 

blpop移出并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。代码如下:

127.0.0.1:6379> blpop num 500
1) "num"
2) "4"

(4)brpop移出并获取列表的最后一个元素。

BRPOP key1 [key2 ] timeout 

brpop移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。代码如下:

127.0.0.1:6379> brpop num 500
1) "num"
2) "5"

(5)brpoplpush命令从列表中取出最后一个元素。

BRPOPLPUSH source destination timeout 

从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它;如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。代码如下:

127.0.0.1:6379> BRPOPLPUSH msg reciver 500
"hello moto"            # 弹出元素的值
(3.38s)               # 等待时长
redis> LLEN reciver
(integer) 1
redis> LRANGE reciver 0 0
1) "hello moto"

假如在指定时间内没有任何元素被弹出,则返回一个nil和等待时长。反之,返回一个含有两个元素的列表,第一个元素是被弹出元素的值,第二个元素是等待时长。

(6)llen返回列表的长度。

llen命令用于返回列表的长度。如果列表key不存在,key被解释为一个空列表,返回0。如果key不是列表类型,返回一个错误。代码如下:

127.0.0.1:6379> llen num
(integer) 0
127.0.0.1:6379> llen num1
(integer) 1

(7)lrange返回列表中指定区间内的元素。

lrange返回列表中指定区间内的元素,区间以偏移量start和end指定。其中0表示列表的第一个元素,1表示列表的第二个元素,以此类推。也可以使用负数下标,以-1表示列表的最后一个元素,-2表示列表的倒数第二个元素,以此类推。语法结构如下:

127.0.0.1:6379> LRANGE KEY_NAME START END
用lrange命令,返回列表num中0到4个元素,代码如下:
127.0.0.1:6379> lrange num 0 4
1) "66"
2) "55"
3) "44"
4) "33"
5) "22"

(8)lrem移除列表的元素。

lrem根据参数count的值,移除列表中与参数value相等的元素。count的值可以是以下几种:count > 0 表示从表头开始向表尾搜索,移除与value相等的元素,数量为count;count < 0 表示从表尾开始向表头搜索,移除与value相等的元素,数量为count的绝对值;count = 0表示移除表中所有与value等的值。语法结构如下:

127.0.0.1:6379> LREM KEY_NAME COUNT VALUE

用lrem命令,删除num列表中2个22的元素,代码如下:

127.0.0.1:6379> lrem num 2 22
(integer) 1
127.0.0.1:6379> lrange num 0 4
1) "66"
2) "55"
3) "44"
4) "33"

4.集合类型

Redis的集合(Set)是String类型的无序集合。集合成员是唯一的,意味着集合中不能出现重复的数据。Redis中集合是通过哈希表实现的,它的添加、删除、查找的复杂度都是O(1)。集合中最大的成员数为232-1个。

(1)sadd成员元素加入到集合。

sadd命令将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略。假如集合key不存在,则创建一个只包含添加的元素做成员的集合。当集合key不是集合类型时,返回一个错误。语法结构如下:

127.0.0.1:6379> SADD KEY_NAME VALUE1..VALUEN

创建一个str集合,并赋值,代码如下:

127.0.0.1:6379> sadd str hello
(integer) 1
127.0.0.1:6379> sadd str foot
(integer) 1
127.0.0.1:6379> sadd str sss
(integer) 1

(2)scard 返回集合中元素的数量。

scard命令返回集合中元素的数量,代码如下:

127.0.0.1:6379> scard str
(integer) 3

(3)sdiff 返回差集。

sdiff命令返回给定集合之间的差集。不存在的集合key将视为空集。差集的结果来自前面的first_key,而不是后面的other_key1,也不是整个first_key other_key1..other_keyn的差集。语法结构如下:

127.0.0.1:6379> SDIFF FIRST_KEY OTHER_KEY1..OTHER_KEYN 

用sdiff命令,返回str和str1之间的差集,代码如下:

127.0.0.1:6379> sadd str1 ss
(integer) 1
127.0.0.1:6379> sadd str1 foor
(integer) 1
127.0.0.1:6379> sadd str1 foot
(integer) 1
127.0.0.1:6379> scard str1
(integer) 3
127.0.0.1:6379> sdiff str str1
1) "sss"
2) "hello"

(4)sinter返回集合的交集。

sinter命令返回所有给定集合的交集。不存在的集合key被视为空集,当给定集合当中有一个空集时,结果也为空集。语法格式:

127.0.0.1:6379> SINTER KEY KEY1..KEYN 

用sinter命令,返回str和str1之间的交集,代码如下:

127.0.0.1:6379> sinter str str1
1) "foot"

(5)sinterstore 将集合之间的交集存储在指定的集合。

sinterstore 命令将给定集合之间的交集存储在指定的集合中。如果指定的集合已经存在,则将其覆盖。语法格式:

127.0.0.1:6379> SINTERSTORE DESTINATION_KEY KEY KEY1..KEYN 

运用sinterstore将str和str1集合的交集,存在str3集中中,代码如下:

127.0.0.1:6379> scard str
(integer) 2
127.0.0.1:6379> scard str1
(integer) 3
127.0.0.1:6379> sinterstore str3 str str1
(integer) 1

(6)sismember判断成员元素是否是集合的成员。

sismember 命令判断成员元素是否是集合的成员,如果成员元素是集合的成员,返回1。如果成员元素不是集合的成员,或key不存在,返回0。语法结构如下:

127.0.0.1:6379> SISMEMBER KEY VALUE 

运用 sismember命令判断“foot”是否str集合中成员,代码如下:

127.0.0.1:6379> sismember str foot
(integer) 1

(7)smembers返回集合中的所有的成员。

smembers命令返回集合中的所有的成员。不存在的集合key被视为空集合。语法格式如下:

127.0.0.1:6379> SMEMBERS key

运用 smembers命令返回str集合中所有成员,代码如下:

127.0.0.1:6379> smembers str
1) "hello"
2) "foot"

(8) smove移动到指定的集合。

smove命令将指定成员member元素从source集合移动到destination集合。如果source集合不存在或不包含指定的member元素,则smove命令不执行任何操作,仅返回0。否则,member元素从source集合中被移除,并添加到destination集合中去。当destination集合已经包含member元素时,smove命令只是简单地将source集合中的member元素删除。当source或destination不是集合类型时,返回一个错误。语法格式如下:

127.0.0.1:6379> SMOVE SOURCE DESTINATION MEMBER 

运用smove命令将str集合中的“foot”元素移到ss集合中,代码如下:

127.0.0.1:6379> smembers str
1) "hello"
2) "foot"
127.0.0.1:6379> smove str ss foot
(integer) 1
127.0.0.1:6379> smembers ss
1) "foot"
127.0.0.1:6379> smembers str
1) "hello"

(9)spop移除元素。

spop命令用于移除集合中的指定key的一个或多个随机元素,移除后会返回移除的元素。该命令类似 srandmember 命令,但spop将随机元素从集合中移除并返回,而srandmember则仅仅返回随机元素,而不对集合进行任何改动。语法格式如下:

SPOP key [count]

运用spop命令移出str集中的2个元素,代码如下:

127.0.0.1:6379> sadd str ff
(integer) 1
127.0.0.1:6379> sadd str foot
(integer) 1
127.0.0.1:6379> sadd str yy
(integer) 1
127.0.0.1:6379> sadd str ll
(integer) 1
127.0.0.1:6379> smembers str
1) "yy"
2) "ll"
3) "foot"
4) "ff"
127.0.0.1:6379> spop str 2
1) "yy"
2) "foot"
127.0.0.1:6379> smembers str
1) "ll"
2) "ff"

(10)srandmember返回集合中的随机元素。

srandmember命令用于返回集合中的一个随机元素。如果count为正数,且小于集合基数,那么命令返回一个包含count个元素的数组,数组中的元素各不相同。如果count大于等于集合基数,那么返回整个集合。语法格式:

SRANDMEMBER KEY [count]

运用srandmember命令返回str集合中随机元素,代码如下:

127.0.0.1:6379> srandmember str
"ff"
127.0.0.1:6379> srandmember str
"ll"

(11)srem移除集合的元素。

srem命令用于移除集合中的一个或多个成员元素,不存在的成员元素会被忽略。语法结构如下:

127.0.0.1:6379> SREM KEY MEMBER1..MEMBERN

运用srem命令移出str集合中元素,代码如下:

127.0.0.1:6379> srem str ff
(integer) 1
127.0.0.1:6379> smembers str
1) "ll"

5.有序集合

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

(1)zadd加入有序集合。

zadd命令用于将一个或多个成员元素及其分数值加入到有序集当中。如果某个成员已经是有序集的成员,那么更新这个成员的分数值,并通过重新插入这个成员元素,来保证该成员在正确的位置上。分数值可以是整数值或双精度浮点数。如果有序集合key不存在,则创建一个空的有序集并执行zadd操作。当key存在但不是有序集类型时,返回一个错误。语法格式:

127.0.0.1:6379> ZADD KEY_NAME SCORE1 VALUE1.. SCOREN VALUEN

运用zadd命令,将一个或多个成员添加myzset集合中,代码如下:

127.0.0.1:6379> ZADD myzset 1 "one"
(integer) 1
127.0.0.1:6379> ZADD myzset 1 "uno"
(integer) 1
127.0.0.1:6379> ZADD myzset 2 "two" 3 "three"
(integer) 2
127.0.0.1:6379> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "uno"
4) "1"
5) "two"
6) "2"
7) "three"
8) "3"

(2) zcard计算集合的元素的数量。

zcard命令用于计算集合中元素的数量,当key存在且是有序集类型时,返回有序集的基数。当key不存在时,返回0。语法格式如下:

127.0.0.1:6379> ZCARD KEY_NAME

运用zcard命令,计算集合中元素的数量,代码如下:

127.0.0.1:6379> ZADD myzset 1 "one"
(integer) 1
127.0.0.1:6379> ZADD myzset 2 "two"
(integer) 1
127.0.0.1:6379> ZCARD myzset
(integer) 2
redis> 

(3)zcount命令用于计算有序集合中指定分数区间的成员数量。

语法结构如下:

127.0.0.1:6379> ZCOUNT key min max

分数值在min和max之间的成员的数量。用zcount命令计算myzset集合中区间的成员数量,代码如下:

127.0.0.1:6379> ZADD myzset 1 "hello"
(integer) 1
127.0.0.1:6379> ZADD myzset 1 "foo"
(integer) 1
127.0.0.1:6379> ZADD myzset 2 "world" 3 "bar"
(integer) 2
127.0.0.1:6379> ZCOUNT myzset 1 3
(integer) 4

(4) zrangebylex命令通过字典区间返回有序集合的成员。

语法格式:

127.0.0.1:6379> ZRANGEBYLEX key min max [LIMIT offset count]

运用zrangebylex命令,返回myzset集合中的成员,代码如下:

127.0.0.1:6379> ZADD myzset 0 a 0 b 0 c 0 d 0 e 0 f 0 g
(integer) 7
127.0.0.1:6379> ZRANGEBYLEX myzset - [c
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> ZRANGEBYLEX myzset - (c
1) "a"
2) "b"
127.0.0.1:6379> ZRANGEBYLEX myzset [aaa (g
1) "b"
2) "c"
3) "d"
4) "e"
5) "f"

(5) zrem移除有序集的成员。

zrem命令用于移除有序集中的一个或多个成员,不存在的成员将被忽略。当key存在但不是有序集类型时,返回一个错误。语法格式:

127.0.0.1:6379> ZREM key member [member ...]

运用zrem命令,移出集合中的成员,代码如下:

#测试数据
127.0.0.1:6379> ZRANGE page_rank 0 -1 WITHSCORES
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"
5) "google.com"
6) "10"
# 移除单个元素
redis 127.0.0.1:6379> ZREM page_rank google.com
(integer) 1
127.0.0.1:6379> ZRANGE page_rank 0 -1 WITHSCORES
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"
# 移除多个元素
127.0.0.1:6379> ZREM page_rank baidu.com bing.com
(integer) 2
127.0.0.1:6379> ZRANGE page_rank 0 -1 WITHSCORES
(empty list or set)
# 移除不存在元素
127.0.0.1:6379> ZREM page_rank non-exists-element
(integer) 0

(6) zremrangebylex 移除给定字典区间的成员。

zremrangebylex 命令用于移除有序集合中给定的字典区间的所有成员。语法结构如下:

127.0.0.1:6379> ZREMRANGEBYLEX key min max

运用 zremrangebylex命令移出有序集合中的成员,代码如下:

127.0.0.1:6379> ZADD myzset 0 aaaa 0 b 0 c 0 d 0 e
(integer) 5
127.0.0.1:6379> ZADD myzset 0 foo 0 zap 0 zip 0 ALPHA 0 alpha
(integer) 5
127.0.0.1:6379> ZRANGE myzset 0 -1
1) "ALPHA"
2) "aaaa"
3) "alpha"
4) "b"
5) "c"
6) "d"
7) "e"
8) "foo"
9) "zap"
10) "zip"
127.0.0.1:6379> ZREMRANGEBYLEX myzset [alpha [omega
(integer) 6
127.0.0.1:6379> ZRANGE myzset 0 -1
1) "ALPHA"
2) "aaaa"
3) "zap"
4) "zip"

(7)zrevrank命令返回有序集中成员的排名。有序集成员按分数值递减(从大到小)排序,分数值最大的成员排名为0。使用zrank命令可以使成员按分数值递增的方式进行排列。

语法格式如下:

127.0.0.1:6379> ZREVRANK key member

运用 zrevrank命令实现集合的成员的排名,代码如下:

127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES   # 测试数据
1) "jack"
2) "2000"
3) "peter"
4) "3500"
5) "tom"
6) "5000"
127.0.0.1:6379> ZREVRANK salary peter   # peter 的工资排第二
(integer) 1
127.0.0.1:6379> ZREVRANK salary tom    # tom 的工资最高
(integer) 0

(8) zscore命令返回有序集中成员的分数值。如果成员元素不是有序集key的成员,或key不存在,返回nil。语法格式:

127.0.0.1:6379> ZSCORE key member

使用 zscore命令返回有序集合中的成员分数值,代码如下:

127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES  # 测试数据
1) "tom"
2) "2000"
3) "peter"
4) "3500"
5) "jack"
6) "5000"
127.0.0.1:6379> ZSCORE salary peter       # 注意返回值是字符串
"3500"

三、Redis事务

Redis中的事务是由一组命令的集合。事务如同命令一样都是Redis最小的执行单位,一个事务中命令要么执行,要么不执行。事务的应用十分普遍,如银行转账过程中,A给B转款,首先系统从A账号中将钱划走,然后向B账户增加相应的金额,这两个步骤属于同一个事务,要么全执行,要么不执行。

在Redis中事务的原理是先将属于一个事务的命令发送给Redis,然后再让Redis依次执行这些命令。一个事务从开始到执行会经历以下三个阶段分别为开始事务、命令入队、执行事务。

【实例7-3-3】Redis处理过程,代码如下:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd "user:1:following" 2
QUEUED
127.0.0.1:6379> sadd "user:2:following" 1
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1

实例7-3-3中演示了事务的使用方式,使用multi命令告诉Redis,下面命令属于同一个事务,先不要执行,而是暂时存起来。使用sadd命令来实现两个关注与被关注的操作,这时Redis没有执行这些命令,而是返回queued表示这两条命令进入等待执行的事务队列了。使用exec命令告诉Redis将等待事务队列的所有命令按照发送顺序依次执行,exec命令的返回值就是这些命令的返回值组成的列表,返回值顺序和命令的顺序是相同的。

Redis保证了一个事务中的所有命令要么都执行,要么都不执行。如果在发送exec命令前客户端断线了,则Redis会清空事务队列,事务中的所有命令都不会执行,而一旦客户端发送了exec命令,所有的命令都会执行,即使此后和客户端断线了也没有关系,因为Redis中已经记录了所有要执行的命令。

1.错误处理

如果事务中的某个命令执行出错,Redis会怎样处理呢?

【实例7-3-4】语法出错的处理,代码如下:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set key alue
QUEUED
127.0.0.1:6379> set kye #语句有错
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> get key
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.

在7-3-4代码中,multi命令后执行后3个命令,其中有2个正确命令,能成功加入事务队列,有一个错误的命令,不能加入事务队列。执行exec命令后,Redis就会直接返回一个错误,连正确的命令也不会执行。

2. watch命令

watch命令用于监视一个或多个key,如果在事务执行之前key被其他命令所改动,那么事务将被打断,返回为OK。语法结构如下:

WATCH key [key ...]

【实例7-3-5】WATCH监控key值,代码如下:

127.0.0.1:6379> set key 1
OK
127.0.0.1:6379> watch key
OK
127.0.0.1:6379> set key 2
OK
127.0.0.1:6379> watch key
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set key 3
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get key
"2"

上列代码执行watch命令后,事务执行前修改了key的值(即为key=2),所以最后事务中的命令set key 3没有执行,exec返回为空结果。

四、Redis发布订阅

Redis发布订阅(pub/sub)是一种消息通信模式,发送者(pub)发送消息,订阅者(sub)接收消息。Redis客户端可以订阅任意数量的频道。

如频道channel1及订阅这个频道的三个客户端client2、client5和client1之间的关系,如图7-3-9所示。

图7-3-9频道 channel1与订阅客户端的关系

当有新消息通过publish命令发送给频道channel1时,这个消息就会被发送给订阅它的三个客户端,如图7-3-10所示。

图7-3- 10 PUBLISH命令发送给频道channel1

以下实例演示了发布订阅是如何工作的。创建了订阅频道名为redisChat。代码如下:

127.0.0.1:6379> SUBSCRIBE redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1

接着重新开启个Redis客户端,然后在同一个频道redisChat发布两次消息,如图7-3-11所示,订阅者就能接收到消息,如图7-3-12所示。代码如下:

127.0.0.1:6379> publish redisChat "hello"
(integer) 1
127.0.0.1:6379> publish redisChat "apple"
(integer) 1
# 订阅者的客户端会显示如下消息
127.0.0.1:6379> subscribe redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
1) "message"
2) "redisChat"
3) "hello"
1) "message"
2) "redisChat"
3) "apple"
图7-3-11开启另一个Redis客户端发送信息
图7-3-12 订阅者接受消息

本章小结

本章主要讲解了NoSQL数据库概述、Memcaches分布式缓存数据库部署与操作、Redis数据库部署与操作。通过本章的学习,读者应掌握NoSQL的数据库分类、Memcached数据库的安装与配置、Memcached数据库的基本操作、Redis数据库的安装与配置、Redis数据库的基本操作等。

本章习题

一、单项选择题

1.NoSQL,是指( )数据库。

A.关系型数据库

B.非关系型数据库

C.分布式数据库

D.文档数据库

2.Memcached作为高速运行的分布式缓存服务器,它是基于( )的事件处理。

A.libevent

B.button

C.ActionEvent

3.Memcached通过( )方式进行连接。

A. FTP

B.TELNET

C.Mail

D.HTTP

4.通过( )启动Redis服务。

A.redis-server

B.redis-cli

C.redis

D.Server

5.Redis的测试客户端程序( )。

A.redis-server

B.redis-cli

C.redis

D.Server

二、多选题

1.NoSQL数据库的缺点( )。

A. 没有标准

B. 没有存储过程

C. 没有二维表

D. 分布式

2.NoSQL数据库的分类( )。

A. 列存储

B. 文档存储

C. Key-Value存储

D. 图存储

3.在Redis数据库中创建的键命令有( )。

A.set

B.MEST

C.get

D.decr