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 seconds50 parallel clients3 bytes payloadkeep alive: 198.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 seconds50 parallel clients3 bytes payloadkeep alive: 199.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 {@Autowiredprivate RedisTemplate redisTemplate;@Testvoid 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操作,比如事务,和基本的CRUDRedisConnection 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序列化方式采用Jacksontemplate.setValueSerializer(objectJackson2JsonRedisSerializer);//hash的序列化方式采用jacksontemplate.setHashValueSerializer(objectJackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NIO(非阻塞I/O)和IO(阻塞I/O)详解

文章目录 一、NIO&#xff08;Non-blocking I/O&#xff0c;非阻塞I/O&#xff09;1、Channel&#xff08;通道&#xff09;与Buffer&#xff08;缓冲区&#xff09;1.1、使用ByteBuffer读取文件1.2、ByteBuffer 方法1.2、ByteBuffer 结构1.3、字符串与 ByteBuffer 互转1.4 Sca…

自然语言处理 (NLP) 中的迁移学习

--懂王 在大数据高速发展的时代&#xff0c;AI的发展日新月异&#xff0c;充满挑战的迎接未来。 自然语言处理 (NLP) 中的迁移学习: 迁移学习在 NLP 中越来越受欢迎&#xff0c;特别是在数据稀缺的情况下。如何有效地利用预训练的语言模型&#xff0c;并将其迁移到新的任务和领…

前端框架编译器之模板编译

未经作者允许&#xff0c;禁止转载 编译原理概述 编译原理&#xff1a;是计算机科学的一个分支&#xff0c;研究如何将 高级程序语言 转换为 计算机可执行的目标代码 的技术和理论。 高级程序语言&#xff1a;Python、Java、JavaScript、TypeScript、C、C、Go 等。计算机可执…

微软开源 MS-DOS「GitHub 热点速览」

上周又是被「大模型」霸榜的一周&#xff0c;各种 AI、LLM、ChatGPT、Sora、RAG 的开源项目在 GitHub 上“争相斗艳”。这不 Meta 刚开源 Llama 3 没几天&#xff0c;苹果紧跟着就开源了手机端大模型&#xff1a;CoreNet。 GitHub 地址&#xff1a;github.com/apple/corenet 开…

golang 基础知识细节回顾

之前学习golang的速度过于快&#xff0c;部分内容有点囫囵吞枣的感觉&#xff0c;写gorm过程中有很多违反我常识的地方&#xff0c;我通过复习去修正了我之前认知错误和遗漏的地方。 itoa itoa自增的作用在编辑error code时候作用很大&#xff0c;之前编辑springboot的error c…

idea常用知识点随记

idea常用知识点随记 1. 打开idea隐藏的commit窗口2. idea中拉取Git分支代码3. idea提示代码报错&#xff0c;项目编译没有报错4. idea中实体类自动生成序列号5. idea隐藏当前分支未commit代码6. idea拉取新建分支的方法 1. 打开idea隐藏的commit窗口 idea左上角File→Settings…

前沿科技应用:AIGC技术的广泛渗透

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

基于alpha shapes的边缘点提取(matlab)

1、原理介绍 由Edelsbrunner H提出的alpha shapes算法是一种简单、有效的快速提取边界点算法。其克服了点云边界点形状影响的缺点&#xff0c;可快速准确提取边界点。如下图所示&#xff0c;对于任意形状的平面点云&#xff0c;若一个半径为a的圆&#xff0c;绕其进行滚动&…

LSTM-KDE的长短期记忆神经网络结合核密度估计多变量回归区间预测(Matlab)

LSTM-KDE的长短期记忆神经网络结合核密度估计多变量回归区间预测&#xff08;Matlab&#xff09; 目录 LSTM-KDE的长短期记忆神经网络结合核密度估计多变量回归区间预测&#xff08;Matlab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.LSTM-KDE的长短期…

Flutter笔记:Widgets Easier组件库(5)使用加减器

Flutter笔记 Widgets Easier组件库&#xff08;5&#xff09;&#xff1a;使用加减器 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress…

机器学习:深入解析SVM的核心概念【三、核函数】

核函数 **问题一&#xff1a;为什么说是有限维就一定存在高维空间可分呢&#xff1f;**原始空间与特征空间为什么映射到高维空间可以实现可分核函数的作用 **问题二&#xff1a;最终怎么得到函数**从对偶问题到决策函数的步骤&#xff1a;结论 **问题三&#xff1a;为什么说特征…

nginx--第三方模块安装上传下载服务

第三方模块安装 准备 cd /usr/local/src/ yum install git -y git clone https://github.com/openresty/echo-nginx-module.git cd nginx-1.24.0 yum -y install perl-devel perl-ExtUtils-Embed zlib-devel gcc-c libtool openssl openssl-devel 编译安装 ./configure \--p…

ZooKeeper知识点总结及分布式锁实现

最初接触ZooKeeper是之前的一个公司的微服务项目中&#xff0c;涉及到Dubbo和ZooKeeper&#xff0c;ZooKeeper作为微服务的注册和配置中心。好了&#xff0c;开始介绍ZooKeeper了。 目录 1.ZooKeeper的基本概念 2.ZooKeeper的节点&#xff08;ZNode&#xff09; 3. ZooKeep…

机器学习之基于Tensorflow(LSTM)进行多变量时间序列预测股价

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 项目简介&#xff1a;机器学习之基于TensorFlow&#xff08;LSTM&#xff09;进行多变量时间序列预测股价 一、项目…

Python量化炒股的获取数据函数—get_fundamentals_continuously()

Python量化炒股的获取数据函数—get_fundamentals_continuously() get_fundamentals()函数只能用于查询某一交易日的股票财务数据信息&#xff0c;如果要查询多个交易日的股票财务数据信息&#xff0c;就要使用get_fundamentals_continuously()函数&#xff0c;语法格式如下&a…

Django数据库创建存储及管理

一、什么是ORM Django的ORM(Object-Relational Mapping)是Django框架中一个非常重要的组件。ORM可以让开发者以面向对象的方式操作数据库,而不需要直接编写SQL语句。 具体来说,Django ORM提供了以下功能: 模型定义:开发者可以在Django应用中定义Python类来表示数据库表,这些…

tensorflow报错

参考 TensorFlow binary is optimized to use available CPU instructions in performance-critical operations._this tensorflow binary is optimized to use availab-CSDN博客 解决Python中cuBLAS插件无法注册问题_unable to register cudnn factory: attempting to re-CS…

45. UE5 RPG 使用元属性(Meta Attributes)以及使用Set by Caller修改伤害

在RPG游戏中&#xff0c;我们是不会直接修改生命值的属性&#xff0c;是因为在修改角色属性时&#xff0c;需要获取角色的属性并进行复杂的计算&#xff0c;所以&#xff0c;我们正常情况下使用元属性&#xff08;Meta Attributes&#xff09;作为计算的中间的媒。在服务器上先…

【讲解下如何解决一些常见的 Composer 错误】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

Ex1-C6油气化工防爆轮式巡检机器人

Ex1系列防爆轮式巡检机器人整机采用防爆设计&#xff0c;防爆等级为Exd II CT4 Gb。机器人通过无轨3D形态导航技术&#xff0c;结合360度防爆云台和无线防爆充电桩&#xff0c;实现整套防爆标准&#xff0c;可广泛应用于石油、燃气、化工、冶金等II类爆炸环境中&#xff0c;代替…