谈谈Redis的那点事儿
Redis
传送:https://aobing.blog.csdn.net/article/details/102841400
传送中文网:http://www.redis.cn/
传送官网:https://redis.io/
为什么要用Redis
因为传统的关系型数据库如Mysql已经不能适用所有的场景了,比如秒杀的库存扣减,APP首页的访问流量高峰等等,都很容易把数据库打崩,所以引入了缓存中间件,目前市面上比较常用的缓存中间件有Redis 、 Memcached 、mongoDB 不过中和考虑了他们的优缺点,最后选择了Redis。
解决
- 高并发读写
- 海量数据的高效率存储和访问
- 高可扩展性和高可用性
NoSql数据库的四大分类
- 键值
- 列存储
- 文档数据库
- 图形数据库
Redis应用场景
- 缓存
- 任务队列
- 应用排行榜
- 网站访问统计
- 数据过期处理
- 分布式集群架构中session分离
Redis安装
$ wget http://download.redis.io/releases/redis-3.2.9.tar.gz(下载)
$ tar zxvf redis-3.2.9.tar.gz
$ cd redis-3.2.9
$ make
Redis基础知识
在Redis数据库中,默认是有16个数据库;
可以通过 select 命令进行切换数据库
下面进行常用命令总结:
-
select
切换数据库
-
DBSIZE
查看当前数据库大小
-
flushdb
清空数据库
-
FLUSHALL
清空所有数据库
-
keys *
查看当前数据库所有的key
-
EXPIRE name 10
设置name的有效时间为10秒
-
ttl name
查询name的有效时间
-
existed name
判断name是否存在
-
move name 1
移动name到1号数据库
-
type name
查看name类型
Redis是单线程,Redis是基于内存操作,CPU并不是Redis的性能瓶颈,Redis的瓶颈是内存和网络带宽,既然可以使用单线程来实现,就使用单线程!所以就使用单线程了。
Redis为什么单线程还这么快?
Redis是C语言写的,官方提供的数据是10万+的QPS,这个并不比Memecache差!
1、误区:高性能的服务器都是多线程的
2、误区:多线程(CPU存在上下文切换)一定比单线程效率高
核心:redis将所有的数据放在内存中,所以采用单线程去操作效率就是最高的,对于内存系统来说,没有上下文切换效率就是最高的,多次读写都是在一个CPU上。
Redis中的数据类型
字符串String、字典Hash、列表List、集合Set、有序集合SortedSet
首先看到官网的介绍说明:
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
String
String的常用方法
append name hello
向name中追加 hello
strlen name
获取name的长度
incr\decr
加一\减一操作
incrby\decrby 指定步长
127.0.0.1:6379[2]> set num 0 OK 127.0.0.1:6379[2]> INCR num (integer) 1 127.0.0.1:6379[2]> INCR num (integer) 2 127.0.0.1:6379[2]> INCR num (integer) 3 127.0.0.1:6379[2]> INCR num (integer) 4 127.0.0.1:6379[2]> get num "4" 127.0.0.1:6379[2]> DECR num (integer) 3 127.0.0.1:6379[2]> INCRBY num 10 (integer) 13 127.0.0.1:6379[2]> INCRBY num 10 (integer) 23 127.0.0.1:6379[2]> DECRBY num 2 (integer) 21 127.0.0.1:6379[2]> DECRBY num 2 (integer) 19 127.0.0.1:6379[2]> ########################################### 127.0.0.1:6379[2]> GETRANGE key1 0 4 //获取指定区间的字符串 相当于substr "hello" 127.0.0.1:6379[2]> GETRANGE key1 0 -1 //获取整字符串 "hello,world" 127.0.0.1:6379[2]> get key1 "hello,world" 127.0.0.1:6379[2]> 127.0.0.1:6379[2]> set key2 abcdefg OK 127.0.0.1:6379[2]> SETRANGE key2 1 xx //相当于replace的用法,替换指定位置的字符串 (integer) 7 127.0.0.1:6379[2]> get key2 "axxdefg" 127.0.0.1:6379[2]> ########################################## # setex 设置过期时间 # setnx 不存在则设置 (在分布式锁中经常使用) 127.0.0.1:6379[2]> SETEX key4 60 hello 设置key4 的有效时间为 一分钟,值为hello OK 127.0.0.1:6379[2]> ttl key4 (integer) 29 127.0.0.1:6379[2]> ttl key4 (integer) 27 127.0.0.1:6379[2]> keys * 1) "key2" 2) "name" 3) "num" 4) "key1" 5) "key3" 127.0.0.1:6379[2]> setnx t "mybits" 如果t 不存在则创建t 值为mybits (integer) 1 127.0.0.1:6379[2]> setnx t "xiugai" 如果t 存在,则创建失败 (integer) 0 ######################################## # mset 批量设值 # mget 批量获取 # msetnx 同理,其中这些为原子性操作,要么成功都成功,要么失败都失败 127.0.0.1:6379[2]> mset k1 v1 k2 v2 k3 v3 OK 127.0.0.1:6379[2]> keys * 1) "k2" 2) "k3" 3) "k1" 127.0.0.1:6379[2]> mget k1 k2 k3 1) "v1" 2) "v2" 3) "v3" 127.0.0.1:6379[2]> msetnx k1 v1 k4 v4 (integer) 0 127.0.0.1:6379[2]> keys * 1) "k2" 2) "k3" 3) "k1" 127.0.0.1:6379[2]> ########################################## # getset 先get 再set 127.0.0.1:6379[2]> keys * (empty list or set) 127.0.0.1:6379[2]> getset key1 value1 如果不存在则返回nil (nil) 127.0.0.1:6379[2]> getset key1 value1 存在则返回原值并设值新值 "value1" 127.0.0.1:6379[2]> getset key1 value2 "value1" 127.0.0.1:6379[2]> get key1 "value2" 127.0.0.1:6379[2]>
List
基本数据类型,列表
在redis里面,我们可以把list玩成栈、队列、阻塞队列
# lpush 左侧插入
# rpush 右侧插入
# lrange 获取区间内的值
# lpop 左侧弹出
# rpop 右侧弹出
# lindex 获取指定下标下的值
127.0.0.1:6379[2]> lpush list one
(integer) 1
127.0.0.1:6379[2]> lpush list two
(integer) 2
127.0.0.1:6379[2]> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379[2]> RPUSH list right
(integer) 3
127.0.0.1:6379[2]> lrange list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379[2]> lpush list three
(integer) 4
127.0.0.1:6379[2]> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379[2]> lpop list
"three"
127.0.0.1:6379[2]> lrange list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379[2]> rpop list
"right"
127.0.0.1:6379[2]> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379[2]> lindex list 1
"one"
######################################
# llen 获取list 长度
127.0.0.1:6379[2]> llen list
(integer) 2
######################################
# lrem 移出list中的元素
127.0.0.1:6379[2]> lrange list 0 -1
1) "one"
2) "two"
3) "one"
127.0.0.1:6379[2]> lrem list 1 one 从List移出一个 one元素
(integer) 1
127.0.0.1:6379[2]> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379[2]> lpush list one
(integer) 3
127.0.0.1:6379[2]> lpush list one
(integer) 4
127.0.0.1:6379[2]> lpush list one
(integer) 5
127.0.0.1:6379[2]> lrange list 0 -1
1) "one"
2) "one"
3) "one"
4) "two"
5) "one"
127.0.0.1:6379[2]> lrem list 3 one
(integer) 3
127.0.0.1:6379[2]> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379[2]>
########################################
# ltrim 截取list 中间项
127.0.0.1:6379[2]> lpush mylist hello1
(integer) 1
127.0.0.1:6379[2]> lpush mylist hello2
(integer) 2
127.0.0.1:6379[2]> lpush mylist hello3
(integer) 3
127.0.0.1:6379[2]> lpush mylist hello4
(integer) 4
127.0.0.1:6379[2]> LRANGE mylist 0 -1
1) "hello4"
2) "hello3"
3) "hello2"
4) "hello1"
127.0.0.1:6379[2]> LTRIM mylist 1 2 截取下标1-2的项,mylist被修改
OK
127.0.0.1:6379[2]> LRANGE mylist 0 -1
1) "hello3"
2) "hello2"
127.0.0.1:6379[2]>
#######################################
# rpoplpush 组合命令,从第一个表中弹出最后一个,添加到新表中
127.0.0.1:6379[2]> lpush mylist hello1
(integer) 1
127.0.0.1:6379[2]> lpush mylist hello2
(integer) 2
127.0.0.1:6379[2]> lpush mylist hello3
(integer) 3
127.0.0.1:6379[2]> lpush mylist hello4
(integer) 4
127.0.0.1:6379[2]> rpoplpush mylist mylist2
"hello1"
127.0.0.1:6379[2]> keys *
1) "mylist2"
2) "mylist"
127.0.0.1:6379[2]> lrange mylist2 0 -1
1) "hello1"
127.0.0.1:6379[2]>
#####################################
# lset 更新指定下标的值
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "hello4"
2) "hello3"
3) "hello2"
127.0.0.1:6379[2]> lset mylist 0 hh 更新mylist[0] 为hh
OK
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "hh"
2) "hello3"
3) "hello2"
127.0.0.1:6379[2]>
####################################
# linseret 在指定项前或后插入项
127.0.0.1:6379[2]> lpush list hello
(integer) 1
127.0.0.1:6379[2]> lpush list world
(integer) 2
127.0.0.1:6379[2]> linsert list before hello test 在hello项后面插入test
(integer) 3
127.0.0.1:6379[2]> lrange list 0 -1
1) "world"
2) "test"
3) "hello"
127.0.0.1:6379[2]> linsert list after hello testafter 在hello项前面插入 testafter
(integer) 4
127.0.0.1:6379[2]> lrange list 0 -1
1) "world"
2) "test"
3) "hello"
4) "testafter"
127.0.0.1:6379[2]>
List总结:
数据结构为双向链表
在两边插入或者改动值效率最高,中间元素效率低
Set
Set的值不可重复
# sadd 添加
# smember 查询全部
# sismember 具体查询
# scard 查询值个数
# srem 移除指定元素
# SRANDMEMBER 随机抽选一个元素
# spop 随机删除
# smove 移动,同list的移动
127.0.0.1:6379[2]> sadd myset set1 向myset中添加 set1
(integer) 1
127.0.0.1:6379[2]> sadd myset set2 向myset中添加 set2
(integer) 1
127.0.0.1:6379[2]> SMEMBERS
(error) ERR wrong number of arguments for 'smembers' command
##################
127.0.0.1:6379[2]> SMEMBERS myset
1) "set1"
2) "set2"
127.0.0.1:6379[2]> SISMEMBER myset set1
(integer) 1
127.0.0.1:6379[2]> SISMEMBER myset set3
(integer) 0
#################
127.0.0.1:6379[2]> scard myset
(integer) 2
127.0.0.1:6379[2]> srem myset set1
(integer) 1
127.0.0.1:6379[2]> SMEMBERS myset
1) "set2"
######################
127.0.0.1:6379[2]> SMEMBERS myset
1) "set4"
2) "set5"
3) "set3"
4) "set2"
127.0.0.1:6379[2]> SRANDMEMBER myset
"set4"
127.0.0.1:6379[2]> SRANDMEMBER myset
"set2"
127.0.0.1:6379[2]> SRANDMEMBER myset
"set4"
127.0.0.1:6379[2]> SRANDMEMBER myset 随机抽一个
"set4"
127.0.0.1:6379[2]> SRANDMEMBER myset 2 随机抽两个
1) "set2"
2) "set3"
127.0.0.1:6379[2]>
##################################################
# SDIFF 差集
# SINTER 交集
# SUNION 并集
127.0.0.1:6379[2]> sadd key1 a
(integer) 1
127.0.0.1:6379[2]> sadd key1 b
(integer) 1
127.0.0.1:6379[2]> sadd key1 c
(integer) 1
127.0.0.1:6379[2]> sadd key2 c
(integer) 1
127.0.0.1:6379[2]> sadd key2 d
(integer) 1
127.0.0.1:6379[2]> sadd key2 e
(integer) 1
127.0.0.1:6379[2]> SDIFF key1 key2 找key1与key2的差集
1) "b"
2) "a"
127.0.0.1:6379[2]> SINTER key1 key2 找key1与key2的交集
1) "c"
127.0.0.1:6379[2]> SUNION key1 key2 找key1 与 key2 的并集
1) "a"
2) "c"
3) "b"
4) "d"
5) "e"
127.0.0.1:6379[2]>
# 应用:共同好友,共同关注
Hash
Map集合,key-map,这个值是一个map集合,本质和String类型没有太大区别,还是一个简单的key-value
# hset 设置哈希值
# hget 获取一个字段值
# hmset 多条设置
# hgetall 获取全部哈希
# hdel 删除哈希
# hlen 获取哈希值个数
127.0.0.1:6379[2]> hset myhash field1 value
(integer) 1
127.0.0.1:6379[2]> hget myhash field1
"value"
127.0.0.1:6379[2]> hset myhash field1 hello
(integer) 0
127.0.0.1:6379[2]> hget myhash field1
"hello"
127.0.0.1:6379[2]> hmset myhash field2 world field3 test
OK
127.0.0.1:6379[2]> hgetall myhash
1) "field1"
2) "hello"
3) "field2"
4) "world"
5) "field3"
6) "test"
127.0.0.1:6379[2]> hdel myhash field1 删除key为filed1的哈希
(integer) 1
127.0.0.1:6379[2]> hgetall myhash 获取所有哈希
1) "field2"
2) "world"
3) "field3"
4) "test"
127.0.0.1:6379[2]> hlen myhash 获取哈希值
(integer) 2
#############################################
# hkeys 获取所有key
# hvals 获取所有value
127.0.0.1:6379[2]> hkeys myhash
1) "field2"
2) "field3"
127.0.0.1:6379[2]> hvals myhash
1) "world"
2) "test"
Zset
有序集合
在set的基础上,增加了一个值
set k1 v1
zset k1 score v1
# zadd 添加
# zrange 查询
127.0.0.1:6379[2]> ZADD myzset 1 one
(integer) 1
127.0.0.1:6379[2]> zadd myzset 2 two 3 three
(integer) 2
127.0.0.1:6379[2]> zrange myzset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379[2]>
#########################
# 排序 部分
127.0.0.1:6379[2]> zadd salary 2500 xiaohong 5000 zhangsan 500 unclebb #添加三个用户信息
(integer) 3
127.0.0.1:6379[2]> ZRANGEBYSCORE salary -inf +inf #查询所有用户,从小到大排序 -inf表负无穷 +inf表正无穷
1) "unclebb"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379[2]> ZRANGEBYSCORE salary -inf 2500 #查询 负无穷到2500的数据,升序
1) "unclebb"
2) "xiaohong"
127.0.0.1:6379[2]> ZREVRANGEBYSCORE salary +inf -inf #查询,降序
1) "zhangsan"
2) "xiaohong"
3) "unclebb"
127.0.0.1:6379[2]>
Zset的语法同Set语法大差不差,注意排序的语法就好了
Zset应用场景:
Zset的排序功能应用于工资表、成绩表等;
普通消息、重要消息,Zset实现带权重;
排行榜应用的实现
特殊数据类型
Geospatial
应用于朋友圈定位、附近的人、打车距离信息
# GEOADD 添加城市经纬信息,通过代码可以一键导入所有的城市经纬信息,这里采用命令行导入测试
127.0.0.1:6379[2]> GEOADD china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379[2]> GEOADD china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379[2]> GEOADD china:city 106.50 29.3 chongqing
(integer) 1
127.0.0.1:6379[2]> GEOADD china:city 114.08 22.54 shenzhen
(integer) 1
127.0.0.1:6379[2]>
# GEOPOS 获取城市的经纬信息
127.0.0.1:6379[2]> GEOPOS china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167091832"
127.0.0.1:6379[2]> GEOPOS china:city beijing chongqing
1) 1) "116.39999896287918091"
2) "39.90000009167091832"
2) 1) "106.49999767541885376"
2) "29.2999989810081729"
#GEODIST 获取两个坐标之间的距离
127.0.0.1:6379[2]> GEODIST china:city beijing shanghai
"1067378.7564"
Hyperloglog
什么是基数?
A{1,2,3,4,5,6,7,8}
B{1,2,3,4,5,6}
基数就是AB两个数据集中不重复的数据的个数
A、B的基数为8
Redis 的 Hyperloglog是用来做基数统计的算法
网页的UV(页面访问量),一个人访问多次但还是算作一个人访问。
传统方式的话是使用set保存用户ID,用来标识用户访问,但是如果访问量比较大的话就会比较麻烦,我们的目的是为了统计数量,而非保存用户ID,这里使用Hyperloglog数据类型
优点:占用的内存是固定的,2^64不同的数据元素,只需要非12kb内存
# PFADD 添加 Hyperloglog 数据类型数据
# PFCOUNT 统计基数数量
# PFMERGE 合并
127.0.0.1:6379> PFADD mykey a b c d e f g h i j k
127.0.0.1:6379> PFADD mykey2 i j z x c v m
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 7
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 15
Bitmap
位存储
只有两个状态的,都可以使用bitmap,比如是否打卡,疫情是否感染,是否登录
# SETBIT 设值
# GETBIT 取值
# BITCOUNT 统计个数
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 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 5
(integer) 1
127.0.0.1:6379> BITCOUNT sign
(integer) 4
事务
mysql的原子性:要么同时成功,要么同时失败;
Redis的单条命令是原子性的,但是Redis的事务是不支持原子性的
Redis的事务没有隔离级别的概念,所有的命令在事务中,并没有被执行,只有在发起执行命令时才会执行;Exec
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
锁:Redis可以实现乐观锁
Redis事务:一组命令的集合;一个事务中所有的命令都会被序列化,在事务的执行过程中,会按照顺序执行
-------- 队列 set set set执行 ---------
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set k1 v1 #命令1
QUEUED
127.0.0.1:6379> set k2 v2 #命令2
QUEUED
127.0.0.1:6379> get k2 #命令3
QUEUED
127.0.0.1:6379> set k3 v3 #命令4
QUEUED
127.0.0.1:6379> EXEC #执行
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379>
######################
# DISCARD 放弃事务
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> 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> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> getset k3 #命令错误 (error) ERR wrong number of arguments for 'getset' command 127.0.0.1:6379> set k4 v4 QUEUED 127.0.0.1:6379> set k5 v5 QUEUED 127.0.0.1:6379> EXEC (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k1 (nil) 127.0.0.1:6379> # 此时所有的命令都不会执行
运行时异常(事务队列中存在语法性错误)其他命令会正常执行,错误命令抛出异常
127.0.0.1:6379> set k1 v1 # 先设置一个String类型的变量 OK 127.0.0.1:6379> MULTI # 开启事务 OK 127.0.0.1:6379> INCR k1 # 字符+1,错误 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> get k3 QUEUED 127.0.0.1:6379> 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> 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>
多线程模拟测试:
线程1:
127.0.0.1:6379> WATCH money #监听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 #在事务还没有执行的时候 线程2对money做了修改
(nil)
127.0.0.1:6379> UNWATCH #取消监听
线程2:
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000 #修改money信息
OK
127.0.0.1:6379>
Jedis
通过使用Java来操作Redis
什么是Jedis
是Redis官方推荐的java连接工具,使用Java操作Redis的中间件,如果使用java操作redis,那么一定要熟悉jedis
使用
导入对应的依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
连接Redis
# 我在使用主机连接虚拟机上的redis时,出现无法正常连接的故障
# 修改了redis.conf的配置文件
protected-mode no
bind字段注释掉
找到 bind 127.0.0.1 默认是只允许本机访问
修改为 bind 0.0.0.0 设置为0.0.0.0 任何主机都可以访问
import redis.clients.jedis.Jedis;
/**
* @program: TestProject
* @description: 测试类
* @author: LiuZhiliang
* @create: 2021-04-09 14:21
**/
public class Test {
public static void main(String[] args) {
//1、创建jedis对象
Jedis jedis = new Jedis("192.168.32.190",6379);
//Jedis 所有的命令就是之前敲得所有指令
System.out.println(jedis.ping());
}
}
Java-事务
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
/**
* @program: TestProject
* @description: 测试Jedis-事务
* @author: LiuZhiliang
* @create: 2021-04-12 10:27
**/
public class TestTX {
public static void main(String[] args) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","unclebb");
String result = jsonObject.toJSONString();
Jedis jedis = new Jedis("192.168.32.190",6379);
//开启事务
Transaction multi = jedis.multi();
try {
multi.set("user1",result);
multi.set("user2",result);
int i = 1/0; //模拟执行错误抛出异常
multi.exec();
} catch (Exception e) {
//异常,放弃事务
System.out.println("抛出异常");
multi.discard();
e.printStackTrace();
} finally {
//关闭连接
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
multi.close();
}
}
}
SpringBoot—Redis
SpringBoot操作数据是通过Spring-Data;包括Jdbc、mongdb、redis等等
在SpringBoot2.X之后,原来使用的Jedis被替换成了lettuce
jedis:采用直连,多个线程操作的话是不安全的;如果想要避免这个不安全,须使用jedis pool连接池,BIO模式
BIO: 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
lettuce:采用netty,实例在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数量,更像NIO模式
NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
BIO、NIO、AIO介绍:https://blog.csdn.net/u012088399/article/details/74923827
Spring所有的配置类都有一个自动配置类; RedisAutoConfiguration
自动配置类都会绑定一个properties配置文件; RedisProperties