b 站 【狂神 Redis 视频】
# 测试:100 个并发连接 100000 请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
默认使用的是第 0 个
Redis 是单线程的(6.0 之后是多线程的)
- Redis 是很快的,官方表示 Redis 是基于内存操作,CPU 不是 Redis 性能瓶颈,Redis 的瓶颈是机器内存和网络带宽,既然可以使用单线程来实现,就使用单线程了
Redis 是 C 写的,官方提供数据为 100000+ 的 QPS,不比同样使用 key-value 的 memecache 差
Redis 为什么单线程还这么快?
- 误区 1:高性能的服务器一定是多线程的
- 误区 2:多线程(CPU上下文切换:耗时的操作)一定比单线程效率高(速度:CPU > 内存 > 硬盘)
- 核心:Redis 是将所有的数据全部放在内存中的,所以使用单线程操作效率最高
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
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 可以是数字):
- 计数器
- 统计多单位的数量
- 粉丝数
- 对象缓存存储
- 基本的数据类型,列表
- 在 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)
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)
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 更适合字符串存储
有点像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
# 添加地理位置(地球两极是无法添加的,一般下载城市数据,通过 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>
基数(不重复的元素)统计的算法
优点:占用内存是固定的,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,非 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>
<dependencies>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.2.0version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.76version>
dependency>
dependencies>
- 连接数据库
- 操作命令
- 断开连接
连接远程:
- redis.conf 修改配置 daemonize yes
- redis.conf 修改配置 注释 bind 127.0.0.1
- redis.conf 修改配置 protected-mode no
- 查看防火墙对 6379 端口有没有开放(命令:firewall-cmd --query-port=6379/tcp)
- 如果 no 开放 6379 端口:
- firewall-cmd --zone=public --add-port=6379/tcp --permanent
- firewall-cmd --reload
- firewall-cmd --query-port=6379/tcp(再次查看是否开放)
- 如果 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());
}
}
正常执行 事务
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();
}
}
}
事务执行失败(存在错误就 放弃事务)
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();
}
}
}
Spring Data 是和 Spring Boot 齐名的项目
在 Spring Boot 2.X 后,原来使用的 Jedis 被替换为了 Luttuce(2.4.X 两个都有)
- Jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免,得使用 jedis pool 连接池,更像 BIO 模式
- Lettuce:采用 netty ,实例可以在多个线程中进行共享,不存在线程不安全的情况,减少线程数量,更像 NIO 模式
@SpringBootTest
class Redis02SpringbootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("name", "jarvis");
System.out.println(redisTemplate.opsForValue().get("name"));
}
}
@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
单位
包含(类似 Spring 中 import)
网络
# 注释掉,如果要远程连接
# 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
# 如果 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
# The filename where to dump the DB
dbfilename dump.rdb