Redis原理和Jedis

16 篇文章 0 订阅
2 篇文章 0 订阅

Redis原理和Jedis

Redis是什么?

Redis 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库。(B/S架构)

Redis 与其他 key - value 缓存产品有以下三个特点:

  • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  • Redis支持数据的备份,即master-slave模式的数据备份。

Redis的优势

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

安装Redis

Windows环境下安装

  1. 下载压缩包

    github:https://github.com/tporadowski/redis/releases

  2. 解压缩

  3. 配置环境变量

    添加REDIS_HOME系统变量:在这里插入图片描述

    再配置环境变量path(即在path中添加):在这里插入图片描述

  4. 试运行

    尝试在任何地方使用cmd运行如下命令:

    redis-server
    

    如果出现如下内容则证明redis安装成功并且环境变量配置成功:在这里插入图片描述

linux环境下安装

Redis默认端口号:6379

Redis服务端

服务端启动Redis服务

  1. 前台启动

    redis-server
    
  2. 后台启动

    redis-server &
    
  3. 指定配置文件启动

    redis-server <config_name> [&] (是否后台启动)
    

服务端停止Redis服务

redis-cli shutdown

Redis客户端

Redis分为客户端和服务端。客户端用于连接Redis服务,并且向Redis服务端发送命令。

启动cli客户端

使用以下命令即可启动redis的客户端程序

redis-cli   		#默认连接127.0.0.1:6379的redis服务
	[-p port]		#指定端口号
	[-h hostname]	#指定主机地址

退出cli客户端

exit [quit]

Redis基本知识

服务端测试Redis性能

redis-benchmark

客户端查看Redis服务是否正常运行

127.0.0.1:6379>ping
PONG	#返回PONG说明Redis服务正常运行

客户端查看Redis服务的统计信息

127.0.0.1:6379>info	#返回redis服务的所有相关信息
	[section]			#查看具体某一指定端的信息

Redis数据库实例

Redis的数据库实例只能由Redis服务来创建和维护,开发人员不能修改和自行创建数据库实例。默认情况下,Redis会自动创建16个数据库实例,并且这些数据库实例由编号来区别,由0—15来使用。Redis的数据库实例本身占用的存储空间很少。Redis客户端默认连接的是编号为0的数据库实例。

客户端切换数据库实例
127.0.0.1:6379>select [index]	#根据编号切换数据库实例
客户端查看数据库实例记录数(key的数量)
127.0.0.1:6379>dbsize
(integer) 2					#返回key的数量		
客户端查看当前数据库实例所有key
127.0.0.1:6379>keys [pattern] [*] # * 所有
1) "key:__rand_int__"	
2) "k1"						#返回有哪些key
客户端清空当前数据库实例
127.0.0.1:6379>flushdb
客户端清空所有数据库实例
127.0.0.1:6379>flushall
客户端查看当前Redis服务的配置信息
127.0.0.1:6379>config get [param] [*]

Redis的五种数据结构

Redis支持五种数据类型:string(字符串),list(列表),hash(哈希),set(集合)及zset(sorted set:有序集合)。

string(字符串)

string是redis最基本的类型,一个key对应一个value,是二进制安全的。可以包含任何数据,比如jpg图片或者序列化的对象。

最大能存储512MB

cli> set key value


127.0.0.1:6379> SET runoob "菜鸟教程"
127.0.0.1:6379> GET runoob
"菜鸟教程"

list(列表)

简单的字符串列表,按照插入顺序排序,可以添加元素到头部或者尾部

列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。

cli> lpush key member
cli> lrange key start end #从start到end遍历key列表

127.0.0.1:6379> lpush runoob redis
127.0.0.1:6379> lpush runoob mongodb
redis 127.0.0.1:6379> lrange runoob 0 10
1) "mongodb"
2) "redis"

set(集合)

字符串的无序集合(基于哈希表实现,故不能插入重复的value)。

cli> sadd key member	#成功返回1,失败返回0(如重复)
cli> smembers key		#遍历key集合

127.0.0.1:6379> sadd runoob redis
127.0.0.1:6379> sadd runoob mongodb
127.0.0.1:6379> smembers runoob	
1) "redis"
2) "mongodb"

hash(哈希)

Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

每个 hash 可以存储 232 -1 键值对(40多亿)。

#存入key对象,其中属性field1的值为value1,属性filed2的值为value2
cli> HMSET key field1 value1 field2 value2
#获取key对象中属性为filed的值value
cli> HGET key filed

127.0.0.1:6379> HMSET runoob field1 "Hello" field2 "World"
"OK"
127.0.0.1:6379> HGET runoob field1
"Hello"
127.0.0.1:6379> HGET runoob field2
"World"

zset(有序集合)

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

cli> zadd key score member
cli> zrangebyscore key start end #根据具体分数(score必须在start和end范围内)排序
cli> zrange key start end		 #排序(根据分数)

redis 127.0.0.1:6379> zadd runoob 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabbitmq
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabbitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE runoob 0 1000
1) "mongodb"
2) "rabbitmq"
3) "redis"

Redis命令的基本语法

cli> COMMAND KEY_NAME

COMMAND是命令,KEY_NAME是键

127.0.0.1:6379> DEL runoobkey

DEL是删除命令,runoobkey是一个键,该命令的目的是删除runoobkey

Redis关于key的命令

查询key的命令(keys)

基本语法格式

cli> keys [pattern]		#pattern是匹配规则
		[*]			#匹配0个或者多个字符
		[?]			#只匹配1个字符
		[[]]		#只匹配[]中的1个字符
		[a*]		#以'a'字符开始的key
		[a*b]		#以'a'开始,'b'结尾的key

127.0.0.1:6379> keys *				#查看所有key
1) "ageZset"
2) "name"
3) "osList"
4) "citySet"
5) "key:__rand_int__"
6) "k1"

127.0.0.1:6379> keys k*				#查看以'k'字符开头的key
1) "key:__rand_int__"
2) "k1"

127.0.0.1:6379> keys a*t			#查看以'a'开头,t结尾的key
1) "ageZset"

127.0.0.1:6379> keys n?me			#查看第1个字符为n,第34字符为me的key
1) "name"

127.0.0.1:6379> keys n[abc]me 		#查看第1个字符为n,第2个字符为[abc]其中一个,第34字符为me的key
1) "name"

判断key是否存在(exists)

cli> exists key	[key key key...]		#判断key是否存在
#存在返回1(或存在的数量),否则返回0

127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists name osList ageZset citySet
(integer) 4
127.0.0.1:6379> exists name osList ageZset citySet test
(integer) 4

移动key到指定的数据库实例(move)

cli> move key index					  	#移动key到index数据库实例

查看key剩余生存时间(ttl)

单位是秒。(TTL:time to live)

cli> ttl key							#查看key的剩余生存时间
#返回-1,永不过期(默认)
#返回-2,key不存在

设置定key的生存时间(expire)

cli> expire key seconds					#给key设置生存时间seconds秒(超出即被删除)

查看key的数据类型(type)

cli> type key								#查看key的数据类型

重命名key(rename)

cli> rename key newkey

删除key(del)

cli> del key [key key key...]
	#返回成功删除key的条数

总结

    • del:删除key
    • move:移动key
    • expire:设置key的生存时间
    • rename:重命名
    • keys:查询key
    • exists:判断key是否存在
    • ttl:查询key的剩余生存时间
    • type:查看key的数据类型

Redis操作string类型

单key——单value

存string类型数据(set)

cli> set key value
		#如果key已经存在,会发生覆盖

取string类型数据(get)

cli> get key

追加字符串(append)

cli> append key value		#往key中追加字符串value
		#返回值是追加后字符串长度
		#如果key不存在,相当于set

获取字符串长度(strlen)

cli> strlen key				#返回key的字符串长度

将字符串数值进行加1运算(incr)

cli> incr key				#key的value+1并且返回

Tips:

  • 如果key不存在,则先创建该key-value,value初始化为0然后+1
  • key的value必须是数值,否则报错

将字符串数值进行减1运算(decr)

cli>decr key

incr同理

将字符串数值进行加offset(指定数值)运算(incrby)

cli>incrby key offset			#key的vaule+offset并且返回

将字符串数值进行减offset(指定数值)运算(decrby)

cli>decrby key offset			#key的vaule-offset并且返回

获取字符串中字符串(getrange)

cli>getrange key startIndex endIndex
				#截取key中value从startIndex到endIndex		

设置字符串中字符串(setrange)

会改变数据库中的值

cli>setrange key startIndex value
				#用value覆盖从下标为startIndex开始的字符串

设置字符串的同时设置生命周期(setex)

cli>setex key seconds value
				#给key-value设置seconds秒后过期
cli> setex k1 30 v1
OK
cli> ttl k1
(integer) 27

设置字符串数据当key不存在才设置,key存在则不设置(setnx)

cli>setnx key value

批量设置字符串数据(mset)

cli>mset key1 value1 key2 value2 key3 value3 ...

批量获取字符串数据(mget)

cli>mget key1 key2 key3 ...

总结

Redis操作string类型

    • set:最基本的存储string类型数据的方式(如果存在则会覆盖)
    • setex:存储数据的同时设置生命周期
    • setnx:如果存在则不存储
    • mset:批量存储
    • set:修改string类型的数据(如果存在则会覆盖,也就是修改)
    • append:在原有的基础上追加数据
    • incr:数值字符串增加
    • decr:数值字符串减少
    • setrange:修改字符串中指定下标的数据
    • get:获得数据
    • mget:批量获得数据
    • getrange:获得字符串中子串
    • strlen:字符串长度

Redis操作list类型

单key——多有序value。顺序跟插入元素的顺序有关

从左侧起存list类型数据(lpush)

依次往左侧(头部)添加元素

cli>lpush key value [value value...]

cli> lpush list1 1 2 3 4
(integer) 4

cli> lrange list1 0 4
1) "4"
2) "3"
3) "2"
4) "1"	
			#可以看到lpush的顺序后插入的元素在列表中越靠前

从右侧起存list类型数据(rpush)

依次从右侧(尾部)添加元素

cli>rpush key value [value value...]

获取指定列表中指定下标区间的元素(lrange)

cli>lrange key startIndex endIndex
					#获取key列表中startIndex到endIndex的元素
					
cli> lrange list1 0 4
1) "4"
2) "3"
3) "2"
4) "1"		

cli> lrange list1 0 -1 	#获取从头到尾的全部元素,-1表示倒数第一个,即尾部
1) "4"
2) "3"
3) "2"
4) "1"		

从指定列表中移除并返回表头(左侧)元素(lpop)

cli> lpop key

从指定列表中移除并返回表尾(右侧)元素(rpop)

cli> rpop key

获取列表中指定下标的元素(lindex)

cli> lindex key index

获取指定列表的长度(元素个数)(llen)

cli> llen key

移除指定列表中数据(lrem)

cli>lrem key count value
			#移除key列表count个中值为value的元素
			# count > 0 从左侧起移除
			# count < 0 从右侧起移除
			# count = 0 移除所有

总结

Redis操作list类型

    • lpush:从左边添加元素
    • rpush:从右边添加元素
    • lpop:从左边弹出元素
    • rpop:从右边弹出元素
    • lrem:移除指定元素
    • lrange:遍历
    • lindex:指定下标的元素
    • llen:长度

Redis操作set类型

单key——多无序且不可重复value

将一个或多个元素添加到指定集合中(sadd)

cli> sadd key value [value value ...]	#若元素重复,则忽略

cli> sadd set1 a b c a
(integer) 3			#成功添加元素的个数
					#只添加成功三个

获取指定集合中的元素(smembers)

cli> smembers key

cli> smembers set1
1) "c"
2) "b"
3) "a"

判断元素在集合中是否存在(sismember)

cli> sismember key member

(integer) 1			#返回1则存在
(integer) 0			#返回0则不存在

获取集合的长度(scard)

cli> scard key

移除集合中1个或者多个元素(srem)

cli> srem key member [member ...]
						#返回成功移除的元素的个数

随机获取集合中的一个或多个元素(srandmember)

cli> srandmember key [count]	
						#count>0 随机获取的元素不会重复
						#count<0 随机获取的元素可能重复

随机移除集合中的一个或多个元素(spop)

cli> spop key [count]

将集合中的元素移动到另一集合(smove)

cli> smove source dest member	#将source集合中的member移动到dest集合

获取集合的差集(sdiff)

即一个集合中有,但是其他集合中没有的元素

cli> sdiff key1 key2 [key3 ...]
					#即key1集合中有,其他集合中没有的元素
					
cli> smembers set1
1) "f"
2) "c"
3) "e"
4) "g"
5) "a"
6) "b"
cli> smembers set2
1) "d"
2) "c"
3) "g"
4) "f"
5) "e"
6) "i"
7) "b"
8) "a"
9) "h"
cli> sdiff set2 set1		#set2中有hid,但是set1中没有
1) "i"
2) "d"
3) "h"

获取集合的交集(sinter)

即指定集合中都有的元素

cli> sinter key key [key key...]

127.0.0.1:6379> smembers set1
1) "f"
2) "c"
3) "e"
4) "g"
5) "a"
6) "b"
127.0.0.1:6379> smembers set2
1) "d"
2) "c"
3) "g"
4) "f"
5) "e"
6) "i"
7) "b"
8) "a"
9) "h"
127.0.0.1:6379> sinter set1 set2
1) "f"
2) "c"
3) "e"
4) "g"
5) "a"
6) "b"
						#set1和set2中都有abcefg

获取集合的并集(sunion)

即指定集合中的所有元素

cli> sunion key key [key key...]

总结

Redis操作set类型

    • sadd:新增元素到集合中
    • srem:移除元素
    • spop:弹出元素
    • smove:移动过元素到另一集合
    • sadd:新增元素到集合中,如果value已经存在则会覆盖
    • smembers:遍历集合元素
    • sismember:判断元素是否存在
    • scard:集合长度
    • srandmember:随机获取集合元素
    • sdiff:差集
    • sinter:交集
    • sunion:并集

Redis操作hash类型

单key——field-value

​ field-value

​ field-value

​ 适合存储对象

将一个或多个field-value(属性—值)对存入hash表,会覆盖(hset)

如果key中的filed之前是存在的,则会强制覆盖(使用hsetnx不会强制覆盖)

cli> hset key field1 value1 [field2 value2 ...]

cli> hset student1 name wqk age 20
(integer) 2

获取指定hash表中指定field值(hget)

cli> hget key field			#获取key哈希表中field的值

cli> hget student1 name
"wqk"

批量获取指定hash表中field的值(hmget)

cli> hmget key field1 [field2 field3 ...]

cli> hmget student1 name age
1) "wqk"
2) "20"

获取hash表中所有的field和value(hgetall)

cli> hgetall key

cli> hgetall student1
1) "name"
2) "wqk"
3) "age"
4) "20"

移除hash表中指定一个或者多个field(hdel)

cli> hdel key field1 [field2 field3 ...]

统计hash表中field数量(hlen)

cli> hlen key

判断hash表中是否存在某field(hexists)

cli> hexists key field
				#返回0表示不存在,1表示存在

获取hash表中所有的field列表(hkeys)

cli> hkeys key

cli> hkeys student2
1) "id"
2) "name"
3) "age"

获取hash表中所有的value(hvals)

cli> hvals key

cli> hvals student2
1) "10001"
2) "test"
3) "18"

对hash表中指定field的value进行整数加法运算(hincrby)

cli> hincrby key field incrment
	#对key哈希表中的field属性的值进行加incrment值运算
				
cli> hget student2 age
"18"

cli> hincrby student2 age 2 
(integer) 20

对hash表中指定field的value进行浮点数加法运算(hincrbyfloat)

cli> hincrbyfloat key field incrment

cli> hset student2 score 85
(integer) 1
cli> hincrbyfloat student2 score 1.5
"86.5"

将一个或多个field-value(属性—值)对存入hash表,不会覆盖(hsetnx)

如果key的field已经存在则放弃设置

cli> hsetnx key filed1 value1 [field2 value2]

总结

Redis操作hash类型

    • hset:存入hash类型数据
    • hsetnx:存入数据,如果存在则不存入
    • hdel:删除hash表中field及其value
    • hset:存入hash类型数据,如果field存在则会覆盖
    • hincrby:value是数值,则会增加
    • hget:获取hash表中指定field的value
    • hmget:批量获取hash表中指定field的value
    • hgetall:获取hash表中所有field及其value
    • hlen:获取hash表中field的数量
    • hexists:判断hash表中该field是否存在
    • hkeys:获取hash表中所有field
    • hvals:获取hash表中所有value

Redis操作zset类型

有序集合。顺序跟score有关

将一个或多个member及其score值加入有序集合(zadd)

score必须是数值。如果member已经存在,则会覆盖

cli> zadd key score member [score member]

cli> zadd zset1 95 wqk 90 lk 98 zp
(integer) 3

获取有序集合中指定下标区间的元素(zrange)

cli> zrange key startIndex endIndex [withscores] #获取key集合startIndex到endIndex区间的元素(是否显示分数)

获取有序集合中指定分数区间(闭区间)的元素(zrangebyscore)

cli> zrangebyscore key min max [withscores] #获取key集合min到max分数区间的元素(是否显示分数)

删除有序集合中一个或者多个元素(zrem)

cli> zrem key member [member]

获取有序集合中元素个数(zcard)

cli> zcard key

获取有序集合中分数在指定区间的元素个数(zcount)

cli> zcount key min max		#获取key有序集合中min到max区间的元素个数

获取有序集合中指定元素的排名(升序)(zrank)

cli> zrank key member

获取有序集合中指定元素的排名(降序)(zrevrank)

cli> zrevrank key member

获取有序集合指定元素的分数(zscore)

cli> zscore key member

总结

Redis操作zset类型

    • zadd:往集合中存入元素
    • zrem:移除集合中的元素
    • zrange:遍历集合元素
    • zrangebyscore:根据score遍历集合元素
    • zcard:集合元素个数
    • zcount:指定分数区间元素个数
    • zrank:元素的排名(正序)
    • zrevrank:元素的排名(倒序)
    • zscore:元素的分数

Redis配置文件

Redis根目录下有个默认的配置文件:.\redis.conf,里面有一些redis默认的配置参数

cli> redis-server [config_name]	#默认使用默认的配置的文件

网络配置

  • port

    指定redis服务所使用的端口号,默认6379

  • bind

    指定客户端连接redis服务时,所能使用的主机地址,默认是redis服务所在主机名,一般情况下都需要指定主机名

  • tcp-keeplive

    TCP连接保活策略,单位是秒。指的是Redis服务端会在指定秒内向连接他的客户端发送一次ACK请求,以确保连接有效。默认是60秒

如果以上配置在配置文件中生效,则在客户端中连接需使用:

redis-cli -h bind -p 6380

常规配置

  • loglevel

    配置日志级别,分为四个级别:debug、verbose、notice、warning。默认是notice

  • logfile

    日志文件持久化存储的地方。默认不会持久化,只会输出到终端

  • databases

    配置Redis服务默认创建的数据库实例个数。默认是16个

安全配置

  • requirepass

    配置Redis客户端访问Redis服务时的密码,需要protected-mode = yes时生效

如配置了requirepass,则在客户端连接中需使用:

redis-cli [-h hostname] [-p port] [-a pwd]

Redis数据持久化

Redis提供持久化策略,可以在适当的时机采用适当的手段把内存中的数据持久化的磁盘。

RDB策略(Redis默认启用的持久化策略)

RDB(Redis DataBase)策略是指,在指定时间间隔内,Redis服务执行指定次数的写操作,会自动触发一次持久化操作。如需修改可以在redis的配置文件中修改RDB相关的配置(dbfilename持久化数据的文件名,默认是dump.rdb,dir持久化数据的目录,默认是./,即根目录)。

AOF策略

记录每一次Redis服务的完整写操作进入日志,每次Redis服务启动时,都会重新执行一遍完整的操作日志以便恢复数据。效率不高,默认不开启AOF(appendonly配置是否开启AOF策略,appendfilename配置操作日志文件)。

Redis事务

数据库事务:一组对数据库的操作一起执行,保证操作的原子性,要么同时成功,要么同时失败。

Redis事务:允许一组Redis命令放在一起,将命令序列化,然后按队列执行,保证部分原子性。

multi

标记一个Redis事务的开始(与exec成对存在)。

exec

标记一个Redis事务的结束(与multi成对存在),即立即开始执行上述事务。

一个完整的Redis事务的输入格式应该是如下

cli> multi				#标识事务开始了
cli> set k1 v1			#操作1
cli> set k2 v2			#操作2
cli> exec				#标识事务结束了(即立即开始执行)
cli> multi
OK		#事务开启
cli> set str1 value1
QUEUED	#操作1加入队列
cli> set str2 value2
QUEUED	#操作2加入队列
cli> exec
1) OK	#操作1执行结果
2) OK	#操作2执行结果

Tips:

Redis事务不是脚本命令。而是在cli执行multi后接着一行一行输入操作的命令(操作会插入队列),最后执行exec才会按队列执行上述操作。为保证事务的部分原子性,Redis事务遵循以下两则规则:

  1. 输入事务操作过程中如果发生错误,则事务输入失败,即事务无法被创建。即multiexec中间操作语句有错误则事务无法被创建。

    cli> multi		
    cli> set k1 v1	
    cli> seta k2 v2		#操作2:命令错误
    cli> exec
                      
    					#该事务无法被创建:操作的命令错误
    
  2. 如果上述规则没有发生错误,则在最后执行操作队列时,即使其中一项操作执行异常仍会继续执行其余操作。

    cli> multi
    cli> set k1 v1
    cli> incr k1		
    cli> exec
    					#事务会被创建,即使incr的执行结果异常
    

discard

清除已经存在于操作队列中的命令

cli> multi
OK
cli> set k5 v5
QUEUED
cli> set k6 v6
QUEUED
cli> discard					#清空上述操作队列
OK
cli> exec
(error) ERR EXEC without MULTI			#exec执行失败的原因是操作命令的队列被清空

watch

在事务开启前(multi)监控某一个键,在事务执行的过程中监控的键的值发生变化,则此次事务放弃执行;否则正常执行。

用以下事务来模拟一个场景:钱包A余额50,钱包B余额50,钱包B向钱包A转账50,那么最后钱包B余额0,钱包A余额100。并发情况下假如多个钱包需要向钱包B转账,那么就会发生错误。所以需要监控该次事务执行前的version是否还是该version。

cli> set balanceA 50
OK
cli> set balanceB 50
OK
cli> set verison 1
OK
cli> watch version			#监控version的值
OK
cli> multi
OK
cli> incrby balanceA 50
QUEUED
cli> decrby balanceB 50
QUEUED
cli> incr version
QUEUED
cli> exec
1) (integer) 100
2) (integer) 0
3) (integer) 2

Tips

watch就是类似于乐观锁,A事务执行前查询一个键(该键会在其他事务进行该事务后发生改变,即标识此次事务执行成功),如果该键在A事务执行过程中发生了改变(说明其他事务对数据进行了操作),即A事务应该不执行,如果该键没有发生改变,则A事务正常执行。

unwatch

放弃监控的所有键。

Redis消息的发布与订阅(了解)

Redis客户端订阅频道,订阅了同一频道的客户端可以发送或者收到来自同一频道的消息。(消息中间件)

subscribe

订阅频道(以便接收消息)

cli> subscribe channel [channel ...]			#可以订阅多个频道

client1:

订阅了ch1频道

cli> subscribe ch1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "ch1"
3) (integer) 1

client2:

订阅了ch1频道

cli> subscribe ch1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "ch1"
3) (integer) 1

此时client1和client2都订阅了ch1频道,如果有客户端往ch1频道发送消息,那么client1和client2都会收到

psubscribe

订阅频道(支持通配符)

cli> psubscribe pattern [pattern ...]

cli> psubscribe news*			#订阅以news开头的频道

publish

往频道上发布消息

cli> publish channel message			#往channel频道发送message消息

client3向ch1频道发送消息:

cli> publish ch1 Hello,Redis!
(integer) 2

此时client1和client2就会收到来自client3的消息:

1) "message"
2) "ch1"
3) "Hello,Redis!"            

Redis主从复制

搭建一主二从的Redis集群并启动

实际开发中应分别在三台不同的服务器上部署三个Redis服务,这里为了方便演示选择在同一台服务器启动三个配置文件不同的Redis服务来模拟,三个Redis服务端口号分别是6379、6380、6381,三份配置文件名命名为:redis_6379.confredis_6380.confredis_6381.conf

redis_6379.conf:

port 6379						#修改端口号
logfile "redis_6379.log"		#修改生成的日志文件名
dbfilename redis_6379.rdb		#修改持久化的文件名

同理,redis_6380.conf和redis_6381.conf也需要修改以上配置

Tips

需要修改日志和持久化文件的文件名,不然可能会造成数据冲突。

接下来使用redis-server <config_name>分别启动三个Redis服务

redis-server redis_6379.conf
redis-server redis_6380.conf
redis-server redis_6381.conf

使用cli分别连接三台Redis服务

redis-cli -p 6379
redis-cli -p 6380
redis-cli -p 6381

查看Redis服务的主从信息

cli> info replication

# Replication
role:master								#角色:主机
connected_slaves:0						#已连接的从机数量:0
master_replid:91792be1189896bfa47e7a99aae23b8702c8c93b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

初始化的Redis服务的角色都是主机,即role:master

设置从机从属于主机(slaveof)

命令格式如下:

cli> slaveof host port     #设置当前连接的服务从属于host:port的redis服务

在已连接从机服务的客户端上执行:slaveof 127.0.0.1 6379,即

127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> info replication
# Replication
role:slave						#角色:从机
master_host:127.0.0.1			#主机host地址
master_port:6379				#主机port端口号
master_link_status:up			#与主机连接状态:up(已连接)
...
127.0.0.1:6381> slaveof 127.0.0.1 6379
OK
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
...

全量复制

一旦主从关系确定,主库会将所有数据立马同步到从库。

增量复制

在主库上写的数据,会同步到从库。

读写分离,主写从读

主机既能读也能写(尽量写),从机只能读不能写。

主机宕机,从机原地待命

主机宕机后,从机仍然可以读数据,可以从info replication中看到与主机的连接状态变为down

127.0.0.1:6380> info replication
# Replication
role:slave									#角色仍是从机
master_host:127.0.0.1
master_port:6379
master_link_status:down						#与主机的连接状态变为down(未连接)

主机恢复,恢复原样

主机恢复后,恢复到原样,从机与主机继续建立连接

127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up						#与主机的连接状态变为up(已连接)

从机宕机

从机宕机后,主机少一台从机,其余从机不受影响

从机恢复

从机恢复后,会初始化到最初的状态(即变为role:master),需要重新设置从属主机,然后主机的数据会全量复制到从机上。

主机宕机,从机上位

主机宕机后,如果主机灾害程度无法短时间恢复则可以使用从机进行上位(即取代主机)。

  1. 选择一个从机取消其原本的从属关系,成为新的主机

    127.0.0.1:6380> slaveof no one			#取消从属关系(成为主机)
    OK
    127.0.0.1:6380> info replication		#查看复制关系
    # Replication
    role:master								#角色:主机(新的主机)
    connected_slaves:0
    ...
    

    此时6380从机成为了新的主机,并且仍保留原有的数据

  2. 其余从机重新从属于新的主机

    127.0.0.1:6381> slaveof 127.0.0.1 6380	#6381从机从属于6380
    OK
    
    127.0.0.1:6381> info replication
    # Replication
    role:slave
    master_host:127.0.0.1
    master_port:6380
    master_link_status:up
    

Tips

主机可读可写,从机只能读,不能写。

Redis哨兵模式

Redis的哨兵模式会在某一Redis服务发生故障(如宕机)时,自动执行从机上位(按照一定规则),并且主机恢复后会从属于新的主机。流程如下:

  1. 配置哨兵的配置文件

    redis_sentinel.conf:(配置文件名称自定义)

    #该哨兵的名称是sentinel-redis。作用是monitor(监控)主机127.0.0.1的6379(端口)上的Redis服务
    sentinel monitor sentinel-redis 127.0.0.1 6379 1
    
  2. 启动哨兵

    cmd> redis-server redis_sentinel.conf --sentinel
    

    出现以下内容则启动成功:

    cmd>redis-server.exe redis_sentinel.conf --sentinel
    [16228] 07 Oct 21:58:19.446 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
    [16228] 07 Oct 21:58:19.446 # Redis version=5.0.10, bits=64, commit=1c047b68, modified=0, pid=16228, just started
    [16228] 07 Oct 21:58:19.446 # Configuration loaded
                    _._
               _.-``__ ''-._
          _.-``    `.  `_.  ''-._           Redis 5.0.10 (1c047b68/0) 64 bit
      .-`` .-```.  ```\/    _.,_ ''-._
     (    '      ,       .-`  | `,    )     Running in sentinel mode
     |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
     |    `-._   `._    /     _.-'    |     PID: 16228
      `-._    `-._  `-./  _.-'    _.-'
     |`-._`-._    `-.__.-'    _.-'_.-'|
     |    `-._`-._        _.-'_.-'    |           http://redis.io
      `-._    `-._`-.__.-'_.-'    _.-'
     |`-._`-._    `-.__.-'    _.-'_.-'|
     |    `-._`-._        _.-'_.-'    |
      `-._    `-._`-.__.-'_.-'    _.-'
          `-._    `-.__.-'    _.-'
              `-._        _.-'
                  `-.__.-'
    
    [16228] 07 Oct 21:58:19.452 # Sentinel ID is 192b87ae0be76565fefed74ead2f28454a49f746
    [16228] 07 Oct 21:58:19.452 # +monitor master sentinel-redis 127.0.0.1 6379 quorum 1
    [16228] 07 Oct 21:58:19.456 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ sentinel-redis 127.0.0.1 6379
    [16228] 07 Oct 21:58:19.457 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ sentinel-redis 127.0.0.1 6379
    
  3. 监控的主机宕机,从机会在短时间后上位(变为新的主机,其余从机从属于该新主机)

    (此时6379宕机)

    6381成为新的主机:

    127.0.0.1:6381> info replication
    # Replication
    role:master
    connected_slaves:1
    slave0:ip=127.0.0.1,port=6380,state=online,offset=22068,lag=0
    ...
    

    6380从属于6381:

    127.0.0.1:6380> info replication
    # Replication
    role:slave
    master_host:127.0.0.1
    master_port:6381
    master_link_status:up
    ...
    
  4. 旧主机恢复,变为新主机的从机

    (此时6379恢复)

    6379从属于6381:

    127.0.0.1:6379> info replication
    # Replication
    role:slave
    master_host:127.0.0.1
    master_port:6381
    master_link_status:up
    ...
    

Tips

哨兵并不会在主机宕机的一瞬间就将从机上位,而是在一定间隔时间内扫描,扫描到主机宕机了再让从机上位,旧主机恢复时,哨兵将其变为新主机的从机时同理(并不会立马执行,而是在一定时间间隔内)。

总结

  1. 查看Redis服务的主从关系

    cli> info replication
    
  2. 设置Redis服务之间的从属关系

    cli> slaveof host port			#执行命令的当前主机从属于host:port上的Redis服务
    
  3. 开启哨兵模式

    cmd> redis-sentinel <sentinel_config>	#以sentinel_config配置文件执行哨兵
    

Tips

  • 主从复制原则:先全量复制,再增量复制
  • 哨兵三大任务:监控、提醒、自动故障迁移

Jedis的使用

Jedis是在Java上使用Redis的客户端,封装了一些命令和操作。类似jdbc,同时Spring官方还提供Spring Data Redis

引入Jedis依赖

采用Maven项目进行演示,需要引入Jedis依赖

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

配置主机和端口号

创建Jedis的连接

JedisConnection.java

public class JedisConnection {
    //host
    public static final String REDIS_HOST = "127.0.0.1";
    //port
    public static final int REDIS_PORT = 6379;

    public static Jedis getJedis(){
        return new Jedis(REDIS_HOST,REDIS_PORT);
    }
}

操作key

具体命令参考Redis操作key

public class JedisKeys {
    public static void main(String[] args) {
        //获得Jedis对象
        Jedis jedis = JedisConnection.getJedis();
        
        //是否能连接上redis服务
        String ping = jedis.ping();
        //查询keys
        Set<String> keys = jedis.keys("*");
        //k1是否存在
        Boolean existsK1 = jedis.exists("k1");
        //k1的生存时间
        Long ttlK1 = jedis.ttl("k1");
        //k1的类型
        String typeK1 = jedis.type("k1");
        
        // ...
    }
}

操作string

具体命令参考Redis操作string类型

public class JedisString {
    public static void main(String[] args) {
        //获得Jedis对象
        Jedis jedis = JedisConnection.getJedis();
        
        //存储string数据
        String setResult = jedis.set("k3", "v3");
        //批量存储string数据
        String msetResult = jedis.mset("k4", "v4", "k5", "v5");
        //获得k3的value
        String getK3 = jedis.get("k3");
        //存储k10并且自加
        jedis.set("k10","5");
        jedis.incr("k10");
        //存储k11并且自加50
        jedis.set("k11","100");
        jedis.incrBy("k11", 50);
        //获得k3的字符串长度
        jedis.strlen("k3");
        
        //...
    }
}

操作list

具体命令参考Redis操作list类型

public class JedisList {
    public static void main(String[] args) {
        Jedis jedis = JedisConnection.getJedis();

        //从左边添加元素
        jedis.lpush("list1","v1","v2","v3");
        //移除左边的元素
        jedis.lpop("list1");
        //遍历list1
        List<String> list1 = jedis.lrange("list1", 0, -1);
        list1.forEach(System.out::println);
        //返回list1长度
        jedis.llen("list1");

        //从右边添加元素
        jedis.rpush("list2","v1","v2","v3");
        //从右边移除元素
        jedis.rpop("list2");
        //遍历list2
        List<String> list2 = jedis.lrange("list2", 0, -1);
        list2.forEach(System.out::println);
        //返回list2长度
        jedis.llen("list2");
        
        //...
    }
}

操作set

具体命令参考Redis操作set类型

public class JedisSet {
    public static void main(String[] args) {
        Jedis jedis = JedisConnection.getJedis();

        //添加元素
        jedis.sadd("set1","v1","v2","v3");
        //判断元素是否存在
        jedis.sismember("set1","v1");
        //(随机)弹出元素
        jedis.spop("set1",1);
        //移除v1,v2元素
        jedis.srem("set1","v1","v2");
        //遍历set1
        Set<String> smembers = jedis.smembers("set1");
        smembers.forEach(System.out::println);
        //set1长度
        jedis.scard("set1");

        //...
    }
}

操作hash

具体命令参考Redis操作hash类型

public class JedisHash {
    public static void main(String[] args) {
        Jedis jedis = JedisConnection.getJedis();

        //存入对象student1
        jedis.hset("student1","name","wqk");
        HashMap<String, String> map = new HashMap<>();
        map.put("age","18");
        map.put("score","98");
        //批量存入student1
        jedis.hmset("student1",map);
        //获取student1对象的name值
        jedis.hget("student1","name");
        //批量获取student1对象的name、age、score
        jedis.hmget("student1","name","age","score");
        //获取student1对象的所有field-value
        jedis.hgetAll("student1");

        //...
    }
}

操作zset

具体命令参考Redis操作zset类型

public class JedisZset {
    public static void main(String[] args) {
        Jedis jedis = JedisConnection.getJedis();

        //向zset1添加v1,v2,v3
        jedis.zadd("zset1",500,"v1");
        jedis.zadd("zset1",1000,"v2");
        jedis.zadd("zset1",100,"v3");

        //按照下标遍历zset1
        Set<String> zset1 = jedis.zrange("zset1", 0, -1);
        zset1.forEach(System.out::println);

        //按照分数遍历zset1
        Set<String> zset11 = jedis.zrangeByScore("zset1", 0, 1000);
        zset11.forEach(System.out::println);
        //zset1长度
        jedis.zcard("zset1");

        //...
    }
}

Redis事务

public class JedisTransaction {
    public static void main(String[] args) {
        Jedis jedis = JedisConnection.getJedis();

        //获取Redis的事务对象
        Transaction transaction = jedis.multi();
        // start
        transaction.set("k6","v6");
        transaction.set("k7","v7");
        // end
        transaction.exec(); //执行事务
    }
}

Redis客户端可视化

一个开源的Redis客户端可视化工具Redis-Desktop-Manager

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值