Redis学习笔记(个人笔记)
Redis 基础知识和使用
0X01#Redis基本常识
Redis 是Server-Client架构Key-Vlue型内存数据库,Redis安装成功后提供的redis-server和redis-cli则分别为其服务端和客户端。内存数据库具备内存高速读写的物理优势,读写速度同MySQL也非同一量级。同时也具备内存数据库的通病:断电容易丢失数据。但Redis也提供持久化策略应对,并非一定会丢失数据。
Redis-cli连接数据库
基本格式: redis-cli -h <host> -p <port> -a <password>
<host>:服务主机名 (IP地址,域名...)
<port>: 端口号
<password> :密码
示例:
root@DESKTOP:/usr/local/src/REDIS/redis-6.2.3/MYCONF# redis-cli -h localhost -p 6379 -a wsharkcoder
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
localhost:6379>
连接测试
PING 通常也是判断Redis机器连通性的判断依据!
localhost:6379> PING
PONG
Redis默认有16个数据库
Redis.conf 配置文件可查看到关于DB数目的信息
# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
#设置数据库的数目.默认数据库是0号,你可以在每个连接的基础上使用 SELECT <DBID> 来选择
#其他数据库,其中<DBID>是一个介于0和database-1之间的数字。
databases 16
199,1 17%
0X01-01#常规命令
Redis数据库作为No-SQL类型数据库,也意味着无法用标准的SQL语句进行数据的增删改查,但Redis数据库提供相应的命令行操作数据的存入与读取。下面先介绍些基本且常见的操作来对数据进行存储与查询。
选择数据库
基本格式: select <index>
localhost:6379> select 1 #选择1号数据库
OK
localhost:6379[1]> select 0 #选择0号数据库
OK
设置 键值对
基本格式:set <key> <value>
localhost:6379[1]> set name val #设置键值对name-val
OK
查看当前数据库键值对数目
基本格式:dbsize
localhost:6379> dbsize
(integer) 1 #返回本数据库键值对数目
查看当前数据库所有键值
基本格式: keys *
localhost:6379> keys *
1) "name"
删除 本数据库 所有键值对
基本格式: flushdb
#0号数据库设置键值对
localhost:6379> select 0
OK
localhost:6379> set name val
OK
#1号数据库设置键值对
localhost:6379> select 1
OK
localhost:6379[1]> set name val
OK
#flushdb删除0号数据库所有键值对
localhost:6379[1]> select 0
OK
localhost:6379> flushdb
OK
#查看0号数据库键值对是否清除
localhost:6379> keys *
(empty array)
#查看1号数据库键值对并未清除
localhost:6379> select 1
OK
localhost:6379[1]> keys *
1) "name"
删除 所有数据库 所有键值对
基本格式: flushall
#0号数据库设置键值对
localhost:6379> select 0
OK
localhost:6379> set name wsharkcoder
OK
#1号数据库设置键值对
localhost:6379> select 1
OK
localhost:6379[1]> set name wsharkcoder
OK
#0号数据库中删除所有数据库
localhost:6379[1]> select 0
OK
localhost:6379> flushall
OK
localhost:6379> keys *
(empty array)
#判断1号数据库中元素是否均删除
localhost:6379> select 1
OK
localhost:6379[1]> keys *
(empty array)
判断 键值对是否存在
基本格式:EXISTS <Key>
localhost:6379> set name "WSharkCoder" #设置键值对name-"WSharkCoder"
OK
localhost:6379> exists name #判断键值对是否存在
(integer) 1 #返回0表示不存在 1表示存在
移动本数据库键值对至其他数据库
基本格式:move <key> <db>
#0号数据库中设置键值对
localhost:6379> select 0
OK
localhost:6379> set name "WSharkCoder"
OK
#移动本数据中键值对至1号数据库
localhost:6379> move name 1
(integer) 1 #返回值0表示移动成功 1表示失败
localhost:6379> keys *
(empty array)
localhost:6379> select 1
OK
localhost:6379[1]> keys *
1) "name"
删除本数据库中键值对
基本格式: del <key> [<key>...]
#设置键值对
localhost:6379> set name "WSharkCoder"
OK
localhost:6379> set age 22
OK
localhost:6379> keys *
1) "name"
2) "age"
#删除键值对
localhost:6379> del name age
(integer) 2 #返回值表示删除成功的键值对数目
localhost:6379> keys *
(empty array)
设置 键值对 过期时间
基本格式:expire <key> <seconds>
localhost:6379> set name "WSharkCoder" #设置键值对name-"WSharkCoder"
OK
localhost:6379> expire name 10 #设置键值对name-"WSharkCoder" 过期时间10秒钟
(integer) 1 #0表示设置过期时间失败 1表示成功
localhost:6379> ttl name #查看键值对过期时间
(integer) 8
查看 键值对 过期时间
基本格式:ttl <key>
返回值:过期时间
localhost:6379> set name "WSharkCoder" #设置键值对name-"WSharkCoder"
OK
localhost:6379> expire name 10 #设置键值对name-"WSharkCoder" 过期时间10秒钟
(integer) 1
localhost:6379> ttl name #查看键值对过期时间
(integer) 8
查看 键值对 数据类型
基本格式:type <key
localhost:6379> set name wsharkcoder
OK
localhost:6379> type name
string
基本操作中除
set之外均是与特定的数据类型不相关的操作,为后续其他操作时作基础~
0X02#Redis数据类型及命令
想必只要在面试中被面到Redis的朋友萌一定会被问到:Redis中有几种数据类型?又分别是哪些?
五种。String,Hash,List,Set,SortedList。
虽然绝大多数人都按上述答案回答,但实际上Redis 中数据类型的数目并不止五种,常用的数据类型包括:String,List,Set,Hash,ZSet,Geospatial,Hyperloglog,BitMap 。每种数据类型的操作方式都不太相同但均类似,类比熟悉即可事半功倍。
0X02-01#String
设置 键值对
基本格式: set <key> <value>
localhost:6379> set name "WSharkCoder" #设置键值对 name-:WSharkCoder"
OK
localhost:6379> keys *
1) "name"
localhost:6379> set name "PIG" #重复设置键值对name-"PIG"
OK #重复设置键值对均会覆盖原键值对
localhost:6379> get name #获取键的值
"PIG"
获取 键对应的值
基本格式:get <Key>
localhost:6379> set name "PIG"
OK
localhost:6379> get name #获取键的值
"PIG"
键对应值后追加字符串
基本格式:append <Key> <Value>
注意:
若本数据库不存在该键值对,则在数据库设置该键值对;
当本数据库存在该键值对,才在值的尾部追加字符串。
localhost:6379> set name "Cat_"
OK
localhost:6379> append name "Tom" #在键值对name-"Cat_"值后追加字符串"Tom"
(integer) 7 #返回追加后字符串长度
localhost:6379> get name
"Cat_Tom"
localhost:6379> keys *
1) "name" #数据库中不存在age键值对
localhost:6379> append age "22" #在键值对age中值追加字符串"22"
(integer) 2
localhost:6379> get age #数据库中被设置键值对"22"
"22"
计算 键对应值 字符串长度
基本格式:strlen <key>
localhost:6379> set name "WSharkCoder" #设置键值对name-"WSharkCoder"
OK
localhost:6379> strlen name #计算 name对应的值的字符串长度
(integer) 11
截取 值 字符串子串
基本格式:getrange <key> <start> <end>
<start> :起始下标
<end>:结束下标
#截取身份证前3位判断地区模拟
localhost:6379> set ID "420116..."
OK
localhost:6379> getrange ID 0 2 #范围下标从0开始
"420"
localhost:6379> getrange ID 0 -1 #范围结束值-1则返回整字符串
"420116..."
替换 值 字符串
基本格式:setrange <key> <offerset> <value>
<offerset>:偏移量 (偏移量==0则直接从头部开始覆盖字符串)
<value>:替换值
localhost:6379> set ID "420115..."
OK
localhost:6379> setrange ID 2 116 #修改上述ID值
(integer) 9 #注意:返回替换后的字符串长度
localhost:6379> get ID
"421165..." #注意偏移量设置问题
VAL++操作
基本格式:incr <key>
#点击量统计模拟
localhost:6379> set TIMES "0" #初始点击次数 "0"次
OK
localhost:6379> incr TIMES #点击数++
(integer) 1 #注意:返回++后的数目
localhost:6379> set TIMES "S1" #重新设置点击次数 "S1"
OK
localhost:6379> incr TIMES #点击数++ 操作失败
(error) ERR value is not an integer or out of range #值超出Integer范围
VAL--操作
基本格式:decr <key>
#库存删减模拟
localhost:6379> set TOTAL "10" #初始库存 "10"
OK
localhost:6379> decr TOTAL #库存--
(integer) 9
含步长VAL++操作
基本格式:incrby <key> <increment>
#批销售-销售量模拟
localhost:6379> set SELL_NUM "10" #初始销售额 "10"
OK
localhost:6379> incrby SELL_NUM 10 #单次销售+"10"
(integer) 20
localhost:6379> get SELL_NUM
"20"
含步长VAL--操作
基本格式:decrby <Key> <Decrment>
#批销售-库存量模拟
localhost:6379> set TOTAL "500" #初始库存 "500"
OK
localhost:6379> decrby TOTAL 100 #单次出库-"100"
(integer) 400
localhost:6379> get TOTAL
"400"
设置 键值对以及过期时间
基本格式:setex <key> <seconds> <value>
setex等价于set with expire,即set命令与expire命令连续使用~
localhost:6379> setex name 100 "WSharkCoder"
OK
localhost:6379> ttl name
(integer) 93
不存在就设置键值对 存在则不设置
基本格式 :setnx <key> <value>
本命令实现功能表述起来比较拗口,而实现的功能主要是:
本数据库中不存在则设置成功,否则设置失败
setnx===set with no exists
localhost:6379> setnx name "WSharkCoder" #不存在设置键值对
(integer) 1 #返回0表示失败 1表示成功
localhost:6379> exists name
(integer) 1
localhost:6379> setnx name "TOM"
(integer) 0
设置 多组键值对
基本格式:mset <Key> <Value> [<Key> <Value>...]
localhost:6379> mset name "WSharkCoder" age "21" #设置多对键值对
OK
localhost:6379> get name
"WSharkCoder"
localhost:6379> get age
"21"
获取 多组键值对
基本格式: mget <Key> [<Key>...]
localhost:6379> mget name age
1) "WSharkCoder"
2) "21"
不存在则设置 存在则不设置 多组键值对
基本格式:msetnx <Key> <Value> [<Key> <Value>...]
本命令具有原子性,要么所有键值对全设置成功,要么所有键值对全设置失败
localhost:6379> set name "WSharkCoder" #设置键值对name-"WSharkCoder"
OK
localhost:6379> msetnx name "TOM" age "22" #设置多组键值对name-"WSharkCoder"
(integer) 0 #设置失败:name-"WSharkCoder"已存在
localhost:6379> exists age
(integer) 0
localhost:6379> flushdb #清楚数据库
OK
localhost:6379> msetnx name "TOM" age "22"
(integer) 1
localhost:6379> keys *
1) "name"
2) "age"
存储对象
JSON格式
#存储JSON格式字符串
localhost:6379> set User:01 "{name:WSharkCoder,age:21}"
OK
localhost:6379> get User:01 #获取用户对象
"{name:WSharkCoder,age:21}"
特殊命名方式
localhost:6379> mset user:01:name "WSharkCoder" user:01:age "21"
OK
localhost:6379> mget user:01:name user:01:age
1) "WSharkCoder"
2) "21"
获取设置 组合命令
基本格式: getset <key> <value>
本数据库中不存在键值对,则返回nil并设置键值对;反之,存在键值对则返回旧值,并更新新值
localhost:6379> set name "WSharkCoder"
OK
localhost:6379> getset name "TOM"
"WSharkCoder"
localhost:6379> get name
"TOM"
应用简介
Redis中String数据类型不仅能存储String类型的数据,还能够存储 Integer类型的数据并进行运算,此功能常用作网站点击量,视频播放计数器等功能的实现。setnx以及msetnx命令常用来构建分布式锁,完成对分布式资源的访问控制等。
0X02-02#List
Redis中List数据结构采用双端链表实现,因此操作可从链表首部和尾部进行,也可借助List实现队列和栈结构。
链表头部插入
基本格式:lpush <Key> <Value> [<value>...]
localhost:6379> lpush color red green #头部插入
(integer) 2
localhost:6379> lrange color 0 -1 #获取链表所有元素
1) "green"
2) "red"
链表尾部插入
基本格式:rpush <Key> <Value> [<Value>...]
localhost:6379> rpush color red green #尾插入
(integer) 2
localhost:6379> lrange color 0 -1 #获取链表所有元素
1) "red"
2) "green"
获取链表范围内元素
基本格式:lrange <Key> <Start> <Stop>
localhost:6379> lpush List A B C D
(integer) 4
localhost:6379> lrange List 0 -1 #获取链表所有元素
1) "D"
2) "C"
3) "B"
4) "A"
localhost:6379> lrange List 0 1 #获取链表部分元素
1) "D"
2) "C"
移除链表首部元素
基本格式: lpop <Key> [<count>]
<count>: POP元素的个数
localhost:6379> lpush List A B C D #链表头部插入元素
(integer) 4
localhost:6379> lpop List 3 #链表头部POP3个元素
1) "D"
2) "C"
3) "B"
移除链表尾部元素
基本格式:rpop <Key>
localhost:6379> lpush List A B C D #链表头部插入元素
(integer) 4
localhost:6379> rpop List 1 #链表尾部POP1个元素
1) "A"
获取链表下表对应元素
基本格式:lindex <key> <index>
localhost:6379> rpush List A B C D E F #链表尾部插入元素
(integer) 6
localhost:6379> lindex List 2 #获取链表下标2的元素
"C"
获取链表长度
基本格式:llen <key>
localhost:6379> rpush List A B C D E F #尾插入链表
(integer) 6
localhost:6379> llen List #计算链表长度
(integer) 6
移除指定个数的某个值
基本格式:lrem <Key> <count> <value>
localhost:6379> rpush List A A A B B C C D D #尾插入数据至链表
(integer) 9
localhost:6379> lrem List 2 A #移除2个元素A
(integer) 2
localhost:6379> lrange List 0 -1
1) "A"
2) "B"
3) "B"
4) "C"
5) "C"
6) "D"
7) "D"
截取链表
基本格式:ltrim <key> <start> <end>
localhost:6379> rpush List A B C D E #尾插入元素
(integer) 5
localhost:6379> ltrim List 0 2 #截取链表元素
OK
localhost:6379> lrange List 0 -1
1) "A"
2) "B"
3) "C"
本链表POP元素并PUSH至其他链表
基本格式:rpoplpush <Source> <Destination>
<Source>:源链表
Destination:目标链表
#注意:链表数据类型同类型才能进行该操作
localhost:6379> rpush L1 1 2 3 4
(integer) 4
localhost:6379> rpush L2 A B C D
(integer) 4
localhost:6379> rpoplpush L2 L1 #POPL2尾元素并PUSH至L1首部
"D"
localhost:6379> lrange L1 0 -1
1) "D"
2) "1"
3) "2"
4) "3"
5) "4"
localhost:6379> lrange L2 0 -1
1) "A"
2) "B"
3) "C"
替换链表下标对应值
基本格式:lset <Key> <index> <value>
localhost:6379> rpush L A B C D #链表添加元素
(integer) 4
localhost:6379> lset L 1 T #替换链表下标1的元素为T
OK
localhost:6379> lrange L 0 -1
1) "A"
2) "T"
3) "C"
4) "D"
指定元素前或后插入元素
基本格式:linsert <Key> before|after <pivot> value
localhost:6379> rpush LIST A B C #链表添加元素
(integer) 3
localhost:6379> linsert LIST BEFORE C D #链表中元素C之前插入元素D
(integer) 4
localhost:6379> lrange LIST 0 -1
1) "A"
2) "B"
3) "D"
4) "C"
应用简介
Redis中List数据类型常用作构成消息对列,以及最新消息列表等功能的实现。
0X02-03#Set
Set数据类型与数学意义上的集合一致,即不包含重复元素的集合。
添加元素至集合中
基本格式:sadd <key> <member> [<memeber>...]
localhost:6379> sadd sex man woman #向集合sex添加元素man,woman
(integer) 2
查看集合中所有元素
基本格式:smembers <key>
localhost:6379> sadd sex man woman
(integer) 2
localhost:6379> smembers sex #查看集合中所有元素
1) "woman"
2) "man"
判断集合是否包含某元素
基本格式:sismember <key> <member>
localhost:6379> sadd sex man woman #添加元素至集合中
(integer) 2
localhost:6379> sismember sex man #判断集合sex中是否包含元素man
(integer) 1
计算集合元素个数
基本格式:scard <Key>
localhost:6379> sadd Color red green blue #添加元素至集合中
(integer) 3
localhost:6379> scard Color #计算集合中元素个数
(integer) 3
移除集合元素
基本格式:srem <key> <member> [<member>...]
localhost:6379> sadd color red green yellow #添加元素至集合中
(integer) 3
localhost:6379> srem color green yellow #移除集合中部分元素
(integer) 2
localhost:6379> smembers color #查看集合所有元素
1) "red"
随机抽取集合中N个元素
基本格式:srandmember <key> [<count>]
<count>:抽取元素的个数,默认值1个
localhost:6379> sadd color red green blue black white
(integer) 4
localhost:6379> srandmember color 2 #随机抽取集合中2个元素
1) "white"
2) "green"
localhost:6379> srandmember color 2 #随机抽取集合中2个元素
1) "red"
2) "green"
随机删除集合中N个元素
基本格式:spop <key> <count>
localhost:6379> sadd color red green blue balck white
(integer) 5
localhost:6379> spop color 2 #随机删除集合中2个元素
1) "green"
2) "red"
localhost:6379> spop color 2 #随机删除集合中2个元素
1) "blue"
2) "balck"
localhost:6379> smembers color
1) "white"
移动本集合元素至其他集合中
基本格式:smove <source> <destination> <member>
localhost:6379> sadd color blue white black green red yellow #颜色集合
(integer) 6
localhost:6379> sadd NUM 1 2 3 #数字集合
(integer) 3
#将颜色集合中元素移动至数字集合中
localhost:6379> smove color NUM blue
(integer) 1
localhost:6379> smembers NUM
1) "blue"
2) "2"
3) "3"
4) "1"
集合操作:差 交 并
差集
基本格式:sdiff <Key> [<Key>...]
交集
基本格式:sinter <Key> [<Key>...]
并集
基本格式:sunion <Key> [<Key>...]
localhost:6379> sadd N1 1 2 3
(integer) 3
localhost:6379> sadd N2 3 4 5
(integer) 3
localhost:6379> sdiff N1 N2 #差集N1-N2
1) "1"
2) "2"
localhost:6379> sinter N1 N2 #交集
1) "3"
localhost:6379> sunion N1 N2 #并集
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
应用简介
Set集合中元素不允许重复出现,因此可用来统计访问网站的IP数目,同时Set集合操作部分可用来实现共同好友等功能。
0X02-04#Hash
设置Hash表以及域-值对
localhost:6379> hset Person name "WSharCoder" age "21" #Hash表不存在就设置Hash表并添加表内键值对
(integer) 2
localhost:6379> hset Person age "22" #Hash表存在就更新Hash表内键值对
(integer) 0
localhost:6379> hget Person name
"WSharCoder"
localhost:6379> hget Person age
"22"
获取Hash表域对应值
基本格式:hget <key> <field>
localhost:6379> hset Person name "WSharCoder" age "21"
(integer) 2
localhost:6379> hget Person name
"WSharCoder"
设置Hash表以及多对域-值对
基本格式:hmset <key> <value> [<key> <value>...]
localhost:6379> hmset person name WSharkCoder age 21 #设置Hash表以及多组域-值对
OK
localhost:6379> hget person name
"WSharkCoder"
命令实行结果上与
hset命令毫无区别,官方解释如下:Sets the specified fields to their respective values in the hash stored at
key. This command overwrites any specified fields already existing in the hash. Ifkeydoes not exist, a new key holding a hash is created.As per Redis 4.0.0, HMSET is considered deprecated. Please prefer HSET in new code.
翻译:
将指定字段设置为存储在键中的散列中的各自值。此命令将覆盖散列中已经存在的任何指定字段。如果key不存在,则创建一个包含散列的新键。
根据Redis 4.0.0, HMSET被认为是不赞成的。请在新代码中选择HSET。
获取Hash表多个域对应域值
基本格式:hmget <key> <field> [<field>...]
localhost:6379> hset person name "WSharkCoder" age "21"
(integer) 2
localhost:6379> hmget person name age #获取Hash表多个域对应的域值
1) "WSharkCoder"
2) "21"
获取Hash表中所有域对应域值
基本格式:hgetall <key>
localhost:6379> hset person name "WSharkCoder" age "21"
(integer) 2
localhost:6379> hgetall person #返回Hash表内域-域值
1) "name"
2) "WSharkCoder"
3) "age"
4) "21"
删除Hash表中指定域
基本格式:hdel <Key> <field> [<filed>...]
localhost:6379> hset person name "WSharkCoder" age "21"
(integer) 2
localhost:6379> hdel person name #删除Hash表中name域
(integer) 1
localhost:6379> hgetall person
1) "age"
2) "21"
计算Hash表长度
基本格式:hlen <key>
localhost:6379> hset u_info name WSharkCoder age 21
(integer) 2
localhost:6379> hlen u_info #计算Hash表中域值对数目
(integer) 2
判断Hash表中是否存在指定域
基本格式:hexists <key> <value>
localhost:6379> hset u_info name WSharkCoder age 21
(integer) 2
localhost:6379> hexists u_info name #判断Hash表u_info中是否存在域name
(integer) 1
获取所有域名
基本格式:hkeys <key>
localhost:6379> hset Person name WSharkCoder age 21
(integer) 2
localhost:6379> hkeys Person #获取Hash表Person中所有域名
1) "name"
2) "age"
获取所有域值
基本格式:hvals <key>
localhost:6379> hset Person name WSharkCoder age 21
(integer) 2
localhost:6379> hvals Person #获取Hash表Person中所有域值
1) "WSharkCoder"
2) "21"
Hash表域值含步长++操作
基本格式:hincrby <key> <field> <increment>
localhost:6379> hset Person name WSharkCoder age 21
(integer) 2
localhost:6379> hincrby Person age 1 #用户年龄+1
(integer) 22
不存在则设置域 存在则不设置
基本格式:hsetnx <key> <field> <value>
localhost:6379> hsetnx Person name WSharkCoder #用户名称信息不存在时设置成功
(integer) 1
localhost:6379> hsetnx Person name WSharkCoder #用户名称信息存在时设置不成功
(integer) 0
应用简介
Hash数据类型同String类型JSON格式存储对象信息一样,也可用来存储热点数据信息等。
0X02-04#Zset
Zset数据类型也即是Redis中Sorted Set排序集合,集合中元素不仅包含元素值,还包含用以排序的数据的权值。
添加元素至集合
基本格式:zadd <key> <score> <member> [<score> <member>...]
<score>:排序权值 (Zset集合内元素默认按升序排列)
<member>:元素值
localhost:6379> zadd Salary 1200 zhangsan 3400 lisi #有序集合中插入元素 默认元素按权值升序排列
(integer) 2
获取排序后指定位次范围元素
升序
基本格式:zrange <key> <start> <stop> [<withscores>]
<start>:起始位次
<stop>:终止位次
<withscores>:是否返回权值
localhost:6379> zadd Salary 2400 li 2300 zhang 1700 wang 1600 chen 1500 huang
(integer) 5
localhost:6379> zrange Salary 0 1 #获取工资集合中工资最低2位的姓名
1) "huang"
2) "chen"
localhost:6379> zrange Salary 0 -1#获取工资集合升序排列的用户姓名
1) "huang"
2) "chen"
3) "wang"
4) "zhang"
5) "li"
降序
基本格式:zrevrange <key> <start> <stop> [<withscores>]
<start>:起始位次
<stop>:终止位次
<withscores>:是否返回权值
localhost:6379> zadd Salary 2400 li 2300 zhang 1700 wang 1600 chen 1500 huang
(integer) 5
localhost:6379> zrevrange Salary 0 1 #获取工资集合中工资最高2位的姓名
1) "li"
2) "zhang"
localhost:6379> zrevrange Salary 0 -1 #获取工资集合中降序排列的用户姓名
1) "li"
2) "zhang"
3) "wang"
4) "chen"
5) "huang"
获取指定权值范围内的元素
升序
基本格式:zrangebyscore <key> <min> <max> [<withscores>]
<min>:权值最小值 (特殊值:负无穷+inf)
<max>:权值最大值 (特殊值:正无穷-inf)
<withscores>:是否返回权值
localhost:6379> zadd Salary 2400 li 2300 zhang 1700 wang 1600 chen 1500 huang
(integer) 5
localhost:6379> zrangebyscore Salary 1550 1770 #获取工资位于1550-1770的员工姓名
1) "chen"
2) "wang"
localhost:6379> zrangebyscore Salary -inf +inf #获取所有员工的姓名
1) "huang"
2) "chen"
3) "wang"
4) "zhang"
5) "li"
降序
基本格式:zrevrangebyscore <key> <max> <min> [<withscores>]
<max>:权值最大值 (特殊值:正无穷-inf)
<min>:权值最小值 (特殊值:负无穷+inf)
<withscores>:是否返回权值
localhost:6379> zadd salary 2400 li 2300 zhang 1700 wang 1600 chen 1500 huang
(integer) 5
localhost:6379> zrevrangebyscore Salary 2000 1000 WITHSCORES #返回工资介于2k-1k之间的用户姓名与工资
1) "wang"
2) "1700"
3) "chen"
4) "1600"
5) "huang"
6) "1500
移除有序集合中元素
基本格式:zrem <key> <member> [<member>...]
localhost:6379> zadd Salary 2400 li 2300 zhang 1700 wang 1600 chen 1500 huang
(integer) 5
localhost:6379> zrem Salary li huang #移除员工li huang
(integer) 2
localhost:6379> zrange Salary 0 -1 #查看剩余所有员工
1) "chen"
2) "wang"
3) "zhang"
计算有序集合中元素个数
基本格式:zcard <key>
localhost:6379> zadd Salary 2400 li 2300 zhang 1700 wang 1600 chen 1500 huang
(integer) 5
localhost:6379> zcard Salary #返回有序集合中的元素个数
(integer) 5
计算有序集某范围内的元素个数
基本格式:zcount <key> <min> <max>
<min>:最小值
<max>:最大值
localhost:6379> zadd Salary 2400 li 2300 zhang 1700 wang 1600 chen 1500 huang
(integer) 5
localhost:6379> zcount Salary 1000 2000 #计算工资介于1k-2k之间的人员数目
(integer) 3
应用简介
Redis中ZSet有序集合数据结构具备丰富的排序操作,因此常常用以构建排行榜等。
0X02-04#Geospatial
Geospatial数据类型用来处理地理位置信息即及维度信息,并利用经纬度信息计算相关的距离以及范围信息。
补充:
经纬度没有精度限制,可以精确到任意精度。民用北斗系统精确度已于2020年提升到1.2米,民用GPS于多年前以保持10米的精度!!
添加经纬度信息
基本格式:geoadd <key> <longitude> <latitude> <member> [<longitude> <latitude > <member>...]
<longitude>: 经度 <latitude>: 维度 <member>: 元素
注意:地球两级(北极和南极)无法直接添加
localhost:6379> geoadd China:city 121.472644 31.231706 shanghai
(integer) 1
localhost:6379> geoadd China:city 114.3475 30.49989 wuhan
(integer) 1
localhost:6379>
查看元素经纬度信息
基本格式:geopos <key> <member>[<member>...]
localhost:6379> geoadd China:city 121.472644 31.231706 shanghai
(integer) 1
localhost:6379> geoadd China:city 114.3475 30.49989 wuhan
(integer) 1
localhost:6379> geopos China:city shanghai
1) 1) "121.47264629602432251"
2) "31.23170490709807012"
计算元素之间的距离
geodist <key> <member> <member> [unit]
unit: 单位(m/km/mi/ft)[米/千米/英里/英尺]
localhost:6379> geoadd China:city 121.472644 31.231706 shanghai
(integer) 1
localhost:6379> geoadd China:city 114.3475 30.49989 wuhan
(integer) 1
#计算上海和武汉之间的距离
localhost:6379> geodist China:city shanghai wuhan km
"684.9909"
以某经纬度信息为中心查找周围元素
基本格式:georadius <key> <longitude> <latitude> <radius> m|km|ft|mi [withcoord][withdist] [count <nums>]
<radius>: 半径
<withcoord>: 返回结果包含经纬度
<withdist>: 返回结果包含距离信息
count <num>: 限定查找的元素个数
localhost:6379> geoadd China:city 121.472644 31.231706 shanghai
(integer) 1
localhost:6379> geoadd China:city 114.3475 30.49989 wuhan
(integer) 1
#查找距离 经度:121 维度: 31 100 公里的城市
localhost:6379> georadius China:city 121 31 100 km
1) "shanghai"
#限制查找数目唯一
localhost:6379> georadius China:city 121 31 100 km count 1
1) "shanghai"
以某元素经纬度为中心查找周围元素
基本格式:georadiusbymember <key> <member> <radius> m|km|ft|mi [withcoord ] [withdist] [count <nums>]
<member>:中心元素
<radius>:半径
<withcoord>: 返回结果包含经纬度
<withdist>: 返回结果包含距离信息
count <num>: 限定查找的元素个数
localhost:6379> geoadd China:city 121.472644 31.231706 shanghai
(integer) 1
localhost:6379> geoadd China:city 114.3475 30.49989 wuhan
(integer) 1
localhost:6379>georadiusbymember China:city shanghai 1000 km
1) "wuhan"
2) "shanghai"
返回元素经纬度11位字符Geohash字符串
基本格式:geohash <key> <member> [<member>...]
localhost:6379> geoadd China:city 121.472644 31.231706 shanghai
(integer) 1
localhost:6379> geoadd China:city 114.3475 30.49989 wuhan
(integer) 1
localhost:6379> geohash China:city shanghai
1) "wtw3sjt9v0"
注意
GEO类型底层基于ZSet数据类型实现,因此针对ZSet类型的操作均可用于操作GEO数据类型~
应用简介
Geospatial数据类型记录着地理经纬度信息,常用来计算用户地理位置信息,距离以及附近用户等功能。
0X02-05#Hyperloglog
Redis中Hyperloglog数据类型官方解释如下:
HyperLogLog是一种概率数据结构,用于对唯一事物进行计数(从技术上讲,这是指估计集合的基数)。通常,对唯一项目进行计数需要使用与要计数的项目数量成比例的内存量,因为您需要记住过去已经看到的元素,以避免多次对其进行计数。但是,有一组算法会以内存换取精度:您最终会得到带有标准误差的估计量度,在Redis实现的情况下,该误差小于1%。该算法的神奇之处在于,您不再需要使用与所计数项目数量成正比的内存量,而是可以使用恒定数量的内存!在最坏的情况下为12k字节,如果您的HyperLogLog(从现在开始将它们称为HLL)看到的元素很少,则少得多。
什么是集合的基数?
举例:
集合A={1,3,5,7,8,7}
集合B={1,3,5,7,8}
集合A B的基数均是5,即非重复元素的个数,并允许误差的出现。
官方文档也解释道Hyperloglog的实现误差小于1%,并在需要计数大量时仅需要恒定数量的内存。
应用举例:
统计网页的UV(Unique Visitor) (网页访问人数)
传统使用前面的Set数据结构保存所有用户ID,然后最后统计Set中元素的数量作为判断标准,但内存消耗和访问人数成正比,大量用户访问网站时占用大量的内存。
添加元素
基本格式:PFadd <key> <element> [<element>...]
localhost:6379> PFAdd UV_A u1 u2 u2 u3 #访问A网站的用户ID
(integer) 1
计算元素数目
基本格式:PFCount <key> [<key>...]
注意:可计算单个Hyperloglog元素数目也可计算多个的元素数目
localhost:6379> PFAdd UV_A u1 u2 u2 u3 #访问A网站的用户数目
(integer) 1
localhost:6379> PFCount UV_A #计算A网站访问用户数目(非重复统计)
(integer) 3
localhost:6379> PFAdd UV_B u2 u3 u4
(integer) 1
localhost:6379> PFCount UV_A UV_B #访问A网站和B网站的用户数目(非重复统计)
(integer) 4
合并某Hyperloglog统计结果至另一Hyperloglog中
基本格式:PFmerge <destkey> <sourcekey> [<sourcekey>...]
localhost:6379> PFadd T1 1 2 2 3 4 #T1统计结果集
(integer) 1
localhost:6379> PFadd T2 3 4 5 #T1统计结果集
(integer) 1
localhost:6379> PFmerge T1 T2 #将T2结果集内容添加至T1结果集中
OK
localhost:6379> PFCount T1
(integer) 5
注意
Hyperloglog数据类型并不存储元素数据,因此只能统计元素个数而不能统计到底包含了哪些元素~
0X02-06#Bitmap
C/C++用户对此类数据结构再熟悉不过,位图主要使用每1比特位记录信息,例如LeetCode中记录用户是打卡功能就可用位图实现:1 已打卡 0 位打卡。 位图当中只有两种状态0/1,通常操作二进制来记录相应的信息!!
设置位图记录
基本格式:set <key> <offset> <value>
<offset>:偏移量
<value>:值
#周一到周日的打卡记录
localhost:6379>setbit sign 0 1
(integer)0
localhost:6379>setbit sign 1 0
(integer)0
localhost:6379>setbit sign 2 1
(integer)0
localhost:6379>setbit sign 3 1
(integer)0
localhost:6379>setbit sign 4 1
(integer)0
localhost:6379>setbit sign 5 1
(integer)0
localhost:6379>setbit sign 6 1
(integer)0
获取位图记录
基本格式:getbit <key> <offset>
<offset>: 偏移量
# 记录周一到周日的打卡记录
localhost:6379>setbit sign 2 1
(integer)0
# 查看周三是否打卡
localhost:6479>getbit sign 2
(integer)1
统计位图信息
基本格式:bitcount <key> [<start> <end>]
# 记录周一到周日的打卡记录
localhost:6379>setbit sign 0 1
(integer)0
localhost:6379>setbit sign 1 1
(integer)0
localhost:6379>setbit sign 2 1
(integer)0
localhost:6379>setbit sign 3 1
(integer)0
localhost:6379>setbit sign 4 1
(integer)0
localhost:6379> bitcount sign #统计一周打卡多少次
(integer) 5
0X03#Redis 事务
Redis单条命令是保证原子性的,但事务并不保证原子性!!
原子性:操作不可被中断,要么全部执行成功要么全部执行失败~
Redis事务的本质更像一组命令的集合,使用multi开始事务后,依次输入命令入队需要依次执行的命令,最后输入exec执行该命令集合!
Redis事务使用的基本方式:
multi #开启事务
#命令入队
exec #执行事务
事务正常执行
通常情况下,事务中的命令都顺序且成功地执行:
localhost:6379> multi #开启事务
OK
localhost:6379(TX)> lpush L1 1 2 3 #命令入队
QUEUED
localhost:6379(TX)> set k1 v1
QUEUED
localhost:6379(TX)> sadd Color Red Green
QUEUED
localhost:6379(TX)> exec #执行命令集合
1) (integer) 3
2) OK
3) (integer) 2
放弃事务
何时放弃事务?又为何放弃事务?
想必看到这里的兄弟萌一定会想着两个问题。在多线程中同时操作同一个事务(事务开启后往事务中添加命令的状态)时,如果操作命令参数缺失,此时则需要放弃事务避免数据库数据的错误!此外如果存在除零操作也同样需要放弃事务。
编译型异常:
编译型异常通常在命令添加至队列时将报相应的错误信息!
localhost:6379> multi #开启事务
OK
localhost:6379(TX)> lpush L1 1 2 3 #线程T1添加命令至队列
QUEUED
localhost:6379(TX)> set k1 v1 #线程T1添加命令至队列
QUEUED
localhost:6379(TX)> getset k1 #线程T2添加具有语法错误的命令至队列
(error) ERR wrong number of arguments for 'getset' command
localhost:6379(TX)> discard #果断放弃事务避免数据错误
运行时异常:
运行时异常通常在运行时才会返回并显示报错信息,但值得注意的是该命令实行失败并不会影响其他命令的实行。即Redis命令事务并不保证原子性!
localhost:6379> set v1 "v1" #v1-“v1”
OK
localhost:6379> multi #开启事务
OK
localhost:6379(TX)> incr v1 #增加值
QUEUED
localhost:6379(TX)> set k1 v1 #设置键值对
QUEUED
localhost:6379(TX)> exec #执行事务
1) (error) ERR value is not an integer or out of range #执行报错
2) OK
疑惑与解答
上述记录了在Redis-cli 中常用的数据结构以及如何使用Redis事务,但相信用过MySQL的用户一定也都用过Navicat这类可视化软件,又何必使用命令行呢?
概述中已经简略介绍过Redis属于NO-SQL数据库,这也意味着没法使用SQL语句操作Redis中的数据,用户需要了解如何使用这些命令。此外更加重要的是Reids给各个编程语言提供的客户端都基本和上述命令行相似,因此学会使用Redis-cli命令行操作数据可以快速上手编程语言相关的API包!
Redis客户端(Redis-cli,Jedis和lettuce等)与Redis 服务端基本结构如下:
0X04#Redis 发布订阅
对象:发布者--频道-- 订阅者
订阅指定的一个或多个频道的信息:SUBSCRIBE channel [channel ...]
将信息发送到指定的频道:PUBLISH channel message
顶退指定的一个或多个频道信息:UNSUBSCRIBE [channel [channel ...]]
订阅一个或多个匹配的频道:PSUBSCRIBE pattern [pattern ...]
退订所有给定模式的频道:PUNSUBSCRIBE [pattern [pattern ...]]
常规应用eg:
订阅者:
localhost:6379> subscribe wsharkcoder #订阅一个频道wsharkcder
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "wsharkcoder"
3) (integer) 1
#等待读取推送的消息
1) "message" #消息
2) "wsharkcoder" #消息对应的频道
3) "hello,world" #消息的内容
1) "message"
2) "wsharkcoder"
3) "hello,redis"
发布者:
localhost:6379> publish wsharkcoder "hello,world" #发布者发布消息到频道!
(integer) 2
localhost:6379> publish wsharkcoder "hello,redis" #发布则发布消息到频道!
(integer) 2
特殊应用eg:Redis中Key到期提醒
#监听者
localhost:6397> config set notify-keyspace-events Ex
OK
#订阅频道
localhost:6397> psubscribe __keyevent@2__:expired
#__keyevent@<DB_ORDER>__:expired
#DB_ORDER:数据库序号
#订阅开启之后这个库的所有key过期时间都会被推送过来;
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyevent@2__:expired"
3) (integer) 1、
4) "name"
1) "pmessage"
2) "__keyevent@0__:expired"
3) "__keyevent@0__:expired"
4) "name"
#发布者
localhost:6397[2]> set name "wsharkcoder" ex 5
OK
0X05#Redis-Java客户端
前面学习Redis数据结构与操作时使用的时Redis内置客户端Redis-cli,但编程语言没办法使用Redis-cli命令行,那编程语言如何操作Redis?
Redis 官网已经解答此问题:
此外官方也提示道,被星号标记的客户端是被推荐到客户端,被笑脸标记的客户端至少已经存在6个月。此外用户也可DIY自己的客户端!!
Java部分被官方推荐客户端有Jedis,lettuce 和Redisson三种客户端,下面就简略记录一下Jedis和lettuce的使用与特点。
0X05-1#Jedis
借用Redis官网中对Jedis的描述:A blazingly small and sane Redis Java client
Jedis并没有相应的官方网站,但该项目开源并在Github平台上维护着,那么直接到Github上该项目README中查看线索即可。
Jedis开源项目地址: https://github.com/redis/jedis
如何使用?
导入依赖
想要使用Jedis必须加载Jar包或添加其Maven依赖,jar包可以在其Github仓库自行寻找,Maven项目直接添加依赖即可:
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
使用示例
public static void main(String[] args) {
//Jedis 建立本地Redis连接
Jedis jedis = new Jedis("localhost", 6379);
//验证身份
jedis.auth("wsharkcoder");
//测试连接是否成功;连接失败直接返回
final String PONG = "PONG";
//测试连接
if (jedis.ping().equals(PONG)) {
System.out.println("Redis Connection Success.");
} else {
return;
}
//flushdb命令-->删除当前数据库内所有键值对
jedis.flushDB();
//select <index>命令-->选择第<index>号数据库
jedis.select(0);
//set <key> <value>命令
jedis.set("name", "WSharkCoder");
//get <key> 命令
String val = jedis.get("name");
System.out.println(val);
//jedis关闭连接
jedis.close();
}
//运行结果
//Redis Connection Success.
//WSharkCoder
Jedis客户端包的使用方式和Redis-cli的命令行基本一一对应 ,均借助Jedis实例的对应方法即可,其使用方法就不作更多罗列~!
事务使用实例
关于Jedis事务部分的操作与数据的处理略微有所不同,依旧补充相应的实例详细说明如何使用其事务部分:
public static void main(String[] args) {
//Jedis 建立本地Redis连接
Jedis jedis = new Jedis("129.211.66.191", 6379);
//验证身份
jedis.auth("wsharkcoder");
jedis.flushDB();
JSONObject jsonObject = new JSONObject();
jsonObject.put("name","wsharkcoder");
jsonObject.put("age", 20);
String result = jsonObject.toJSONString();
jedis.watch("name");
//开启事务
Transaction multi = jedis.multi();
try {
multi.set("user1", result);
multi.set("user2", result);
//代码抛出事务异常,执行失败!
int i = 1 / 0;
//执行事务
multi.exec();
} catch (Exception e) {
//放弃事务
multi.discard();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
//关闭连接
jedis.close();
}
}
本示例redis-client-jedis 仅仅只包含单纯的如何使用Jedis,更加重要的其他内容并没有讨论,例如连接池,Watch以及Lua等,日后进行补充与深入 。关于了解这些高级部分的博客在源码库README部分链接中也有列举,急需使用这部分的兄弟可以去查阅查阅 !
0X05-2#Lettuce
Lettuce官网是这样描述Lettuce的:Lettuce is a scalable client for building non-blocking Reactive applications.
为什么已经了解过Jedis 还要学习Lettuce?
本人第一次了解到Lettuce是在SpringBoot中,在SpringBoot 2.x之后SpringBoot默认的的客户端就由原来的Jedis变为Lettuce 。这也就意味着SpringBoot官方团队认为Lettuce比Jedis更加优秀!
关于网上关于二者的讨论很多,本人感觉最重要的两个优点是:非阻塞IO 线程安全
和上述Jedis一样本模块内仅仅讨论如何使用,关于Lettuce更高阶的部分日后再详细讨论!
如何使用?
导入依赖
普通项目下载Jar包并添加到项目中即可,Maven项目则直接在POM文件中添加一下依赖即可:
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.1.2.RELEASE</version>
</dependency>
使用示例
public static void main(String[] args) {
//1.创建RedisClient实例
//参数解释:
//redis://WSharkCoder@localhost:6379/1
//密码:WSharkCoder 主机IP:localhost 端口号:6379 数据库编码:1号
RedisClient redisClient = RedisClient.create("redis://WSharkCoder@localhost:6379/1");
//2.打开Redis独立链接
StatefulRedisConnection<String,String> connection=redisClient.connect();
//3.同步执行
RedisCommands<String, String> commands = connection.sync();
//4.发送命令与执行命令
String val=commands.ping();
System.out.println(val);
commands.set("K1", "V1");
val = commands.get("K1");
System.out.println(val);
//5.关闭链接
connection.close();
//6.关闭客户端实例以释放线程和资源
redisClient.shutdown();
}
Lettuce使用相对于Jedis而言复杂很多,官方文档也对其中很多参数做了相当多的定义与规定,而且API也分为同步,异步,响应式三种。此处示例中仅包含最简单的同步方式,与上述Jedis的示例基本一致。
0X05-3#SpringData集成Redis
首先先总结一下上述两部分中咱都聊了个啥?其实本质上只解决了两个问题,Redis常用数据类型和如何操作这些数据类型,其中如何操作数据类型包括命令行方式和编程语言调度两部分。 Java框架中是有集成Redis使用,那么下面记录一下SpringBoot中如何快速高效地使用Redis。
集成Redis?
SpringBoot参考文档中关于NOSQL数据库使用中介绍道:Spring Data provides additional projects that help you access a variety of NoSQL technologie即SpringBoot直接借助Spring Data项目中关于Redis等NOSQL的实现部分。如果再查找到Spring Data Redis 的文档,就会发现其内部的实现其实是依赖于Jedis 和Lettuce的。简而言之,Spring Data Redis对Jedis和Lettuce封装集成和统一API,而SpringBoot自动配置Spring Data Redis 达到开箱即用的目的!
如何使用?
SpringBoot项目基本由Maven构建,直接添加依赖即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
g
SpringData 集成Redis
SpringBoot操作数据:spring-data jpa jdbc mongdb redis!
SpringData 也是SpringBoot齐名项目!
说明:在SpringBoot2.x之后,原来使用的Jedis被替换成Lettuce?
Jedis:采用直连,多个线程操作的话,是不安全的,如果避免不安全,使用Jedis pool 连接池!BIO模式
lettuce: 采用netty,s实例可以在多个线程中共享,不存在线程不安全的情况! 可以减少线程数据!NIO模式
-
源码分析:
@Bean @ConditionalOnMissingBean(name = "redisTemplate")//可自定义redisTemplate来替换这个默认的! public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { //默认的RedisTemplate没有过多的设置,redis对象都是需要序列化! //两个泛型都是Object,Object的类型,我们后使用需要强制转换<String,Object> RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean//由于String是redis中最常见的类型,所以说单独提出一个Bean来! public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; }
-
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> -
配置Redis
#配置连接Redis—Server 主机IP spring.redis.host=localhost #配置连接Redis—Server 端口号 spring.redis.port=6379 -
测试
@SpringBootTest public class RedisTest { @Autowired private RedisTemplate redisTemplate; @Test public void Test_01(){ //redisTemplate 操作不同的数据类型,ops和命令是类似的 //opsForValue() 操作字符串 类似String //opsForList() //opsForSet() //opsForHash() //opsForZSet() //opsForGeo() //opsForHyperLogLog() redisTemplate.opsForValue().set("name", "Wsharkcoder"); // 除了上方的操作外,常用的方法都可以直接通过RedisTemplate操作,比如事务,和基本CRUD // RedisConnection redisConnection= redisTemplate.getConnectionFactory().getConnection(); // redisConnection.flushAll(); // redisConnection.flushDb(); String name = (String) redisTemplate.opsForValue().get("name"); System.out.println(name); } }
==自定义RedisTemplate==
说明:
- Redis 存储对象时需要将对象经行序列化!默认采用JDK序列化方式!
为什么需要自定义RedisTemplate?
解释:SpringData-Redis 默认采用的是将JDK序列化方式,而JDK序列化方式引发Redis数据库编码异常问题,即数据在Redis中以乱码的呈现,其次自定义以JSON格式经行序列化更符合企业级要求!!
-
自定义配置Redis
@Configuration //自定义RedisTemplate public class RedisConfig { @Bean(name="diyRedisTemplate") @SuppressWarnings("all") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { //方便开发,一般直接使用<String,Object> RedisTemplate<String , Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); //JSON序列化配置 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); //String序列化 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); //所有Key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); //Hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); //所有Value采用JackSon序列化方式 template.setValueSerializer(jackson2JsonRedisSerializer); //Hash Value序列化方式采用Jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } } -
测试自定义RedisTemplate
@SpringBootTest public class RedisTest { @Autowired @Qualifier(value = "redisTemplate") private RedisTemplate redisTemplate; @Autowired private RedisTemplate diyRedisTemplate; @Test public void Test_02(){ User user = new User("方", 20); // 报错: // org.springframework.data.redis.serializer.SerializationException: // Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: // Failed to serialize object using DefaultSerializer; // nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable // payload but received an object of type [cn.wsharkcoder.redis_springboot_demo.entity.User] redisTemplate.opsForValue().set("my_info", user); // 报错原因: User类无法进行序列化 // 解决方法: // public class User // 改为: // public class User implements Serializable System.out.println(redisTemplate.opsForValue().get("my_info")); } @Test public void Test_03() { User user = new User("方", 20); diyRedisTemplate.opsForValue().set("my_info", user); System.out.println(diyRedisTemplate.opsForValue().get("my_info")); } }
0X06#Redis配置
Redis 启动的时候,都是通过配置文件来启动!eg:
./redis-server /path/to/redis.conf
配置文件 redis.conf
## Generated by install_server.sh ##
# Redis configuration file example.
#
# Note that in order to read the configuration file, Redis must be
# started with the file path as first argument:
#
# ./redis-server /path/to/redis.conf
# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
配置文件unit单位对大小写不敏感!
可以引入其他配置文件
################################## INCLUDES ###################################
# Include one or more other config files here. This is useful if you
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# include /path/to/local.conf
# include /path/to/other.conf
网络配置相关
bind 127.0.0.1 #绑定IP(此处配置表示只能在本地进行连接!)
protected-mode no #保护模式
port 6379 #端口设置
常规配置项
daemonize yes #以守护进程的方式运行,默认是no,我们需要自己开启
pidfile /var/run/redis_6379.pid #如果以后台方式运行,需要自定一个pid文件!
#日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
logfile /var/log/redis_6379.log #日志的文件位置名
databases 16 #数据库数目,默认是16个数据库
always-show-logo yes #是否总是显示LOGO
RDB持久化相关配置
持久化,在规定时间内, 执行多少次操做,则会持久到文件.rdb.aof
redis 是内存数据库,如果没有持久化,那么数据断电即失!
#如果900秒内,如果至少有一个key进行修改,将及时进行持久化
save 900 1
#如果300秒内,如果至少10个key进行修改,将及时进行持久化
save 300 10
#如果60秒内,如果进行10000个key进行修改,将及时进行持久化
save 60 10000
stop-writes-on-bgsave-error yes #持久化如果出错,是否还需要继续工作
rdbcompression yes #是否压缩rdb文件
rdbchecksum yes #保存rdb文件的时候,进行错误校验
dir /var/lib/redis/6379 #rdb文件保存的目录!
AOF持久化配置
appendonly no #默认不开启AOF模式,默认是使用RDB方式持久化,大部分所有情况下,RDB模式完全够用!
appendfilename "appendonly.aof" #持久化的名字
# appendfsync always #每次修改都会sync,消耗性能
appendfsync everysec #每秒执行一次sync, 可能会丢失1s的数据!
# appendfsync no #不执行sync,这个时候操作系统自己同步数据,速度最快!
安全相关配置
可以搜索关键词
SECCURITY
################################## SECURITY ###################################
# Require clients to issue AUTH <PASSWORD> before processing any other
# commands. This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
requirepass 123456
配置文件中以上部分对应基本的
Redis密码配置信息! (默认情况下,Redis并没有密码的!)
#Redis配置安全信息
localhost:6379> ping
PONG
localhost:6379> config get requirepass #获取Redis的密码
1) "requirepass"
2) "wsharkcoder"
localhost:6379> config set requirepass "12345" #设置Redis密码
OK
localhost:6379> config get requirepass #获取Redis 密码
1) "requirepass"
2) "12345"
localhost:6379> config get requirepass
(error) NOAUTH Authentication required. #命令没有权限!
localhost:6379> auth "123456" #使用密码进行登陆
OK
限制客户端数量配置
maxclients 10000 #设置能连接上Redis的最大 客户端数量
maxmemory <bytes> #Redis配置最大的内存容量
maxmemory-policy noeviction # 内存达到上限之后的处理策略
1. volatile-lru:从已设置过期时间的内存数据集中挑选最近最少使用的数据 淘汰;
2. volatile-ttl: 从已设置过期时间的内存数据集中挑选即将过期的数据 淘汰;
3. volatile-random:从已设置过期时间的内存数据集中任意挑选数据 淘汰;
4. allkeys-lru:从内存数据集中挑选最近最少使用的数据 淘汰;
5. allkeys-random:从数据集中任意挑选数据 淘汰;
6. no-enviction(驱逐):禁止驱逐数据。(默认淘汰策略。当redis内存数据达到maxmemory,在该策略下,直接返回OOM错误);
0X07#Redis 主从复制
0X07-1#实现主从复制
主库用来经行增删信息操作,从库主要用来进行读取操作!通过单台主机和多台从机形成
Redis集群,减缓服务器压力!·
-
环境配置(构建一主二从 Redis集群)
只需要配置从 库,不配置主库!
查看单Redis主从复制信息:
localhost:6379> info replication #查看当前库信息(主从复制信息;) # Replication role:master #角色(主库) connected_slaves:0 #从机个数 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 localhost:6379>复制三份
Redis.conf文件 ,修改基本配置信息 :- 端口
- pid 名称
- log名称
- DUMP文件名称
主机修改配置
redis-6379-port.conflogfile "6379.log" #日志文件名称 dbfilename dump-6379.rdb #RDB持久文件名从机—01修改配置
redis-6380-port.confport 6380 #配置从机-01端口 pidfile /var/run/redis_6380.pid #配置PID logfile "6380.log" #日志文件名称 dbfilename dump-6380.rdb #RDB持久文件名 masterauth wsharkcoder #主机认证密码从机-02修改配置
redis-6381-port.confport 6381 #配置从机-01端口 pidfile /var/run/redis_6381.pid #配置PID logfile "6381.log" #日志文件名称 dbfilename dump-6381.rdb #RDB持久文件名 masterauth wsharkcoder #主机认证密码通过配置文件分别开启三个Redis服务
[root@VM_0_8_centos src]# redis-server ../redis-79-port.conf #开启组 [root@VM_0_8_centos src]# redis-server ../redis-80-port.conf [root@VM_0_8_centos src]# redis-server ../redis-81-port.conf [root@VM_0_8_centos src]# ps -ef | grep redis-server #查看Redis进程信息 root 1933 1 0 20:01 ? 00:00:00 redis-server 127.0.0.1:6380 root 1982 1 0 20:02 ? 00:00:00 redis-server 127.0.0.1:6381 root 2900 18572 0 20:07 pts/0 00:00:00 grep --color=auto redis-server root 29958 1 0 19:33 ? 00:00:01 redis-server 127.0.0.1:6379 -
命令行配置从机关系
默认情况下,每台Redis服务器都是主节点; 一般情况下,只需要配置从机就好了!
slaveof <IP> <Port>将本机配置为
<IP><Port>主机的从机!#[6380从机] [root@VM_0_8_centos redis-3.2.8]# redis-cli -h localhost -p 6380 -a wsharkcoder localhost:6380> slaveof localhost 6379 #将 6380-port Redis从机配置为 6379-port Redis主机的从机 OK localhost:6380> info replication #查看当前库信息 # Replication role:slave #角色(从机) master_host:localhost #主机IP(localhost) master_port:6379 #主机端口(6379) master_link_status:down master_last_io_seconds_ago:-1 master_sync_in_progress:0 slave_repl_offset:1 master_link_down_since_seconds:1602073077 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 #[6381从机] [root@VM_0_8_centos redis-3.2.8]# redis-cli -h localhost -p 6381 -a wsharkcoder localhost:6381> slaveof localhost 6379 OK localhost:6381> info replication # Replication role:slave master_host:localhost master_port:6379 master_link_status:down master_last_io_seconds_ago:-1 master_sync_in_progress:0 slave_repl_offset:1 master_link_down_since_seconds:1602073848 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 #[6379 主机] [root@VM_0_8_centos redis-3.2.8]# redis-cli -h localhost -p 6381 -a wsharkcoder localhost:6379> info replication # Replication role:master connected_slaves:2 #从机数目 slave0:ip=127.0.0.1,port=6381,state=online,offset=155,lag=1 #从机-01 基本信息 slave1:ip=127.0.0.1,port=6380,state=online,offset=155,lag=1 #从机-02 基本信息 master_repl_offset:155 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:154 -
修改配置文件配置主从关系
slaveof <masterip> <masterport>配置主机IP&端口
slaveof localhost 6379
0X07-2#主从复制细节
-
主从(Redis库)读写测试
主机可以写, 从机不能写职能读!主机中所有信息和数据,都会自动被从机保存!==
# [6379 主机 可写&可读] [root@VM_0_8_centos redis-3.2.8]# redis-cli -h localhost -p 6379 -a wsharkcoder localhost:6379> set k1 v1 OK localhost:6379> get k1 "v1" # [6380 从机 不可写&可读] [root@VM_0_8_centos src]# redis-cli -h localhost -p 6380 -a wsharkcoder localhost:6380> set k2 v2 (error) READONLY You can't write against a read only slave. localhost:6380> get k1 "v1" -
模拟主(Redis库)宕机
测试:主机断开连接 ,从机依旧连接到主机,但是没有写操作,这时候,如果主机重新连接回来,从机依旧可以直接获取到主机写的信息!
#模拟6379主机宕机 [root@VM_0_8_centos redis-3.2.8]# redis-cli -h localhost -p 6379 -a wsharkcoder localhost:6379> shutdown #主机宕机 not connected> exit #6380从机获取键值对 [root@VM_0_8_centos src]# redis-cli -h localhost -p 6380 -a wsharkcoder localhost:6380> get k2 (nil) #模拟6379主机恢复连接 [root@VM_0_8_centos src]# redis-server ../redis-79-port.conf [root@VM_0_8_centos src]# redis-cli -h localhost -p 6379 -a wsharkcoder localhost:6379> set k2 v2 OK #6380从机获取键值对====>(主机重新连接回来,从机依旧可以直接获取到主机写的信息) [root@VM_0_8_centos src]# redis-cli -h localhost -p 6380 -a wsharkcoder localhost:6380> get k2 "v2"
0X07-3#复制原理
-
Slave 启动成功连接到Master后会发送 Sync 同步命令.
-
Master 收到Sync 命令后,启动后台的存盘进程,同时收集所有接受到的用于修改数据集命令,在后台进程执行完毕之后,Master将传送整个数据文件到Slave,并完成一次完全同步.
全复制: Slave服务在接收到数据库文件后,将其存盘并加载到内存中.
增量复制: Master继续将新的所有收集到的修改命令依次传递个Slave ,完成同步.
只要重新连接Master, 一次完全同步(全量复制)将被执行。
参考资料
【狂神说Java】Redis最新超详细版教程通俗易懂-----B站狂神-Redis篇章
Redis Architecture----Client/Server结构图参考