为什么要用Nosql
我们现在处理什么时代?2020年,大数据时代
我们现在处理什么年代2020年,
压力越来越来越大 适者生存!!一定要逼着自己学习
90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够!!
那个时候,更多的去使用静态网页Html-服务器没有太大的压力
考虑一下这种情况下:整个网站的瓶颈是什么?
1 .数据量如果太大,一个机器放不下!!
2. 数据的索引(B+Tree),一个机器内存也放不下
3. 访问量(读写混合),一个服务器承受不了-
只要你开始出现以上的三种情况之一,那么你就需要晋级
Memcached(缓存)+MySql+垂直拆分(读写分离)
网站的80%的情况都是在读,每次都要去查询数据库的话就十分麻烦! 所以说我们就希望减轻数据的压力,我们可以使用缓存来保证效率!
发展过程:优化数据结构和索引–>文件缓存(IO)—》Memcached(当时最热门的技术)/
3.分库分表+水平拆分+Mysql集群
技术和业务在发展的同时,对人的要求也是越来越高!
本质:数据库就是读和写
早年Mysqm:表锁,十分影响效率!高并发下就会出现很严重的锁的问题
转型Innodb:行锁
User()
慢慢的就开始使用分库分表来解决压力!mysql在那个年代推出了表分区!这个并没有多少公司使用。
4如今最近
技术爆炸:
2010–2020十年之间,世界已经发生了翻天覆地的变化(定位也是一种数据音乐,热榜)
Mysql关系型数据库就不够用了!数量很多,变化的很快~
Mysql有的使用他来组装一些很大的文件 博客,图片!数据库表的很大,小路就低了!如果有一种数据库来专门的处理这种数据
Mysql压力变得就十分小(研究如何让处理这些问题!)大数据的额io压力下,表几乎不能更大
为什么要用Nosql!
用户的个人的信息的,社交网络 ,地理位置,用户自己产生的数据,用户的日志等等爆式的成长!
这时候我们就要是哟个Nosql数据库的,Nosql可以很好的的处理以上的情况
NoSQL
Nosql=Not Only sql
泛指非关系型数据库的,随着web2.0互联网的诞生 传统的关系型号数据库很难对付web2,0的时代!!尤其式超大规模的高并发的社区!暴露出来很对难以克服的问题 Nosql在当今的 大数据环境下发展的十分迅速 redis是发展的最快的的 而且是我们当下要掌握的一个技术。
.
.
多的数据类型的用户的 个人的信息,社交网络,地理位置,这些数据的类型的存储不需要一个固定的格式!不需要多月的操作就可以横向扩展的!Map《String,Object》使用键值对的来控制!
- Nosql的特点
解耦!
- 方便扩展(数据之间没有关系,很好的扩展)
- 大数据高性能的(Redis一秒写8万次,读取十一万,弄上去了的缓存记录级,是一种细颗粒度的缓存,性能会比较高!)
- 数据类型是多样型的!(不需要事先社会设计数据库!!!随去随用!!如果数据量十分大的表,很多人就无法设计了)
- 传统的RDBMS和NoSQL
传统的RDBMS
结构化组织
SQL
数据和关系都存在单独的表中
操作 数据的定义语言
严格的一致性
基础的事务
。。。。
Nosql
不仅仅是数据
没有固定的的查询语言
键值对存储,列存储,文档存储,图形数据库存储(社交关系)
最终一致性
CAB定理和BASE
高性能,高可用,高可扩
。。。
真正在公司中的实践:nosq加RDBMS一起使用才是最强的,阿里巴巴的架构演进
技术没有高低,就看你如何使用(提升内功,,不要在意表面,思维的提升)
MongoDB(一般必须掌握)
Redis 是什么?
Redis 就是远程字典服务
是一个开源的使用ANS C语言的编写。支持网络,可给予内存的也可以持久化的日志型,key-value的数据库数据库,并且提供多种的APi
免费和开源的!!是当下的最热门的NoSQl技之一!也被人们称之为结构化数据库!!
Redis能干吗?
特性:
…
官网:https://redis.io/
中文网:http://www.redis.cn/
下载安装包:https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100
1.下载安装包 http://www.redis.cn/
用java -verson显示运行的环境如果有问题
使用 linux命令 vim /etc/profile 编辑运行的环境
source /etc/profile 刷新资源
下载的解压包放在home文件夹下
cd /home
ls
移动 压缩包位置 mv redis -5.0.8.tar.gz /opt
cd /opt
ls
tar -zxvf redis -5.0.8.tar.gz
ls
cd redis -5.0.8
yum install gcc-c++
yum install gcc-c++ tcl 6.0.6版本的
gcc -v 查看版本信息
执行make命令自动安装配置
执行make install
6.将redis配置文件,复制到我们当前目录下
7.redis默认不是后台启动的,修改配置文件
vim redis.conf
8.通过指定的配置文件启动redis服务
9. 使用 redis-cli 进行测试连接
10. 查看redis进程是否开启
11. 如何让关闭redis服务器?
12.再次查看进程是否保存
redis-benchmark是一个压力测试工具!!
官方自带的性能测试工具
redis-benchmark参数命令!!
简单测试:
# 测试:100个并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
结果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210606201237907.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubm V0L20wXzUyOTQ2MTA0,size_16,color_FFFFFF,t_70)
redis默认有16个数据库
cd /usr/local/bin
ps -ef|grep redis
ls
cd kconfig/
vim redis.conf 可以看到redis默认的数据库有16个
默认使用的是第0个数据库
可以使用select 进行数据库的切换
Redis 是单线程的!
明白Redis是很快的 官方表示,Redis是基于内存操作,cpu不是redis性能瓶颈,Reids的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就是用单线程来实现!
所以就使用了单线程!
Redis是c语言的 ,官方的数据为100000+的QPS,完全不比同样是使用的key-vale的Memecache差!
Redis为什么单线程还那么块?
1.误区:高性能的服务器 一定是单线程的?
2. 误区:多线程(CPU上下文切换!)一定比单线程的效率高!
3. CPU>内存>硬盘的速度要有所了解!!
4. 核心:redis是所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程的(cpu上下文会切换:耗时的操作!!),对于内存系统来说,如果没有上下文切换效率就是最高的!!多次读写都是在一个cpu上的,在内存情况下,这个就是最佳的方案!
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。
下面学习的命令:
127.0.0.1:6379> keys * # 查看当前数据库所有key
(empty list or set)
127.0.0.1:6379> set name qinjiang # set key
OK
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> move age 1 # 将键值对移动到指定数据库
(integer) 1
127.0.0.1:6379> EXISTS age # 判断键是否存在
(integer) 0 # 不存在
127.0.0.1:6379> EXISTS name
(integer) 1 # 存在
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> keys *
1) "age"
127.0.0.1:6379[1]> del age # 删除键值对
(integer) 1 # 删除个数
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> EXPIRE age 15 # 设置键值对的过期时间
(integer) 1 # 设置成功 开始计数
127.0.0.1:6379> ttl age # 查看key的过期剩余时间
(integer) 13
127.0.0.1:6379> ttl age
(integer) 11
127.0.0.1:6379> ttl age
(integer) 9
127.0.0.1:6379> ttl age
(integer) -2 # -2 表示key过期,-1表示key未设置过期时间
127.0.0.1:6379> get age # 过期的key 会被自动delete
(nil)
127.0.0.1:6379> keys *
"name"
127.0.0.1:6379> type name # 查看value的数据类型
string
普通的set、get直接略过。
常用命令及其示例:
APPEND key value: 向指定的key的value后追加字符串
127.0.0.1:6379> set msg hello
OK
127.0.0.1:6379> append msg " world"
(integer) 11
127.0.0.1:6379> get msg
“hello world”
DECR/INCR key: 将指定key的value数值进行+1/-1(仅对于数字)
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> incr age
(integer) 21
127.0.0.1:6379> decr age
(integer) 20
INCRBY/DECRBY key n: 按指定的步长对数值进行加减
127.0.0.1:6379> INCRBY age 5
(integer) 25
127.0.0.1:6379> DECRBY age 10
(integer) 15
INCRBYFLOAT key n: 为数值加上浮点型数值
127.0.0.1:6379> INCRBYFLOAT age 5.2
“20.2”
STRLEN key: 获取key保存值的字符串长度
127.0.0.1:6379> get msg
“hello world”
127.0.0.1:6379> STRLEN msg
(integer) 11
GETRANGE key start end: 按起止位置获取字符串(闭区间,起止位置都取)
127.0.0.1:6379> get msg
“hello world”
127.0.0.1:6379>GETRANGE msg 0 -1 #表示截取的全部值
127.0.0.1:6379> GETRANGE msg 3 9
“lo worl”
SETRANGE key offset value:用指定的value 替换key中 offset开始的值
127.0.0.1:6379> set msg hello
OK
127.0.0.1:6379> setrange msg 2 hello
(integer) 7
127.0.0.1:6379> get msg
"hehello"
127.0.0.1:6379> set msg2 world
OK
127.0.0.1:6379> setrange msg2 2 ww
(integer) 5
127.0.0.1:6379> get msg2
"wowwd"
GETSET key value: 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
127.0.0.1:6379> GETSET msg test
“hello world”
SETNX key value: 仅当key不存在时进行set
127.0.0.1:6379> SETNX msg test
(integer) 0
127.0.0.1:6379> SETNX name sakura
(integer) 1
SETEX key seconds value: set 键值对并设置过期时间
## setex(set with expire)# 设置过期的时间
# setnx(set if not exist )# 不存在再设置
127.0.0.1:6379> setex name 10 root
OK
127.0.0.1:6379> get name
(nil)
MSET key1 value1 [key2 value2…]: 批量set键值对
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3
OK
MSETNX key1 value1 [key2 value2…]: 批量设置键值对,仅当参数中所有的key都不存在时执行
127.0.0.1:6379> MSETNX k1 v1 k4 v4
(integer) 0
MGET key1 [key2…]: 批量获取多个key保存的值
127.0.0.1:6379> MGET k1 k2 k3
1) “v1”
2) “v2”
3) “v3”
PSETEX key milliseconds value: 和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间
String类似的使用场景:value除了是字符串还可以是数字,用途举例:
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
首先我们列表,可以经过规则定义将其变为队列、栈、双端队列等。
正如图Redis中List是可以进行双端操作的,所以命令也就分为了LXXX和RLLL两类,有时候L也表示List例如LLEN
rpoplpush # 移除列表的最后一个元素
lset 将列表中指定下标的值替换为另一个值,更新操作
将某个具体的value值插入到列中,你指定的元素的前后?
---------------------------LPUSH---RPUSH---LRANGE--------------------------------
127.0.0.1:6379> LPUSH mylist k1 # LPUSH mylist=>{1}
(integer) 1
127.0.0.1:6379> LPUSH mylist k2 # LPUSH mylist=>{2,1}
(integer) 2
127.0.0.1:6379> RPUSH mylist k3 # RPUSH mylist=>{2,1,3}
(integer) 3
127.0.0.1:6379> get mylist # 普通的get是无法获取list值的
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> LRANGE mylist 0 4 # LRANGE 获取起止位置范围内的元素
"k2"
"k1"
"k3"
127.0.0.1:6379> LRANGE mylist 0 2
"k2"
"k1"
"k3"
127.0.0.1:6379> LRANGE mylist 0 1
"k2"
"k1"
127.0.0.1:6379> LRANGE mylist 0 -1 # 获取全部元素
"k2"
"k1"
"k3"
---------------------------LPUSHX---RPUSHX-----------------------------------
127.0.0.1:6379> LPUSHX list v1 # list不存在 LPUSHX失败
(integer) 0
127.0.0.1:6379> LPUSHX list v1 v2
(integer) 0
127.0.0.1:6379> LPUSHX mylist k4 k5 # 向mylist中 左边 PUSH k4 k5
(integer) 5
127.0.0.1:6379> LRANGE mylist 0 -1
"k5"
"k4"
"k2"
"k1"
"k3"
---------------------------LINSERT--LLEN--LINDEX--LSET----------------------------
127.0.0.1:6379> LINSERT mylist after k2 ins_key1 # 在k2元素后 插入ins_key1
(integer) 6
127.0.0.1:6379> LRANGE mylist 0 -1
"k5"
"k4"
"k2"
"ins_key1"
"k1"
"k3"
127.0.0.1:6379> LLEN mylist # 查看mylist的长度
(integer) 6
127.0.0.1:6379> LINDEX mylist 3 # 获取下标为3的元素
"ins_key1"
127.0.0.1:6379> LINDEX mylist 0
"k5"
127.0.0.1:6379> LSET mylist 3 k6 # 将下标3的元素 set值为k6
OK
127.0.0.1:6379> LRANGE mylist 0 -1
"k5"
"k4"
"k2"
"k6"
"k1"
"k3"
---------------------------LPOP--RPOP--------------------------
127.0.0.1:6379> LPOP mylist # 左侧(头部)弹出
"k5"
127.0.0.1:6379> RPOP mylist # 右侧(尾部)弹出
"k3"
---------------------------RPOPLPUSH--------------------------
127.0.0.1:6379> LRANGE mylist 0 -1
"k4"
"k2"
"k6"
"k1"
127.0.0.1:6379> RPOPLPUSH mylist newlist # 将mylist的最后一个值(k1)弹出,加入到newlist的头部
"k1"
127.0.0.1:6379> LRANGE newlist 0 -1
"k1"
127.0.0.1:6379> LRANGE mylist 0 -1
"k4"
"k2"
"k6"
---------------------------LTRIM--------------------------
127.0.0.1:6379> LTRIM mylist 0 1 # 截取mylist中的 0~1部分
OK
127.0.0.1:6379> LRANGE mylist 0 -1
"k4"
"k2"
初始 mylist: k2,k2,k2,k2,k2,k2,k4,k2,k2,k2,k2
---------------------------LREM--------------------------
127.0.0.1:6379> LREM mylist 3 k2 # 从头部开始搜索 至多删除3个 k2
(integer) 3
删除后:mylist: k2,k2,k2,k4,k2,k2,k2,k2
127.0.0.1:6379> LREM mylist -2 k2 #从尾部开始搜索 至多删除2个 k2
(integer) 2
删除后:mylist: k2,k2,k2,k4,k2,k2
---------------------------BLPOP--BRPOP--------------------------
mylist: k2,k2,k2,k4,k2,k2
newlist: k1
127.0.0.1:6379> BLPOP newlist mylist 30 # 从newlist中弹出第一个值,mylist作为候选
"newlist" # 弹出
"k1"
127.0.0.1:6379> BLPOP newlist mylist 30
"mylist" # 由于newlist空了 从mylist中弹出
"k2"
127.0.0.1:6379> BLPOP newlist 30
(30.10s) # 超时了
127.0.0.1:6379> BLPOP newlist 30 # 我们连接另一个客户端向newlist中push了test, 阻塞被解决。
"newlist"
"test"
(12.54s)
小结
list实际上是一个链表,before Node after , left, right 都可以插入值
如果key不存在,则创建新的链表
如果key存在,新增内容
如果移除了所有值,空链表,也代表不存在
在两边插入或者改动值,效率最高!修改中间元素,效率相对较低
应用:
消息排队!消息队列(Lpush Rpop),栈(Lpush Lpop)
Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
---------------SADD--SCARD--SMEMBERS--SISMEMBER--------------------
127.0.0.1:6379> SADD myset m1 m2 m3 m4 # 向myset中增加成员 m1~m4
(integer) 4
127.0.0.1:6379> SCARD myset # 获取集合的成员数目
(integer) 4
127.0.0.1:6379> smembers myset # 获取集合中所有成员
"m4"
"m3"
"m2"
"m1"
127.0.0.1:6379> SISMEMBER myset m5 # 查询m5是否是myset的成员
(integer) 0 # 不是,返回0
127.0.0.1:6379> SISMEMBER myset m2
(integer) 1 # 是,返回1
127.0.0.1:6379> SISMEMBER myset m3
(integer) 1
---------------------SRANDMEMBER--SPOP----------------------------------
127.0.0.1:6379> SRANDMEMBER myset 3 # 随机返回3个成员
"m2"
"m3"
"m4"
127.0.0.1:6379> SRANDMEMBER myset # 随机返回1个成员
"m3"
127.0.0.1:6379> SPOP myset 2 # 随机移除并返回2个成员
"m1"
"m4"
将set还原到{m1,m2,m3,m4}
---------------------SMOVE--SREM----------------------------------------
127.0.0.1:6379> SMOVE myset newset m3 # 将myset中m3成员移动到newset集合
(integer) 1
127.0.0.1:6379> SMEMBERS myset
"m4"
"m2"
"m1"
127.0.0.1:6379> SMEMBERS newset
"m3"
127.0.0.1:6379> SREM newset m3 # 从newset中移除m3元素
(integer) 1
127.0.0.1:6379> SMEMBERS newset
(empty list or set)
下面开始是多集合操作,多集合操作中若只有一个参数默认和自身进行运算
setx=>{m1,m2,m4,m6}, sety=>{m2,m5,m6}, setz=>{m1,m3,m6}
-----------------------------SDIFF------------------------------------
127.0.0.1:6379> SDIFF setx sety setz # 等价于setx-sety-setz
差集
"m4"
127.0.0.1:6379> SDIFF setx sety # setx - sety
"m4"
"m1"
127.0.0.1:6379> SDIFF sety setx # sety - setx
"m5"
-------------------------SINTER---------------------------------------
共同关注(交集)
127.0.0.1:6379> SINTER setx sety setz # 求 setx、sety、setx的交集
"m6"
127.0.0.1:6379> SINTER setx sety # 求setx sety的交集
"m2"
"m6"
-------------------------SUNION---------------------------------------
127.0.0.1:6379> SUNION setx sety setz # setx sety setz的并集
"m4"
"m6"
"m3"
"m2"
"m1"
"m5"
127.0.0.1:6379> SUNION setx sety # setx sety 并集
"m4"
"m6"
"m2"
"m1"
"m5"
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Set就是一种简化的Hash,只变动key,而value使用默认值填充。可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。
HSET key field value 将哈希表 key 中的字段 field 的值设为 value 。重复设置同一个field会覆盖,返回0
HMSET key field1 value1 [field2 value2…] 同时将多个 field-value (域-值)对设置到哈希表 key 中。
HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值。
HEXISTS key field 查看哈希表 key 中,指定的字段是否存在。
HGET key field value 获取存储在哈希表中指定字段的值
HMGET key field1 [field2…] 获取所有给定字段的值
HGETALL key 获取在哈希表key 的所有字段和值
HKEYS key 获取哈希表key中所有的字段
HLEN key 获取哈希表中字段的数量
HVALS key 获取哈希表中所有值
HDEL key field1 [field2…] 删除哈希表key中一个/多个field字段
HINCRBY key field n 为哈希表 key 中的指定字段的整数值加上增量n,并返回增量后结果 一样只适用于整数型字段
HINCRBYFLOAT key field n 为哈希表 key 中的指定字段的浮点数值加上增量 n。
HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对。
代码示例
删除指定的key字段!对应的value值也就消失了!
获取hash表字段的数量
判断hash中的指定的字段是否存在?
#只获得所有的filed
#获得所有的value
指定增量以及减少量 以及如果不存在时设置
hash变更的数据 user name age ,尤其是用户之类的,经常变动的信息!hash更加适合于对象的存储,String更佳适合字符串的存储!
------------------------HSET--HMSET--HSETNX----------------
127.0.0.1:6379> HSET studentx name sakura # 将studentx哈希表作为一个对象,设置name为sakura
(integer) 1
127.0.0.1:6379> HSET studentx name gyc # 重复设置field进行覆盖,并返回0
(integer) 0
127.0.0.1:6379> HSET studentx age 20 # 设置studentx的age为20
(integer) 1
127.0.0.1:6379> HMSET studentx sex 1 tel 15623667886 # 设置sex为1,tel为15623667886
OK
127.0.0.1:6379> HSETNX studentx name gyc # HSETNX 设置已存在的field
(integer) 0 # 失败
127.0.0.1:6379> HSETNX studentx email 12345@qq.com
(integer) 1 # 成功
----------------------HEXISTS--------------------------------
127.0.0.1:6379> HEXISTS studentx name # name字段在studentx中是否存在
(integer) 1 # 存在
127.0.0.1:6379> HEXISTS studentx addr
(integer) 0 # 不存在
-------------------HGET--HMGET--HGETALL-----------
127.0.0.1:6379> HGET studentx name # 获取studentx中name字段的value
"gyc"
127.0.0.1:6379> HMGET studentx name age tel # 获取studentx中name、age、tel字段的value
"gyc"
"20"
"15623667886"
127.0.0.1:6379> HGETALL studentx # 获取studentx中所有的field及其value
"name"
"gyc"
"age"
"20"
"sex"
"1"
"tel"
"15623667886"
"email"
"[email protected]"
--------------------HKEYS--HLEN--HVALS--------------
127.0.0.1:6379> HKEYS studentx # 查看studentx中所有的field
"name"
"age"
"sex"
"tel"
"email"
127.0.0.1:6379> HLEN studentx # 查看studentx中的字段数量
(integer) 5
127.0.0.1:6379> HVALS studentx # 查看studentx中所有的value
"gyc"
"20"
"1"
"15623667886"
"[email protected]"
-------------------------HDEL--------------------------
127.0.0.1:6379> HDEL studentx sex tel # 删除studentx 中的sex、tel字段
(integer) 2
127.0.0.1:6379> HKEYS studentx
"name"
"age"
"email"
-------------HINCRBY--HINCRBYFLOAT------------------------
127.0.0.1:6379> HINCRBY studentx age 1 # studentx的age字段数值+1
(integer) 21
127.0.0.1:6379> HINCRBY studentx name 1 # 非整数字型字段不可用
(error) ERR hash value is not an integer
127.0.0.1:6379> HINCRBYFLOAT studentx weight 0.6 # weight字段增加0.6
"90.8"
不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。
score相同:按字典顺序排序
有序集合的成员是唯一的,但分数(score)却可以重复
倒序列
127.0.0.1:6379> zrevrangebyscore salary +inf -inf withscores
1) "zhangsan"
2) "5000"
3) "xiaohong"
4) "2500"
5) "kuangshen"
6) "500"
正序
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores
1) "kuangshen"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"
移除rem中的元素 ,移除有序集合中指定的元素的/获取有序集合中 的个数
获取指定区间的成员变量!
其余的一些Api,通过饿哦们的学习,你们剩下的如果工作中有需要,这个时候,你们可以查官方文档!!
案列思路:set排序 存储班级成绩表,工资表排序
普通消息,1.重要消息2.带权重进行判断!
排行榜应用实现
-------------------ZADD--ZCARD--ZCOUNT--------------
127.0.0.1:6379> ZADD myzset 1 m1 2 m2 3 m3 # 向有序集合myzset中添加成员m1 score=1 以及成员m2 score=2..
(integer) 2
127.0.0.1:6379> ZCARD myzset # 获取有序集合的成员数
(integer) 2
127.0.0.1:6379> ZCOUNT myzset 0 1 # 获取score在 [0,1]区间的成员数量
(integer) 1
127.0.0.1:6379> ZCOUNT myzset 0 2
(integer) 2
----------------ZINCRBY--ZSCORE--------------------------
127.0.0.1:6379> ZINCRBY myzset 5 m2 # 将成员m2的score +5
"7"
127.0.0.1:6379> ZSCORE myzset m1 # 获取成员m1的score
"1"
127.0.0.1:6379> ZSCORE myzset m2
"7"
--------------ZRANK--ZRANGE-----------------------------------
127.0.0.1:6379> ZRANK myzset m1 # 获取成员m1的索引,索引按照score排序,score相同索引值按字典顺序顺序增加
(integer) 0
127.0.0.1:6379> ZRANK myzset m2
(integer) 2
127.0.0.1:6379> ZRANGE myzset 0 1 # 获取索引在 0~1的成员
"m1"
"m3"
127.0.0.1:6379> ZRANGE myzset 0 -1 # 获取全部成员
"m1"
"m3"
"m2"
testset=>{abc,add,amaze,apple,back,java,redis} score均为0
------------------ZRANGEBYLEX---------------------------------
127.0.0.1:6379> ZRANGEBYLEX testset - + # 返回所有成员
"abc"
"add"
"amaze"
"apple"
"back"
"java"
"redis"
127.0.0.1:6379> ZRANGEBYLEX testset - + LIMIT 0 3 # 分页 按索引显示查询结果的 0,1,2条记录
"abc"
"add"
"amaze"
127.0.0.1:6379> ZRANGEBYLEX testset - + LIMIT 3 3 # 显示 3,4,5条记录
"apple"
"back"
"java"
127.0.0.1:6379> ZRANGEBYLEX testset (- [apple # 显示 (-,apple] 区间内的成员
"abc"
"add"
"amaze"
"apple"
127.0.0.1:6379> ZRANGEBYLEX testset [apple [java # 显示 [apple,java]字典区间的成员
"apple"
"back"
"java"
-----------------------ZRANGEBYSCORE---------------------
127.0.0.1:6379> ZRANGEBYSCORE myzset 1 10 # 返回score在 [1,10]之间的的成员
"m1"
"m3"
"m2"
127.0.0.1:6379> ZRANGEBYSCORE myzset 1 5
"m1"
"m3"
--------------------ZLEXCOUNT-----------------------------
127.0.0.1:6379> ZLEXCOUNT testset - +
(integer) 7
127.0.0.1:6379> ZLEXCOUNT testset [apple [java
(integer) 3
------------------ZREM--ZREMRANGEBYLEX--ZREMRANGBYRANK--ZREMRANGEBYSCORE--------------------------------
127.0.0.1:6379> ZREM testset abc # 移除成员abc
(integer) 1
127.0.0.1:6379> ZREMRANGEBYLEX testset [apple [java # 移除字典区间[apple,java]中的所有成员
(integer) 3
127.0.0.1:6379> ZREMRANGEBYRANK testset 0 1 # 移除排名0~1的所有成员
(integer) 2
127.0.0.1:6379> ZREMRANGEBYSCORE myzset 0 3 # 移除score在 [0,3]的成员
(integer) 2
testset=> {abc,add,apple,amaze,back,java,redis} score均为0
myzset=> {(m1,1),(m2,2),(m3,3),(m4,4),(m7,7),(m9,9)}
----------------ZREVRANGE--ZREVRANGEBYSCORE--ZREVRANGEBYLEX-----------
127.0.0.1:6379> ZREVRANGE myzset 0 3 # 按score递减排序,然后按索引,返回结果的 0~3
"m9"
"m7"
"m4"
"m3"
127.0.0.1:6379> ZREVRANGE myzset 2 4 # 返回排序结果的 索引的2~4
"m4"
"m3"
"m2"
127.0.0.1:6379> ZREVRANGEBYSCORE myzset 6 2 # 按score递减顺序 返回集合中分数在[2,6]之间的成员
"m4"
"m3"
"m2"
127.0.0.1:6379> ZREVRANGEBYLEX testset [java (add # 按字典倒序 返回集合中(add,java]字典区间的成员
"java"
"back"
"apple"
"amaze"
-------------------------ZREVRANK------------------------------
127.0.0.1:6379> ZREVRANK myzset m7 # 按score递减顺序,返回成员m7索引
(integer) 1
127.0.0.1:6379> ZREVRANK myzset m2
(integer) 4
mathscore=>{(xm,90),(xh,95),(xg,87)} 小明、小红、小刚的数学成绩
enscore=>{(xm,70),(xh,93),(xg,90)} 小明、小红、小刚的英语成绩
-------------------ZINTERSTORE--ZUNIONSTORE-----------------------------------
127.0.0.1:6379> ZINTERSTORE sumscore 2 mathscore enscore # 将mathscore enscore进行合并 结果存放到sumscore
(integer) 3
127.0.0.1:6379> ZRANGE sumscore 0 -1 withscores # 合并后的score是之前集合中所有score的和
"xm"
"160"
"xg"
"177"
"xh"
"188"
127.0.0.1:6379> ZUNIONSTORE lowestscore 2 mathscore enscore AGGREGATE MIN # 取两个集合的成员score最小值作为结果的
(integer) 3
127.0.0.1:6379> ZRANGE lowestscore 0 -1 withscores
"xm"
"70"
"xg"
"87"
"xh"
"93"
可以推算地理位置的信息,两地之间的距离,方圆几公里之内的人
需要注意:
可以查询一些测试数据:http://www.jsons.cn/lngcodeinfo/0706D99C19A781A3
官方文档:https://www.redis.net.cn/order/3685.html
getadd
#getadd 添加地理位置
# 规则:两极无法直接添加 我们一般的会下载城市数据 直接通过Java程序一次性导入
# 有效的精度从-180度到180度
# 有效的的维度从-85.05112878度到85.05112878
# 参数key 值(维度,精度,名称)
127.0.0.1.6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1.6379> geoadd china:city 121.47 31.23 shanghai
(integer)1
127.0.0.1.6379> geoadd china::city 106.50 29.53 chongqing 114.05 22.52 shenzheng
(integer) 2
127.0.0.1.6379>geopos china:city beijing#获取指定城市的精度和维度
。。。。
127.0.0.1.6379>geopos china:city beijing chongqing
。。。。
127.0.0.1:6379> geodist key:city beijing shanghai
"1088645.3557"
127.0.0.1:6379> geodist key:city beijing shanghai km
"1088.6454"
127.0.0.1:6379>
两个人之间的距离!!
单位
** georadius 以给定的经纬度为中心**,找出某一半径内的元素
我附近的人?(获取所有附近的人的地址,定位!)通过半径来查询!
获得指定的人数,200
所有数据应该都录入:china:city,才会让结果更加请求!!
127.0.0.1:6379> georadius china:city 110 30 1000km #以110 30这个维度为中心。寻求方圆1000米的城市
127.0.0.1:6379> georadius china:city 110 30 500km withdist #显示到中心距离的位置。
127.0.0.1:6379> georadius china:city 110 30 500km withcoord #显示他人的定位信息
127.0.0.1:6379> georadius china:city 110 30 500km withcoord count 1 #筛选出指定的结果
Georadiusbymember 获取指定范围的城市
这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点
指定成员的位置被用作查询的中心。
关于 GEORADIUSBYMEMBER 命令的更多信息, 请参考 GEORADIUS 命令的文档。
例子
# 找出指定元素周围的元素
georadiusbymember china:city beijing 1000km
georadiusbymember china:city shanghai 400km
该命令将返回11个字符串的Geohash字符串
# 将二维的经纬度转换为一维的字符串,如果两个字符串越来越接近,那么则距离越接近
127.0.0.1:6379> geohash china:city beijing chongqing
`
127.0.0.1:6379> zrange china:city 0 -1 #查看地图中所有全部的元素
127.0.0.1:6379> zrem china:city beijing #移除指定的元素!!
什么是基数
A{1,3,5.7,8,7}
B{1,3,5,7,8}
基数( 不重复的元素 )=5 可以接受误差
简介
是一种概率数据结构,计数唯一事物,从技术上讲估计一个集合的基数,通常
计数唯一项需要使用成比例的内存,因为需要记录使用过的元素,以免多次记录,但是hyperloglog的算法可以用内存换精度,虽然有误差,但是误差小于1%,算法的神奇之处在于只需要很小的内存,最大也不超过12k,类似集合的功能,能记录2^64的计数,从内存的角度来说,hyperloglog是首选能用在网页的UV
127.0.0.1:6379>Pfadd mykey a b c d e f g h i j #添加一些元素
127.0.0.1:6379>pfcount mykey #统计元素个数
127.0.0.1:6379>Pfadd mykey f g s d f d k j #添加一些元素
127.0.0.1:6379>pfmergm mykey3 mykey mykey2 #合并mykey和mykey2
如果允许容错,那么一定可以Hyperloglog
如果不允许容错,就使用set或者自己的数据类型即可
¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
位存储
统计用户信息,活跃,不活跃!登录,未登录!打卡,365打卡!都可以使用bitmaps!bitmaps位图,数据结构!都是操作二进制位来进行记录,即使只有0和1两个状态
365天=365bit 1字节=8bit 46个字节左右!
使用bitmap来记录周一到周日的打卡
周一:1 周二 :0 周三:0 周四:1.。。
Redis单条命令保存原子性的,但是事务不保证原子性!!
Redis事物本质:一组命令的集合!一个事物中所有的的命令都会被序列化,在事务执行的过程中,会按照执行的顺序执行!!
一次性 顺序性 排他性 执行一些列的命令!
-- 队列 set set set 执行 --
redis事务的没有隔离级别的概念!
所有的命令在事务的中,并没有直接执行!只有发起执行命令的时候才会执行!
== redis单条事务命令式保存原子性的,但是事务不保存原子性==、
redis的事务:
127.0.0.1:6379> multi
127.0.0.1:6379>set k1 v1
127.0.0.1:6379>set k2 v2
127.0.0.1:6379>set k4 v4
127.0.0.1:6379>discard #取消事务
127.0.0.1:6379>get k4 #队列中的事务都不会执行!
悲观锁:
正常执行
127.0.0.1:6379>set money 100
127.0.0.1:6379>set out 0 #花出去0元
127.0.0.1:6379>watch money #监视money
127.0.0.1:6379> multi #开启事务
127.0.0.1:6379>decrby money 20 这边消费了20
127.0.0.1:6379>incrby out 20 那边加20
127.0.0.1:6379>exec
测试多线程修改值,使用watch可以当作redis的乐观操作!!
127.0.0.1:6379> watch money #监视 money
OK
127.0.0.1:6379> decrby money 10
(integer) 70
127.0.0.1:6379> incrby money 10
(integer) 80
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec 执行之前,另外一个线程修改了我们的值,这个时候,就会导致事务执行失败
(nil)
我们要使用java来操作Redis
什么是jedis 是redis官方推荐的java连接开发工具!使用java操作Redis中间件!如果你要使用java操作redis,那么就要对jedis十分的熟练!
测试
1.导入对应的依赖
<dependencies>
<!--导入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.76</version>
</dependency>
</dependencies>
2.编码测试
package conn.kuang;
import redis.clients.jedis.Jedis;
public class TestPing {
public static void main(String[] args){
//1.new jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
//jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
System.out.println(jedis.ping());
}
}
输出:
"C:\Program Files\jdk11\jdk-11.0.6\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=57410:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\day16\kuang-redis\target\classes;D:\mvnjar\redis\clients\jedis\3.2.0\jedis-3.2.0.jar;D:\mvnjar\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\mvnjar\org\apache\commons\commons-pool2\2.6.2\commons-pool2-2.6.2.jar;D:\mvnjar\com\alibaba\fastjson\1.2.76\fastjson-1.2.76.jar conn.kuang.TestPing
PONG
Process finished with exit code 0
package conn.kuang;
import redis.clients.jedis.Jedis;
import java.util.Set;
public class TestPing {
public static void main(String[] args){
//1.new jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
//jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
System.out.println(jedis.ping());
System.out.println("清空数据:"+jedis.flushDB());
System.out.println("判断某个键值是否存在:"+jedis.exists("username"));
System.out.println("新增的<'username','kuangshen'>的键值对:"+jedis.set("username","kuangshen"));
System.out.println("新增的<'password','password'>的键值对:"+jedis.set("password","password"));
System.out.println("系统中所有的 键如下");
Set<String> keys = jedis.keys("*");
System.out.println(keys);
System.out.println("删除键password:"+jedis.del("password"));
System.out.println("判断键password是否存在"+jedis.exists("password"));
System.out.println("查看键username所存储的值的类型"+jedis.type("username"));
System.out.println("随机返回key空间的一个"+jedis.randomKey());
System.out.println("重命名key:"+jedis.rename("username","name" ));
System.out.println("取出改后的name:"+jedis.get("name"));
System.out.println("搜索引查询"+jedis.select(0));
System.out.println("返回当前选择数据库中的所有的key的数目:"+jedis.dbSize());
System.out.println("删除所有数据库中的key"+jedis.flushAll());
}
}
package conn.kuang;
import redis.clients.jedis.Jedis;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class TestPing2 {
public static void main(String[] args){
//1.new jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
//jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
jedis.flushDB();
System.out.println("======增加数据======");
System.out.println(jedis.set("key1","value1"));
System.out.println(jedis.set("key2","value2"));
System.out.println(jedis.set("key3","value3"));
System.out.println("删除键key2:"+jedis.del("key2"));
System.out.println("获取键key2:"+jedis.get("key2"));
System.out.println("修改key1:"+jedis.set("key1","valueChanged"));
System.out.println("获取key1的值:"+jedis.get("key1"));
System.out.println("在key3后面加入值"+jedis.append("key3","end"));
System.out.println("key3的值"+jedis.get("key3"));
System.out.println("增加多个键值对:"+jedis.mset("key01","value01","key02","value02","key03","value03"));
System.out.println("获取多个键值对"+jedis.mget("key01","key02","key03"));
System.out.println("删除多个键值对"+jedis.del("key01","key02"));
System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
jedis.flushDB();
System.out.println("======新增键值对防止覆盖原先的值======");
System.out.println(jedis.setnx("key1","value1"));
System.out.println(jedis.setnx("key2","value2"));
System.out.println(jedis.setnx("key2","value-new"));
System.out.println(jedis.get("key1"));
System.out.println(jedis.get("key2"));
System.out.println("===========新增键值对并设置有效时间==================");
System.out.println(jedis.setex("key3",2,"value3"));
System.out.println(jedis.get("key3"));
try {
TimeUnit.SECONDS.sleep(3);
}
catch(Exception e){
e.printStackTrace();
}
System.out.println(jedis.get("key3"));
System.out.println("=======获取值,更新为新的值============");
System.out.println(jedis.getSet("key2","key2GetSet"));
System.out.println(jedis.get("key2"));
System.out.println("获得key2的值的字符串" +jedis.getrange("key2",2,4)) ;
}
}
package conn.kuang;
import redis.clients.jedis.Jedis;
import java.util.concurrent.TimeUnit;
public class TestPing3List {
public static void main(String[] args) {
//1.new jedis 对象即可
Jedis jedis = new Jedis(“127.0.0.1”, 6379);
//jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
jedis.flushDB();
System.out.println(“添加一个list=”);
jedis.lpush(“collections”,“ArrayList”,“vector”,“stack”,“hashMap”,“weakHashMap”,“linkList”);
jedis.lpush(“collections”,“HashSet”);
jedis.lpush(“collections”,“ThreeSet”);
jedis.lpush(“collections”,“TreeMap”);
System.out.println(“collections的内容:”+jedis.lrange(“collections”,0,-1));
System.out.println("========");
//删除列表指定的值。第二个参数为删除的个数(有重复时),后add进去的值先被删除,类似于出栈
System.out.println(“删除指定元素的个数:”+jedis.lrem(“collections”,2,“hashMap”));
System.out.println(“collections的内容”+jedis.lrange(“collections”,0,-1));
System.out.println(“删除下表0-3区间之外的元素”+jedis.ltrim(“collections”,0,3));
System.out.println(“collections的内容”+jedis.lrange(“collections”,0,-1));
System.out.println(“collections列表出栈(左端)”+jedis.lpop(“collections”));
System.out.println(“collections的内容”+jedis.lrange(“collections”,0,-1));
System.out.println(“collections 添加元素从列表的右端,与lpush相对应”+jedis.rpush(“collections”,“hashput”));
System.out.println(“collections的内容”+jedis.lrange(“collections”,0,-1));
System.out.println(“修改collections指定下标的内容”+jedis.lset(“collections”,1,“LinkedList”));
System.out.println(" collections的内容"+jedis.lrange(“collections”,0,-1));
System.out.println("");
System.out.println("collections的长度"+jedis.llen("collections"));
System.out.println("获取collections下标为二的的元素"+jedis.lindex("collections",2));
System.out.println("=================");
jedis.lpush("sortedList", "3","2","9","8","4","6");
System.out.println("排序前:"+jedis.lrange("sortedList",0,-1));
System.out.println(jedis.sort("sortedList"));
System.out.println("排序后"+jedis.lrange("sortedList",0,-1));
}
}
package conn.kuang;
import redis.clients.jedis.Jedis;
public class TestPing3Set {
public static void main(String[] args) {
//1.new jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1", 6379);
//jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
jedis.flushDB();
System.out.println("===============向集合中添加元素(不重复)================");
System.out.println(jedis.sadd("eleSet","1","2","3","4","5","6"));
System.out.println(jedis.sadd("e6"));
System.out.println(jedis.sadd("e6"));
System.out.println("eleSet的所有的元素为"+jedis.smembers("eleSet"));
System.out.println("删除一个元素1"+jedis.srem("elsSet","1"));
System.out.println("eleSet的所有的元素为"+jedis.smembers("eleSet"));
System.out.println("删除俩个元素"+jedis.srem("elsSet","2","3"));
System.out.println("eleSet的所有的元素为"+jedis.smembers("eleSet"));
System.out.println("随机的移除一个元素"+jedis.spop("eleSet"));
System.out.println("随机的移除一个元素"+jedis.spop("eleSet"));
System.out.println("eleSet的所有的元素为"+jedis.smembers("sleSet"));
System.out.println("eleSet包含元素的个数"+jedis.scard("eleSet"));
System.out.println("3是否在eleSet中"+jedis.sismember("sleSet","3"));
System.out.println("2是否在eleSet中"+jedis.sismember("sleSet","2"));
System.out.println("4是否在eleSet中"+jedis.sismember("sleSet","4"));
System.out.println("eleSet的所有的元素为"+jedis.smembers("sleSet"));
System.out.println("=============================================");
System.out.println(jedis.sadd("eleSet1","e1","e2","e3","e4","e5","e6"));
System.out.println(jedis.sadd("eleSet2","e1","e2","e3","e0","e5","e8"));
System.out.println("将eleSet1中删除e1并且存入eleSet3中:"+jedis.smove("eleSet1","eleSet3","5"));
System.out.println("将eleSet1中删除e2并且存入eleSet3中:"+jedis.smove("eleSet2","eleSet3","2"));
System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
System.out.println("eleSet3中的元素:"+jedis.smembers("eleSet3"));
System.out.println("=============集合运算=======================");
System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
System.out.println("eleSet2中的元素:"+jedis.smembers("eleSet2"));
System.out.println("eleSet1和eleSet2之间的交集"+jedis.sinter("eleSet1","eleSet2"));
System.out.println("eleSet3和eleSet2之间的并集"+jedis.sunion("eleSet3","eleSet2"));
System.out.println("eleSet3和eleSet2之间的差集"+jedis.sdiff("eleSet3","eleSet2"));
jedis.sinterstore("eleSet4","eleSet1","eleSet2");
System.out.println("eleSet4中的元素:"+jedis.smembers("eleSet4"));
}
}
package conn.kuang;
import redis.clients.jedis.Jedis;
import java.util.HashMap;
public class TestPing3Hash {
public static void main(String[] args) {
//1.new jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1", 6379);
//jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
jedis.flushDB();
HashMap<String, String> map = new HashMap<>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
map.put("key4","value4");
//添加名称hash(key)的hash元素
jedis.hmset("hash",map);
//向名称为hash的hash中添加key为key5 value为value5元素
jedis.hset("hash","key5","value5");
System.out.println("数列hash所有的键值为"+jedis.hgetAll("hash"));
System.out.println("散列的所有的值为"+jedis.hvals("hash"));
System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6"+jedis.hincrBy("hash","key6", 2));
System.out.println("散列hash的所有的键值对为"+jedis.hgetAll("hash"));
System.out.println("删除一个或者多个键值对"+jedis.hdel("hash","key2"));
System.out.println("散列的所有的键值对为"+jedis.hgetAll("hash"));
System.out.println("散列hash所有的键值对的个数为"+jedis.hlen("hash"));
System.out.println("判断hash中是否存在key2"+jedis.hexists("hash","key2"));
System.out.println("判断hash中是否在key3"+jedis.hexists("hash","key3"));
System.out.println("获取hash中是否存在key3"+jedis.hmget("hash","key3"));
System.out.println("获取hash中是否存在key3"+jedis.hmget("hash","key3","key4"));
System.out.println("");
System.out.println("");
}
}
package conn.kuang;
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class TestTX {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello", "world");
jsonObject.put("name", "kuangshen");
//开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
try {
multi.set("user1", result);
multi.set("user2", result);
int i=1/0;//代码抛出异常事务执行失败
multi.exec();//执行事务
}catch(Exception e){
multi.discard();//放弃事务
e.printStackTrace();
}finally{
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();//关闭连接
}
}
}
@Bean
@ConditionalOnMissingBean(name = {"redisTemplate"}) // 我们可以自己定一个redisTemplate来替换这个默认的!
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
// 默认的RedisTemplate没有过多的设置,redis对象都是需要序列化的
// 两个泛型都是Object,Object类型的,我们后续使用需要强制转换成<String,Object>
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
整合测试一哈
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
#配置redis
spring.redis.host=127.0.0.1
sping.redis.port=6379
3.测试!!!
package com.kuang;
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.core.RedisTemplate;
@SpringBootTest
class RedisSpringboot02ApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//redisTemplate
// opsForValue 操作字符串 类似 String
//opsForList 操作List 类似list
//opsForSet
//opsForHash
//opsForZSet
// opsForGeo
//除了基本的操作,我们常用的方法可以都可以直接通过redisTemplate操作,比如事务。和基本的CRUD
//获取连接对象
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// connection.flushDb();
// connection.flushAll();
redisTemplate.opsForValue().set("mykey","kuangshenshuojava");
System.out.println(redisTemplate.opsForValue().get("mykey"));
}
}
package com.kuang.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 {
//这个是我给大家写好的一个固定的模板,大家在企业中,拿去就可以使用
//编写我们自己的 redisTemplate
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> stringRedisTemplate(RedisConnectionFactory factory) {
//为了我们自己使用开发方便,一般直接使用《String,object》
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
//json序列化配置
Jackson2JsonRedisSerializer<Object> oJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om=new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//String的序列化配置
StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
//key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
//hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value序列化方式采用jackson
template.setValueSerializer(oJackson2JsonRedisSerializer);
//hash的value序列化方式采用jackson
template.setHashKeySerializer(oJackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
包含
网络
bind 127.0.0.1 #绑定的ip
protected-mode yes #保护模式
port 6379 #端口设置
通用GENERAL
daemonize yes # 以守护进程的方式运行,默认是no 我们需要自己开启为yes!
pidfile /var/run/redis_6379.pid # 如果以后台的方式运行,我们就需要指定一个pid文件!!
#日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)生产环境
# warning (only very important / critical messages are logged)警告
loglevel notice
logfile "" #日志的文件位置
database 16 #数据库的数量 默认是16个数据库
always-show-logo yes #是否总是显示log
快照
持久化 在规定的时间内,执行了多少次操作,则会持久化到文件 rdb.aof
redis 是内存数据库,如果没有持久化,那么数据断电即失
#如果 900秒内至少一个1key 进行了修改,我们即进行持久化操作
save 900 1
# 如果300秒内至少100个key 进行了修改,我们即进行持久化操作
save 300 10
# 如果60秒内至少10000个key 进行了修改,我们即进行持久化操作
save 60 10000
#我们之后学习持久化,会自己定义这个测试
stop-writes-on-bgsave-error yes # 持久化 如果出错,redis是否还继续工作(持久化失败后是否停止写入操作)
rdbcompression yes #是否压缩rdb 文件,需要消耗一些cpu资源
rdbchecksum yes #保存rdb文件的时候,进行错误的检查效验!
dir ./#rdb文件保存的目录
REPLICATION复制,我们面讲解主从复制的时候再进行解析
security 安全性
可以在这里设置redis的密码,默认是没有密码
127.0.0.1:6379> config set requirepass "123456" #设置redis的密码
127.0.0.1:6379> config get requirepass #获取redis的密码
1) "requirepass"
2) "123456"
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> auth 123456 #登录使用密码
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"
限制 CL TENTS
maxclients 10000 #设置能连接上redis 的最打客户端的数量
maxmemory <bytes>#redis 配置最大的内存数量
maxmemory-policy noeviction #内存达到上限之后的处理
1. volatile-lru:只对设置了过期时间的key进行Lru(默认值)
2. allkeys-lru :删除lru算法的key
3. volatile-random:随机删除即将过期的key
4. allkeys-random:随机删除
5. volatile-ttl:删除即将过期的
6. noevition:永不过期,返回错误
APPEND ONLY 模式 aof配置
appendonly no #默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下 rdb够用
appendfilename "appendonly.aof" #持久化的文件的名字
# appendfsync always #每次修改都会 sync 消耗性能
appendfsync everysec #每秒执行一次sync 可能会丢失这ls的数据
# appendfsync no #不执行 sync 这个时候操作系统自己同步数据,速度最快!
具体的配置,在redis的持久化 中 演示
Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以Redis提供了持久化功能!
触发机制
如何恢复rdb文件!!
0.0.1:6379>config get dir
1)"dir"
2)"/usr/local/bin" #如果在这个目录下存在 dump.rdb文件 ,启动就会自动的恢复其中的数据
几乎就他自己默认的配置就足够用了,但是我们还是需要去学习!!
优点:
1.适用大规模的数据恢复
2.对数据的完整性要求不高!
缺点:
将我们的所有的命令都记录下来。history,恢复的时候就是把这个文件全部执行一遍!!!
以日志的形式来记录每个写操作,将Redis执行过的所有的命令记录下来(读操作不记录),只许追加的文件但不许改写文件,redis启动初会读取该配置文件重新构造数据,换言之,redis重启的话就会根据日志文件的内容将写的指令从前到后执行一次完成数据的恢复。
Aop保存的是appendonly.aof文件
append
默认是不开启的,需要手动配置!我们只需要将appendonly改为yes就开启了aof
重启,redis就生效了!
如果这个aof文件有错位,这个时候redsi是启动不起来的,我们需要修复这个aof文件
redis 给我们提供了一个工具 redis-check-aof–fix
此处我故意使用 vim appendonly.aof 来破坏了这个文件
修复命令
重写规则说明
aof默认就是文件的无限追加,文件就会越来越大!!
如果aof文件大于64mb ,打打了! fork一个新的进程,来将我们的文件进行重写!
优点和缺点
优点:
通信 队列
Redis发部订阅(pub/sub)是一种通信模式模式:发送者(pub)发送消息,订阅者(sub)接受消息。
Redis客户端可以订阅任意数量的频道
订阅/发部消息图:
第一个:消息发送者,第二个:频道 第三个:消息订阅者!!
下图展示了频道的channel1,以及订阅这个频道的三个客户端 client2 client5 和client1之间的关系:
当新的消息通过publish命令发送给频道channel1时,这个消息就会被发送给订阅他的三个客户端:
命令
这些命令被广泛 用于构建即时通信应用的,比如网络聊天室(chatroom)和实时广播,实时提醒等。
测试
订阅端
127.0.0.1:6379> subscribe kuangshen #订阅一个频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "kuangshen"
3) (integer) 1
#等待读取推送的信息
1) "message"
2) "kuangshen"# 哪个频道的消息
3) "nihao"# 频道发消息的内容
发送端
127.0.0.1:6379> publish kuangshen "nihao" #发送者 发部消息到频道
(integer) 1
127.0.0.1:6379>
原理
使用场景:
主从复制。是指的将一台Redis服务的数据,复制到其他的Redis服务器,前者称为u主节点(master/leader),后者称为从节点(slace/follower);数据的复制是单向的,只能由从节点到从节节点 Master以写为u主 ,slave以读为主
默认情况下,每台Redis服务器都是主节点
况且一个主节点可以有多个从节点(或者没有从节点)但是一个从节点只能有一个主节点,(类似于一个爸爸只能有两个亲儿子,而不能一个儿子有两个亲爹)
主从复制的作用主要包括:
一般来说:要将Redis运用于工程项目中,只使用一台Redis是不可能的:原因如下:
主从复制,读写分离!百分之八十的情况下都是在进行读取操作!减缓服务器的压力!!框架中经常使用!!一主二从!!
只要在公司中,主从复制是必须使用的,因为项目中不可能单机使用redis
只配置从库,,不用配置主库!!
复制3个配置文件,然后修改对应的信息
默认的情况下,每台redis服务器都i是主节点我们一般情况下只用配置从机就好了!
认老大!一主(79)二从(80,81)
第二步认老大
真实的主从配置应该是在配置文件中配置,这样的化是永久的,我们这里使用的命令,是暂时的
测试:主机断开连接,从主机旧连接到主的,但是没有写操作,这个时主机如果回来了,从机依旧可以直接获取到主机写的信息!!
如果是使用命令行,来配置的主从。这个 时候从机如果重启了,就会变回主机!!!!只要变回从机,立刻就会从主机获取值!
复制原理
层层链路
如果没有老大了,这个时候能不能选择一个老大出来? 手动
谋朝篡位
如果主机断开了连接,我们可以使用slaveof no one 让自己变成主机!!其他的节点就可以手动的连接到最新的这个主节点(手动)!如果这个老大修复了,那就重新连接(重新认老大)
哨兵模式(自动选取老大的的模式)
(自动选取老大的模式)
概述
主从切换技术的方法是:当主服务宕机后。需要手动的把一台服务器切换为一台主服务器,这就需要人工的干预,费时费力,还会造成一段时间的内服务不可用,这不是一种推荐的方式,更多的时候,我们优先考虑哨兵模式,Redis从2.8开始正式提供了Sentinel(哨兵)架构来解决这个问题。
谋朝篡位是一种特殊的模式,首先Redis‘提供了哨兵的命令,哨兵是一个独立的进程!!作为进程,他会独立的运行,其原理是哨兵通过发送命令,等大Redis’服务的响应,从而监控运行的多个redis实例,
测试
我们目前的状态 就是一主二从
1.编写哨兵模式的配置文件 sentinel.conf
#sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6379 1
后面的这个数字1,代表主机挂机了,slave 投票看让谁接替主机,票数最多的,会成为主机!
redis-sentinel konfig/sentinel.conf
如果Master节点断开了,这个时候就会从主机中随机选择一个服务器!(这里是一个投票的算法)
如果主机此时回来了,只能归并到新的主机下,当作从机,这就是哨兵模式的规则!
哨兵模式
优点:
哨兵模式的全部配置!
概念
j解决的方案
微博服务器宕机(60是 60.1 0.1s–>>)
概念
小结
视频连接:
https://www.bilibili.com/video/BV1S54y1R7SB?p=36&spm_id_from=pageDriver
http://www.redis.cn/ 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)。
结合网页找出相对应的方法,决定用生什么集合https://www.runoob.com/redis/hashes-hmset.html
然后StringRedisTemplate相对应的集合中的方法中找到想用方法
@Service
public class RedisService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private IOEventService ioEventService;
//设置value
public void setValue(String keyPrefix, EnumRedisKey redisKey, String value) {
String key = redisKey.buildKey(keyPrefix);
if (redisKey.getExpire() == null) {
redisTemplate.opsForValue().set(key, value);
} else {
redisTemplate.opsForValue().set(key, value, redisKey.getExpire());
}
}
//获取value
public Optional<String> getValue(String keyPrefix, EnumRedisKey redisKey) {
String key = redisKey.buildKey(keyPrefix);
String value = redisTemplate.opsForValue().get(key);
return Optional.ofNullable(value);
}
//
private void setExpire(String keyPrefix, String key, EnumRedisKey redisKey) {
if (redisKey.getExpire() != null) {
ioEventService.execute(keyPrefix, "设置redis过期时间", () -> {
redisTemplate.expire(key, redisKey.getExpire());
});
}
}
//list集合中的右边添加一个value
public void leftPush(String keyPrefix, EnumRedisKey redisKey, String value) {
String key = redisKey.buildKey(keyPrefix);
redisTemplate.opsForList().leftPush(key, value);
setExpire(keyPrefix, key, redisKey);
}
//hash集合方法添加一个key 和一个map的key以及value
public void hashPut(String keyPrefix, EnumRedisKey redisKey, String field, String value) {
String key = redisKey.buildKey(keyPrefix);
redisTemplate.opsForHash().put(key, field, value);
setExpire(keyPrefix, key, redisKey);
}
//获取hash对应的key以map的key(Stirng field)
public String hashGet(String keyPrefix, EnumRedisKey redisKey, String field) {
String key = redisKey.buildKey(keyPrefix);
return (String) redisTemplate.opsForHash().get(key, field);
}
//移除hash对用的key 以及map key对应得value
public void hashRemove(String keyPrefix, EnumRedisKey redisKey, String field) {
String key = redisKey.buildKey(keyPrefix);
redisTemplate.opsForHash().delete(key, field);
}
//hash集合一次获取多个值
public Map<String, String> hashMGet(String keyPrefix, EnumRedisKey redisKey, List<String> field) {
String key = redisKey.buildKey(keyPrefix);
//给它一个想要指定得泛型
HashOperations<String, String, String> hash = redisTemplate.opsForHash();
HashMap<String, String> map = new HashMap<>();
//
List<String> values = hash.multiGet(key, field);
//将从list取出得keys 和values 一一放入map集合中;
if (values == null) {
return map;
} else {
for (int i = 0; i < field.size(); i++) {
String mapValue = values.get(i);
String mapKey = field.get(i);
if (mapValue != null) {
map.put(mapKey, mapValue);
}
}
return map;
}
}
//获取list集合指定得范围得数
public List<String> getListOfRang(String keyPrefix, EnumRedisKey redisKey, int start, int stop) {
String key = redisKey.buildKey(keyPrefix);
List<String> range = redisTemplate.opsForList().range(key, start, stop);
return range;
}
public void deleteFromList(String keyPrefix, EnumRedisKey redisKey, String value) {
String key = redisKey.buildKey(keyPrefix);
redisTemplate.opsForList().remove(key, 0, value);//删除以value为key得列表
}
//删除指定得key对应得value
public void delete(String keyPrefix, EnumRedisKey redisKey) {
String key = redisKey.buildKey(keyPrefix);
redisTemplate.delete(key);
}
//string,如果不存在就设置key,存在就不修改
public boolean setIfAbsent(String keyPrefix, EnumRedisKey redisKey, String value) {
String key = redisKey.buildKey(keyPrefix);
Boolean expired = redisTemplate.opsForValue().setIfAbsent(key, value);
setExpire(keyPrefix,key,redisKey);
return expired;
}
public long atomicAddValue(EnumRedisKey redisKey) {
Long increment = redisTemplate.opsForValue().increment(redisKey.getRedisKey());
return increment;
}
}