这篇文章我们主要是想介绍 Redis
的使用,现在开发中 Redis
是使用的非常频繁的,一般都是作为缓存来使用,进而减小用户请求对数据库的压力,这样就能使系统运行更加稳定,其实 Redis
数据库中保存的就是比较固定的数据,比如用户的信息,当有请求想要访问某个用户的信息时,就可以直接从 Redis
缓存中获取得到了,而不用每次都去查询数据库了,这样数据库的压力就减轻了,同时当用户信息有修改时,我们也同时修改 Redis
缓存中的信息,这样的话就相当于对系统中数据的存储进行了优化。
1.介绍
Redis
也是一种数据库,不过是一种 NoSQL
数据库,也就是 Not Only SQL
的意思,一般的数据库大多都是文件系统的,而 Redis
数据库则不仅有文件系统,同时也有内存,可以将数据保存在内存中,同时也可以持久化到硬盘上面,Redis
数据库中的数据都是以 key-value
这种键值对的形式进行保存的,Redis
这样的缓存数据库有下面 3
种特点:
1.支持数据的持久化。数据不仅可以保存在内存中,同时也可以保存在磁盘中,并且可以进行交互。
2.不仅支持保存简单的字符串string格式的数据,还支持list、set、zset、hash等多种格式的数据。
3.支持数据的备份。也就是主从模式的备份。
同时,Redis
相对于其它的缓存数据库来说,还具有以下优势:
1.性能好。读写速度都非常快。
2.丰富的数据类型。也就是我们上面所提到的5种数据类型。
3.原子性操作。Redis中所有的操作都是原子性的,而且还支持几个操作合并后的原子性执行。
4.丰富的特性。Redis还支持publish/subscribe,通知以及key过期等高级特性。
2.安装
现在我们就来看如何安装 Redis
数据库,安装之前当然是需要先下载了,下载的话我们就可以直接到官网进行下载了,也就是 https://redis.io/
这个网站了,进入网站在导航栏会有一个 Download
按钮,点击该按钮就可以进入到下载页面了,进入下载页面之后我们就能看到不同版本的 Redis
供我们下载了,我们这时候就可以选择稳定版 (Stable)
进行下载了,就是点击 Stable
那一栏下面的 Download
按钮了,当然这样下载的话,肯定就是下载到 Windows
系统中了,而我们是需要在 Linux
系统中进行安装的,因此还需要将该下载的文件上传到 Linux
系统中,不过也可以直接在 Linux
系统中进行下载,那就是直接登录 Linux
系统,然后进入到期望进行安装操作的目录中,直接使用 wget
命令进行下载,比如:
wget http://download.redis.io/releases/redis-3.2.8.tar.gz
这样的话,同样也是可以进行下载操作的,不过需要保证是有网络环境的,这样操作之后,我们的安装包就下载好了,这里我们使用 Redis
的版本为 3.2.8
的,还需要说明的一点就是,Redis
数据库原生是只支持 Linux
系统的,不支持 Windows
系统的,所以 Windows
版本的 Redis
数据库是微软自己建立的分支,当然是根据 Redis
官方的源码进行编译、发布和维护的,这样的话,Windows
版本的 Redis
版本就总是低于 Linux
版本的,但是使用起来是没有什么区别的。
下面我们就来看如何进行安装操作,之前我们已经得到了 Redis
数据库的安装包,也就是 redis-3.2.8.tar.gz
的安装包,我们在 Linux
系统中安装软件时一般都会将其安装到 /usr/local
目录下,因此我们可以先将 Redis
数据库的安装包上传到该目录,然后我们就可以进行安装操作了,第一步便是解压安装包了,我们可以使用 tar -zxvf redis-3.2.8.tar.gz
命令进行解压缩包操作,执行完解压操作之后,就可以在同一级目录中看到有一个 redis-3.2.8
目录,当然就是我们压缩包解压出来的了,那接下来就是第二步的操作了,因为 redis-3.2.8
目录中的都是 Redis
数据库的源码,所以我们需要对该源码进行编译和安装操作,第二步我们便是进行编译操作,首先需要进入到 redis-3.2.8
目录,然后执行 make
命令就行了,这个命令便是进行编译源码操作的,这里需要注意的一点是,这里由于系统环境的原因可能会报错,如:
/bin/sh: cc: command not found
这个报错是因为当前 Linux
系统中缺少 gcc
环境的原因,所以我们需要先安装 gcc
环境,安装方式为:
yum install gcc
这样我们安装了 gcc
环境之后,再执行 make
命令进行源码编译就是可以的了,执行了编译操作之后,下面就是最后一步安装操作了,即将我们的 Redis
安装到指定的目录当中,当然还是在 redis-3.2.8
目录当中,执行如下命令:
make PREFIX=/usr/local/redis install
这个命令便是将 Redis
安装到 /usr/local/redis
这个目录当中,但是因为现在 /usr/local
目录是没有 redis
目录的,所以需要先去新建这个目录,然后再执行上面的安装操作,操作完成之后就安装完成了,这便是第三步安装操作,这样的话,Redis
数据库的安装便完成了。
下面我们就可以到 Redis
数据库的安装目录 /usr/local/redis
目录进行查看了,会发现其中有一个 bin
目录,进入该 bin
目录,会发现有几个文件,我们一个一个进行说明。
redis-server:Redis服务器
redis-cli:Redis命令行客户端
redis-benchmark:Redis性能检测工具
redis-check-aof:AOF文件修复工具
redis-check-rdb:RDB文件修复工具
安装好 Redis
服务器之后,我们可以启动服务然后测试是否可以正常使用的,启动的方式十分简单,可以直接到 redis
目录,然后再到 bin
目录,直接执行 ./redis-server
命令,这时候我们就可以看到日志提示 Redis
数据库正常启动了,这种方式在测试时可以使用,但是在实际使用中一般不会这样,在实际使用中我们通常会使用配置文件的方式启动服务,配置文件就是 redis-3.2.8
目录中的 redis.conf
文件,我们可以先进入到 redis-3.2.8
目录,然后使用下面的命令将配置文件拷贝到 Redis
的安装目录中。
cp redis.conf /usr/local/redis
复制好文件之后,我们就可以回到 redis
目录中启动 Redis
数据库了,执行命令为:
./bin/redis-server ./redis.conf
通过控制台打印的日志我们就能看到启动好了,也就是 Redis
数据库的服务端启动好了,然后下面我们还需要启动 Redis
数据库的命令行客户端,这样就便于我们进行测试,启动命令行客户端也十分简单,只需要进入到 bin
目录,然后执行 ./redis-cli
命令就好了,当然执行这种命令的话,就是默认连接本地的 Redis
数据库了,那如果想要连接指定 IP
和端口的服务器呢,就可以使用类似下面的命令了。
./redis-cli -h 127.0.0.1 -p 6379
上面的命令就是指定连接本地主机端口号为 6379
的 Redis
数据库了,连接成功后,我们便可以使用 Redis
数据库了,我们可以输入一个 ping
命令进行测试,这时候会返回一个 PONG
,这就表示 Redis
数据库安装是正常的了。
需要说明的一点是,当 Redis
数据库启动时默认是只能在本机进行访问的,也就是别的主机是访问不了的,这是在 Redis
的配置文件 redis.conf
中进行设置的,配置为 bind 127.0.0.1
,也就是为 Redis
数据库绑定的 IP
地址为 127.0.0.1
,然后我们看这个配置上面的说明的话,就可以看到,之所以默认设置为只能本机访问,是出于安全考虑。
3.Redis数据类型与常见操作
Redis
数据库提供了丰富的数据类型供我们使用,一共有 5
种数据类型,分别为:
string:普通字符串
list:链表
set:集合
zset:有序集合(sorted set)
hash:哈希
下面我们就分别针对这五种数据类型分别进行说明,看看它们各自都有什么方法供我们使用。
3.1 string类型数据
字符串类型数据是 Redis
数据库中最基本的一种数据类型,在 Redis
数据库中是使用二进制进行安全存储的,因此我们便可以存储任何格式的数据,比如 json
格式的数据,甚至是 jpeg
这样的图片格式数据,同时,在 Redis
数据库中的字符串类型的 value
最大可以存储 512M
大小的数据,那对于 Redis
的基本数据类型字符串,也就是 string
来说,有哪些常用的命令是可以供我们使用的呢,下面我们一一进行说明。
set key value
这个 set
命令便是用来设置键值对的,即设置 key
所对应的值为 value
,如果这个 key
已经存在了,则会覆盖这个 key
所对应的原来的 value
值。比如可以这样使用:
set name kobe
上面这个命令便是设置 name
这个键所对应的值为 kobe
,那如果想要获取到某个键对应的值呢?那就可以使用与上面这个 set
命令相对应的 get
命令了,使用 get
命令我们便可以获取 key
所对应的 value
值了。
get key
这个 get
命令便是用来获取键所对应的值的,如果 key
在 Redis
数据库中不存在的话,则会返回 nil
,表示该 key
不存在,并且该 key
所对应的 value
也不存在。
mset key value [key value...]
这个 mset
命令是用来设置多个键值对的,和 set
命令相同的,当对应的 key
已经存在时,这个命令同样也会覆盖 key
所对应的 value
值,所以这个命令完全可以看成是 set
命令的多次迭代操作。具体的使用则可以如下:
mset age 18 sex boy
这里便是设置了两组键值对,一组为年龄,另一组则为性别,key
为 age
所对应的 value
为 18
,而 key
为 sex
所对应的 value
则为 boy
,这样就完成了设置多组键值对的操作。有设置多个键值对的命令,当然也有对应的根据多个键获取对应值的操作了,也就是 mget
命令了。
mget key [key...]
这个 mget
命令就是根据多个键获取得到对应的值了,当有的 key
不存在时,则会返回 nil
,因此 mget
可以完全看作是 get
命令的多次迭代操作,例如我们可以这样使用:mget sex age
,这样的话我们就能获取得到 sex
和 age
这两个键所对应的 value
了。
setnx key value
这个 setnx
命令同样也是用来设置某个键所对应的值的,但是它比较特别的地方在于,当这个 key
不存在时,setnx
命令的效果和 set
命令是一样的,都是设置这个 key
所对应的 value
,并且此时的返回值为 1
,但是当命令中设置的 key
已经在 Redis
中存在时,那么这时候不会做任何操作,这时的返回值为 0
。具体的使用可以如下:
setnx age 19
setnx birth 2019-01-18
第一个命令是来设置 age
这个 key
对应的 value
值为 19
的,但是之前我们已经设置过 age
对应的 value
为 18
,因此这里使用 setnx
命令不会修改 age
所对应的 value
值,而且此时的返回值也为 0
;再看第二个命令,是设置 birth
这个 key
的,因为我们之前是没有设置过这个 key
的,因此此时是可以设置成功的,返回值也应该为 1
。
msetnx key value [key value...]
这个 msetnx
命令是原子性的完成参数中所有 key/value
的设置操作,相当于是循环迭代的 setnx
命令,有一点需要注意的就是,如果命令中的某个 key
已经存在了,那么该命令所有的操作都会回滚,也就是全部的操作都会失效,返回值为 0
,具体的使用可以如下:
msetnx age 10 like basketball
在上面这个命令中,由于我们之前已经设置过 key
为 age
的键值对,因此在这里再次设置时是不会生效的,而且由于该命令具有原子性的特点,因此命令中所有的键值对设置都不会生效。
append key value
这个 append
命令是用来追加某个 key
所对应的 value
值的,当命令中的 key
已经存在时,那这个命令就会将命令中的 value
追加到命令中 key
所对应 value
的后面,如果命令中的 key
在 Redis
数据库还不存在的话,那么就相当于是一次设置操作,具体的使用可以如下:
append name ' is a boy'
因为在之前的命令中,我们已经设置 name
这个 key
所对应的 value
为 kobe
了,然后在这里再使用 append
追加命令,那么现在 name
这个 key
所对应的 value
就应该是 kobe is a boy
了,当然我们是可以使用 get name
这个命令来进行验证的。
decr key
这个 decr
命令是将 key
所对应的 value
值递减 1
,当然这时候就必须要求这个 key
所对应的 value
必须是能转换成整形值的数据了,否则就会报错了,同时,如果命令中的 key
不存在的话,那么就会默认这个 key
所对应的 value
为 0
,然后再进行递减 1
的操作,返回的就是 -1
了,其实这里的 key
如果不存在的话,就相当于是先设置该 key
所对应的 value
值为 0
了,然后再进行的递减操作。具体使用可以如下:
decr age
由于之前我们已经将 age
这个 key
所对应的 value
设置为 18
了,然后这里 decr
命令的结果就会返回 17
了。
incr key
这个 incr
命令的效果便是和上面 decr
命令的效果是相对的,也就是将 key
所对应的 value
递增 1
,同样也要求 key
所对应的 value
值是能转换成整形值的数据,当命令中的 key
在 Redis
中不存在时,会先设置该 key
所对应的 value
为 0
,然后再进行递增 1
的操作,这时候就会返回 1
了,具体的使用如下:
incr age
因为之前的操作,Redis
数据库中 age
对应的 value
应该是 17
了,而这里使用 incr
命令的话就会返回 18
了。
decrby key decrement
这个 decrby
命令的作用和 decr
命令的作用是一样的,也是将命令中 key
所对应的 value
值进行减操作,只不过 decr
命令每次都是确定的减 1
,而 decrby
命令则可以根据命令中的参数减少指定的数据,不过有一点是相同的,那就是当命令中的 key
不存在时,Redis
会先指定该 key
对应的 value
值为 0
,然后再进行减操作。具体的使用可以如下:
decrby count 4
这里因为之前在 Redis
中是不存在 count
这个 key
的,所以返回的应该是 -4
。
incrby key increment
这个命令则是和上面的 decrby
命令相对应的,incrby
命令主要是用来对某个 key
所对应的 value
值进行增加操作,同样可以指定所增加值的大小,并且当 key
不存在时,也会先将 key
所对应的 value
置为 0
,具体使用可以如下:
incrby count 5
由于之前 count
所对应的值已经设为 -4
了,在这里进行加 5
的操作,那最后返回的结果就应该是 1
了。
getset key value
这个 getset
命令同样也是用来设置键值对的,作用的效果其实和 set
命令是一样的,不过有一点区别的就是,getset
命令不仅会设置键值对,而且还会返回这个 key
所对应的原来 value
值,具体使用可以如下:
getset age 16
因为之前 age
这个 key
所对应的 value
是 18
,所以执行上面这个命令时,就会将 age
这个 key
所对应的 value
设置为 16
,同时也会返回原来的对应值 18
。
strlen key
这个 strlen
命令则是用来返回 key
所对应 value
值的长度,我们可以直接使用:
strlen name
由于之前 name
这个 key
所对应的 value
值为 kobe is a boy
,因此这里的返回值应该是 13
。
setex key seconds value
这个 setex
命令主要是用来设置键值对,同时设置键的生命周期的,也就是这个 key
在 Redis
中存在的时长,之前我们使用 set
命令设置的键值对,其生命周期都是永久的,在实际开发中我们一般都会指定 key
的生命周期,这样就不会使 Redis
中的 key
过多。具体的使用可以如下:
setex age 10 6
上面这个命令的作用就是设置 age
这个 key
所对应的 value
值为 6
,并且该 key
的存活时间为 10
秒钟,我们这里如果想要查看某个 key
的生命周期的话,是可以使用 ttl
命令的,比如 ttl age
命令,便是查看 age
这个 key
的生命周期了,我们之前使用 set
命令设置的键值对,其生命周期为永远,这时候使用 ttl
命令返回的就是 -1
了。
setrange key offset value
这个 setrange
命令主要是用来对 key
所对应的 value
值进行局部替换操作,替换的位置就是 offset
了,替换的值也就是命令中的 value
值了,当命令中的 offset
值大于 key
所对应 value
的长度时,那就会在原 value
值和 offset
之间插入 \u0000
值,在我们之前的操作中,name
这个 key
所对应的 value
值应该是 kobe is a boy
,我们现在如果使用下面这个命令的话:
setrange name 10 girl
那这时候,name
这个 key
所对应的 value
值就应该是 kobe is a girl
了。同时,如果命令中的 key
是不存在的话,那就会直接为该 key
进行设置操作了,并且如果该命令中的 offset
大于 0
的话,同样的也会在 offset
位置之前插入 \u0000
字符的。
getrange key start end
这个 getrange
命令则是用来获取 key
所对应 value
中的某部分值的,命令中的 start
和 end
则是表示获取数据的区间,这里的区间是闭区间,也就是说获取 value
值上面的第 start
个元素到 end
个元素时,start
和 end
位置上面的元素都会包含在内,比如我们可以如下进行使用:
getrange name 0 1
那上面这个命令就会返回 ko
了,当然,如果想要获取得到 key
所对应的 value
完整值的话,其实是可以使用这个命令的,getrange name 0 -1
,0
表示的是从第 1
位开始,-1
则是表示到最后一位。
getbit key offset
这个 getbit
命令是用来获取 key
所对应 value
值二进制位的,每个二进制位只可能是 0
或者 1
,这里就需要说一下十进制数和字母如何用二进制进行表示了,比如十进制数 1
,如果用 8
位二进制表示的话,那就应该是 0000 0001
了,其实所有的十进制数都可以使用二进制来表示的,那字母呢?其实也是可以的,这里就需要使用 ASCII
码表了,这张表上面就列出了字母所表示的十进制数以及二进制数,比如 a
,它的十进制表示就是 97
,二进制表示就是 0110 0001
了,而 A
的话,它的十进制表示就是 65
,二进制表示则是 0100 0001
了,如果我们先设置一个 key
所对应的 value
为 a
,那再获取得到它每一个二进制位上面的数据,就可以看到该 value
的二进制表示了。
> set key a
OK
> get key
"a"
> getbit key 0
0
> getbit key 1
1
> getbit key 2
1
> getbit key 3
0
> getbit key 4
0
> getbit key 5
0
> getbit key 6
0
> getbit key 7
1
上面我们得到的结果其实依次来看的话,就是 0110 0001
了,和我们上面说的 a
的二进制表示是一致的。
setbit key offset value
这个 setbit
命令是用来设置 key
所对应 value
上的二进制位的,其实理解了上面的 getbit
命令,那这个 setbit
命令也就很好理解了,因为这两个命令刚好也是相对的,setbit
命令刚好就是用来修改 value
上面某个二进制位上面的值的,如果想看到效果的话,那我们可以直接对上面的 key
所对应的 a
这个 value
进行修改,a
所对应的二进制为 0110 0001
,而 A
所对应的二进制为 0100 0001
,两者的二进制表示主要就是第 3
个二进制位的差别,那我们可以执行下面的命令:
setbit key 2 0
也就是将原 value
值的二进制表示第 3
位上面的数值改为 0
,那这样的话,现在 key
所对应的 value
值就是 A
了。
3.2 list类型数据
下面我们再来看 list
类型的数据,其实就是数据结构中的链表了,对于链表来说,效率最高的操作就是在链表的头尾进行元素的增删了,Redis
对于 list
类型的数据也提供了很多在头尾进行数据增删的操作,下面我们具体的来看。
lpush key value [value...]
这个 lpush
命令主要是向链表中插入一个或多个元素,当命令中的 key
还不存在时,则会新建该 key
,也就是一个链表了,需要注意一下的就是,往链表中插入元素时,先插入的元素是在链表尾的,后插入的元素则是在链表头部的,下面看具体的使用:
lpush lkey01 1 2 3 4 5
上面这个命令则是向 lkey01
这个链表中插入了 5
个元素,由于在 Redis
数据库中 lkey01
这个链表是不存在的,因此在这里会先创建该链表,然后再往里面插入元素。
lpushx key value [value...]
这个 lpushx
命令同样也是往链表中插入元素的,只是有一点不一样的地方,就是当命令中 key
已经存在时,那么使用 lpushx
命令插入元素是和 lpush
命令一样的,但是如果命令中的 key
不存在,那么插入元素的操作就不会执行,因此如果想要使用 lpushx
插入元素时则必须保证链表已经是存在的了,下面看具体的使用:
lpushx lkey01 10
上面这个命令就是向 lkey01
链表又插入了一个元素 10
,如果是还不存在的链表的话,那就不会进行插入操作了。
lrange key start end
这个 lrange
命令主要是用来查看链表中的元素的,start
和 end
就是元素所在的位置了,都是以 0
为开始计数的,同样的,查询区间也是闭区间,即 start
和 end
位置上面的元素都会被查出来,而 -1
的话则是表示最后一个位置,以此类推,-2
则是表示倒数第二个位置,所以当我们想查询链表中全部的元素时,就可以使用如下的命令:
lrange lkey01 0 -1
上面便可以查询出链表中所有的元素来了。
lpop key
这个 lpop
命令主要是从链表中取出头部元素,如果链表不存在的话,就会返回 nil
,因此当我们想要从链表中取得头部元素时就可以使用该命令了,具体使用可以如下:
lpop lkey01
这里便是取出 lkey01
这个链表中的头部元素了,很显然这里取出的就应该是 10
这个元素了。
llen key
这个 llen
命令则是用来获取链表的长度了,也就是链表中元素的个数,当链表还不存在时,就会返回 0
,已经存在的话则会返回链表实际长度,具体使用可以如下:
llen lkey01
这里返回的值就是 5
了,因为现在链表中还有 5
个元素。
lrem key count value
这个 lrem
命令主要是用来删除链表中元素的,即删除链表中 count
个值为 value
的元素,当 count
的值大于 0
,则是表示依次从头到尾进行删除,当 count
的值小于 0
时,则表示依次从尾到头进行删除,当 count
的值等于 0
时,则表示删除链表中所有值为 value
的元素。
lset key index value
这个 lset
命令主要是用来设置链表中某个位置上面的元素值的,我们就可以用来进行替换操作了,索引值 index
是从 0
开始的,当命令中的 index
大于链表的实际长度时,就会报索引越界错误了,如果我们想设置链表中第一个元素为 100
,则可以使用如下命令:
lset lkey01 0 100
这样的话,lkey01
链表的头部元素就会被设置为 100
。
lindex key index
这个 lindex
命令主要是返回链表中某个位置的元素值,我们便可以根据这个命令做查询相关的操作了,命令中的 index
同样是以 0
为开始的,0
表示的是链表的头部元素所在位置,-1
则是表示链表中尾部元素所在的位置,如果我们想获取到链表中的头部元素,那就可以这样:
lindex lkey01 0
这样的话便能得到链表的头部元素了,结合上面的 lset
命令来看的话,这里获得的元素应该是 100
了。
ltrim key start end
这个 ltrim
命令主要是用来将链表中的元素截取一部分然后保存下来,start
和 end
则是表示保留链表中元素所在位置的区间了,start
和 end
都是以 0
为开始的,并且区间也是闭区间,即 start
和 end
所在位置的元素都会包含在内。
linsert key before|after pivot value
这个 linsert
命令主要是用来向链表中插入元素的,即向链表中元素值为 pivot
的元素前面或者后面插入值为 value
的元素,命令中的 before
和 after
就是指定是在元素前面还是后面进行插入了。
rpush key value [value...]
前面我们介绍的往链表中插入元素和取出一个元素,都是在链表头部进行操作的,其实我们同样也是可以在链表尾部进行这样的操作的,比如这个 rpush
命令,则是向链表尾部插入元素,同样的,如果命令中的链表还不存在,则先创建该链表然后再在尾部进行插入操作。
rpushx key value
这个 rpushx
命令同样也是在链表的尾部进行插入操作,和 lpushx
类似的,这个 rpushx
只有当链表已经存在时才会进行插入操作,如果链表不存在就不会做任何操作。
rpop key
这个 rpop
命令主要是在链表尾部取出一个元素,也就是尾部元素了。
rpoplpush source destination
这个 rpoplpush
命令是作用于两个链表的,即将 source
链表的尾部元素弹出,然后插入到 destination
链表的头部,如果 source
链表不存在,则只会返回 nil
,而不会做其它操作了,如果 source
链表和 destination
链表是同一个链表,那就相当于是将这个链表的尾部元素弹出然后插入到头部了。
3.3 hash类型数据
下面我们再来看 Redis
数据库中的 hash
类型的数据,Redis
数据库中的 hash
类型数据类似于 Java
中的 Map
集合类型的数据,在这里我们可以简单的将 hash
类型的数据理解为一个 key
所对应的值为一个 Map
集合,因此我们可以使用该类型的数据保存对象信息,比如 key
为 user
,然后该 key
所对应的 Map
则保存该用户对象的各种属性,比如姓名,性别以及年龄,这样就能很好的描述一个对象了,下面我们具体来看 hash
类型的数据有哪些常用命令。
hset key field value
这个 hset
命令主要是用来设置一个 hash
类型的数据,当命令中的 key
不存在时,便会执行新增操作,命令中的 field
参数则是 key
所对应 Map
类型数据中的键,value
则是 key
所对应 Map
类型数据中的值,下面通过具体的命令来学习。
hset user username tom
上面这个命令则是设置了一个 hash
类型的数据,该数据的 key
则为 user
,该 key
所对应的则是一个 Map
集合,现在该 Map
中有一对键值对,键为 username
,值则为 tom
。
hget key field
hget
命令则是用来获取 key
所对应 hash
类型数据中 field
所对应的值的,显然它的作用便是和 hset
是相对的,实际使用可以如下:
hget user username
这样的话,我们就能获得 user
这个 key
所对应 hash
类型数据中 username
这个键所对应的值了,很显然,这个值就是 tom
了。
hsetnx key field value
hsetnx
这个命令也是用来设置命令中 key
所对应的 hash
类型数据的,只不过和 hset
命令有区别的是,只有当命令中的 key
或者 field
不存在的时候,命令才会起作用,否则是不会起作用的,使用可以如下:
hsetnx user age 20
由于之前 user
所对应的 hash
类型数据中是不存在 age
这个键的,因此上面的命令便是相当于对 user
这个 key
所对应的 hash
类型数据的一次设置操作,设置其中 age
这个键所对应的值为 20
。当然如果使用下面这个命令的话那就没有用了:
hsetnx user username jerry
由于 user
这个 key
所对应的 hash
类型数据中之前就已经有了 username
这个键,因此再对这个键进行设置时,就不会再起作用了。
hexists key field
hexists
命令主要是用来判断 key
所对应的 hash
类型数据中是否包含 field
这个键,如果包含的话就会返回 1
,不包含或者连 user
这个 key
都不存在的话就会返回 0
了。具体使用可以如下:
hexists user username
上面这个命令则是用来判断 user
这个 key
所对应的 hash
类型数据中是否包含 username
这个键,很显然是包含的,因此会返回 1
。
hlen key
hlen
命令是用来确定 key
所对应的 hash
类型数据中包含 field
的个数,如果 key
不存在的话,则会返回 0
。具体使用可以如下:
hlen user
上面这个命令则是为了确定 user
这个 key
所对应 hash
类型数据中 field
的个数,很显然现在 user
所对应 hash
类型数据中包含的 field
有 username
和 age
这两个,因此上面命令的返回值为 2
。
hdel key field [field...]
hdel
命令是用来删除 key
所对应 hash
类型数据中对应的 field
,当命令中的 field
不存在时,则会忽略该 field
,具体使用可以如下:
hdel user age
上面的命令则是用来删除 user
这个 key
所对应的 hash
类型数据中的 age
这个 field
,当然在我们之前的操作中,age
这个 field
是存在的,因此在这里的删除操作也是可以进行的。
hincrby key field increment
hincrby
命令是用来对 key
所对应 hash
类型数据中 field
所对应值进行增减操作的命令,当然这就必须要保证 field
对应的值必须是可以被转换为整数型值的,当命令中的 key
或者 field
不存在时,就会新建命令中的 key
或者 field
,并将 field
所对应的值设为 0
,然后再进行加减操作。具体使用可以如下:
hincrby user age 10
上面这个命令便是对 user
这个 key
所对应的 hash
类型数据中的 age
这个 field
对应的值进行加 10
操作,由于之前已经不存在 age
这个 field
了,因此这里 age
所对应的值先设置为 0
,然后再进行加 10
操作,最后返回的就是 10
了。
hgetall key
hgetall
命令是用来获取 key
所对应 hash
类型数据中所有的 field
以及 field
所对应的值的,相当于是对 key
所对应 hash
类型数据的遍历,具体使用可以如下:
hgetall user
上面的命令便是获取 user
这个 key
所对应 hash
类型数据中所有的数据了,包括 field
以及其所对应的值。
hkeys key
hkeys
命令是用来获取 key
所对应 hash
类型数据中所有的 field
,类似于 Java
中获取 Map
集合中所有的 key
,因此还是很好理解的,具体使用可以如下:
hkeys user
上面命令是用来获取 user
这个 key
所对应 hash
类型数据中所有 field
,根据现在的情况,返回的就应该是 username
和 age
这两个 field
了。
hvals key
hvals
命令是用来获取 key
所对应 hash
类型数据中所有 field
所对应的值的,相当于是 Java
中获取 Map
集合中的所有 value
值,该命令的具体使用如下:
hvals user
上面的命令是用来获取 user
这个 key
所对应 hash
类型数据中所有 field
所对应的值的。
hmget key field [field...]
hmget
命令是用来获取 key
所对应 hash
类型数据中多个 field
所对应的值的,如果命令中 field
不存在,返回的就是 nil
了,如果命令中的 key
都不存在,那返回的就是一组 nil
了。具体使用可以如下:
hmget user username age
上面这个命令是用来获取 user
这个 key
所对应 hash
类型数据中 username
和 age
这两个 field
所对应的值的。
hmset key field value [field value...]
hmset
命令是用来设置 key
所对应 hash
类型数据中多个 field
所对应的值的,这个的作用当然就是刚好和 hmget
命令是相对的了,具体的使用可以如下:
hmset user addr hubei birth 2019-01-01
上面命令的作用便是设置 user
这个 key
所对应的 hash
类型数据中 addr
这个 field
所对应的值为 hubei
,birth
这个 field
所对应的值为 2019-01-01
。
3.4 set类型数据
下面我们来介绍 Redis
数据库中的 set
类型数据,Redis
数据库中的 set
类型数据就相当于 Java
中的 Set
集合类型数据,和 Java
中一样,Redis
数据库中的 set
类型数据中存放的也是无序的数据,可以简单地看成是一组无序的字符串集合,同时,set
类型数据还可以在两个集合间完成交并差等集合操作,所以还是十分实用的,下面我们具体来看 set
类型数据为我们提供了哪些实用的命令。
sadd key member [member...]
sadd
命令相当于是往 key
这个集合中添加元素,当命令中的元素在集合中已经存在时,我们会忽略该元素,而其它的元素仍会添加成功,具体使用可以如下:
sadd set a b c d
上面命令就是往集合 set
中添加 4
个元素了,同时 4
个元素之间是无需排列的。
scard key
scard
命令则是用来获取集合中元素的个数,当集合还不存在时会返回 0
,否则就是返回实际元素个数了。具体使用可以如下:
scard set
上面这个命令便是获取 set
这个集合中元素个数了,很明显这里是有 4
个元素的,所以应该返回 4
。
sismember key member
sismember
命令则是用来判断某个元素是否是属于某个集合的,返回值 1
表示存在,而 0
则是表示不存在。具体使用可以如下:
sismember set a
上面命令则是判断 a
这个元素是否在 set
这个集合当中,很显然是在集合当中的,因此应该返回 1
。
smembers key
smembers
命令是用来查看集合中所有元素的,也就相当于是对集合的遍历了。具体使用可以如下:
smembers set
上面这个命令便是查看 set
集合中的所有元素。
spop key
spop
命令是从 key
集合中返回并删除一个元素,由于集合中元素是无序排列的,因此每次返回并删除都相当于是随机的。具体使用可以如下:
spop set
上面命令就会从 set
集合中随机返回并删除一个元素。
srandmember key
srandmember
命令是随机返回集合中的某个元素,与 spop
不一样的是,srandmember
命令只会返回元素,而不会将元素从集合中删除。具体使用可以如下:
srandmember set
上面命令就是从集合中随机返回一个元素,因为集合中元素是排列无序的,因此每次返回的元素也是随机的。
srem key member [member...]
srem
命令主要是用来删除集合中指定元素的,如果命令中的元素在集合中不存在,则会忽略该元素。具体使用可以如下:
srem set a c
上面命令则是从集合中删除 a
和 c
这两个元素。
smove source destination member
smove
命令是用于将 source
集合中的元素移到 destination
集合当中,也就是源集合和目标集合了,假如 set
集合中的元素为 a
,b
,c
,而 set2
集合中元素为 1
,2
,3
,当我们想把 a
元素从 set
集合移到 set2
集合时,我们就可以使用下面这个命令:
smove set set2 a
这样的话我们就可以达到我们想要的效果了,即将 a
元素从 set
集合移动到 set2
集合了。
sdiff key [key...]
sdiff
命令是用来获取第一个集合与其后所有集合的差集的,为了便于说明,我们假定有两个集合,set
与 set2
,set
中元素为 1
,2
,3
,4
,5
,而 set2
中元素为 4
,5
,6
,7
,8
,当我们想求两个集合的差集时,则可以使用如下命令:
sdiff set set2
通过上面的命令,我们就得到了 set
集合与 set2
集合的差集了,很明显我们这里的结果就是 1
,2
,3
了。
sdiffstore destination key [key...]
sdiffstore
命令同样也是用来计算集合之间的差集的,只不过和 sdiff
命令之间的差别在于,sdiffstore
命令会将集合之间的差集保存在 destination
集合当中,也就是目标集合当中,而 sdiff
命令不会,因此上面同样的两个集合求差集,也可以使用下面这个命令:
sdiffstore sdiffset set set2
上面这个命令会将两个集合之间求差集的结果保存到 sdiffset
集合当中,保存的元素也就是 1
,2
,3
了。
sinter key [key...]
sinter
命令则是用来求第一个集合与其后所有集合之间的交集的,这样来看的话,当命令中有一个集合为空集合或者集合不存在的话,那我们运算的结果也就是空集合了。这里我们还是求 set
和 set2
这两个集合之间的交集,命令如下:
sinter set set2
经过上面的命令,我们便能得到 set
和 set2
集合之间的交集了,很明显这里我们得到的结果应该是 4
,5
。
sinterstore destination key [key...]
sinterstore
命令也是用来计算第一个集合与其后所有集合之间的交集的,与 sinter
命令稍微有点不同的是,sinterstore
命令会将交集计算的结果保存在 destination
集合中,也就是目标集合中,而 sinter
命令则是将交集计算的结果直接返回。如果要计算 set
和 set2
这两个集合之间的交集的话,那我们就还可以使用下面的命令:
sinterstore sinterset set set2
上面这个命令则是将两个集合的交集结果保存到 sinterset
集合当中了,很显然其中就是 4
和 5
这两个元素了。
sunion key [key...]
sunion
命令则是用来获取第一个集合与其后所有集合之间的并集,为了便于演示,我们还是使用 set
和 set2
这两个集合,以求它们的并集:
sunion set set2
上面这个命令便是求 set
集合和 set2
集合之间的并集了,很明显可以看出结果就是 1
,2
,3
,4
,5
,6
,7
,8
。
sunionstore destination key [key...]
sunionstore
命令同样也是用来求两个集合之间的并集的,同样的,与 sunion
命令不同的地方就还是在于,sunionstore
命令会将集合之间并集运算的结果保存到 destination
集合中,也就是目标集合当中,而 sunion
命令则是直接返回结果,具体使用如下:
sunionstore sunionset set set2
上面命令则是将 set
集合和 set2
集合并集的结果保存在 destination
集合之中了,很明显其中集合元素也是 1
,2
,3
,4
,5
,6
,7
,8
。
3.5 sortedSet类型数据
下面我们再来介绍 Redis
数据库中的 sortedSet
类型数据,其实就是相当于 Java
中的 Set
集合,不过这里的 sortedSet
是有序集合,那它其中元素是如何保持有序状态的呢?其实就是因为集合中每个元素都会有一个分数,然后每个元素都根据分数的大小进行排列,这样就能保证集合中元素是有序的了,同时,集合中元素是不允许重复的,但是元素所带有的分数是允许重复的,由于 sortedSet
中元素都是有序排列的,因此即使是在集合中部进行增删操作,效率也是很高的,下面我们再具体来看 Redis
中提供了哪些实用的方法供我们使用。
zadd key score member [score member...]
zadd
命令的作用就是往集合中添加元素了,只不过在添加元素时也会指定元素所对应的分数,这样集合中元素就能根据分数大小进行排序了,如果命令中的元素在集合中已经存在了,那这时就会根据命令中元素对应的分数对集合中元素对应的分数进行更新,同时调整集合中元素的排序。下面看具体的使用:
zadd zset 1 a 2 b 3 c 4 d 5 e
这样我们就创建了一个新的有序集合 zset
,并往其中添加了 5
个元素,并指定了 5
个元素所对应的分数是多少。
zincrby key increment member
zincrby
命令的作用主要是调整集合中元素所对应的分数,当命令中的元素在集合中并不存在时,我们就会新增该元素到集合之中,并指定该元素所对应的分数为命令中的分数参数,具体使用可以如下:
zincrby zset 2 d
上面的命令就是将集合中的 d
元素所对应的分数增加了 2
,从而也就改变了元素之间的排序序列。
zcard key
zcard
命令则是用来获取集合中的元素个数的,具体使用可以如下:
zcard zset
上面这个命令就是用来获取 zset
集合中元素个数了,很明显这里会返回 5
。
zcount key min max
zcount
命令同样也是用来获取集合中元素数量的,只不过它获取的是分数在 min
和 max
之间元素的个数,需要注意的是在默认情况下这个区间是闭区间,也就是两边都是包含的,如果想表示开区间的话,则可以使用 (
符号,如 zcount key (min (max
,这样的话就是表示分数在最小值和最大值之间的元素个数是多少了,当然如果只想表示单个闭区间也是可以的,总结起来的话就是哪一边想表示开区间的话哪一边就需要加上 (
符号。下面看具体使用:
zcount zset 2 4
上面这个命令就是得到分数大于等于 2
并小于等于 4
之间的元素个数。
zrange key start stop [withscores]
zrange
命令是用来根据索引范围获取元素的,命令中的 start
和 stop
都是表示元素在集合之中的索引区间,这里的索引都是从 0
开始的,0
表示的是第一个元素,-1
则是表示的最后一个元素,因此总的来说就相当于是获取某个索引区间之中的元素,需要注意的一点是,命令中还有一个可选参数 withscore
,表示的是返回元素时是否带上元素所对应的分数,存在该参数时就会带上元素所对应的分数,下面看具体的使用:
zrange zset 0 -1 withscore
上面的命令则是表示获取集合中所有的元素以及它们各自所对应的分数。
zrevrange key start stop [withscores]
zrevrange
命令同样也是根据索引获取集合中的元素以及元素所对应的分数的,和 zrange
命令唯一的区别就是 zrevrange
命令是将元素进行倒序排列,即根据元素所对应的分数按从大到小进行排列的,具体使用可以如下:
zrevrange zset 0 -1 withscores
上面这个命令同样也是获取 zset
集合中元素以及元素对应分数的,只不过和上面使用 zrange
命令不一样的是,这里所得到的元素排列顺序是和 zrange
命令刚好相反的。
zrangebyscore key min max [withscores] [limit offset count]
zrangebyscore
命令的作用主要是根据元素所对应的分数来获取集合中的元素,即返回分数大于等于 min
并且小于等于 max
的元素,命令中的可选参数 withscorea
则是表示是否需要返回元素所对应的分数,后面还有一个可选参数 limit
,则类似于 MySQL
数据中进行分页的关键字 limit
,同样的,这里的 offset
也是表示从第多少个元素开始返回,而 count
则是表示每次返回多少个元素,具体使用可以如下:
zrangebyscore zset 2 4
上面命令则是表示返回 zset
集合中分数在 2
和 4
之间的元素。
zrevrangebyscore key max min [withscores] [limit offset count]
zrevrangebyscore
命令同样也是根据分数来获取集合中的元素,不过和 zrangebyscore
命令不同的是,zrevrangebyscore
命令是根据分数从高到低来进行排列元素的,需要注意的一点是,在使用 zrevrangebyscore
命令的时候,参数中的分数的最大值是放在前面的,而分数的最小值是放在后面的,具体使用可以如下:
zrevrangebyscore zset 4 2
上面的命令则是获取分数在 2
和 4
之间的元素,并按分数从高到低进行排列返回。
zrank key member
zrank
命令是用来获取指定元素在集合中的索引值,当然,索引值是从 0
开始计数的,具体使用可以如下:
zrank zset d
上面这个命令便是获取 d
这个元素在 zset
集合中所在的索引值是多少。
zrevrank key member
zrevrank
命令也是用来获取指定元素在集合中的索引值的,只不过和 zrank
命令相反的是,zrevrank
返回的顺序和 zrank
命令返回的刚好是相反的。具体使用可以如下:
zrevrank zset d
上面这个命令也是获取 d
这个元素在 zset
集合之中的索引值的,其实就是相当于,zrank
命令获取索引值的时候是从前往后数的,而 zrevrank
命令是从后往前数的。
zscore key member
zscore
命令的作用就是获取集合中指定元素所对应的分数,当元素不存在时就直接返回 nil
了,具体使用可以如下:
zscore zset d
上面这个命令就是获取 d
这个元素在 zset
集合中所对应的分数。
zrem key member [member...]
zrem
命令是用来删除集合中指定元素的,如果命令中的元素在集合中并不存在时,则直接忽略,具体使用可以如下:
zrem zset a
上面这个命令就是直接删除掉 zset
集合中的 a
元素。
zremrangebyrank key start stop
zremrangebyrank
命令则是根据元素的索引值来删除集合中的元素,start
和 stop
都是表示的索引值,从 0
开始计数,0
表示的是第一个元素,而 -1
则是表示的最后一个元素,命令的作用就是删除索引值在 start
和 stop
之间的元素,具体使用可以如下:
zremrangebyrank zset 0 0
上面这个命令便是相当于删除 zset
集合中索引值为 0
的元素。
zremrangebyscore key min max
zrenrangebyscore
命令则是根据元素所对应的分数来删除元素,将分数处在 min
和 max
之间的元素进行删除,具体使用可以如下:
zremrangebyscore zset 5 5
上面命令则是删除 zset
集合中元素对应分数为 5
的元素。
3.6 key通用操作
介绍完了 Redis
数据库中的 5
种基本数据类型,我们再来看一些专门针对于 key
的操作命令,比如列出所有的 key
以及删除某些 key
,使用这些命令我们可以完成许多我们想要的功能,因此也是十分实用的,下面具体来看。
keys pattern
keys
命令的作用是列出所有匹配给定模式的 key
,其中的 pattern
参数即为正则表达式,在实际生产中,还是应该尽量避免使用该命令的,因为该命令是非常耗时的,对 Redis
数据库的性能消耗也是非常高的,下面我们来看一下具体使用:
keys *
上面这个命令的作用便是列出当前数据库中的所有 key
,因为生产环境的数据量都是很大的,因此在生产环境最好不要使用该命令。
del key [key...]
del
命令则是用于删除 Redis
数据库中指定的 key
,当然当命令参数中的 key
不存在时,那就会直接将 key
忽略掉了,下面看具体使用:
del name
上面的命令则是直接删除掉 name
这个 key
。
exists key
exists
命令主要是用来判断指定 key
在当前数据库中是否存在,如果存在则是返回 1
,不存在则是返回 0
,下面看具体使用:
exists name
上面这个命令便是判断 name
这个 key
是否存在于当前的数据库中。
move key db
move
命令的作用就是将指定的 key
移动到指定的数据库中,当指定的 key
在目标数据库中已经存在或者在当前数据库中不存在时,移动操作就不会发生。在 Redis
的配置文件 redis.conf
中默认配置的数据库数量是 16
,而我们默认使用的数据库是 0
号数据库,当我们想切换数据库时就可以使用 select
命令,比如想切换到 10
号数据库,那我们就可以使用 select 10
这个命令,下面我们来看移动指定的 key
到别的数据库的命令。
move name 10
上面这个命令便是将 name
这个 key
移动到了 10
号数据库。
rename key newkey
rename
命令主要是为指定的 key
改名,如果当 newkey
已经存在时,那么命令实际的效果就是 key
所对应的数据会覆盖 newkey
所对应的数据,下面看具体使用:
rename username name
上面的命令就是将 username
这个 key
名称替换为 name
,当 name
这个 key
在之前就已经存在时,那么就会使用 username
这个 key
所对应的数据去覆盖 name
这个 key
所对应的数据。
renamenx key newkey
renamenx
这个命令同样也是为指定的 key
改名,但是稍有不用的是,只有当 newkey
不存在时,操作才会执行成功,当返回值为 1
时表示操作成功,为 0
时则表示操作失败,下面看具体使用:
renamenx username name
上面这个命令便是将 username
这个 key
名称修改为 name
,当然只有当 name
这个名称不存在时才会成功。
expire key second
expire
命令是用来设置指定 key
的有效时间的,这里的有效时间主要是秒数了,在我们之前的操作中,都是没有设置有效期的,那默认的就是永久有效,但是在实际生产中,所有的数据都是永久有效的话,就会占用相当大空间的内存,同时有些数据保留永久也没有很大意义,这样的话,为了优化,我们一般会对数据设置一个有效时间,这样就能保证缓存空间的大小不会过大,下面我们来看该命令的具体使用:
expire name 100
上面这个命令便是设置 name
这个 key
的有效时间为 100
秒,当时间过了 100
秒之后,该 key
就会自动被删除了,同时对应的数据也就被删除了。
expireat key timestamp
expireat
命令同样也是用来设置有效时间的,不过不同之处在于,expireat
是使用的绝对时间,而不是相对时间,该时间参数是 Unix timestamp
格式的,也就是从 1970-01-01
这一天开始所流经的秒数,因此可以想到使用该命令设置有效时间时是一个很大的数了,具体使用可以如下:
expireat name 100
上面这个命令的作用是设置 name
这个 key
有效时间为 1970-01-01
开始时的 100
秒,当我们再次查看该 key
时,就会发现这个 key
已经过期了,这是因为我们现在肯定远远过去 1970-01-01
这一天很长时间了。
ttl key
ttl
命令用于获取指定 key
剩下的有效生存时间,当默认情况下,key
是永久生效时,使用 ttl
命令会返回 -1
,表示是永久有效的,当然对于这个 key
来说,之前指定的有效生存时间也就永久延长了,具体使用可以如下:
ttl name
上面这个命令便是查看 name
这个 key
所剩下的有效生存时间,因为本身时间也会自然流逝,因此每次我们使用该命令时,所返回的结果也是不一样的,不过肯定是慢慢减少的。
persist key
persist
命令的作用是在当 key
有生效时间时,将 key
所对应的生效时间设置为永久,并且可以持久化存储,当 key
的有效时间被设置为永久之后,我们再使用 ttl
命令查看 key
所对应的有效时间就会返回 -1
了,下面看具体使用:
persist name
上面这个命令便是将 name
这个 key
所对应的有效生存时间设置为永久。
randomkey
randomkey
命令的作用便是从当前数据库中随机返回一个 key
,当然具体的使用也就是直接调用该命令了。
type key
type
命令用于返回指定 key
所对应的数据类型,因为 Redis
只有 5
种数据类型,因此返回值也就只有这 5
种,具体为 string
,list
,set
,hash
和 zset
,同时当命令中的 key
在数据库中并不存在时,那这时候就会返回 none
了。具体使用可以如下:
type name
上面这个命令便是返回 name
这个 key
所对应的数据类型了,很明显这时候返回的应该是 string
。
3.7 事务
下面我们再来看 Redis
数据库中的事务,和 MySQL
数据库中一样,Redis
数据库中的事务也有两个特点:
1.事务是一个单独的隔离操作。
2.事务是一个原子操作。
事务是一个单独的隔离操作,是表示事务中所有的命令都会序列化,按顺序进行执行,在事务执行命令的过程中,不会被其它客户端发送的命令请求所打断;事务是一个原子操作,表示在事务中的多个命令,要么全部执行成功,要么全部执行失败。
关于事务的命令有如下这些,我们先看一下:
multi:表示事务开始
exec:表示执行事务
discard:表示取消事务
watch:表示对指定key进行监视
unwatch:取消对指定key的监视
下面我们通过事务执行的流程,来对事务相关的命令进行详细说明,事务执行的一般流程为先开启事务,然后多个命令进入事务队列,最后我们就是执行事务或者取消事务,这边是一个完整的事务流程,针对上面说的流程,我们的一般命令的执行流程为:
执行事务:
multi
多条命令入队
exec
取消事务:
multi
多条命令入队
discard
需要说明的是,当我们取消事务时,事务中对 key
执行的操作都不会生效,下面我们再看最后的两个命令,watch
和 unwatch
命令。
watch
命令的作用是监视一个或多个 key
,在事务执行之前如果有别的命令对 key
有修改操作的话,那事务将会被打断;unwatch
命令则是用于取消 watch
命令对于 key
的监视。
4.使用Java操作Redis数据库
上面我们已经介绍了 Redis
数据库中的 5
种数据类型以及一些常用的命令,而我们在实际开发中,肯定是不能一直使用命令来操作数据库的,一般都会采用开发语言来进行操作,我们当然是使用 Java
语言了,因此我们下面就来介绍应该如何使用 Java
来操作 Redis
数据库。
4.1 使用Jedis连接Redis
对于 Java
语言使用者来说,我们一般都会使用 Jedis
来操作 Redis
数据库,Jedis
是一种十分强大并且简单的操作 Redis
数据库的工具,使用它我们就可以完成对 Redis
数据库的各种操作,平时开发中也能帮助我们快速的完成相关任务,下面先看如何使用 Jedis
连接 Redis
数据库。
首先我们需要得到 Jedis
的 jar
包,我们这里使用的是 jedis-2.8.2.jar
,有了 jar
包我们就能使用其中提供的工具类完成我们想要的功能了,所以我们就将上面这个 jar
包加入到我们的工程当中,下面我们就开始写代码连接 Redis
数据库了。
Jedis jedis = new Jedis("10.21.10.27", 6379);
jedis.set("name", "kobe");
String name = jedis.get("name");
System.out.println(name);
上面的代码便是连接 Redis
,然后往里面存入一个 key
为 name
的键值对,我们下面再获取其对应的值,预想是可以得到我们想要的值的,执行代码,我们会发现有报错,报错为 connect timed out
,也就是说未连接到 Redis
数据库,那这是什么原因呢?其实我们在之前就已经提到过,Redis
出于安全考虑,默认情况下只允许本地访问,因此当我们在 windows
中写代码访问 Linux
中安装的 Redis
数据库时是访问不了的,因此我们应该允许 Redis
数据库被外围访问,这个设置是在 Redis
的配置文件 redis.conf
当中进行设置的,也就是:
bind 127.0.0.1
我们需要做的修改就是使用 #
将这句设置注释掉,注释掉之后,Redis
数据库就能被外围所访问了,因此当我们修改好配置文件之后,就可以重启 Redis
数据库了,然后我们就可以接着执行上面的代码,看看是否可以了,执行的话发现还是不行,这次的报错信息是没有设置密码,因此我们需要设置 Redis
数据库的密码,同样也是在配置文件 redis.conf
当中,就是:
# requirepass foobared
默认情况下是被注释的,也就是没有密码的,然后我们就需要将上面注释放开,然后设置新的密码,比如下面这样:
requirepass admin
这样就是设置 Redis
数据库的密码为 admin
了,当然修改了 Redis
的密码设置之后,同样也是需要重启 Redis
数据库的,这时候我们的代码也需要做一些调整,也就是使用密码去做认证,具体的代码如下:
Jedis jedis = new Jedis("10.21.10.27", 6379);
jedis.auth("admin");
jedis.set("name", "kobe");
String name = jedis.get("name");
System.out.println(name);
这样再执行代码的话我们就可以得到预期的结果了,还有一点需要说明的是,当我们 Redis
的服务端设置了密码之后,我们再使用 Redis
命令行客户端进行连接时,可以使用如下的命令:
./redis-cli -h 127.0.0.1 -p 6379 -a admin
在上面的命令中,我们指定密码时使用的 -a
参数,这样的话当服务器端设置了密码我们也能在客户端进行连接了。
4.2 使用Jedis操作string类型数据
下面我们就具体来看使用 Jedis
操作 Redis
数据库中基本的数据类型了,其实 Jedis
中提供的方法和 Redis
中提供的命令名称一般都是一致的,因此使用起来也是非常简单与便于我们理解的,下面我们先看 string
类型数据。
由于之前已经介绍了 Redis
中所提供命令的作用,而且 Jedis
提供方法的名称也是和命令一致的,那我们就直接看代码了。
public class RedisDemo2 {
private Jedis jedis;
@Before
public void createJedis() {
jedis = new Jedis("10.21.10.27", 6379);
jedis.auth("admin");
}
// set和get
@Test
public void test() {
jedis.set("name", "kobe");
String name = jedis.get("name");
System.out.println(name);
}
// mset和mget
@Test
public void test2() {
jedis.mset("password", "admin", "age", "20");
List<String> values = jedis.mget("name", "password", "age");
System.out.println(values);
}
// append setrange getrange
@Test
public void test3() {
// jedis.append("name", " is boy");
System.out.println(jedis.get("name"));
jedis.setrange("name", 8, "girl");
System.out.println(jedis.get("name"));
System.out.println(jedis.getrange("name", 8, -1));
}
}
看上面代码中的每一个测试方法应该都是非常好理解的,因为每个方法的作用都是和命令是一致的,这里只是简单地演示了几个方法,不过其它的方法也是类似的,因此大家可以自己探索。
4.3 使用Jedis操作list类型数据
下面我们就开始介绍使用 Jedis
操作 Redis
数据库中的 list
类型数据,其实也和 string
类型数据一样的,使用起来非常简单,因为提供的 api
方法和命令名称是一致的,下面我们就直接看代码了。
public class RedisDemo3 {
private Jedis jedis;
@Before
public void createJedis() {
jedis = new Jedis("10.21.10.27", 6379);
jedis.auth("admin");
}
@After
public void destroyJedis() throws IOException {
if (null != jedis) {
jedis.close();
}
}
// lpush lrange
@Test
public void test() {
jedis.lpush("names", "tom", "jerry", "kobe", "james");
List<String> names = jedis.lrange("names", 0, -1);
System.out.println(names);
}
// lset
@Test
public void test2() {
jedis.lset("names", 0, "wade");
List<String> names = jedis.lrange("names", 0, -1);
System.out.println(names);
}
// lindex
@Test
public void test3() {
String value = jedis.lindex("names", 1);
System.out.println(value);
}
// linsert
@Test
public void test4() {
jedis.linsert("names", LIST_POSITION.BEFORE, "kobe", "james");
List<String> names = jedis.lrange("names", 0, -1);
System.out.println(names);
}
// lrem
@Test
public void test5() {
jedis.lrem("names", 1, "james");
List<String> names = jedis.lrange("names", 0, -1);
System.out.println(names);
}
}
4.4 使用Jedis操作hash类型数据
下面我们开始介绍使用 Jedis
操作 Redis
数据库中的 hash
类型数据,同样的我们也是直接给出代码了。
public class RedisDemo4 {
private Jedis jedis;
@Before
public void createJedis() {
jedis = new Jedis("10.21.10.27", 6379);
jedis.auth("admin");
}
@After
public void destroyJedis() throws IOException {
if (null != jedis) {
jedis.close();
}
}
// hset hget
@Test
public void test() {
jedis.hset("user", "username", "tom");
String username = jedis.hget("user", "username");
System.out.println(username);
}
// hmset hmget
@Test
public void test2() {
Map<String, String> map = new HashMap<>();
map.put("password", "123");
map.put("sex", "male");
jedis.hmset("user", map);
List<String> values = jedis.hmget("user", "username", "password", "sex");
System.out.println(values);
}
// hgetall hkeys kvals
@Test
public void test3() {
Map<String, String> map = jedis.hgetAll("user");
for (String key : map.keySet()) {
System.out.println(key + " " + map.get(key));
}
Set<String> keys = jedis.hkeys("user");
System.out.println(keys);
List<String> vals = jedis.hvals("user");
System.out.println(vals);
}
// hdel
@Test
public void test4() {
jedis.hdel("user", "sex");
Map<String, String> map = jedis.hgetAll("user");
for (String key : map.keySet()) {
System.out.println(key + " " + map.get(key));
}
}
}
4.5 使用Jedis操作set类型数据
下面我们再来介绍使用 Jedis
操作 Redis
数据库中的 set
类型数据,同样的我们直接看代码:
public class RedisDemo5 {
private Jedis jedis;
@Before
public void createJedis() {
jedis = new Jedis("10.21.10.27", 6379);
jedis.auth("admin");
}
@After
public void destroyJedis() throws IOException {
if (null != jedis) {
jedis.close();
}
}
// sadd smembers
@Test
public void test() {
jedis.sadd("language", "java", "c++", "ruby", "python");
Set<String> smembers = jedis.smembers("language");
System.out.println(smembers);
}
// srem
@Test
public void test2() {
jedis.srem("language", "java");
Set<String> smembers = jedis.smembers("language");
System.out.println(smembers);
}
// sdiff
@Test
public void test3() {
jedis.sadd("language", "java", "c++", "ruby", "python");
jedis.sadd("language2", "c", "c++", "c#", "php");
Set<String> sdiff = jedis.sdiff("language", "language2");
System.out.println(sdiff);
}
// sinter
@Test
public void test4() {
jedis.sadd("language", "java", "c++", "ruby", "python");
jedis.sadd("language2", "c", "c++", "c#", "php");
Set<String> sinter = jedis.sinter("language", "language2");
System.out.println(sinter);
}
// sunion
@Test
public void test5() {
jedis.sadd("language", "java", "c++", "ruby", "python");
jedis.sadd("language2", "c", "c++", "c#", "php");
Set<String> sunion = jedis.sunion("language", "language2");
System.out.println(sunion);
}
}
4.6 使用Jedis操作sortedSet类型数据
下面我们开始介绍使用 Jedis
操作 Redis
数据库中的 sortedSet
类型数据,同样的我们直接看代码:
public class RedisDemo6 {
private Jedis jedis;
@Before
public void createJedis() {
jedis = new Jedis("10.21.10.27", 6379);
jedis.auth("admin");
}
@After
public void destroyJedis() throws IOException {
if (null != jedis) {
jedis.close();
}
}
// zadd zrange zrangeByScore
@Test
public void test() {
Map<String, Double> map = new HashMap<>();
map.put("张三", 60.0);
map.put("李四", 70.0);
map.put("王五", 80.0);
map.put("赵六", 90.0);
map.put("孙七", 50.0);
jedis.zadd("zset", map);
Set<String> zset = jedis.zrange("zset", 0, -1);
System.out.println(zset);
Set<String> zset2 = jedis.zrangeByScore("zset", 70, 90);
System.out.println(zset2);
}
// zrangeWithScores
@Test
public void test2() {
Map<String, Double> map = new HashMap<>();
map.put("张三", 60.0);
map.put("李四", 70.0);
map.put("王五", 80.0);
map.put("赵六", 90.0);
map.put("孙七", 50.0);
jedis.zadd("zset", map);
Set<Tuple> tuples = jedis.zrangeWithScores("zset", 0, -1);
for (Tuple tuple : tuples) {
System.out.println(tuple.getScore() + " " + tuple.getElement());
}
}
// zrank
@Test
public void test3() {
Map<String, Double> map = new HashMap<>();
map.put("张三", 60.0);
map.put("李四", 70.0);
map.put("王五", 80.0);
map.put("赵六", 90.0);
map.put("孙七", 50.0);
jedis.zadd("zset", map);
Long zrank = jedis.zrank("zset", "李四");
System.out.println(zrank);
}
// zscore
@Test
public void test4() {
Map<String, Double> map = new HashMap<>();
map.put("张三", 60.0);
map.put("李四", 70.0);
map.put("王五", 80.0);
map.put("赵六", 90.0);
map.put("孙七", 50.0);
jedis.zadd("zset", map);
Double zscore = jedis.zscore("zset", "张三");
System.out.println(zscore);
}
// zrem
@Test
public void test5() {
Map<String, Double> map = new HashMap<>();
map.put("张三", 60.0);
map.put("李四", 70.0);
map.put("王五", 80.0);
map.put("赵六", 90.0);
map.put("孙七", 50.0);
jedis.zadd("zset", map);
jedis.zrem("zset", "王五");
Set<Tuple> tuples = jedis.zrangeWithScores("zset", 0, -1);
for (Tuple tuple : tuples) {
System.out.println(tuple.getScore() + " " + tuple.getElement());
}
}
}
4.7 使用Jedis进行key的通用操作
下面我们开始介绍使用 Jedis
进行 Redis
中 key
的通用操作,比如获取 Redis
数据库中所有的 key
,删除某些 key
,对 key
设置过期时间之类的,我们先看具体的代码:
public class RedisDemo7 {
private Jedis jedis;
@Before
public void createJedis() {
jedis = new Jedis("10.21.10.27", 6379);
jedis.auth("admin");
}
@After
public void destroyJedis() throws IOException {
if (null != jedis) {
jedis.close();
}
}
// keys pattern
@Test
public void test() {
Set<String> keys = jedis.keys("*");
System.out.println(keys);
}
// del
@Test
public void test2() {
Long del = jedis.del("user");
System.out.println(del);
}
// 关于key的时间设置
@Test
public void test3() throws ParseException {
jedis.set("name", "kobe");
String name = jedis.get("name");
System.out.println(name);
Long ttl = jedis.ttl("name");
System.out.println(ttl);
jedis.expire("name", 100);
Long ttl2 = jedis.ttl("name");
System.out.println(ttl2);
jedis.persist("name");
Long ttl3 = jedis.ttl("name");
System.out.println(ttl3);
}
}
关于对 key
设置过期时间,在默认情况下,key
在 Redis
数据库中是永久有效的,我们使用 ttl()
方法查看 key
的有效期时,返回值为 -1
,当我们使用 expire()
方法为 key
设置了有效期之后,然后再使用 ttl()
方法获取有效期时,那返回的就是剩余的有效期了,最后 persist()
方法是将 key
的有效期限设置为永远,也就是使 key
在 Redis
数据库中永久有效。
5.Redis数据持久化
下面我们来介绍一下 Redis
的数据持久化方面知识,Redis
数据库的数据持久化方式一共有 2
种,那就是 RDB
和 AOF
持久化方式,我们先分别介绍一下:
RDB持久化:在指定的时间间隔内将内存中的数据集快照写入到磁盘上面。
AOF持久化:以日志的形式记录服务器端进行的每一个写操作,在Redis数据库启动之时,会读取该文件构建数据库,保证启动之后数据库中的数据是完整的。
了解了 Redis
数据库的两种持久化方式之后,我们可以在 Redis
数据库中使用的持久化机制就有下面 4
种了。
1.RDB持久化;
2.AOF持久化;
3.同时应用RDB和AOF;
4.无持久化。
介绍了 Redis
数据库持久化机制之后,下面再对 RDB
和 AOF
这两种持久化方式进行详细说明。
5.1 RDB持久化方式
在 Redis
数据库中默认使用的持久化方式便是 RDB
方式了,因此如果我们不修改相关设置的话,那我们就是使用的这种持久化方式,我们在 Redis
数据库的配置文件 redis.conf
中可以看到以下配置:
save 900 1
save 300 10
save 60 10000
这便是针对于 RDB
持久化方式进行的设置,前面一个数字为时间的秒数,后一个数字为操作 key
的个数,比如 save 900 1
,就是表示如果在 900
秒以内有 1
个 key
被修改的话,那么就会将内存中的数据集快照写入到硬盘中的 dump.rdb
文件,当然这个 dump.rdb
文件名称也是在 redis.conf
配置文件中进行配置的,这样的话,其它的配置也是一样的了,都是在多少秒之内有多少个 key
被修改就会将内存快照写入到磁盘当中。
5.2 AOF持久化方式
如果想要启用 AOF
方式的持久化的话,那就需要修改 redis.conf
文件当中的一个配置,也就是:
appendonly no
修改的方式也就是将其中的 no
改为 yes
,这样的话便是启用 AOF
的持久化方式了。
AOF
持久化方式的策略一共有三种,在 redis.conf
配置文件中的默认配置为:
# appendfsync always
appendfsync everysec
# appendfsync no
可以看出一共是三种方式,第一种为每次有修改操作时都会追加写 AOF
文件;第二种为每一秒进行追加写 AOF
文件;第三种则是不同步。从上面的设置可以看出默认是采用的第二种方式,也就是每秒钟进行追加写 AOF
文件,这种方式通常情况也是最适中的。
关于 AOF
持久化方式,还有一点需要说明的是,因为是采用的追加写日志的方式,因此我们是可以对日志文件进行瘦身处理的,也就是只保留对 key
修改最近的一条,这样的话也是能保证数据是完整的,其实在 redis.conf
文件中也有配置,那就是:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
第一条设置是说当对操作记录追加写的比率达到 100%
时,就会进行自动瘦身了;第二条则是当日志文件大小达到 64mb
时,也会进行自动瘦身操作。其实除了配置之外,我们还可以使用命令来主动进行文件瘦身,那就是 bgrewriteaof
命令了。
5.3 RDB和AOF方式对比
上面已经介绍了 RDB
和 AOF
方式的相关特性,下面我们就将它们对比来看一下彼此的优缺点。
RDB方式:
优势:
1.数据的备份和恢复非常方便,因为一个数据库只有一个持久化文件;
2.性能更高;对Redis服务进程来说,当需要做持久化时,只需要创建出子进程,然后让子进程去做这些持久化的工作,这样就可以避免服务进程进行IO操作了;
3.对比AOF来说,当持久化文件较大时,RDB方式的启动效率会更高。
劣势:
1.当系统在持久化时宕机,那还没来得及写入磁盘的数据就会丢失了;
2.由于RDB方式是通过创建子进程来协助完成数据持久化工作的,当要持久化的数据集较大时,可能会导致整个服务器停止服务几百毫秒;
AOF方式:
优势:
1.更高的数据安全性,也就是数据持久性,提供了3种同步策略,每秒同步,每次修改同步和不同步;
2.由于对持久化文件是采用追加写入的方式,因此即使在写入过程中发生服务器宕机,也不会影响已经写入的数据内容;
3.当持久化文件过大时,Redis可以进行自动瘦身;
4.AOF日志文件格式清晰,便于理解,很容易用该文件完成数据的重建。
劣势:
1.对于相同的数据来说,AOF文件要大于RDB持久化文件;
2.根据同步策略的不同,AOF运行的效率会慢于RDB方式。每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB是一样的。
6.总结
其实上面的 Redis
知识更多的是一些基础类知识,也是很容易掌握的,通过自己写的过程,也能使自己的学习更加深刻,这便是我写博客的目的,掌握了最基础的知识之后,自己也能有信心去学更多精深的内容,保持乐观,耐心地积累下去吧。