Redis学习笔记(基础)

Redis学习笔记(基础)

  • 一、Nosql概述
    • 1.1、为什么使用Nosql
    • 1.2、什么是Nosql
    • 1.3、阿里巴巴演进分析
    • 1.4、NoSQL的四大分类
  • 二、 Redis入门
    • 2.1、概述
    • 2.2、Windows使用Redis
    • 2.3、linux安装
    • 2.4、redis-benchmark性能测试
    • 2.5、Redis基础知识
  • 三、五大数据类型
    • 3.1、Redis-Key
    • 3.2、String
    • 3.3、List
    • 3.4、Set
    • 3.5、Hash
    • 3.6、Zset
  • 四、三种特殊数据类型
    • 4.1、Geospatial地理位置
    • 4.2、Hyperloglog
    • 4.3、Bitmap
  • 五、事务
    • 5.1、Redis中的基本事务
    • 5.2、监控
  • 六、Jedis
  • 七、Springboot的整合
    • 7.1、Springboot整合Redis
    • 7.2、自定义RedisTemplate

一、Nosql概述

1.1、为什么使用Nosql

1. 单机MySQL的年代

在这里插入图片描述

90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够!
那个时候,更多的去使用静态网页 Html~ 服务器根本没有太大的压力!
思考一下,这种情况下 : 整个网站的瓶颈是什么 ?

  • 数据量如果太大,一个机器放不下
  • 数据的索引(B+ Tree),一个机器内存也放不下
  • 访问量(读写混合),一个服务器承受不住

2. Mencached(缓存)+MySQL+垂直拆分(读写分离)

站80%的情况都是在读,每次都要去查询数据库的话就十分的麻烦! 所以说我们希望减轻数据的压力,我们可以使用缓存来保证效率!

在这里插入图片描述

3. 分库分表+水平拆分+MySQL集群

在这里插入图片描述

4. 如今年代

在这里插入图片描述
为什么使用Nosql?
用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!
这时候我们就需要使用NoSQL数据库的,Nosgl 可以很好的处理以上的情况!

1.2、什么是Nosql

NoSQL ——> not only SQL(不仅仅是SQL)
泛指非关系型数据库的,随着web2.0互联网的诞生! 传统的关系型数据库很难对付web2.0时代! 尤其是超大规模的高并发的社区!暴露出来很多难以克服的问题,NOSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的,而且是我们当下必须要掌握的一个技术!

很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式!不需要多月的操作就可以横向扩展的 ! Map<String,obiect> 使用键值对来控制 !

Nosql特点

  1. 方便扩展(数据之间没有关系,很好扩展!)
  2. 大数据量高性能(Redis一秒可以写8万次读取11万,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高!)
  3. 数据类型是多样的(不需要事先设计数据库如果数据量十分大的表,很多人就无法设计了)
  4. 传统RDBMS和NoSQL
传统的RDBMS
- 结构化组织
- SQL
- 数据和关系都存在单独的表中
- 操作操作,数据定义语言
- 严格的一致性
- 基础的事务
- ...
NoSQL
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库(社交关系)
- 最终一致性
- CAP定理和BASE(异地多活)初级架构师
- 高性能,高可用,高可扩
- ...

大数据时代的3V:主要是描述问题的

  1. 海量Volume
  2. 多样Variety
  3. 实时Velocity

大数据时代的3高:主要是对程序的要求

  1. 高并发
  2. 高可拓
  3. 高性能

真正在公司中的实践:NOSOL + RDBMS 一起使用才是最强的

1.3、阿里巴巴演进分析

  1. 商品的基本信息
    名称、价格、商家信息;
    关系型数据库就可以解决了! MSOL /oracle (淘宝早年就去IOE了!- 王坚:推荐文章;阿里云的这群疯子:40分钟重要!)淘宝内部的 MySQL 不是大家用的 MySQL

  2. 商品的描述、评论(文字比较多)
    文档型数据库中,MongoDB

  3. 图片
    分布式文件系统 FastDFS

    • 淘宝自己的TFS
    • Gooale 的GFS
    • Hadoop HDFS
    • 阿里云的 oss
  4. 商品的关键字 (搜索)
    搜索引擎 solr elasticsearch
    Iserach: 多隆(多去了解一下这些技术大佬!)
    所有牛逼的人都有一段苦逼的岁月!但是你只要像SB一样的去坚持,终将牛逼!

  5. 商品的交易,外部支付接口

    • 内存数据库
    • Redis Tair、Memache…
  6. 商品的交易、外部的支付接口

    • 三方应用

要知道,一个简单地网页背后的技术一定不是大家所想的那么简单!

大型互联网应用问题:

  • 数据类型太多了!
  • 数据源繁多,经常重构!
  • 数据要改造,大面积改造

1.4、NoSQL的四大分类

KV键值对

  • 新浪:Redis
  • 美团:Redis+Tair
  • 阿里、百度:Redis + memcache

文档型数据库(bson格式 和json一样)

  • MongoDB(一般必须要掌握)
    • MongoDB 是一个基于分布式文件存储的数据库,C++ 编写,主要用来处理大量的文档!
    • MongoDB 是一个介于关系型数据库和非关系型数据中中间的产品!MongoDB 是非关系型数据库中功能最丰富,最像关系型数据库的!
  • ConthDB

列存储数据库

  • HBase
  • 分布式文件系统

图关系数据库
在这里插入图片描述

  • 他不是存图形,放的是关系,比如 : 朋友圈社交网络,广告推荐!
  • Neo4j , InfoGrid ;
分类Examples举例典型应用场景数据模型优点缺点
键值( key-value )Tokyo Cabinet/Tyrant,Redis, Voldemort ,Oracle BDB内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。Key 指向Value 的键值对,通常用hash table来实现查找速度快数据无结构化,通常只被当作字符串或者二进制数据
列存储数据库Cassandra,HBase,Riak分布式的文件系统以列簇式存储,将同一列数据存在一起查找速度快,可扩展性强,更容易进行分布式扩展功能相对局限
文档型数据库CouchDB,MongoDbWeb应用( 与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容)Key-Value对应的键值对,Value为结构化数据数据结构要求不严格,表结构可变,不需要像关系数据库一样需要预先定义表结构查询性能不高,而且缺乏统一的查询语法。
图形(Graph)数据库Neo4J, InfoGrid, Infinite Graph社交网络,推荐系统等。专注于构建关系图谱图结构利用图结构相关算法。比如最短路径寻址,N度关系查找等很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方鑫。

二、 Redis入门

2.1、概述

Reids是什么?

Redis(Remote Dictionary Server),远程字典服务
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
免费开源!是当下最热门的NoSQL技术之一Q!也被人们成为结构化数据库!

redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

读的速度是110000次/s,写的速度是81000次/s 。

Redis可以干什么?

  1. 内存存储,持久化,内存中是断电即失,所以说持久化很重要(rdb、aof)
  2. 效率高,可以用于高速缓存
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器,计数器(浏览量!)

特性

  1. 多样的数据类型
  2. 持久化
  3. 集群
  4. 事务

2.2、Windows使用Redis

  1. 启动Redis

在这里插入图片描述
2. 测试使用Redis

在这里插入图片描述

2.3、linux安装

查询其他资料

2.4、redis-benchmark性能测试

参数描述默认值
-h指定服务器主机名127.0.0.1
-p指定服务器端口6379
-s指定服务器socket
-c指定并发连接数50
-n指定请求数10000
-d以字节的形式指定 SET/GET 值的数据大小2
-k1=keep alive 0=reconnect1
-rSET/GET/INCR 使用随机 key, SADD 使用随机值
-P通过管道传输请求1
-q强制退出 redis。仅显示 query/sec 值
–csv以 CSV 格式输出
-l生成循环,永久执行测试
-t仅运行以逗号分隔的测试命令列表。
-IIdle 模式。仅打开 N 个 idle 连接并等待。

​ redis启动后进入到bin目录中,执行以下命令进行性能测试:

# 执行测试性能命令
./redis-benchmark -t set,get -n 100000

执行结果如下:

====== SET ======
  100000 requests completed in 1.31 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

98.64% <= 1 milliseconds
99.85% <= 2 milliseconds
99.99% <= 3 milliseconds
100.00% <= 3 milliseconds
76335.88 requests per second

====== GET ======
  100000 requests completed in 1.26 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.27% <= 1 milliseconds
100.00% <= 1 milliseconds
79365.08 requests per second

TPS、QPS、RT
在描述系统的高并发能力时,经常根据以下三个指标来决定:

  • 响应时间RT:响应时间是指系统对请求作出响应的时间。一个系统通常会提供许多功能,而不同功能的业务逻辑也千差万别,因而不同功能的响应时间也不尽相同。在讨论一个系统的响应时间时,通常是指该系统所有功能的平均时间或者所有功能的最大响应时间。
  • 吞吐量TPS:吞吐量是指系统在单位时间内处理请求的数量。不同系统的平均响应时间随用户数增加而增长的速度也不大相同,这也是采用吞吐量来度量并发系统的性能的主要原因。一般而言,吞吐量是一个比较通用的指标,两个具有不同用户数和用户使用模式的系统,如果其最大吞吐量基本一致,则可以判断两个系统的处理能力基本一致。
  • 查询率QPS:每秒查询率QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,在互联网中,经常用每秒查询率来衡量服务器的性能。对应fetches/sec,即每秒的响应请求数,也即是最大吞吐能力,查询率通常是针对单机进行压力测试。

2.5、Redis基础知识

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

  1. 可以使用DBSIZE查看当前数据库的大小
127.0.0.1:6379> DBSIZE
(integer) 0
  • 选择使用的数据库 select index
127.0.0.1:6379> select 2 #切换数据库
OK
127.0.0.1:6379[2]>
  1. 查看数据库所有的key keys *
127.0.0.1:6379[2]> keys *
1) "name"
  1. 清空当前数据库 flushdb [ASYNC]
127.0.0.1:6379[2]> flushdb
OK
127.0.0.1:6379[2]>
  1. 清空所有数据库 flushall [ASYNC]
127.0.0.1:6379> keys *
1) "name"
2) "article:viewCount"
3) "mylist"
4) "myset:__rand_int__"
5) "counter:__rand_int__"
6) "\xac\xed\x00\x05t\x00\x04info"
7) "namme"
8) "\xac\xed\x00\x05t\x00\x03age"
9) "key:__rand_int__"
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379>

Redis是单线程的!

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

Redis 是C 语言写的,官方提供的数据为 100000+ 的QPS,完全不比同样是使用 key-vale的Memecache差

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

  1. 误区一:高性能的服务器一定是多线程的?
  2. 误区二::多线程(CPU上下文会切换!)一定比单线程效率高!

先去CPU>内存>硬盘的速度要有所了解!

核心:redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!

三、五大数据类型

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库高速缓存消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。

3.1、Redis-Key

  1. 判断是否存在existes key
127.0.0.1:6379> set name smulll # 设置一个key-value
OK
127.0.0.1:6379> set age 19
OK
127.0.0.1:6379> keys * #查看所有key
1) "name"
2) "age"
127.0.0.1:6379> EXISTS name # 查看该key是否存在
(integer) 1 #存在
  1. 移除keymove key db
127.0.0.1:6379> move name 1 # 删除某个库中的key
(integer) 1
  1. 设置过期时间expire key seconds
    查看当前key剩余时间ttl key
127.0.0.1:6379> set name smull
OK
127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)
  1. 查看key的类型type key
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> type name
string
127.0.0.1:6379> type age
string

在这里插入图片描述
当遇到不会的命令时可以在官网进行查看文档

3.2、String

90% 的 java程序员使用 redis 只会使用一个String类型!

  1. 在字符串后面追加字符串append key value
    • 追加字符串,如果当前key不存在,就像相当于set key
127.0.0.1:6379> set key1 c1
OK
127.0.0.1:6379> append key1 zhangsan
(integer) 10
127.0.0.1:6379> get key1
"c1zhangsan"
  1. 获取字符串的长度strlen key
127.0.0.1:6379> get key1
"c1zhangsan"
127.0.0.1:6379> strlen key1
(integer) 10
  1. 自增incr key 自减decr key
    带有步长的自增incrby key increment 带有步长的自减decrby key increment
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> incr views #自增
(integer) 1
127.0.0.1:6379> decr views #自减
(integer) 0
127.0.0.1:6379> decr views
(integer) -1
127.0.0.1:6379> incrby views 10 #带有步长的自增
(integer) 9
127.0.0.1:6379> decrby views 20 #带有步长的自减
(integer) -11
  1. 获取部分字符串getrange key start end
127.0.0.1:6379> set myname hello,zhangsan
OK
127.0.0.1:6379> getrange myname 0 3
"hell"
127.0.0.1:6379> getrange myname 0 -1 #倒序查看 -1是最后一个 和get key一样
"hello,zhangsan"
  1. 替换部分字符串setrange key offset value
127.0.0.1:6379> set key1 asdasda112
OK
127.0.0.1:6379> setrange key1 1 xxxx # 替换指定位置开始的字符串
(integer) 10
127.0.0.1:6379> get key1
"axxxxda112"
  1. 设置过期时间setex key seconds value
    如果不存在设置setnx key value
127.0.0.1:6379> setex key3 30 "zhangsan" #设置key3的值 并且设置过期时间
OK
127.0.0.1:6379> ttl key3 # 查看key3的到期时间
(integer) 25
127.0.0.1:6379> setnx key3 zhangsan
(integer) 0
127.0.0.1:6379> setnx key4 xiaowang1 #如果key4不存在则设置
(integer) 1
127.0.0.1:6379> get key3
(nil)
127.0.0.1:6379> get key4
"xiaowang1"
127.0.0.1:6379> setnx key4 lisi #如果key4存在,则失败
(integer) 0
127.0.0.1:6379> get key4
"xiaowang1"
  1. 批量设置keymset key value [key value ...]
    批量回去key的值mget key [key ...]
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
  1. 如果不存在则创建(批量)msetnx key value [key value ...]
    • msetnx是一个原子性的操作,要么一起成功,要么一起失败
127.0.0.1:6379> msetnx k1 v111 k4 v444
(integer) 0
127.0.0.1:6379> msetnx k4 011 k5 000
(integer) 1

进阶用法类似于Java中的对象

set user:1{name:zhangsan,age:3}

在redis里的巧妙用法:user:{id}:{filed},如此设计在Redis里是完全OK的

127.0.0.1:6379> mset user:1:name zhangsan user:1:age 19
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "19"
  1. 先获取再设置getset key value
127.0.0.1:6379> getset test mysql #获取设置一个空的 如果没有值则返回nil
(nil)
127.0.0.1:6379> get test
"mysql"
127.0.0.1:6379> getset test redis #如果存在值,则获取原来的值再设置
"mysql"
127.0.0.1:6379> get test
"redis"

String类似的使用场景:value除了是我们的字符串还可以是我们的数字!

  • 计数器
  • 统计多单位的数量。
  • 粉丝数
  • 对象缓存存储!

3.3、List

在redis里面,我们可以将list变成栈、队列、阻塞队列

多有的list命令都是使用l开头

  1. 将值加到列表的头部lpush key value [value ...]
    将值加到列表的尾部rpush key value [value ...]
    获取列表中的部分值lrange key start stop
127.0.0.1:6379> lpush list one tow #在列表的头部加入值
(integer) 2
127.0.0.1:6379> lpush list three #在列表的头部加入值
(integer) 3
127.0.0.1:6379> lrange list 0 -1 #获取列表中所有的值
1) "three"
2) "tow"
3) "one"
127.0.0.1:6379> rpush list righr #在列表的尾部加入值
(integer) 4
127.0.0.1:6379> lrange list 0 -1 #获取列表中所有的值
1) "three"
2) "tow"
3) "one"
4) "righr"
  1. 移除左边第一个数据lpop
    移除右边第一个数据rpop
127.0.0.1:6379> lrange list 0 -1 #获取列表中所有的值
1) "three"
2) "tow"
3) "one"
4) "righr"
127.0.0.1:6379> lpop list
"three"
127.0.0.1:6379> rpop list
"righr"
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
  1. 通过下标获取列表中的某一个值lindex key index
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
127.0.0.1:6379> lindex list 1
"one"
127.0.0.1:6379> lindex list 0
"tow"
  1. 获取列表的长度llen key
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
127.0.0.1:6379> llen list
(integer) 2
  1. 移除指定的值lrem key count value
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "tow"
3) "one"
127.0.0.1:6379> lrem list 1 one #移除list集合中指定个数的value,精确匹配
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
127.0.0.1:6379> lpush list one
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "tow"
3) "one"
127.0.0.1:6379> lrem list 2 one
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "tow"
  1. 修剪listltrim key start stop
127.0.0.1:6379> lrange mylist 0 -1
1) "hello123"
2) "hello12"
3) "hello1"
4) "hello"
127.0.0.1:6379> ltrim mylist 1 2 #通过下标截取指定长度,截取的时候已经改变了list,只剩下截取的元素
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello12"
2) "hello1"
  1. 移除列表中的最后一个元素,并将它移动到新的列表中rpoplpush source destination
127.0.0.1:6379> lrange mylist 0 -1
1) "hello3"
2) "hello3"
3) "hello12"
4) "hello1"
127.0.0.1:6379> rpoplpush mylist myotherlist #将原来列表的最后一个元素移动到新的列表中
"hello1"
127.0.0.1:6379> lrange mylist 0 -1
1) "hello3"
2) "hello3"
3) "hello12"
127.0.0.1:6379> lrange myotherlist 0 -1
1) "hello1"
  1. 在指定下标替换值lset key index value
127.0.0.1:6379> exists list #判断列表是否存在
(integer) 0
127.0.0.1:6379> lset list 0 item #在不存在的列表中替换值会报错
(error) ERR no such key
127.0.0.1:6379> lpush list value1 #新建列表并且加一个值
(integer) 1
127.0.0.1:6379> lset list 0 item #如果存在会更新值
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
127.0.0.1:6379> lset list 1 item2 #在不存在的下标下替换值会报错
(error) ERR index out of range
  1. 在指定列表前面或后面插入一个值linsert key BEFORE|AFTER pivot value
127.0.0.1:6379> rpush mylist hello
(integer) 1
127.0.0.1:6379> rpush mylist world
(integer) 2
127.0.0.1:6379> linsert mylist after world other #在world后面加入一个other
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
3) "other"
127.0.0.1:6379> linsert mylist before world wowowow #在world前面加入一个wowowow
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "wowowow"
3) "world"
4) "other"

小结

  • 他实际上是一个链表,before Node after ,left,right 都可以插入值
  • 如果key 不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,空链表,也代表不存在!
  • 在两边插入或者改动值,效率最高!中间元素,相对来说效率会低一点~

消息排队!消息队列(Lpush Rpop),栈( Lpush Lpop)

3.4、Set

set中的值不能重复

  1. 在set里面存储值sadd key member [member ...]
127.0.0.1:6379> sadd myset hello #在指定set中添加一个元素
(integer) 1
127.0.0.1:6379> sadd myset world
(integer) 1
127.0.0.1:6379> sadd myset world #重复添加会报错
(integer) 0
127.0.0.1:6379> sadd myset Smulll
(integer) 1
  1. 获取set里面的值smembers myset
127.0.0.1:6379> smembers myset #获取指定set的值
1) "world"
2) "hello"
3) "Smulll"
  1. 查看set中是否包含某值sismember key member
127.0.0.1:6379> smembers myset
1) "world"
2) "hello"
3) "Smulll"
127.0.0.1:6379> sismember myset Smulll #判断某一个元素是否在set中
(integer) 1
127.0.0.1:6379> sismember myset zhangsan
(integer) 0
  1. 查看set中元素的值scard key
127.0.0.1:6379> smembers myset
1) "world"
2) "hello"
3) "Smulll"
127.0.0.1:6379> scard myset
(integer) 3
  1. 移除某个元素srem key member [member ...]
127.0.0.1:6379> srem myset Smulll
(integer) 1
127.0.0.1:6379> scard myset
(integer) 2
127.0.0.1:6379> smembers myset
1) "world"
2) "hello"
  1. 随机获取set中的某一个值srandmember key [count]
127.0.0.1:6379> smembers myset
1) "world"
2) "hello"
3) "smulll"
4) "Smulll"
5) "smulll11"
127.0.0.1:6379> srandmember myset # 随机抽出一个元素
"smulll11"
127.0.0.1:6379> srandmember myset
"Smulll"
127.0.0.1:6379> srandmember myset
"hello"
127.0.0.1:6379> srandmember myset 2 # 随机抽选出指定个数的元素
1) "world"
2) "smulll"
  1. 随机指定key的某个元素spop key [count]
127.0.0.1:6379> smembers myset
1) "hello"
2) "Smulll"
3) "smulll11"
4) "world"
5) "smulll"
127.0.0.1:6379> spop myset #随机删除myset中的1个元素
"smulll11"
127.0.0.1:6379> smembers myset
1) "hello"
2) "Smulll"
3) "world"
4) "smulll"
127.0.0.1:6379> spop myset 2 #随机删除myset中的2个元素
1) "world"
2) "smulll"
127.0.0.1:6379> smembers myset
1) "hello"
2) "Smulll"
  1. 将set下指定某个元素移动到另外的set中smove source destination member
127.0.0.1:6379> smembers myset
1) "world"
2) "hello"
3) "smulll"
4) "smulll1"
5) "smulll2"
127.0.0.1:6379> smove myset myset2 smulll
(integer) 1
127.0.0.1:6379> smembers myset
1) "smulll1"
2) "hello"
3) "smulll2"
4) "world"
127.0.0.1:6379> smembers myset2
1) "smulll"
  1. 获取几个set中的差集sdiff key [key ...]
    获取几个set中的交集sinter key [key ...]
    获取几个set中的并集sunion key [key ...]
127.0.0.1:6379> smembers set1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> smembers set2
1) "c"
2) "e"
3) "d"
4) "a"
127.0.0.1:6379> sdiff set1 set2 #获取 set1 和 set2 的差集
1) "b"
127.0.0.1:6379> sinter set1 set2 #获取 set1 和 set2 的交集
1) "c"
2) "a"
127.0.0.1:6379> sunion set1 set2 #获取 set1 和 set2 的并集
1) "e"
2) "a"
3) "b"
4) "c"
5) "d"

使用场景:

  • 微博,A用户将所有关注的人放在一个set集合中!将它的粉丝也放在一个集合中!
  • 共同关注,共同爱好,二度好友,推荐好友!(六度分割理论)

3.5、Hash

Map集合,key-map!时候这个值是一个map集合!本质和string类型没有太大区别,还是一个简单的key-value

set myhash field kuangshen

  1. 创建一个Hash hset key field value
127.0.0.1:6379> hset myhash field1 smulll
(integer) 1
  1. 获取Hash中的一个字段hget key field
127.0.0.1:6379> hget myhash field1
"smulll"
  1. 批量添加数据到hash中hmset key field value [field value ...]
127.0.0.1:6379> hmset myhash field1 smulll fiedl2 zhangsan fiedl3 lisi
OK
  1. 批量获取hash中的字段值hmget key field [field ...]
127.0.0.1:6379> hmget myhash field1 fiedl2 fiedl3
1) "smulll"
2) "zhangsan"
3) "lisi"
  1. 获取全部字段值hgetall key
127.0.0.1:6379> hgetall myhash
1) "field1"
2) "smulll"
3) "fiedl2"
4) "zhangsan"
5) "fiedl3"
6) "lisi"
  1. 删除某一个字段的值hdel key field [field ...]
127.0.0.1:6379> hdel myhash field1 #删除某个指定的字段
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "fiedl2"
2) "zhangsan"
3) "fiedl3"
4) "lisi"
  1. 查看hash中的字段数量hlen key
127.0.0.1:6379> hset myhash f1 zhangsan
(integer) 1
127.0.0.1:6379> hset myhash f1 zhangsan f2 lsii f3 wangwu
(integer) 2
127.0.0.1:6379> hgetall myhash #获取全部的hash字段
1) "f1"
2) "zhangsan"
3) "f2"
4) "lsii"
5) "f3"
6) "wangwu"
127.0.0.1:6379> hlen myhash #获取字段长度
(integer) 3
  1. 按段hash的某个字段是否存在hexists key field
127.0.0.1:6379> hgetall myhash #获取全部的hash字段
1) "f1"
2) "zhangsan"
3) "f2"
4) "lsii"
5) "f3"
6) "wangwu"
127.0.0.1:6379> HEXISTS myhash f1
(integer) 1
  1. 只获取hash所有的字段hkeys key
    只获取hash所有的值 hvals key
127.0.0.1:6379> hkeys myhash
1) "f1"
2) "f2"
3) "f3"
127.0.0.1:6379> hvals myhash
1) "zhangsan"
2) "lsii"
3) "wangwu"
  1. 给某个字段自增HINCRBY key field increment
127.0.0.1:6379> hset myhash fi1 1
(integer) 1
127.0.0.1:6379> HINCRBY myhash fi1 1
(integer) 2
127.0.0.1:6379> HINCRBY myhash fi1 -9
(integer) -7
  1. 判断是否存在以及是否能设置在这里插入代码片
127.0.0.1:6379> hsetnx myhash field1 zhangsan #不存在则设置
(integer) 1
127.0.0.1:6379> hsetnx myhash field1 lisi # 存在则不能设置
(integer) 0

使用场景:

  • hash变更的数据 user name age,尤其是是用户信息之类的,经常变动的信息!
  • hash更适合于对象的存储
  • String更适合字符串的存储
127.0.0.1:6379> hset user:1 name zhangsan
(integer) 1
127.0.0.1:6379> hget user:1 name
"zhangsan"
127.0.0.1:6379> hset user:1 age 19
(integer) 1
127.0.0.1:6379> hmget user:1 name age
1) "zhangsan"
2) "19"
127.0.0.1:6379> hgetall user:1
1) "name"
2) "zhangsan"
3) "age"
4) "19"

3.6、Zset

在set的基础上,增加了一个值,set k1 v1 | zset k1 score1 v1

  1. 在Zset中添加值zadd key [NX|XX] [CH] [INCR] score member [score member ...]
127.0.0.1:6379> zadd myset 1 one #在zset中加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three #在zset中加多个值
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
  1. 根据score进行排列zrangebyscore key min max [WITHSCORES] [LIMIT offset count](最小值到最大值)
127.0.0.1:6379> zadd salary 1000 zhangsan # 添加数据
(integer) 1
127.0.0.1:6379> zadd salary 2000 lisi
(integer) 1
127.0.0.1:6379> zadd salary 4000 wangwu
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf #从负无穷到正无穷进行排列
1) "zhangsan"
2) "lisi"
3) "wangwu"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores #从负无穷到正无穷进行排列并且打印score
1) "zhangsan"
2) "1000"
3) "lisi"
4) "2000"
5) "wangwu"
6) "4000"
127.0.0.1:6379> zrangebyscore salary -inf 2000 withscores #从负无穷到2000进行排列并且打印score
1) "zhangsan"
2) "1000"
3) "lisi"
4) "2000"
  1. 据score进行排列zrevrange key start stop [WITHSCORES](从最大值到最小值)
127.0.0.1:6379> zrevrange salary 0 -1 withscores
1) "wangwu"
2) "4000"
3) "lisi"
4) "2000"
  1. 移除zset中的指定元素在这里插入代码片
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores #从负无穷到正无穷进行排列并且打印score
1) "zhangsan"
2) "1000"
3) "lisi"
4) "2000"
5) "wangwu"
6) "4000"
127.0.0.1:6379> zrem salary zhangsan #移除zhangsan
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores 
1) "lisi"
2) "2000"
3) "wangwu"
4) "4000"
  1. 获取集合中的个数zcard key
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores
1) "lisi"
2) "2000"
3) "wangwu"
4) "4000"
127.0.0.1:6379> zcard salary
(integer) 2
  1. 获取指定区间的成员数量zcount key min max
127.0.0.1:6379> zadd newset 1 smulll
(integer) 1
127.0.0.1:6379> zadd newset 2 xiaohong 3 xiaohuang 4 xiaowang
(integer) 3
127.0.0.1:6379> zcount newset 1 2 #获取指定区间的成员数量
(integer) 2

其与的一些API,通过我们的学习吗,你们剩下的如果工作中有需要,这个时候你可以去査査看官方文档!
使用场景:

  • 案例思路:set 排序 存储班级成绩表,工资表排序!
  • 普通消息,1,重要消息 2,带权重进行判断!
  • 排行榜应用实现,取Top N 测试!

四、三种特殊数据类型

4.1、Geospatial地理位置

朋友的定位,附近的人,打车距离计算?
Redis 的 Geo 在Redis3.2 版本就推出了!这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!

  1. 添加位置geoadd key longitude latitude member [longitude latitude member ...]
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.48 31.40 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 39.90 116.40 beijing
(error) ERR invalid longitude,latitude pair 39.900000,116.400000

有效的经度从-180度到180度。
有效的纬度从-85.05112878度到85.05112878度。
当坐标位置超出上述指定范围时,该命令将会返回一个错误。
南北极无法添加,我们一般会下载城市数据,直接通过Java一次性导入数据

  1. 查询位置在这里插入代码片
127.0.0.1:6379> geopos china:city beijing #获取指定城市的经纬度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city beijing shanghai
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "121.48000091314315796"
   2) "31.40000025319353938"
  1. 获取两个位置之间的距离geodist key member1 member2 [unit]
127.0.0.1:6379> geodist china:city beijing shanghai #查看背景到上海的直线距离
"1050524.9458"
127.0.0.1:6379> geodist china:city beijing shanghai km #查看背景到上海的直线距离 km为单位
"1050.5249"

m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。

  • GEODIST 命令在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。

我附近的人?(获得所有附近的人的地址,定位!)通过半径来查询!

  1. 获取半径内集合中的位置,中心点自己设置georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC
127.0.0.1:6379> georadius china:city 110 30 10000 km
1) "shanghai"
2) "beijing"
127.0.0.1:6379> georadius china:city 110 30 10000 km withcoord withdist #符带经度纬度
1) 1) "shanghai"
   2) "1108.3830"
   3) 1) "121.48000091314315796"
      2) "31.40000025319353938"
2) 1) "beijing"
   2) "1245.2858"
   3) 1) "116.39999896287918091"
      2) "39.90000009167092543"
127.0.0.1:6379> georadius china:city 110 30 10000 km withcoord withdist count 1 #加上count可以限制查出的数量
1) 1) "shanghai"
   2) "1108.3830"
   3) 1) "121.48000091314315796"
      2) "31.40000025319353938"
  1. 找出位于指定范围内的元素,中心点是由给定的位置元素决定georadiusbymember key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DES
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km
1) "beijing"
127.0.0.1:6379> georadiusbymember china:city beijing 3000 km
1) "shanghai"
2) "beijing"
  1. 返回一个或多个位置元素的 Geohash 表示在这里插入代码片
    该命令将返回11个字符的Geohash字符串,所以没有精度Geohash,损失相比,使用内部52位表示。
127.0.0.1:6379> geohash china:city beijing shanghai
1) "wx4fbxxfke0"
2) "wtw6sk5n300"

GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来操作geo!

可以使用zrem来删除goe位置

127.0.0.1:6379> zrange china:city 0 -1 #查看全部元素
1) "shanghai"
2) "beijing"
127.0.0.1:6379> zrem china:city beijing #删除北京位置
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "shanghai"

4.2、Hyperloglog

什么是基数?

A{1,3,4,5,7,8,7}
B{1,3,5,7,8}
基数(不重复的元素)= 5,可以接受误差!

简介

Redis 2.8.9 版本就更新了 Hyperloglog 数据结构!
Redis Hyperloglog 基数统计的算法!

  • 优点:占用的内存是固定,2^64 不同的元素的基数,只需要废 12KB内存!如果要从内存角度来比较的话 Hyperloglog首选!
  • 网页的 UV (一个人访问一个网站多次,但是还是算作一个人!)
    传统的方式 ,set 保存用户的id,然后就可以统计 set 中的元素数量作为标准判断!
    这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id;
    0.81% 错误率!统计UV任务,可以忽略不计的!
  1. 添加元素在这里插入代码片
127.0.0.1:6379> pfadd myset a b j o w s a x r t
(integer) 1
127.0.0.1:6379> pfadd myset2  j r t z d l o p j k
(integer) 1
  1. 统计元素基数pfcount key [key ...]
127.0.0.1:6379> pfadd myset a b j o w s a x r t
(integer) 1
127.0.0.1:6379> pfcount myset
(integer) 9
  1. 合并两个setpfmerge destkey sourcekey [sourcekey ...]
127.0.0.1:6379> pfmerge myset myset2
OK
127.0.0.1:6379> pfcount myset
(integer) 14

如果允许容错,那么一定可以使用 Hyperloglog!

如果不允许容错,就使用set 或者自己的数据类型即可!

4.3、Bitmap

位图

统计用户信息,活跃,不活跃! 登录 、未登录!打卡,365打卡!两个状态的,都可以使用 Bitmaps!
Bitmaps 位图,数据结构!都是操作二进制位来进行记录,就只有0和1两个状态

365天=365 bit 1字节=8bit 46 个字节左右!

  1. 设置bitsetsetbit key offset value

模拟周一至周日打卡

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 0
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0
  1. 获取bitsetgetbit key offset
127.0.0.1:6379> getbit sign 1
(integer) 0
127.0.0.1:6379> getbit sign 2
(integer) 1
  1. 统计bitcount key [start end]
127.0.0.1:6379> bitcount sign
(integer) 4

五、事务

5.1、Redis中的基本事务

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

一次性、顺序性、排他性!执行一系列的命令

队列 set set set 执行

Redis事务没有隔离级别的概念!

所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec

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

redis的事务:

  • 开启事务multi -
  • 命令入队......
  • 执行事务exec -
  • 取消事务discard -

执行事务

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> 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> discard #取消事务
OK
127.0.0.1:6379> get k1 #事务队列中命令都不会执行
(nil)

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

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)

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

 127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 #会执行的时候失败
QUEUED
127.0.0.1:6379> set ke 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"

5.2、监控

悲观锁:

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

乐观锁:

  • 很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据
  • 获取version
  • 更新的时候比较 version

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
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

测试多线程,使用watch可以当作redis的乐观锁操作

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 #执行之前,另外一个线程修改了值,就会导致事务执行失败
(nil)

如果获取失败,获取最新的值就好

127.0.0.1:6379> unwatch # 解锁
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 money 20
QUEUED
127.0.0.1:6379> exec # 结束事务
1) (integer) 60
2) (integer) 80

六、Jedis

我们要使用Java在操作Redis

什么是Jedis 是 Redis 官方推荐的java连接开发工具!使用ava 操作Redis 中间件!如果要使用Java操作redis,那么一定要对jedis十分的熟悉!

  1. 导入依赖
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>5.1.0</version>
</dependency>
<!--json格式-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.67_noneautotype2</version>
</dependency>
  1. 测试连接
package com.example;

import redis.clients.jedis.Jedis;

public class redistest {
    public static void main(String[] args) {
        //创建一个对象
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //测试连接 jedis所有的命令就是我们之前学习的所有指令
        System.out.println(jedis.ping());
    }
}

在这里插入图片描述

事务测试

package com.example;

import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class Redistest2 {
    public static void main(String[] args) {
        Jedis jedis = new Jedis();
        JSONObject jsonObject = new JSONObject();
        jedis.flushDB();//清空数据

        jsonObject.put("name","zhangsan");
        jsonObject.put("age",18);
        Transaction multi = jedis.multi();
        String string = jsonObject.toString();
        try {
            multi.set("user1",string);
            multi.set("user2",string);
            multi.exec();//执行事务
        }catch (Exception exception){
            multi.discard();//放弃事务
            exception.printStackTrace();
        }finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }

    }
}

七、Springboot的整合

7.1、Springboot整合Redis

SpringBoot 操作数据:spring-data jpa jdbc mongodb redis!
SpringData 也是和 SpringBoot 齐名的项目!

说明:在 SpringBoot2.x之后,原来使用的jedis 被替换为了 lettuce?
jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池! BIO
lettuce:采用netty,实例可以再多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像Nio模式

整合测试

  1. 导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置连接
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
  1. 测试
package com.smulll;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class RedisTestApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        //opsForValue操作String的
        redisTemplate.opsForValue();
        //opsForList操作list的
        redisTemplate.opsForList();
        //opsForSet操作Set的
        redisTemplate.opsForSet();
        //opsForHash操作Hash的
        redisTemplate.opsForHash();
        //opsForZSet操作Zset的
        redisTemplate.opsForZSet();
        //opsForGeo操作Geo的
        redisTemplate.opsForGeo();
        //opsForHyperLogLog操作HyperLogLog的
        redisTemplate.opsForHyperLogLog();

        //除了进本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务,和基本的CRUD
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//        connection.flushDb();
//        connection.flushAll();
        redisTemplate.opsForValue().set("name","Smulll");
        System.out.println(redisTemplate.opsForValue().get("name"));
    }
}

在这里插入图片描述
默认的序列化
在这里插入图片描述

7.2、自定义RedisTemplate

一个固定的模板

package com.smulll.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    //这是一个固定模板
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        //我们为了自己开发方便,一般直接使用<String,Object>
        RedisTemplate<String,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        //JSON序列化配置
        Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        //key采用stinger的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key也采用Sring的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //Value序列化方式采用Jackson
        template.setValueSerializer(objectJackson2JsonRedisSerializer);
        //hash的序列化方式采用jackson
        template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

所有的redis操作,其实对于iava开发人员来说,十分的简单,更重要是要去理解redis的思想和每一种数据结构的用处和作用场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/590723.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux专栏08:Linux基本指令之压缩解压缩指令

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Linux基本指令之压缩解压缩指令 编号&#xff1a;08 文章目录 Linu…

初始Java篇(JavaSE基础语法)(7)抽象类和接口(上)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaSE 目录 抽象类 抽象类的概念&#xff1a; 抽象类语法 抽象类特性 抽象类的作用 接口 接口的概念&#xff1a; 语法规则 接口…

ssm104园区停车管理系统+jsp

园区停车管理系统的设计与实现 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管…

Atlassian Jira 信息泄露漏洞(CVE-2019-3403) 排查思路

Atlassian Jira&#xff1a; 企业广泛使用的项目与事务跟踪工具&#xff0c;被广泛应用于缺陷跟踪、客户服务、需求收集、流程审批、任务跟踪、项目跟踪和敏捷管理等工作领域。 简述&#xff1a; 近日发现多个内网IP触发的Atlassian Jira 信息泄露漏洞的告警。 告警的检测规…

C语言:内存操作函数memcpy、memmove、memset和memcpy的使用和模拟实现

一&#xff1a;memcpy的使用和模拟 memcpy使用时需要包含的头文件为#include<string.h> void* memcpy(void* destination,const void* source,size_t num) 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置&#xff08;特别注意的是…

Python | Leetcode Python题解之第66题加一

题目&#xff1a; 题解&#xff1a; class Solution:def plusOne(self, digits: List[int]) -> List[int]:n len(digits)for i in range(n - 1, -1, -1):if digits[i] ! 9:digits[i] 1for j in range(i 1, n):digits[j] 0return digits# digits 中所有的元素均为 9retu…

「C++ 内存管理篇 04」动态二维数组

目录 一. 使用calloc/free开辟和释放二维数组 二、 使用new/delete开辟和释放二维数组 一. 使用calloc/free开辟和释放二维数组 让一个二级指针变量存放动态开辟的一级指针数组的起始地址&#xff0c;然后让这些一级指针指向动态开辟的基本类型的数组&#xff1a; // 开辟一个大…

【C++】模拟实现string

文章目录 前言成员变量成员函数构造函数浅拷贝深拷贝构造函数实现 析构函数赋值重载 迭代器begin & endrbegin & rend 空间管理函数元素访问元素修改字符串运算流提取 & 流插入流提取流插入 总结 前言 模拟实现不是为了写得和库里面一样好。而是为了更好的了解底层…

一行代码将文件存储到本地或各种存储平台

一行代码将文件存储到本地或各种存储平台 这里我们介绍的是一个开源项目。 这个是他的官网 简介 (xuyanwu.cn) 下面来看他的一个介绍&#xff1a; 一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿里云 OSS、华为云 OBS、七牛云 Kodo、腾讯云 COS、百度云 BOS、又拍云 USS…

R语言学习—1—将数据框中某一列数据改成行名

将数据框中某一列数据改成行名 代码 结果

【大语言模型LLM】-基于大语言模型搭建客服助手(2)

&#x1f525;博客主页&#xff1a;西瓜WiFi &#x1f3a5;系列专栏&#xff1a;《大语言模型》 很多非常有趣的模型&#xff0c;值得收藏&#xff0c;满足大家的收集癖&#xff01; 如果觉得有用&#xff0c;请三连&#x1f44d;⭐❤️&#xff0c;谢谢&#xff01; 长期不…

408数据结构-树的基本概念与性质 自学知识点整理

树的定义 树是 n n n&#xff08; n ≥ 0 n≥0 n≥0&#xff09;个结点的有限集。当 n 0 n0 n0时&#xff0c;称为空树。 任意一棵非空树应具有以下特性&#xff1a; 有且仅有一个特定的被称为根的结点&#xff08;根结点&#xff09;。当 n &#xff1e; 1 n&#xff1e;1 …

【C语言】/*数据类型和变量*/

目录 前言 一、数据类型的介绍 二、内置数据类型的介绍 2.1 字符型 2.2 整型 2.3 浮点型 2.4 布尔类型 三、数据类型长度的计算 3.1 sizeof 操作符 3.2 数据类型的长度(VS2022) 3.3 sizeof中表达式不计算 四、signed 和 unsigned 五、数据类型的取值范围 六、变…

.360勒索病毒的威胁:如何恢复您的数据?

引言&#xff1a; 近年来&#xff0c;网络安全威胁层出不穷&#xff0c;其中.360勒索病毒以其独特的攻击方式和广泛的传播能力&#xff0c;成为了众多企业和个人面临的重大挑战。本文将对.360勒索病毒进行深入剖析&#xff0c;并探讨应对此类病毒的有效策略&#xff0c;以帮助…

UE5入门学习笔记(六)——编译低版本插件

对于有些低版本的插件&#xff0c;可以通过此方法自己编译到高版本而无需等待插件作者更新 使用工具&#xff1a;如图所示 步骤1&#xff1a;打开cmd&#xff0c;并使用cd命令切换到此目录 步骤2&#xff1a;输入如下指令 RunUAT.bat BuildPlugin -Plugin“路径1” -Package“…

Java中的进程和线程

进程定义 进程是正在运行的程序&#xff0c;是系统进行资源分配和调用的独立单位&#xff0c;每一个进程都有它自己的内存空间和系统资源 线程的定义 线程是进程中单个顺序控制流&#xff0c;是一种执行路径 单线程&#xff1a; 一个进程如果只有一条路径则被称为单线程 多…

python学习笔记----面向对象(十)

一、什么是类 类是一个抽象的模板&#xff0c;用于创建具体的实例。可以将类理解为一个蓝图&#xff0c;它定义了一系列对象共有的属性&#xff08;数据&#xff09;和方法&#xff08;函数&#xff09;。类是对一组具有相同属性和功能的对象的抽象。例如&#xff0c;你可以定…

数据结构——循环结构:for循环

今天是星期五&#xff0c;明天休息&#xff0c;后天补课&#xff0c;然后就是运动会&#xff0c;接着是放假。&#xff08;但这些都和我没关系啊&#xff0c;哭死&#xff01;&#xff09;今天脑袋难得清醒一会儿&#xff0c;主要是醒的比较早吧&#xff0c;早起学了一会&#…

【VueUse】超越基本功能的高级 Vue 元素操作

在vue开发中我们经常需要操作DOM元素&#xff0c;从简单的添加类到动态创建元素&#xff0c;这些操作都是不可避免的。而在VueUse库中&#xff0c;Elements相关API函数为我们提供了一系列强大而灵活的工具&#xff0c;帮助我们更轻松地处理DOM元素。无论是优雅地处理元素、动态…

[XYCTF新生赛]-PWN:fmt解析(scanf格式化字符串漏洞,任意地址写)

查看保护 查看ida 这里没什么好说的 完整exp&#xff1a; from pwn import* context(log_leveldebug) #pprocess(./fmt) premote(gz.imxbt.cn,20975) backdoor0x4012BEp.recvuntil(bgift: ) printf_addrint(p.recv(14),16) print(hex(printf_addr)) libcELF(./libc-2.31.so) …