Redis 学习笔记

Redis 入门

b 站 【狂神 Redis 视频】

安装 Redis

通过指定的配置文件 redis.conf 启动 redis 服务
在这里插入图片描述
在这里插入图片描述

使用 redis-cli 连接 6379 端口
在这里插入图片描述
在这里插入图片描述

测试 get、set、keys * 命令
Redis 学习笔记_第1张图片

关闭 redis
在这里插入图片描述
在这里插入图片描述

性能测试

redis-benchmark(官方自带的性能测试工具)
Redis 学习笔记_第2张图片

# 测试:100 个并发连接 100000 请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

Redis 学习笔记_第3张图片

Redis 基础知识

Redis 默认有 16 个数据库
在这里插入图片描述

默认使用的是第 0 个

  • select 进行切换数据库
  • DBSIZE 查看数据库大小
    在这里插入图片描述
  • flushdb 清空当前数据库
  • flushall 清空全部数据库
    Redis 学习笔记_第4张图片

Redis 是单线程的(6.0 之后是多线程的)

  • Redis 是很快的,官方表示 Redis 是基于内存操作,CPU 不是 Redis 性能瓶颈,Redis 的瓶颈是机器内存和网络带宽,既然可以使用单线程来实现,就使用单线程了

Redis 是 C 写的,官方提供数据为 100000+ 的 QPS,不比同样使用 key-value 的 memecache 差

Redis 为什么单线程还这么快?

  • 误区 1:高性能的服务器一定是多线程的
  • 误区 2:多线程(CPU上下文切换:耗时的操作)一定比单线程效率高(速度:CPU > 内存 > 硬盘)
  • 核心:Redis 是将所有的数据全部放在内存中的,所以使用单线程操作效率最高

五大数据类型

Redis 学习笔记_第5张图片

Redis-Key
127.0.0.1:6379> set name Jarvis
OK
127.0.0.1:6379> set age 3
OK
127.0.0.1:6379> exists name # 判断当前 key 是否存在
(integer) 1
127.0.0.1:6379> move name 1 # 将当前 key 从当前数据库移到 1 数据库
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "name"
127.0.0.1:6379[1]> move name 0
(integer) 1
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> expire name 20 # 设置当前 key 20 秒后过期
(integer) 1
127.0.0.1:6379> ttl name # 查看倒计时还有几秒
(integer) 16
127.0.0.1:6379> ttl name
(integer) 8
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> type name # 查看当前 key 的类型
string
127.0.0.1:6379> type age
string

String(字符串)
127.0.0.1:6379> set key1 v1
OK
127.0.0.1:6379> get key1
"v1"
127.0.0.1:6379> append key1 "hello" # 追加字符串,如果该键不存在,相当于 set key
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> strlen key1 # 获取字符串的长度
(integer) 7
127.0.0.1:6379> append key1 ",Jarvis"
(integer) 14
127.0.0.1:6379> get key1
"v1hello,Jarvis"
127.0.0.1:6379> 

127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views # 相当于 views++ 自增1
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> type views
string
127.0.0.1:6379> decr views # 相当于 views-- 自减1
(integer) 1
127.0.0.1:6379> incrby views 10 # 相当于 views += 10
(integer) 11
127.0.0.1:6379> decrby views 10 # 相当于 views -= 10
(integer) 1
127.0.0.1:6379> 

127.0.0.1:6379> set key1 "hello,Jarvis"
OK
127.0.0.1:6379> get key1
"hello,Jarvis"
127.0.0.1:6379> getrange key1 0 3 # 截取字符串 [0, 3]
"hell"
127.0.0.1:6379> getrange key1 0 -1 # 获取全部字符串
"hello,Jarvis"
127.0.0.1:6379> 
127.0.0.1:6379> set key2 abcdefg
OK
127.0.0.1:6379> get key2
"abcdefg"
127.0.0.1:6379> setrange key2 1 xx # 替换指定位置开始的字符串
(integer) 7
127.0.0.1:6379> get key2
"axxdefg"

# setex(set with expire) 设置到期时间
# setnx(set if not exists) 如果不存在...(在分布式锁中会常用)
127.0.0.1:6379> setex key3 30 hello
OK
127.0.0.1:6379> ttl key3
(integer) 25
127.0.0.1:6379> setnx key4 redis
(integer) 1
127.0.0.1:6379> keys *
1) "key2"
2) "key1"
3) "key4"
127.0.0.1:6379> setnx key4 mongoDB
(integer) 0
127.0.0.1:6379> get key4
"redis"
127.0.0.1:6379> 

127.0.0.1:6379> mset key1 v1 key2 v2 key3 v3 # 同时设置多个值
OK
127.0.0.1:6379> keys *
1) "key3"
2) "key2"
3) "key1"
127.0.0.1:6379> mget key1 key2 key3 # 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx key1 v1 key4 v4 # 原子性操作,要么都成功,要么都失败
(integer) 0
127.0.0.1:6379> 
# 对象
127.0.0.1:6379> set user:1 {name:zhangsan,age:3} # 设置一个 user:1 对象,值为 json字符来保存对象
OK
127.0.0.1:6379> get user:1
"{name:zhangsan,age:3}"
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 12
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "12"
127.0.0.1:6379> 
127.0.0.1:6379> getset db redis
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongoDB
"redis"
127.0.0.1:6379> get db
"mongoDB"
127.0.0.1:6379> 

String 类型使用场景(value 可以是数字):

  • 计数器
  • 统计多单位的数量
  • 粉丝数
  • 对象缓存存储
List
  • 基本的数据类型,列表
  • 在 Redis 中,可以把 List 玩成 栈、队列、阻塞队列
# l 和 r 是 left 和 right 的意思
# 双端队列
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> rpush list four
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379> 
127.0.0.1:6379> keys *
1) "list"
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379> lpop list 1 # 从左开始移除一个元素
1) "three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "four"
127.0.0.1:6379> rpop list 2 # 从右开始移除两个元素
1) "four"
2) "one"
127.0.0.1:6379> lrange list 0 -1
1) "two"
127.0.0.1:6379> 
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "two"
127.0.0.1:6379> lindex list 0 # 根据下标获取 list 中的值
"one"
127.0.0.1:6379> llen list # 获取 list 长度
(integer) 2
127.0.0.1:6379> 

# count > 0: 从头往尾移除值为 value 的元素;
# count < 0: 从尾往头移除值为 value 的元素;
# count = 0: 移除所有值为 value 的元素
127.0.0.1:6379> lrange list 0 -1
1) "four"
2) "three"
3) "four"
4) "three"
5) "one"
6) "two"
127.0.0.1:6379> lrem list -1 four # 移除 1 个 four
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "four"
2) "three"
3) "three"
4) "one"
5) "two"
127.0.0.1:6379> 
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
4) "hello4"
127.0.0.1:6379> ltrim list 1 2 # 只留下标 1 - 2 中的值
OK
127.0.0.1:6379> lrange list 0 -1
1) "hello2"
2) "hello3"
127.0.0.1:6379> 

127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
4) "hello4"
127.0.0.1:6379> rpoplpush list anotherList
"hello4"
127.0.0.1:6379> lrange anotherList 0 -1
1) "hello4"
127.0.0.1:6379> 

127.0.0.1:6379> lset list 0 newHello1 # 把下标为 0 的值设置为 newHello1
OK
127.0.0.1:6379> lrange list 0 -1
1) "newHello1"
2) "hello2"
3) "hello3"
127.0.0.1:6379> 

127.0.0.1:6379> linsert list before hello2 beforeHello2 # 在 hello2 前插入 beforeHello2
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "newHello1"
2) "beforeHello2"
3) "hello2"
4) "hello3"
127.0.0.1:6379> 

可以用来实现消息队列(MQ)

Set
127.0.0.1:6379> sadd set hello # 添加元素
(integer) 1
127.0.0.1:6379> sadd set Jarvis
(integer) 1
127.0.0.1:6379> sadd set Hututu
(integer) 1
127.0.0.1:6379> smembers set # 查看指定 set 的所有元素
1) "hello"
2) "Hututu"
3) "Jarvis"
127.0.0.1:6379> sismember set hello # 判断某个元素是否在 set 中
(integer) 1
127.0.0.1:6379> sismember set zz
(integer) 0
127.0.0.1:6379> scard set # 获取 set 的大小
(integer) 3
127.0.0.1:6379> sadd set hello # set 中元素不能重复,这里添加失败
(integer) 0
127.0.0.1:6379> srem set hello # 移除 set 中某个元素
(integer) 1
127.0.0.1:6379> smembers set
1) "Hututu"
2) "Jarvis"
127.0.0.1:6379> sadd set hello
(integer) 1
127.0.0.1:6379> SMEMBERS set
1) "hello"
2) "Hututu"
3) "Jarvis"
127.0.0.1:6379> SRANDMEMBER set 1 # 随机挑选一个幸运元素
1) "hello"
127.0.0.1:6379> spop set 1 # 随机移除一个倒霉元素
1) "Hututu"
127.0.0.1:6379> SMEMBERS set
1) "hello"
2) "Jarvis"
127.0.0.1:6379> SMOVE set anotherset hello # 移动元素
(integer) 1
127.0.0.1:6379> SMEMBERS set
1) "Jarvis"
127.0.0.1:6379> SMEMBERS anotherset
1) "hello"
127.0.0.1:6379> 
127.0.0.1:6379> SMEMBERS set1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> SMEMBERS set2
1) "c"
2) "d"
3) "b"
127.0.0.1:6379> SDIFF set1 set2 # 差集
1) "a"
127.0.0.1:6379> SINTER set1 set2 # 交集
1) "c"
2) "b"
127.0.0.1:6379> SUNION set1 set2 # 并集
1) "c"
2) "b"
3) "d"
4) "a"
127.0.0.1:6379> 

应用场景:将所有关注的人放在一个 set 中,将其所有粉丝放入一个 set 中,共同关注(SINTER)

Hash

key-map

127.0.0.1:6379> hset hash1 field1 jarvis # set
(integer) 1
127.0.0.1:6379> hget hash1 field1
"jarvis"
127.0.0.1:6379> hmset hash1 field1 hello field2 world # set 多个
OK
127.0.0.1:6379> hmget hash1 field1 field2 # 获取多个
1) "hello"
2) "world"
127.0.0.1:6379> hgetall hash1 # 获取全部数据
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> hdel hash1 field1 # 删除指定 key-value
(integer) 1
127.0.0.1:6379> hgetall hash1
1) "field2"
2) "world"
127.0.0.1:6379> hlen hash1 # 获取 hash1 键值对数量
(integer) 1
127.0.0.1:6379> HEXISTS hash1 field2 # 判断 hash1 中 field2 是否存在
(integer) 1
127.0.0.1:6379> HEXISTS hash1 field1
(integer) 0
127.0.0.1:6379> HKEYS hash1 # 获取所有 key
1) "field2"
127.0.0.1:6379> HVALS hash1 # 获取所有 value
1) "world"
127.0.0.1:6379> hset hash1 field3 1
(integer) 1
127.0.0.1:6379> HGETALL hash1
1) "field2"
2) "world"
3) "field1"
4) "value1"
5) "field3"
6) "1"
127.0.0.1:6379> HINCRBY hash1 field3 1 # 增
(integer) 2
127.0.0.1:6379> HINCRBY hash1 field3 -3
(integer) -1
127.0.0.1:6379> HSETNX hash1 field1 new # 如果存在 set
(integer) 0
127.0.0.1:6379> HSETNX hash1 field4 new
(integer) 1
127.0.0.1:6379> HGETALL hash1
1) "field2"
2) "world"
3) "field1"
4) "value1"
5) "field3"
6) "-1"
7) "field4"
8) "new"
127.0.0.1:6379> 

应用场景:Hash 更适合对象的存储,String 更适合字符串存储

Zset

有点像Treeset,有序集合

127.0.0.1:6379> zadd zset 1 one # 添加一个值
(integer) 1
127.0.0.1:6379> zadd zset 2 two 3 three # 添加多个值
(integer) 2
127.0.0.1:6379> ZRANGE zset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> ZADD salary 2500 x 5000 y 500 z
(integer) 3
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 排序(从小到大)
1) "z"
2) "x"
3) "y"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "z"
2) "500"
3) "x"
4) "2500"
5) "y"
6) "5000"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores # -∞ ~ 2500
1) "z"
2) "500"
3) "x"
4) "2500"
127.0.0.1:6379> ZREVRANGEBYSCORE salary +inf -inf withscores # 排序(从大到小)
1) "y"
2) "5000"
3) "x"
4) "2500"
5) "z"
6) "500"
127.0.0.1:6379> zrem salary x # 删除
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "z"
2) "y"
127.0.0.1:6379> ZCARD salary # 获取 有序集合 大小
(integer) 2
127.0.0.1:6379> ZREVRANGE salary 0 -1 # 逆序获取
1) "y"
2) "z"
127.0.0.1:6379> ZRANGE zset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> ZCOUNT zset 1 3 # 获取区间之间成员数量
(integer) 3

三种特殊数据类型

geospatial(地理位置)
# 添加地理位置(地球两极是无法添加的,一般下载城市数据,通过 Java 程序一次性导入)
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 106.50 29.53 chongqing 114.05 22.52 shenzhen
(integer) 2
127.0.0.1:6379> GEOADD china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
127.0.0.1:6379> GEOPOS china:city beijing chongqing # 获取 经度 和 维度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "106.49999767541885376"
   2) "29.52999957900659211"
127.0.0.1:6379> GEODIST china:city beijing chongqing km # 查看直线距离(最后一个参数是 单位) 
"1464.0708"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km # 查看指定范围内的城市
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 1
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km # 以北京为中心查询方圆1000km内的城市
1) "beijing"
2) "xian"
127.0.0.1:6379> GEOHASH china:city beijing chongqing # 将二维的经纬度转换为一维的字符串
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
127.0.0.1:6379> 

底层实现原理:zset

127.0.0.1:6379> ZRANGE china:city 0 -1 # 查看地图中全部元素
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> ZREM china:city beijing # 移除指定元素
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
127.0.0.1:6379> 

hyperloglog

基数(不重复的元素)统计的算法

优点:占用内存是固定的,2^64 不同元素的基数,只需 12 KB 的内存

127.0.0.1:6379> PFADD key a b c d e f g h i j # 创建 key
(integer) 1
127.0.0.1:6379> PFCOUNT key # 统计 key 中基数数量
(integer) 10
127.0.0.1:6379> PFADD key2 i j z x c v b n m
(integer) 1
127.0.0.1:6379> PFCOUNT key2
(integer) 9
127.0.0.1:6379> PFMERGE key3 key key2 # 合并 key key2 放在 key3 中
OK
127.0.0.1:6379> PFCOUNT key3
(integer) 15
127.0.0.1:6379> 

如果允许容错,可以使用 hyperloglog
如果不允许容错,就使用 set

bitmaps

位存储

两个状态的可以使用 bitmaps,非 0 即 1

# 记录周一到周日的打卡 (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 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
127.0.0.1:6379> GETBIT sign 4 # 查看某天是否打卡
(integer) 1
127.0.0.1:6379> GETBIT sign 6
(integer) 0
127.0.0.1:6379> BITCOUNT sign # 计数打卡天数
(integer) 3
127.0.0.1:6379> 

事务

Redis 事务本质:一组命令的集合
一个事务中所有命令都会被序列化,在事务执行过程中,命令会按照顺序执行
一次性、顺序性、排他性 执行一系列命令

---------- 队列
set
set
set
----------

Redis 事务没有隔离级别的概念,所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会去执行

Redis 单条命令是保证原子性的,但 Redis 事务不保证原子性

Redis 事务:

  • 开启事务(multi)
  • 命令入队
  • 执行事务(exec)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379> 
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> DISCARD # 放弃事务
OK
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> 

编译型异常(代码有问题,命令有错误):事务中所有命令不会被执行

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> getset k3 # 错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k4 # 所有命令都不会被执行
(nil)

运行时异常(1 / 0),如果事务队列中存在语法错误,执行命令的时候其他命令可以正常执行

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> INCR k1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> EXEC
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
127.0.0.1:6379> 

监控

悲观锁

  • 认为什么时候都会出问题,无论做什么都加锁

乐观锁

  • 认为什么时候都不会出问题,所以不会上锁,更新数据的时候判断在此期间是否有人修改过这个数据

Redis 监视测试

# 正常执行成功
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 # 监视 money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> EXEC
1) (integer) 80
2) (integer) 20
127.0.0.1:6379> 

# 客户端 1
# watch 可以当作 乐观锁
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
127.0.0.1:6379(TX)> EXEC
(nil)
# 客户端 2
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000 # 事务执行前改动数据
OK
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(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
127.0.0.1:6379(TX)> EXEC
1) (integer) 990
2) (integer) 30
127.0.0.1:6379> 

Jredis

  1. 导入依赖
    <dependencies>
        <dependency>
            <groupId>redis.clientsgroupId>
            <artifactId>jedisartifactId>
            <version>3.2.0version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.76version>
        dependency>
    dependencies>
  1. 编码测试
  • 连接数据库
  • 操作命令
  • 断开连接

连接远程:

  1. redis.conf 修改配置 daemonize yes
  2. redis.conf 修改配置 注释 bind 127.0.0.1
  3. redis.conf 修改配置 protected-mode no
  4. 查看防火墙对 6379 端口有没有开放(命令:firewall-cmd --query-port=6379/tcp)
  5. 如果 no 开放 6379 端口:
    1. firewall-cmd --zone=public --add-port=6379/tcp --permanent
    2. firewall-cmd --reload
    3. firewall-cmd --query-port=6379/tcp(再次查看是否开放)
  6. 如果 yes , java 测试,PONG 就说明成功了
public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("120.79.178.225", 6379);
        System.out.println(jedis.ping());
    }
}

Redis 学习笔记_第6张图片
事务

正常执行 事务

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("120.79.178.225", 6379);
        System.out.println(jedis.ping());

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("name", "jarvis");
        String s = jsonObject.toJSONString();

        // 开启事务
        Transaction multi = jedis.multi();

        try {
            multi.set("user1", s);
            multi.set("user2", s);
            multi.exec();// 执行事务
        } catch (Exception e) {
            multi.discard();// 放弃事务
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }
    }
}

Redis 学习笔记_第7张图片

事务执行失败(存在错误就 放弃事务)

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("120.79.178.225", 6379);
        System.out.println(jedis.ping());

        jedis.flushDB();

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("name", "jarvis");
        String s = jsonObject.toJSONString();

        // 开启事务
        Transaction multi = jedis.multi();

        try {
            multi.set("user1", s);
            multi.set("user2", s);
            int i = 1 / 0;
            multi.exec();// 执行事务
        } catch (Exception e) {
            multi.discard();// 放弃事务
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }
    }
}

Redis 学习笔记_第8张图片

Spring Boot 整合

Spring Data 是和 Spring Boot 齐名的项目

在 Spring Boot 2.X 后,原来使用的 Jedis 被替换为了 Luttuce(2.4.X 两个都有)

  • Jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免,得使用 jedis pool 连接池,更像 BIO 模式
  • Lettuce:采用 netty ,实例可以在多个线程中进行共享,不存在线程不安全的情况,减少线程数量,更像 NIO 模式

Redis 学习笔记_第9张图片
Redis 学习笔记_第10张图片
测试

@SpringBootTest
class Redis02SpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("name", "jarvis");
        System.out.println(redisTemplate.opsForValue().get("name"));
    }

}

Redis 学习笔记_第11张图片
对象(需要被序列化)

@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private String name;
    private int age;
}
@SpringBootTest
class Redis02SpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() throws JsonProcessingException {
        // 真实开发一般使用 JSON 传递对象
        User user = new User("Jarvis", 3);
//        String jsonUser = new ObjectMapper().writeValueAsString(user);
//        redisTemplate.opsForValue().set("user", jsonUser);
        redisTemplate.opsForValue().set("user", user);
        System.out.println(redisTemplate.opsForValue().get("user"));
    }
}

自定义 RedisTemplate

@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();

        template.setConnectionFactory(connectionFactory);
        //自定义Jackson序列化配置
        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, DefaultTyping.NON_FINAL);
        jsonRedisSerializer.setObjectMapper(objectMapper);

        //key使用String的序列化方式
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        //hash的key也是用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value的key使用jackson的序列化方式
        template.setValueSerializer(jsonRedisSerializer);
        //hash的value也是用jackson的序列化方式
        template.setHashValueSerializer(jsonRedisSerializer);
        template.afterPropertiesSet();

        return template;

    }
}

使用自定义 RedisTemplate 后:

127.0.0.1:6379> keys *
1) "user"
127.0.0.1:6379> get user
"[\"com.jarvis.pojo.User\",{\"name\":\"Jarvis\",\"age\":3}]"
127.0.0.1:6379> 

Redis 工具类

链接:https://www.cnblogs.com/zhzhlong/p/11434284.html

Redis.conf 详解

单位

Redis 学习笔记_第12张图片

包含(类似 Spring 中 import)

Redis 学习笔记_第13张图片

网络

# 注释掉,如果要远程连接
# bind 127.0.0.1
# yes 改为 no,如果要远程连接
protected-mode no
# 设置端口
port 6379

通用

# 以守护线程的方式运行 yes(默认 no)
daemonize yes

# 如果以守护线程的方式运行,就需要指定一个 pid 文件
pidfile /www/server/redis/redis.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 "/www/server/redis/redis.log"

# 数据库数量(默认 16)
databases 16

# 是否总是显示 Redis 的 logo
always-show-logo yes

快照 SNAPSHOTTING

  • 在规定时间内执行了多少次操作,就会持久化到文件 .rdb .aof
  • Redis 是内存数据库,没有持久化,数据就断电即失
# 如果 900s 内,如果至少有 1 个 key进行了修改,就进行持久化操作
save 900 1
# 如果 300s 内,如果至少有 10 个 key进行了修改,就进行持久化操作
save 300 10
# 如果 60s 内,如果至少有 10000 个 key进行了修改,就进行持久化操作
save 60 10000

# 持久化如果出错,是否要继续工作
stop-writes-on-bgsave-error yes
# 是否压缩 rdb 文件,需要消耗一定的 CPU
rdbcompression yes
# 保存 rdb 文件时校验
rdbchecksum yes
# rdb 文件保存的目录
dir /www/server/redis/

复制 REPLICATION(后面 主从复制 讲)

安全 SECURITY

# 设置密码 默认没密码
config set requirepass 

CLIENTS

# 限制最大客户端数 10000
# maxclients 10000

MEMORY MANAGEMENT

# 设置最大的内存容量
# maxmemory 
# 内存到达上限以后的处理策略
# maxmemory-policy noeviction

APPEND ONLY MODE (aof 配置)

# 默认不开启 aof 模式(默认使用 rdb 持久化,大部分情况下,rdb 够用了)
appendonly no
# 持久化文件的名字
appendfilename "appendonly.aof"

# appendfsync always
appendfsync everysec # 每秒执行一次 sync ,可能会丢失这 1s 的数据
# appendfsync no

Redis 持久化

RDB(Redis DataBase)
# The filename where to dump the DB
dbfilename dump.rdb

在这里插入图片描述

AOF(Append Only File)

未完…

你可能感兴趣的:(Redis,redis)