Redis(Remote Dictionary Server)安装
安装步骤
-
官网下载linux版本
-
解压压缩包
tar -zxvf 安装包路径
- 安装gcc-c++ 并使用make 指令进行编译
yum install gcc-c++
//gcc安装完成后,使用make指令,期间会有询问,直接输入Y即可
make
-
更改配置文件redis.conf
-
启动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>
- 主从环境搭建
- 复制配置文件到同一个配置文件目录下
- 更改配置文件,端口号、日志文件路径、rdb文件名、进程版本号等信息
端口号:
进程版本号:
日志文件路径:
rdb文件名:
三个redis搭建完成,只要配置三个不同的配置文件即可
对从机进行从节点配置:
对从机,执行命令slaveof host port。通过info replication显示主从关系。
主节点:
从节点:
通过指令配置是临时性的,需要通过配置文件才能配置永久性的主从关系,这样在下一次重新启动时,不需要在通过指令重新配置。
主从配置完成之后,主机用于读写,从机只能用于读(这跟配置文件中的配置信息有关,且符合要求。)
- 哨兵模式
没有完全理解
查找资料进行了哨兵模式的配置。
- 从redis的解压包中会有sentinel.conf文件,该文件就是进行哨兵模式配置的文件。将文件复制到redis的安装目录下。需要三份conf文件,复制即可。
- 修改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宕机,则会让该主机客观下线。
- 启动三个哨兵进程,redis-sentinel sentinel.conf
- 当主机意外宕机(可以进行模拟操作),再次查看redis状态时(info replicaton),发现之前的两个从机,有一个会变成主机,且另一个从机的所属主机也会发生改变。
大家可以去B站,关注“遇见狂神说”。
https://www.bilibili.com/video/BV1S54y1R7SB/?p=36
未完待续……