谈谈Redis的那点事儿

33 篇文章 2 订阅

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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值