Redis

Redis(Remote Dictionary Server)安装

安装步骤

  1. 官网下载linux版本
    在这里插入图片描述

  2. 解压压缩包

tar -zxvf 安装包路径
  1. 安装gcc-c++ 并使用make 指令进行编译
yum install gcc-c++
//gcc安装完成后,使用make指令,期间会有询问,直接输入Y即可
make
  1. 更改配置文件redis.conf
    在这里插入图片描述

  2. 启动redis
    需要根据配置文件进行启动。首先进入安装目录下,会找到redis-server 的文件。

//输入指令
redis-server 配置文件路径

在这里插入图片描述
检查是否可以正常通信,如图所示,是可以正常通信的
在这里插入图片描述

性能测试工具redis-benchmark

在这里插入图片描述
在这里插入图片描述

基础知识

redis默认有16个数据库。通过配置文件可以看出。如图所示:
在这里插入图片描述
默认使用第一个数据库,可通过命令select 数据库编号 进行数据库切换。
常用指令:

指令解释
dbsize查看数据库中存储数据的容量
flushdb清空当前数据库中的所有数据
flushall清空redis中所有数据库中的所有数据
keys *查看当前数据库下的键值
set 键名 键值添加键值信息
get 键名获取键值信息
select 数据库序号切换数据库
exist查看键值是否存在(存在则返回1,不存在返回0)
move 键值 数据库序号从当前数据库移除该键值
expire 键值 秒数设置键值生存时间,时间一到则取不到该数据信息
ttl查看键值生存时间(还有多长时间失效)
type查看键值存储的数据类型
del 键名删除数据库中的键值对
…………

redis是单线程的!
redis是基于内存操作的,CPU不是redis的瓶颈,它的瓶颈在于内存的大小以及网络带宽。

五大基本数据类型

string
127.0.0.1:6379> set name hello #设置值
OK
127.0.0.1:6379> get name #获取值
"hello"
127.0.0.1:6379> strlen name #查看值的长度
(integer) 5
127.0.0.1:6379> append name "world" #追加值,如果key不存在则相当于set key
(integer) 10
127.0.0.1:6379> get name
"helloworld"
127.0.0.1:6379> type name #查看值的类型
string
127.0.0.1:6379> exists name #查看键值是否存在
(integer) 1
127.0.0.1:6379> 
#redis中的自增、自减命令,可用于用户量的统计
127.0.0.1:6379> set views 1
OK
127.0.0.1:6379> type views
string
127.0.0.1:6379> incr views
(integer) 2 
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> incrby views 5  #添加步长,一次增加指定长度
(integer) 6
127.0.0.1:6379> set key1 "hello,world"
OK
127.0.0.1:6379> get key1
"hello,world"
127.0.0.1:6379> setnx key1 "hello" #若不存在该键值就创建,存在则返回0
(integer) 0
127.0.0.1:6379> get key1
"hello,world"
127.0.0.1:6379> setex key1 20 "world" #设置一个有有效期的键值
OK
127.0.0.1:6379> ttl key1
(integer) 15
127.0.0.1:6379> ttl key1
(integer) 9
127.0.0.1:6379> ttl key1
(integer) 0
127.0.0.1:6379> ttl key1 #负数,说明该键值已经过期
(integer) -2
127.0.0.1:6379> mset k1 v1 k2 v2 #批量插入多个值
OK
127.0.0.1:6379> keys *
1) "k1"
2) "views"
3) "k2"
4) "name"
127.0.0.1:6379> mget k1 k2 #批量获取多个值
1) "v1"
2) "v2"
127.0.0.1:6379> mset k1 v3 k3 v3
OK
127.0.0.1:6379> get k1
"v3"
127.0.0.1:6379> msetnx k1 v4 k4 v4 #批量插入,若其中某一个键值存在则终止该操作,该操作具有原子性
(integer) 0
127.0.0.1:6379> set user:1:name zhouze #设置对象
OK
127.0.0.1:6379> set user:1:age 15
OK
127.0.0.1:6379> get user:1
(nil)
127.0.0.1:6379> get user
(nil)
127.0.0.1:6379> mget user:1:name
1) "zhouze"
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhouze"
2) "15"
127.0.0.1:6379> keys * #对象信息的存储跟普通的类似,键值不同
1) "views"
2) "k3"
3) "user:1:age"
4) "name"
5) "k1"
6) "user:1:name"
7) "k2"

String的使用场景:可以是字符串,也可以是数值,还可以是:

  • 计数器
  • 统计多单位的数量
  • 粉丝数
  • 对象缓存存储

list(数据结构是数组)

127.0.0.1:6379> lpush list1 1 #创建集合并添加元素值
(integer) 1
127.0.0.1:6379> lpush list2
(error) ERR wrong number of arguments for 'lpush' command
127.0.0.1:6379> lpush list2 2
(integer) 1
127.0.0.1:6379> lrange list1 0 -1 #查看集合中的元素,后面参数为位置指定
1) "1"
127.0.0.1:6379> lpush list1 2
127.0.0.1:6379> lrange list1 0 -1
1) "2"
2) "1"
127.0.0.1:6379> lpop list1 #取出集合中的位于左边的首元素,返回元素值
"2"
127.0.0.1:6379> lrange list1 0 -1
1) "1"
127.0.0.1:6379> lrem list1 1 1 #从左边移除集合中位置为1开始的连续1个元素
(integer) 1
127.0.0.1:6379> lpush list1 1
(integer) 1
127.0.0.1:6379> lpush list1 2
(integer) 2
127.0.0.1:6379> lpush list1 3
(integer) 3
127.0.0.1:6379> ltrim list1 1 1
OK
127.0.0.1:6379> llen list1 #集合长度
(integer) 1
127.0.0.1:6379> lrange list1 0 -1
1) "2"
127.0.0.1:6379> lrange list1 0 -1
1) "2"
127.0.0.1:6379> lpush list1 1
(integer) 2
127.0.0.1:6379> lpush list1 3
(integer) 3
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> rpoplpush list1 list2 #将首元素从一个集合取出移动到另一个集合中,若集合不存在则自行创建。没有lpoprpush这个指令
"2"
127.0.0.1:6379> lrange list2 0 -1
1) "2"
127.0.0.1:6379> lrange list1 0 -1
1) "3"
2) "1"
127.0.0.1:6379> lindex list1 0 #获取集合中第一个下标的值
"3"
127.0.0.1:6379> lindex list1 2
"1"
127.0.0.1:6379> lset list1 0 78 #更改集合中指定下标值
OK
127.0.0.1:6379> lrange list1 0 -1
1) "78"
2) "2"
3) "1"
127.0.0.1:6379>
127.0.0.1:6379> 
#在集合的某个具体值的前后插入值
127.0.0.1:6379> linsert list1 after 1 89
(integer) 6
127.0.0.1:6379> linsert list1 before 1 45
(integer) 7
127.0.0.1:6379> 
127.0.0.1:6379> lrange list1 0 -1
1) "78"
2) "2"
3) "23"
4) "45"
5) "1"
6) "89"
7) "23"

小结

  • list实际上是一个链表
  • 在两边插入,取出效率高,在中间效率稍微慢一些
  • 如果key不存在则创建集合,若存在则增添元素内容
  • 集合中元素没有了,该集合也就不存在了
  • 可做队列,可做栈
  • list指令的开头全部为L开头

set(不可重复的集合)

127.0.0.1:6379> sadd set1 1 #创建集合并添加元素
(integer) 1
127.0.0.1:6379> sadd set1 2
(integer) 1
127.0.0.1:6379> sadd set1 3
(integer) 1
127.0.0.1:6379> smembers set1 #查看集合元素
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> sismember set1 1 #检查元素在集合中是否存在
(integer) 1
127.0.0.1:6379> sismember set1 5
(integer) 0
127.0.0.1:6379> scard set1 #查看集合长度
(integer) 3 
127.0.0.1:6379> srem set1 1 #移除集合中指定元素
(integer) 1
127.0.0.1:6379> smembers set1
1) "2"
2) "3"
127.0.0.1:6379> srandmember set1 #随机返回集合中的一个元素
"2"
127.0.0.1:6379> srandmember set1
"3"
127.0.0.1:6379> srandmember set1
"3"
127.0.0.1:6379> srandmember set1
"2"
127.0.0.1:6379> spop set1 #随机取出集合中一个元素
"3"
127.0.0.1:6379> smembers set1
1) "2"
127.0.0.1:6379> smove set1 set2 2 #移除集合中的元素
(integer) 1
127.0.0.1:6379> smembers set1
(empty array)
127.0.0.1:6379> smembers set2
1) "2"
127.0.0.1:6379> keys * #当集合中的元素为空时,该元素也就不存在了
1) "set2"
127.0.0.1:6379> sadd set1 1
(integer) 1
127.0.0.1:6379> sadd set1 2
(integer) 1
127.0.0.1:6379> sadd set1 3
(integer) 1
127.0.0.1:6379> sadd set2 4
(integer) 1
127.0.0.1:6379> sadd set2 5
(integer) 1
127.0.0.1:6379> sadd set2 1
(integer) 1
127.0.0.1:6379> keys *
1) "set2"
2) "set1"
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> sdiff set1 set2 #取出第一个集合在第二个集合中没有的元素
1) "2"
2) "3"
127.0.0.1:6379> sdiff set2 set1
1) "4"
2) "5"
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> sunion set1 set2 #两集合取并集
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> sunion set2 set1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> sinter set1 set2 #交集
1) "1"
127.0.0.1:6379> 

hash(value中存储的是map集合)

127.0.0.1:6379> hset myhash field1 1 #创建hash并赋值
(integer) 1
127.0.0.1:6379> hset myhash field1 2
(integer) 0
127.0.0.1:6379> hset myhash field2 2
(integer) 1
127.0.0.1:6379> 
127.0.0.1:6379> keys *
1) "myhash"
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> hget myhash field1 #获取hash值
"2"
127.0.0.1:6379> hget myhash field2
"2"
127.0.0.1:6379> hmset myhash field3 3 field4 4 #同时设置多个hash值
OK 
127.0.0.1:6379> hgetall myhash
1) "field1"
2) "2"
3) "field2"
4) "2"
5) "field3"
6) "3"
7) "field4"
8) "4"
127.0.0.1:6379> hdel myhash field4 #删除指定hash键值
(integer) 1
127.0.0.1:6379> hgetall myhash #获取全部hash键值
1) "field1"
2) "2"
3) "field2"
4) "2"
5) "field3"
6) "3"
127.0.0.1:6379> hlen myhash #键值长度
(integer) 3
127.0.0.1:6379> hexists myhash field1 #检查元素是否存在
(integer) 1
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> hkeys myhash #显示hash中所有的键名
1) "field1"
2) "field2"
3) "field3"
127.0.0.1:6379> hvals myhash #显示hash中所有的值
1) "2"
2) "2"
3) "3" 
127.0.0.1:6379> hincrby myhash field1 2 #元素自增,添加步长值
(integer) 4
127.0.0.1:6379> hkeys myhash
1) "field1"
2) "field2"
3) "field3"
127.0.0.1:6379> hget myhash field1
"4"
127.0.0.1:6379> hsetnx myhash field5 5 #不存在则创建新的hash键值
(integer) 1
127.0.0.1:6379> 

ZSet(有序集合)

127.0.0.1:6379> zadd myset field one 
(error) ERR value is not a valid float
127.0.0.1:6379> zadd myset 1 one #添加带有整型标签(权重)的内容,该标签用于后面排序使用
(integer) 1
127.0.0.1:6379> 
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zadd myset 3 three
(integer) 1
127.0.0.1:6379> zrange myset 0 -1 #遍历Zset集合
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zrangebyscore myset -inf +inf #根据权重进行排序后显示,可以[-oo,+oo],没有[+oo,-oo]
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zrangebyscore myset +inf -inf
(empty array)
127.0.0.1:6379> zrangebyscore myset -inf +inf
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zrangebyscore myset -inf +inf withscores
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> zrem myset one #移除有序集合中指定的元素
(integer) 1
127.0.0.1:6379> zrange myset 0 -1
1) "two"
2) "three"
127.0.0.1:6379> zrevrange myset 0 -1 #反转排序,相当于[+oo,-oo]
1) "three"
2) "two"
127.0.0.1:6379> zcount myset 2 2 #统计在某个权重范围内元素的个数
(integer) 1
127.0.0.1:6379> zcount myset 6 7
(integer) 0
127.0.0.1:6379> 

小结

  • 存储班级成绩,工资表
  • 重要消息的权重
  • 各种排行榜

三种特殊的数据类型

geospatial 地理位置(底层数据结构为Zset)

该数据类型的命令共有6个:
在这里插入图片描述

127.0.0.1:6379> 
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing #添加地理位置
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai 108.96 34.26 xian
(integer) 1
127.0.0.1:6379> glen #没有这个指令
(error) ERR unknown command `glen`, with args beginning with: 
127.0.0.1:6379> geohash china:city shanghai #返回一个标准的地理空间geohash字符串
1) "wtw3sj5zbj0"
127.0.0.1:6379> geohash china:city xian
1) "wqj6zky6bn0"
127.0.0.1:6379> geohash china:city beijing
1) "wx4fbxxfke0"
127.0.0.1:6379> geopos china:city xian #获取指定城市的经纬度
1) 1) "108.96000176668167114"
   2) "34.25999964418929977"
127.0.0.1:6379> geodist china:city beijing xian #获取两个地理位置之间的直线距离
"910056.5237"
127.0.0.1:6379> georadius china:city 108.99 34.25 200 km #查询某一经纬度半径范围内的城市
1) "xian"
127.0.0.1:6379> georadius china:city 108.99 34.25 20000 km
1) "xian"
2) "shanghai"
3) "beijing"
127.0.0.1:6379> georadiusbymember china:city beijing 20000 km  #查询以某一地方为中心点,半径的范围内的所有城市
1) "xian"
2) "shanghai"
3) "beijing"
127.0.0.1:6379> 
hyperloglogs(基数统计)

优点:占用的内存是固定的,仅有12KB内存,相比使用set存储,使用hyperloglog更加节约内存。
缺点:官方文档表示容错率在0.81%
应用场景:各种场景下的统计问题。
常用命令有3个: pfadd(添加)、pfcount(统计)、pfmerge(合并)

127.0.0.1:6379> 
127.0.0.1:6379> pfadd myset a b c d e #添加元素
(integer) 1
127.0.0.1:6379> type myset #myset是一个字符串类型的数据
string
127.0.0.1:6379> keys *
1) "myset"
127.0.0.1:6379> get myset #存储的值都进行可转化
"HYLL\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\\{\x80Dv\x80P\xb1\x84I\x8c\x80Bm\x80BZ"
127.0.0.1:6379> pfadd myset1 q w e r b c
(integer) 1
127.0.0.1:6379> pfcount myset #统计集合中的元素个数
(integer) 5
127.0.0.1:6379> 
127.0.0.1:6379> pfcount myset1
(integer) 6
127.0.0.1:6379> pfmerge myset2 myset myset1 #集合合并,去除重复元素
OK 
127.0.0.1:6379> pfcount myset2
(integer) 8
127.0.0.1:6379> 
bitmaps(位存储,存储的数值只有0和1)

应用场景:用于统计两种情况的数量,例如:网站用户的活跃量和不活跃量、生病人数和不生病人数、登录和不登录、打卡和不打卡……

127.0.0.1:6379> setbit sign 0 1 #添加元素
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0 
127.0.0.1:6379> getbit sign 3 #获取元素
(integer) 1
127.0.0.1:6379> getbit sign 4
(integer) 0
127.0.0.1:6379> bitcount sign #统计个数
(integer) 5
127.0.0.1:6379> 

Redis基本的事务操作

redis事务的本质:一组命令的集合,一组命令都会被序列化,在事务执行过程中,会按照顺序执行。一次性、顺序性、排他性。执行一些列的命令。
redis事务没有隔离级别的概念。所有命令在事务中不会被立即执行,当发出指令 exec 才会按照入队顺序依次执行。
redis单条命令为原子性,但是事务不保证原子性

127.0.0.1:6379> 
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k1 v1 #命令入队
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec #开始执行
1) OK
2) OK
3) "v2"
127.0.0.1:6379> 
#编译器异常,队内事务全都不会执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> setget k3 v3
(error) ERR unknown command `setget`, with args beginning with: `k3`, `v3`, 
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> 
#运行期异常,队内有错误的指令停止执行,没有错误的按顺序执行
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k3
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> get k5
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) "v5"
127.0.0.1:6379> 

Redis乐观锁和悲观锁

面试常问
悲观锁: 认为执行任何事务都会加锁
乐观锁: 认为执行任何事务都不用加锁,更新数据的时候会去判断在此期间是否有人做了修改,version。在更新数据时,先获取version,然后判断version。
使用watch
事务执行成功后监听会自动取消掉。

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监听键值
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
127.0.0.1:6379> 

添加监听,当有另外一个线程过来更改数据后,该事务将执行失败。

127.0.0.1:6379> 
127.0.0.1:6379> watch money #添加监听,当做乐观锁操作
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 50
QUEUED
127.0.0.1:6379> incrby out 50
QUEUED
127.0.0.1:6379> exec #另一个线程执行完毕后,该线程将执行失败
(nil)
127.0.0.1:6379> 
#另一个线程过来
127.0.0.1:6379> 
127.0.0.1:6379> set money 10000
OK
127.0.0.1:6379> set money 15000
OK

执行失败后,需要重新执行的话,需要先释放锁 unwatch ,然后再添加锁 watch

jedis(java连接开发工具)

创建一个普通的maven项目,在pom.xml中添加jedis的依赖包

<dependencies>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.73</version>
        </dependency>
    </dependencies>

创建测试类,测试能否连接到linux下的redis

public class clientTest {
    public static void main(String[] args) {

        /**1、new jedis
         * 2、jedis中的命令就是学习过程中的全部指令,每个指令对应一个方法*/
        Jedis jedis = new Jedis("192.168.5.130",6379);
        String ping = jedis.ping();
        System.out.println(ping);
    }
}

出现报错,无法连接到redis。经过检查发现,需要开启linux的防火墙权限,允许redis的端口6379对外开放。

#从普通用户进行root
[xxx@localhost ~]$ su
Password: 
#开放对应端口权限
firewall-cmd --zone=public --add-port=6379/tcp --permanent
#开启之后,重启才可以生效
#检查端口是否已经成功对外开放
[root@localhost bin]# firewall-cmd --query-port=6379/tcp
yes

重新测试,连接成功。
在这里插入图片描述

通过jedis理解事务

public class TransactionTest {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("xxxx", 6379);

        //清空redis
        /*String s = jedis.flushAll();
        System.out.println(s);*/
        //添加内容
        /*String money = jedis.set("money", String.valueOf(100));
        String out = jedis.set("out", String.valueOf(0));*/
        //开启事务
        Transaction multi = jedis.multi();
        //添加单挑任务指令到队列中
        //成功状态下
        try {
            multi.set("money", String.valueOf(100));
            multi.set("out", String.valueOf(0));
            multi.exec();//成功执行
        }catch (Exception e){
            multi.discard();//取消执行,放弃事务
            e.printStackTrace();
        }finally{
            System.out.println(jedis.get("money")+"%%%"+jedis.get("out"));
            jedis.close();//关闭客户端
        }
    }
}
public class TransactionTest {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.5.130", 6379);
        //清空redis
        /*String s = jedis.flushAll();
        System.out.println(s);*/
        //添加内容
        /*String money = jedis.set("money", String.valueOf(100));
        String out = jedis.set("out", String.valueOf(0));*/
        //添加监听
        jedis.watch("money");
       new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(2000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               //开启事务
               Transaction multi = jedis.multi();
               try {

                   multi.decrBy("money",10);
                   multi.incrBy("out",10);
                   multi.exec();//成功执行
               }catch (Exception e){
                   System.out.println("取消事务");
                   multi.discard();//取消执行,放弃事务
                   e.printStackTrace();
               }finally{
                   System.out.println(jedis.get("money")+"%%%"+jedis.get("out"));
                   System.out.println("最后关闭客户端");
                   jedis.close();//关闭客户端
               }
           }
       },"thread1").start();

       new Thread(new Runnable() {
           @Override
           public void run() {
               jedis.set("money",String.valueOf(1000));
           }
       }).start();

    }
}

在这里插入图片描述

springboot集成redis

创建spring boot项目,在idea中创建spring boot项目的时候会有redis的选项,选中后在创建项目的时候会自动导入相应的redis依赖。

  • 创建spring boot项目
  • 添加依赖
  • 测试连接

在这里插入图片描述
spring boot中通过注解来装配对象,在项目中对象的注解包里面已经封装了默认的对象装配。
在这里插入图片描述
redis自动装配的默认实现
在这里插入图片描述
用于在properties配置文件下配置redis信息的类
在这里插入图片描述
在这里插入图片描述

自定义redis的template

redis采用的默认模板,可以根据自己的需要进行模板重写。
在这里插入图片描述

redis存储对象信息,对象需要进行序列化才可以,直接存储对象会直接报错。在需要存储的对象,所在类需要实现序列化接口Serializable
在这里插入图片描述
自定义redisTemplate

@Configuration
public class RedisConfig {
    //编写自己的模板方法,redisTemplate
    //为了开发方便一般使用RedisTemplate<String, Object>
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        //拥有多种序列化方式
        /*JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        GenericToStringSerializer<Object> objectGenericToStringSerializer = new GenericToStringSerializer<Object>();*/
        //json序列化配置
        Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        //String的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//配置具体的序列化方式
        //key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key也采用string序列化的方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value采用json序列化方式
        template.setValueSerializer(objectJackson2JsonRedisSerializer);
        //hash的value也采用json的序列化方式
        template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}
//测试类使用
 @Test
    public void testUser() throws JsonProcessingException {
        User user = new User("zhou", 8);
        /*
        //将对象转换成json字符串进行存储
        ObjectMapper objectMapper=new ObjectMapper();
        String s = objectMapper.writeValueAsString(user);
        redisTemplate.opsForValue().set("user",s);
        System.out.println(redisTemplate.opsForValue().get("user"));*/
        redisTemplate.opsForValue().set("user",user);
        System.out.println(redisTemplate.opsForValue().get("user"));

        //redisTemplate.opsForValue().set("user2",);
    }

redis的不同序列化方式

  • 使用JdkSerializationRedisSerializer序列化
    默认使用jdk的序列化方式,但是在linux客户端查询时,会有多余的转义字符保存了下来,查看起来很不方便。
  • 使用JacksonJsonRedisSerializer序列化
    将存储对象转换成json字符串进行存储,不需要让类实现序列化接口。
  • 使用StringRedisSerializer序列化
    存储的数据为key、value时,采用StringRedisSerializer即可。

redis配置文件redis.conf

  • 通过配置文件启动redis

  • 配置文件对大小写不敏感

  • 配置文件更改后直接save保存更改的内容

  • 可以包含多个配置文件
    在这里插入图片描述

  • 网络配置信息
    在这里插入图片描述
    在这里插入图片描述

  • 快照
    在这里插入图片描述

#用于设置持久化记录时间,可以自定义
save 900 1 #在900秒内,有1次key发生变化,则持久化一次
save 300 10 #在300秒内,有10次key发生变化,则持久化一次
save 60 10000 #在60秒内,有10000次key发生变化,则持久化一次
  • security,添加密码
    在这里插入图片描述
127.0.0.1:6379> config get requirepass #获取密码
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass '123456' #设置密码
OK
127.0.0.1:6379> exit
[root@localhost bin]# 
[root@localhost bin]# 
[root@localhost bin]# redis-cli
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.  
127.0.0.1:6379> auth 123456 #密码登录
OK
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> 
  • AOF持久化
    在这里插入图片描述
    默认不开启,一般使用rdb足够了
# appendfsync always
appendfsync everysec #每秒持久化一次
# appendfsync no

redis持久化

redis运行在内存中,断电则数据消失,需要将数据保存在在磁盘上。需要用到持久化操作。
持久化生成的文件为dump.rdb 文件。
在这里插入图片描述

触发机制:

  • 配置文件中的时间约束
  • 使用flushdb、flushall指令也会触发
  • 关闭redis也会触发

备份自动生成dump.rdb
将rdb文件放在redis启动文件的目录下,启动时会自动查找rdb文件并恢复其中的数据。

优点

  • 大规模的数据保存;
  • 对数据完整性要求不高
    缺点
  • 要创建一个子线程fork,占用一定的内存空间
  • 如果宕机,最后一次的数据可能无法保存

redis订阅发布

常用命令
在这里插入图片描述

#订阅端
127.0.0.1:6379> subscribe kuangshen #订阅相关频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "kuangshen"
3) (integer) 1
1) "message"
2) "kuangshen"
3) "helloworld"

#发送端
127.0.0.1:6379> 
127.0.0.1:6379> publish kuangshen 'helloworld' #发送相关频道的内容
(integer) 1
127.0.0.1:6379> 
//TODO springboot+redis实现订阅发布

redis主从配置

  • 查看redis主从信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_replid:bedb519c3c64550bcdb2574c359ce5b50ec540b0
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
127.0.0.1:6379> 
  • 主从环境搭建
  1. 复制配置文件到同一个配置文件目录下
    在这里插入图片描述
  2. 更改配置文件,端口号、日志文件路径、rdb文件名、进程版本号等信息
    端口号:
    在这里插入图片描述
    进程版本号:
    在这里插入图片描述
    日志文件路径:
    在这里插入图片描述
    rdb文件名:
    在这里插入图片描述
    三个redis搭建完成,只要配置三个不同的配置文件即可
    在这里插入图片描述
    对从机进行从节点配置:
    对从机,执行命令slaveof host port。通过info replication显示主从关系。
    主节点:
    在这里插入图片描述
    从节点:
    在这里插入图片描述
    通过指令配置是临时性的,需要通过配置文件才能配置永久性的主从关系,这样在下一次重新启动时,不需要在通过指令重新配置。

在这里插入图片描述
主从配置完成之后,主机用于读写,从机只能用于读(这跟配置文件中的配置信息有关,且符合要求。)
在这里插入图片描述

  • 哨兵模式
    没有完全理解
    查找资料进行了哨兵模式的配置。
  1. 从redis的解压包中会有sentinel.conf文件,该文件就是进行哨兵模式配置的文件。将文件复制到redis的安装目录下。需要三份conf文件,复制即可。
  2. 修改conf文件。修改内容有:
  • 端口号 port
  • 是否开启守护线程,需要开启daemonize yes
  • 配置进程序号pidfile “/var/run/redis-sentinel.pid”,修改pid文件的名字即可。
  • 日志文件logfile "logfile_sentinel.log"
  • 监测的主机sentinel monitor mymaster 127.0.0.1 6379 2,最后的2表示当有两个哨兵监测到redis宕机,则会让该主机客观下线
  1. 启动三个哨兵进程,redis-sentinel sentinel.conf
  2. 当主机意外宕机(可以进行模拟操作),再次查看redis状态时(info replicaton),发现之前的两个从机,有一个会变成主机,且另一个从机的所属主机也会发生改变。

大家可以去B站,关注“遇见狂神说”。

https://www.bilibili.com/video/BV1S54y1R7SB/?p=36
未完待续……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值