NoSQL概述
为什么要用Nosql
我们现在处在什么年代? 2020年,大数据时代: 一般的数据库无法进行分析处理的! hadoop等 2006年hadoop就出来了
现在的最低的要求就要 springboot + springcloud必须要掌握才行
适者生存 --》 不努力就要被淘汰 要一直学习 社会生存的唯一法则。
1.单击mysql的年代! 淘宝一开始买的是php的网站,后来由于分布式问题,改成java了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-amliXg10-1596736770732)(/upload/7月/ks-3687547b0a6d49b5a2545f5cd156d917.png “单击的mysql图”)]
90年代,2000年是一个世纪,我们是上个世纪的人。一个基本的网站的访问量不会很大,单个数据库完全足够了!那个时候更多的是去使用静态的网页(导航网站),就已经很牛逼了。服务器根本没有太大的压力。这种情况下,整个网站的瓶颈是什么?
- 数据量如果太大,一台机器放不下了!
- 数据的索引 300万一定要建立索引,如果不使用索引,就会查找的很慢, B+Tree,一个机器内存也放不下
- 访问量(读写混合),一个服务器承受不了
只要出现上述的情况的三种之一,就必须要使用分布式。
2.Memcached(缓存) + Mysql + 垂直的拆分(读写分离)
网站80%的情况都是在读,如果每次都要查询数据库的话是十分的麻烦的!所以说,我们希望减轻服务器的压力,我们可以走缓存来保证效率!
发展过程:
- 优化数据结构和索引
- 文件缓存(IO)
- Memcache(当时最热门的技术)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xsaVSgqs-1596736770735)(/upload/7月/ks-53d9911ed56248ecb9cdb2cf1d2f9941.png “加缓存”)]
mysql2 负责写,然后同步到mysql1 和 mysql3,2专门用来读,1和3用来写,实现读写的分离。
3.分库分表 + 水平拆分 + Mysql集群
技术在发展的同时,对于人的要求也越来越高
本质: 数据库(读/写)
早些年 MyISAM: 表锁 (查找张三 ---- 会把这个表给锁起来,影响效率,高并发出现很多的锁的问题)
转战Innodb: 行锁,只锁这一行
慢慢的开始使用分库分表来解决写的压力 根据字段还有业务,拆分表 可以做成为服务! MySQL在那个年代退出了表分区!! 并没有多少公司使用,给那个年代带来了希望。
MySQL的集群,很好的满足了那个年代的需求。
4.最近的年代
技术爆炸: 文艺复兴开始 2010(按键手机)-2020(触屏手机) 10年之间世界发生了翻天覆地的变化;(定位,也是一种数据,音乐热榜,哔哩哔哩排行榜)
MySQL的关系型数据库就不够用了!数据量很大,变化很快
图型数据库 JSON也可以做数据库 BOSN(MongoDB) 通过redis保存到本地 10W+ 过一段固定的时间来进行持久化
MySQL有的人使用它存储比较大的文件,博客,图片!数据库很大,效率就降低了! 如果有一种数据库专门处理这些数据,MySQL的压力就会变得十分的小(研究如何处理这些问题)大数据的IO压力下,表几乎没法更改 一亿条数据 加列很难加
灰度发布 平滑升级 (设计数据库的时候,后面的不能更改)
为什么要用Nosql!
个人一般户的信息,社交网络,地理位置。用户产生的数据,用户日志等等爆发式的增长!
这个时候我们使用Nosql数据库,Nosql可以很好的解决处理以上的情况。
什么是Nosql
Nosql
Nosql = Not Only sql(不仅仅是sql)
关系型数据库: 表格,行,列(POI技术 通过java操作excle表格)
泛指是非关系型数据库,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代(视频,音乐等),尤其是超大规模的高并发的社区!Nosql在当今大数据时代发展的十分迅速,redis发展最快,而且是我们当下必须要掌握的技术。
很多的数据类型个人一般户的信息,社交网络,地理位置。用户产生的数据,用户日志的数据类型的存储不需要固定格式的存储(行和列)!不需要多余的操作就可以横向扩展的!Map<String , Object>可以存储很多。Get和Set方法, 用键值对来控制,只是其中的一种。
Nosql特点
- 方便扩展(数据之间没有关系,很好扩展!)
- 大数据量高性能(Redis1s可以写80000次可以读取110000次,可以百度,Nosql的缓存记录级的,是一种细粒度的缓存,性能会比较高)
- 数据类型是多样的(不需要事先设计数据库!随取随用!如果是数据库量十分大的表,很多人就无法设计了)
- DBMS和Nosql
传统的RDBMS
- 结构化组织
- sql
- 数据和关系都存在单独的表中
- 数据操作,数据定义语言
- 严格的一致性
- 基础的事务操作
Nosql
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库(社交关系)
- 最终一致性
- CAP定理和BASE理论 (异地多活) 初级架构师
- 高性能,高可用,高可扩
了解: 3V+3高
大数据的3V:主要是描述问题的
1.海量Volume
2.多样Variety
3.实时Velocity
大数据的3高:主要是对于程序的需求
1.高并发
2.高可扩
3.高性能
公司的实践: Nosql + RDBMS 一起使用才是最强的, 阿里巴巴的架构演进。
阿里巴巴网站演进分析
用户能用就好
大量的公司做的都是相同的业务:(竞品协议)
随着这样的竞争,我们的业务是越来越完善的,对于开发的技术越来越好。 高中或者是大一,就要认真的学习了。
如果未来相当架构师: 没有是加一层解决不了的,如果解决不了,就加两层。
# 1. 商品的基本信息
名称、价格、商家信息; 关系型数据库就可以解决了 Mysql orcle (淘宝早年就去IOE了 王坚)
淘宝内部的Mysql不是大家用的mysql
# 2. 商品的描述、评论(文字比较多)
文档型数据库中,MongoDB
# 3. 图片
分布式文件系统 FastDFS
- 淘宝自己的 TFS
- Google的 GFS
- adoop HDFS
- 阿里云的 oss
# 商品的关键字(搜索)
- 搜索引擎 solr elasticsearch
- ISearch 多隆(一个人开发出来的)
# 5. 商品的热门的波段信息
- 内存数据库
- Redis、Tair、Memcache······
# 6. 商品的交易,外部的支付的接口
- 三方应用
要知道一个简单的网页,背后的技术一定不是大家想像的那么简单。
大型互联网应用问题
1.数据类型太多了
2.数据源繁多,经常重构!
3.数据要改造,大面积改造
Nosql的四大分类
KV键值对:
1.新浪 : Redis
2.美图 : Redis + Tair
3.阿里、百度: Redis + Memcache
文档型数据库(bson和json一样)
1.MongoDB(一般必须要掌握) : 是一个基于分布式文件存储的数据库, c++编写,处理大量的文档,MongoDB是一个介于关系型数据库和非关系型数据库中间的产品!MongDB是非关系型数据库中功能最丰富,最像关系型数据库的!
2.ConthDB
列存储数据库
1.HBase
2.分布式文件系统
图关系数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UJqkUj3n-1596736770754)(/upload/7月/ks-bef300e495ba4f419ecc9a97f593b44c.png “图关系数据库”)]
1.他不是存储图形的,放的是关系,比如: 朋友圈社交网络、广告推荐!
2.Neo4j,infoGrid;
四者的对比
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
---|---|---|---|---|---|
键值对(KV) | Tokyo、Cabinet/Tyrant、Redis、Voldemort、Orcle、BDB | 内容缓存,主要用于处理大量的数据的搞=高访问负载,也用于一些日志系统等等。 | key指向value的键值对,通常对hash table来实现 | 查找速度快 | 数据无结构化,通常只被当做字符串或者二进制数据 |
列存储数据库 | Cassandra、HBase、Riak | 分布式文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 |
文档型数据库 | CouchDB、MongoDB | Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) | Key-Value对用的键值对,Value为结构化数据 | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义结构 | 查询性能不高,而且缺乏统一的查询的语法 |
图形数据库 | Neo4J,InfoGrid、Infinite、Graph | 社交网络,推荐系统。专注于构建关系图谱 | 图结构 | 利用图结构相关算法。比如最短路径寻址,N度关系查找等 | 很多时候需要对整个图做计算,而且这种结构不太好做分布式的集群方案 |
Redis概述
Redis是什么
Redis(Remote Dictionary Server ),即远程字典服务!
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ui8HpURy-1596736770755)(/upload/7月/ks-ff55b61fcec44bea82349dad2711e594.png “Redis支持的语言”)]
免费和开源!是当下最热门的技术之一,也被人们称为结构化数据库。
Redis 能干啥
结果:读的速度是110000次/s,写的速度是81000次/s 。
1.内存存储、持久化,内存中是断电即失的,所以说持久化很重要(rdb和aof)
2.效率高,可用于告诉缓存
3.发布订阅系统
4.地图信息分析
5.计时器、计数器(微信微博的浏览量)
6.······
Redis特性
1.多样的数据类型
2.持久化
3.支持集群
4.支持事务
5.······
学习中需要用到的东西
1.狂神公众号
2.官网 https://redis.io/
3.中文网 http://redis.cn/
4.下载地址:通过官网下载即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6aJYCGjB-1596736770756)(/upload/7月/ks-e00ef0d4cf1c4a0b8b894c5b1cd407ee.png “Redis下载地址”)]
注意: window在github下载(停更很久了)
Redis推荐在linux服务器搭建,兼容性不好,基于Linux学习。
window下安装
1.下载安装包 https://github.com/dmajkic/redis/releases
2.下载完毕得到压缩包
3.解压到自己的电脑下的环境,十分的小,只有5M
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LdryiF3r-1596736770757)(/upload/7月/ks-b1d90201ebd84534b6f9820f93ba2a05.png “redis解压图”)]
4.开启Redis,双击服务运行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CN6Z31LZ-1596736770758)(/upload/7月/ks-794b2ccd5f424d739ed936cf14f48f63.png “开启redis”)]
默认的端口是6379
5.使用Redis客户端连接Redis
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fjuVEbqd-1596736770760)(/upload/7月/ks-50d1cf8aa62e473382f84ae1b464d8be.png “测试连接”)]
window使用确实简单,但是redis推荐使用linux开发使用。
Linux安装
1.下载获取安装包!
2.解压redis, 程序放在opt
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ncAEuPhx-1596736770761)(/upload/7月/ks-1c16b755b49c4667a5b637fb86e5703d.png “解压完成redis”)]
3.进入解压后的文件,可以s看到redis 的配置文件
4.yum install gcc - c++ 基本的环境安装
yum install gcc - c++
make
# 如果出现错误,那就是版本太低,需要执行下面的操作
1、安装gcc套装
yum install cpp
yum install binutils
yum install glibc
yum install glibc-kernheaders
yum install glibc-common
yum install glibc-devel
yum install gcc
yum install make
2、升级gcc
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash
3、当上面这几步完成后,再执行make命令即可
上述的是腾讯云的
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash
echo "source /opt/rh/devtoolset-9/enable" >> /etc/profile
gcc -v
make install
5.redis默认安装路径usr/local/bin
6.将redis的配置文件复制到当前目录
7.redis默认不是后台启动的,修改配置文件
8.启动redis服务
9.使用redis客户端连接并且查询所有的key
10.查看redis的进程是否开启
11.如何关闭redis服务shutdown
12.再次查看进程是否存在
使用单击多redis或者是redis集群进行测试!!!!
测试性能
redis-benchmark 是官方自带的性能测试工具!!
redis-benchmark 命令
简单测试
# 测试: 100个并发 100000个请求====== PING_BULK ======
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
100000 requests completed in 0.89 seconds
100 parallel clients
3 bytes payload
keep alive: 1
host configuration "save": 900 1 300 10 60 10000
host configuration "appendonly": no
multi-thread: no
95.60% <= 1 milliseconds
99.86% <= 2 milliseconds
99.96% <= 3 milliseconds
100.00% <= 4 milliseconds
111856.82 requests per second
====== SET ======
100000 requests completed in 0.87 seconds
# 100000个请求在0.87秒内完成
100 parallel clients
3 bytes payload
# 每次写入3个字节
keep alive: 1
# 只有一台服务器处理请求 单击性能
host configuration "save": 900 1 300 10 60 10000
host configuration "appendonly": no
multi-thread: no
98.06% <= 1 milliseconds
99.54% <= 2 milliseconds
99.71% <= 3 milliseconds
99.80% <= 11 milliseconds
99.88% <= 12 milliseconds
99.90% <= 13 milliseconds
99.99% <= 14 milliseconds
100.00% <= 14 milliseconds
# 所有的请求在14秒处理完成
115074.80 requests per second
# 每一秒处理115074.80个请求
====== GET ======
100000 requests completed in 1.11 seconds
100 parallel clients
3 bytes payload
keep alive: 1
host configuration "save": 900 1 300 10 60 10000
host configuration "appendonly": no
multi-thread: no
77.97% <= 1 milliseconds
99.85% <= 2 milliseconds
100.00% <= 2 milliseconds
89847.26 requests per second
基础的知识
redis默认有16个数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nf7HjcUk-1596736770777)(/upload/7月/ks-773ea490d9584d428f3980513e1691c8.png “redis默认有16个数据库”)]
默认使用第0个数据库
可以使用select进行数据库的切换
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> select 3 # 切换数据库
OK
127.0.0.1:6379[3]> dbsize # 查看数据库的大小
(integer) 0
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> keys * # 查看当前数据库所有的key
1) "name"
127.0.0.1:6379> flushall # 清除所有的数据库的数据
OK
127.0.0.1:6379> flushdb # 清除当前的数据库
OK
思考 为啥redis是6379!是一个女的的9键对应的是6379,后被长期使用,就是粉丝效应
Redis是单线程的!
redis是很快的,官方表示redis是基于内存操作的,CPU不是redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络的带宽,既然可以使用单线程实现,就使用单线程使用!所以就使用单线程了。
Redis是c语言写的,官方提供的数据为 100000 + 的QPS,完全不比同样是使用key-value的Memcache差
redis为什么单线程还这么快?
1.误区1:高性能的服务器一定是多线程的? 不一定
2.误区2:多线程(cpu调度,上下文会切换)一定比单线程效率高!
cpu>内存>硬盘的速度
核心:redis是将所有的数据全部放在内存里面的,所以说使用单线程去操作,效率就是最高的多线程:(cpu上下文会切换:耗时的操作),对于系统内存来说,如果没有上下文的切换,效率就是最高的。多次读写都是在一个cpu上面的,在内存情况下,这个就是最佳的方案。
#五大基本数据类型
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)。
现在讲的所有的命令,都是这些 单点登录
Redis-Key
127.0.0.1:6379> keys * # 查看所有的key
1) "name"
2) "age"
127.0.0.1:6379>
127.0.0.1:6379> exists name # 查看当前的key是否存在
(integer) 1 # 当前的key存在,返回值为1
127.0.0.1:6379> exists name1
(integer) 0 # 当前的key不存在,返回值为0
127.0.0.1:6379> move name 1 # 移除当前的key
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> set name chenliuhong
OK
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> get name # 获取key的对应的value
"chenliuhong"
127.0.0.1:6379> expire name 10 # 设置key的过去的时间为10秒
(integer) 1
127.0.0.1:6379> ttl name # 查看当前的key还有多长时间过期
(integer) -2 # -2 表示当前的key已经过期了
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> type name # ch查看当前的key的类型
string
127.0.0.1:6379> type age
string
后面如果遇到不会的命令,就可以查看官网的命令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6r832VQq-1596736770778)(/upload/7月/ks-ef266620008049138217ba647c298e50.png “官网查看命令”)]
String(字符串)
90%的java程序员,只会使用String类型!
127.0.0.1:6379> flushdb # 清除数据库
OK
127.0.0.1:6379> set key1 v1 # 设置值
OK
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> exists k1 # 判断某一个key是否存在
(integer) 1
127.0.0.1:6379> append k1 "hello" # 在某个字符串后面追加一个字符串 如果当前的key不存在,就相当于set了一个key
(integer) 7
127.0.0.1:6379> get k1 # 获取key
"v1hello"
127.0.0.1:6379> strlen k1 # 获取字符串的长度
(integer) 7
127.0.0.1:6379> append k1 ",chenliuhong" # 追加一个字符串
(integer) 19
127.0.0.1:6379> get k1 # 获取key的值
"v1hello,chenliuhong"
127.0.0.1:6379> strlen k1 # 获取字符串的长度
(integer) 19
##################################################
127.0.0.1:6379> set views 0 # 初始浏览量为0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views # 自增1 浏览量变为1
(integer) 1
127.0.0.1:6379> get views # 获取浏览量
"1"
127.0.0.1:6379> incr views # 自增1
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> decr views # 自减1 浏览量减1
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> decr views
(integer) 0
127.0.0.1:6379> decr views
(integer) -1
127.0.0.1:6379> get views
"-1"
127.0.0.1:6379> incrby views 10 # 根据步长来进行浏览量的增加
(integer) 9
127.0.0.1:6379> get views
"9"
127.0.0.1:6379> decrby views 5 # 根据步长来进行浏览量的减少
(integer) 4
127.0.0.1:6379> get views
"4"
##################################################
字符串范围 range
127.0.0.1:6379> set k1 "hello,chenliuhong" # 设置k1的值
OK
127.0.0.1:6379> getrange k1 0 3 # 截取0-3的字符串[0,3]
"hell"
127.0.0.1:6379> getrange k1 0 -1 # 获取全部的字符串 跟get一样
"hello,chenliuhong"
# 替换
127.0.0.1:6379> set k2 abcdefg
OK
127.0.0.1:6379> get k2
"abcdefg"
127.0.0.1:6379> setrange k2 1 xx # 替换指定位置开始的字符串
(integer) 7
127.0.0.1:6379> get k2
"axxdefg"
##################################################
# setex(set with expire) # 设置过期时间
# setnx(set if not exist) # 不存在设置 分布式锁中会常常使用
127.0.0.1:6379> setex k3 30 "hello" # 设置一个k3的值为hello 30秒后过期
OK
127.0.0.1:6379> ttl k3
(integer) 27
127.0.0.1:6379> ttl k3
(integer) 20
127.0.0.1:6379> setnx mykey "redis" # 如果mykey不存在,创建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "k1"
2) "v2"
3) "mykey"
4) "k2"
127.0.0.1:6379> ttl k3
(integer) -2
127.0.0.1:6379> setnx mykey "MongDB" # 如果mykey存在,创建失败
(integer) 0
127.0.0.1:6379> get mykey
"redis"
##################################################
mset
mget
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同时设置多个值
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
127.0.0.1:6379> mget k1 k2 k3 # 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 # 如果不存在创建 是一个原子性操作,要么一起成功,要么一起失败
(integer) 0
127.0.0.1:6379> get k4
(nil)
#对象
set user:1 {name:zs,age:3} # 设置user1对象为json字符串来保存一个对象
#这里的key是一个巧妙的设计user:{id}:{field} , 如此设置redis中是可以的
127.0.0.1:6379> mset user:1:name zs user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zs"
2) "2"
##################################################
getset 先get再set
127.0.0.1:6379> getset db redis # 如果不存在值返回null 如果存在值,获取原来的值,并设置新的值
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongdb #
"redis"
127.0.0.1:6379> get db
"mongdb"
CAS比较交换
数据结构相同,未来会研究jedis(java连接)
String 使用的场景: value除了可以是字符串还可以是数字!
1.计数器
2.统计多单位的数量 uid (当前用户):id:view 0 incr可以
3.粉丝数
4.对象缓存存储!
List
基本的数据类型,列表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JSn1tJzs-1596736770779)(/upload/7月/ks-23e0be9df09f47369d024ed265c89f8e.png “list图解”)]
可以吧list作为栈或者是队列 阻塞队列
所有的list命令都是以l开头的 Redis不区分大小写命令
127.0.0.1:6379> lpush list one # 将一个值或者是多个值插入列表的头部(左)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1 # 获取列表的所有的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1 # 通过区间获取具体的值
1) "three"
2) "two
127.0.0.1:6379> rpush list right # 将一个值或者是多个值插入列表的尾部(右)
(integer) 4
127.0.0.1:6379> lrange list 0 -1 # 获取列表所有的值
1) "three"
2) "two"
3) "one"
4) "right"
##################################################
lpop
rpop
127.0.0.1:6379> lpop list # 移除列表的第一个元素
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379> rpop list # 移除列表的最后一个元素
"right"
127.0.0.1:6379> lrange list 0 -1 # 查看列表的所有的元素
1) "two"
2) "one"
##################################################
lindex
127.0.0.1:6379> lindex list 0 # 通过下标获取list中的某一个值
"two"
127.0.0.1:6379> lindex list 1
"one"
##################################################
llen
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> llen list # 返回列表的长度
(integer) 3
##################################################
移除指定的值!
lrem # 移除list中的某一个指定的数量的值
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> llen list
(integer) 3
127.0.0.1:6379> lpush list three
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 one # 移除list中的一个one
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379> lrem list 2 three # 移除list中的两个three
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "two"
##################################################
trim 修剪: list 截断
127.0.0.1:6379> rpush mylist "hello1"
(integer) 1
127.0.0.1:6379> rpush mylist "hello2"
(integer) 2
127.0.0.1:6379> rpush mylist "hello3"
(integer) 3
127.0.0.1:6379> rpush mylist "hello"
(integer) 4
127.0.0.1:6379> ltrim mylist 1 2 # 通过下标截取指定的长度,list已经被改变了,截断了,只剩下截取的元素
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello2"
2) "hello3"
##################################################
rpoplpush # 移除列表的最后一个元素并且添加一个新元素
127.0.0.1:6379> rpush mylist "hello"
(integer) 1
127.0.0.1:6379> rpush mylist "hello1"
(integer) 2
127.0.0.1:6379> rpush mylist "hello2"
(integer) 3
127.0.0.1:6379> rpoplpush mylist myotherlist # 移除列表的最后一个元素,并且移到一个新的列表中
"hello2"
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
127.0.0.1:6379> lrange myotherlist 0 -1 # 查看目标的列表的元素
1) "hello2"
##################################################
lset # 将列表中指定下标的值替换为另一个值,更新操作
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> lrange list 0 0
1) "value1"
127.0.0.1:6379> lset list 0 item # 如果存在列表,去更新
OK
127.0.0.1:6379> lrange list 0 0
1) "item"
127.0.0.1:6379> lrange list 1 other ## 如果不存在列表中对应的下标的值,去更新就会报错
(error) ERR value is not an integer or out of range
##################################################
linsert # 将某一个具体的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 before "world" "other"
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "other"
3) "world"
127.0.0.1:6379> linsert mylist after "world" "new"
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "other"
3) "world"
4) "new"
小结
1.他实际上是一个链表: before Node after, left right都可以插入值
2.如果key不存在,创建新的链表
3.如果key存在,增加新的内容
4.如果移除了所有的元素,空链表,也代表不存在
5.如果在两边插入或者是改动值,效率最高!中间的元素相对来说低一点
消息排队! 消息队列(Lpush Rpop),栈(Lpop Rpush) 既可以作为队列,也可以作为栈
Set(集合类型)
set中的值是不能重复的
127.0.0.1:6379> clear
127.0.0.1:6379> sadd myset hello # set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset chenliuhong
(integer) 1
127.0.0.1:6379> sadd myset love chenliuhong
(integer) 1
127.0.0.1:6379> smembers myset # 查看集合中所有的元素
1) "love"
2) "chenliuhong"
3) "hello"
127.0.0.1:6379> sismember myset hello # 查看set中某一个元素是否存在
(integer) 1
127.0.0.1:6379> sismember myset world
(integer) 0
##################################################
127.0.0.1:6379> scard myset # 获取集合元素的个数
(integer) 3
127.0.0.1:6379> sadd myset chenliuhong
(integer) 0
127.0.0.1:6379> sadd myset chenliuhong2
(integer) 1
127.0.0.1:6379> scard myset
(integer) 4
##################################################
srem # 移除set集合中的指定元素
127.0.0.1:6379> scard myset
(integer) 4
127.0.0.1:6379> srem myset hello
(integer) 1
127.0.0.1:6379> scard myset
(integer) 3
##################################################
set 是无需的不重复的集合
127.0.0.1:6379> srandmember myset 2 # 随机抽选出两个元素
1) "chenliuhong"
2) "love"
127.0.0.1:6379> srandmember myset 2 # 随机抽选出指定元素的个数
1) "chenliuhong2"
2) "love"
127.0.0.1:6379> srandmember myset 1 # 随机抽选出一个元素
1) "chenliuhong2"
##################################################
移除一个指定的key,随机删除key!
127.0.0.1:6379> clear
127.0.0.1:6379> smembers myset
1) "chenliuhong2"
2) "love"
3) "chenliuhong"
127.0.0.1:6379> spop myset # 随机移除集合的元素
"chenliuhong2"
127.0.0.1:6379> smembers myset
1) "love"
2) "chenliuhong"
##################################################
将一个指定的值移动到另一个集合中
127.0.0.1:6379> sadd myset "hello"
(integer) 1
127.0.0.1:6379> sadd myset "world"
(integer) 1
127.0.0.1:6379> sadd myset "chenliuhong"
(integer) 1
127.0.0.1:6379> sadd myset2 "set"
(integer) 1
127.0.0.1:6379> smove myset myset2 chenliuhong # 移除指定的元素到另一个集合
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello"
2) "world"
127.0.0.1:6379> smembers myset2
1) "set"
2) "chenliuhong"
##################################################
微博或者是b站 共同关注(并集)
数字集合类:
- 差集 SDIFF
- 交集 SINTER
- 并集 SUNION
127.0.0.1:6379> sadd myset a
(integer) 1
127.0.0.1:6379> sadd myset b
(integer) 1
127.0.0.1:6379> sadd myset c
(integer) 1
127.0.0.1:6379> sadd myset d
(integer) 1
127.0.0.1:6379> sadd myset2 c
(integer) 1
127.0.0.1:6379> sadd myset2 d
(integer) 1
127.0.0.1:6379> sadd myset2 de
(integer) 1
127.0.0.1:6379> sadd myset2 e
(integer) 1
127.0.0.1:6379> sadd myset2 f
(integer) 1
127.0.0.1:6379> sdiff myset myset2 # 差集
1) "b"
2) "a"
127.0.0.1:6379> sinter myset myset2 # 交集,共同的好友
1) "d"
2) "c"
127.0.0.1:6379> sunion myset myset2 # 并集
1) "c"
2) "e"
3) "d"
4) "f"
5) "de"
6) "b"
7) "a"
微博或者b站,A用户将所有关注的人放在集合中!将他的粉丝也放在一个集合中!
共同关注,共同爱好,二度好友(六度分割理论)推荐好友(qq的)
Hash(哈希)
map集合,key-map集合! key-,这时候值是一个map集合,本质和String类型没有太大的区别,是一个简单的key-value集合
127.0.0.1:6379> hset hash field1 chenliuhong # set一个key-value
(integer) 1
127.0.0.1:6379> hget hash field1 # 获取一个字段值
"chenliuhong"
127.0.0.1:6379> hmset hash field1 hello field2 world # set多个key-value
OK
127.0.0.1:6379> hmget hash field1 field2
1) "hello"
2) "world"
127.0.0.1:6379> hgetall hash # 获取全部的数据
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> hdel hash field1 # 删除hash指定的key字段!对用的value也就消失了
(integer) 1
127.0.0.1:6379> hgetall hash
1) "field2"
2) "world"
##################################################
hlen # 获取hash表的字段的数量
127.0.0.1:6379> hmset hash field1 hello field2 world # p批量设置hash的值
OK
127.0.0.1:6379> hgetall hash # 获取所有的hash的值
1) "field2"
2) "world"
3) "field1"
4) "hello"
127.0.0.1:6379> hlen hash # 获取hash表的长度
(integer) 2
##################################################
hexists 判断hash中的指定的字段是否存在
127.0.0.1:6379> hexists hash field
(integer) 0
127.0.0.1:6379> hexists hash field1
(integer) 1
##################################################
# 只获得所有的key
# 只获得所有的值
127.0.0.1:6379> hkeys hash # 获取所有的key
1) "field2"
2) "field1"
127.0.0.1:6379> hvals hash # 获取所有的value
1) "world"
2) "hello"
##################################################
hincrby decr(没有这个)
127.0.0.1:6379> hset hash field3 5
(integer) 1
127.0.0.1:6379> hincrby hash field3 1
(integer) 6
127.0.0.1:6379> hincrby hash field3 -1
(integer) 5
127.0.0.1:6379> hsetnx hash field4 hello
(integer) 1
127.0.0.1:6379> hsetnx hash field4 world
(integer) 0
##################################################
127.0.0.1:6379> hsetnx hash field4 hello # ruf如果不存在可以设置,如果存在则不能设置
(integer) 1
127.0.0.1:6379> hsetnx hash field4 world
(integer) 0
hash的应用:
1.变更的数据 user name age , 尤其是用户信息的保存,经常变动的信息!hash更适合于对象的存储,string更加适合字符串的存储
127.0.0.1:6379> hset user:1 name chenliuhong
(integer) 1
127.0.0.1:6379> hget user:1 name
"chenliuhong"
Zset(有序集合)
在set的基础上增加了一个值 set k1 v1 zset k1 score v1
127.0.0.1:6379> zadd zset 1 one # 添加一个值
(integer) 1
127.0.0.1:6379> zadd zset 2 two
(integer) 1
127.0.0.1:6379> zadd zset 3 three
(integer) 1
127.0.0.1:6379> zrange zset 0 -1 # 获取值
1) "one"
2) "two"
3) "three"
##################################################
排序如何实现 +inf 表示正无穷 -inf表示负无穷
127.0.0.1:6379> zadd salary 2500 xiaohong # 添加三个用户
(integer) 1
127.0.0.1:6379> zadd salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 500 chenliuhong
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf # 显示用户从小到大排序
1) "chenliuhong"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrangebyscore salary 0 -1 # 0 -1也不能生效
(empty array)
127.0.0.1:6379> zrangebyscore salary 0 -1 withscores
(empty array)
127.0.0.1:6379> zrangebyscore salary +inf -inf withscores # 这里不能从大到小排列
(empty array)
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores # 获取所有的排序
1) "chenliuhong"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores # 获取指定范围的值
1) "chenliuhong"
2) "500"
3) "xiaohong"
4) "2500"
127.0.0.1:6379> zrevrange salary 0 -1 # 从大到小排序
1) "zhangsan"
2) "chenliuhong"
##################################################
移除元素 zrem
127.0.0.1:6379> zrem salary xiaohong # 移除有序集合指定的元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1 # 打印所有的值
1) "chenliuhong"
2) "zhangsan"
127.0.0.1:6379> zcard salary # 获取有序集合中的个数
(integer) 2
##################################################
ZCOUNT # 获取指定区间的数量
127.0.0.1:6379> zadd zset 1 hello
(integer) 1
127.0.0.1:6379> zadd zset 2 world
(integer) 1
127.0.0.1:6379> zadd zset 3 chenliuhong
(integer) 1
127.0.0.1:6379> ZCOUNT zset 1 2
(integer) 2
127.0.0.1:6379> ZCOUNT zset 1 3
(integer) 3
其余的api,剩下的如果工作中有需要,这个时候可以看官方文档http://redis.cn/commands.html
案例思路:
1.set排序 存储班级成绩表,工资表排序
2.普通消息 1 重要消息 2 带权重进行判断
3.排行榜应用
4.取top ten排行榜
三种特殊的数据类型
geospatial
朋友的定位,附近的人 ,打车距离计算
redis的Geospatial,可以推算地理位置的信息,两地的距离,方圆几里的人
查询经度和维度http://www.jsons.cn/lngcode/
只有6个命令
GEOADD # 添加位置
# 两极无法直接添加 一般会下载程序,直接通过java程序一次性导入
# 参数key 值(维度经度名称)
# 有效的经度: -180度到180度
# 有效的维度: -85.05112878度到85.05112878度
# 如果超出指定的范围,该命令会返回一个错误
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
(integer) 1
127.0.0.1:6379> geoadd china:city 114.05 22.52 shenzhen
(integer) 1
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 108.96 34.26 xian
(integer) 1
##################################################
GEOPOS # 从指定的key获取指定的位置的经度和维度
# 获得当前定位,一定是一个坐标值
127.0.0.1:6379> GEOPOS china:city beijing shanghai
1) 1) "116.39999896287918091" # 经度
2) "39.90000009167092543" # 维度
2) 1) "121.47000163793563843"
2) "31.22999903975783553"
##################################################
GEODIST # 返回两个给定位置的距离
127.0.0.1:6379> GEODIST china:city beijing shanghai # 获取北京到上海的距离
"1067378.7564"
127.0.0.1:6379> GEODIST china:city beijing shanghai km # 获取北京到上海的距离指定单位
"1067.3788"
127.0.0.1:6379> GEODIST china:city beijing chongqing km # 北京到重庆的距离
"1464.0708"
##################################################
GEORADIUS # 以给出的经度纬度为中心,找出半径内的元素
# 附近的人 (获取附近的人的定位!会定时刷新) 通过半径来查询!
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km # 获取当前半径为1000km距离的所有的位置 前提是所有的数据都要在city中
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km # 获取半径为500m的距离的位置的所有的位置
1) "chongqing"
2) "xian"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist # 获取半径为500m的距离的位置的所有的位置 同时显示距离
1) 1) "chongqing"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord # 获取半径为500m的距离的位置的所有的位置 同时显示经度和维度
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 1 # 获取半径为500m的距离的位置的所有的位置 同时显示距离、经度和维度还有限制数量
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 2 # 获取半径为500m的距离的位置的所有的位置 同时显示距离、经度和维度还有限制数量
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) "483.8340"
3) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 3 # 获取半径为500m的距离的位置的所有的位置 同时显示距离、经度和维度还有限制数量
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) "483.8340"
3) 1) "108.96000176668167114"
2) "34.25999964418929977"
##################################################
GEORADIUSBYMEMBER # 找出指定范围内的元素,中心点由给定的元素决定
# 找出指定元素范围内的元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 400 km
1) "hangzhou"
2) "shanghai"
##################################################
GEOHASH
该命令返回一个或多位置元素的Geohash字符串 如果两个字符串越接近,那么这两个距离约接近
127.0.0.1:6379> GEOHASH china:city beijing chongqing
1) "wx4fbxxfke0" # 当前经纬度的字符串,是一个11位的hash
2) "wm5xzrybty0"
##################################################
Geo底层的实现原理其实就是zset!我们可以适应zset使用geo
127.0.0.1:6379> ZRANGE china:city 0 -1 # 使用zset来查看所有的元素
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> zrem china:city beijing # 移除地图中的某个元素
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
hyperloglog
什么是基数
A{1,3,5,7,8,7,9} B{1,3,5,7,8} 基数: 不重复的元素 = 5, 可以接受误差
简介:
Redis2.8.9版本就更新了Hyperloglog数据结构
Redis Hyperloglog 基数统计的算法!
网页的UV(一个人访问一个网站多次,但是还是算作一个人!)
传统的方式,set保存用户的id,set集合不允许重复,然后就可以统计set中的元素的个数来进行判断, 如果保存打来那个的用户id就会比较麻烦!!!我们的目的是为了计数,不是为了保存用户的id!
优点: 占用的内存是有限的,2^64不同的元素的技术,只需要12kb的内存!如果要从内存角度比较的话,Hyperloglog就是首选!0.81%的错误率! 统计UV的任务,可以忽略不计
# 测试使用
127.0.0.1:6379> pfadd mykey a b c d e f g h i j # 创建第一组元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey # 统计第一组元素的数量
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j k c m n j s # 创建第二组元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 7
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 合并两组元素 是一个并集 可以去查看并集的数量
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 14
如果允许容错,可以使用hyperloglog!
如果不允许容错,就使用set或者是自己的数据类型就可以!
bitmaps 可以多一条思路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5idkki6g-1596736770780)(/upload/7月/ks-04c91bc63e29414c9ab0d60a82ec3b6e.png “打卡图”)]
位存储
疫情: 14亿个0 很容易计算
统计疫情感染的人数 统计用户的信息(活跃,不活跃 登录,未登录 打卡365天 userid status day)
只要是两个状态的都可以使用bitmaps , 也是一种数据结构! 操作二进制位来进行记录 0 和 1来进行记录 365天 = 365字节 只需要几个字节
测试:
127.0.0.1:6379> clear # 记录周一到周日的打卡
127.0.0.1:6379> setbit sign 0 1 # 1代表打卡 0 代表没有打卡
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
查看某一天是否有打卡:
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
统计操作,统计打卡的天数
127.0.0.1:6379> bitcount sign # 查看几天打卡 统计打卡的记录,就可以看到是否有全勤!
(integer) 4
事务
MySQL ACID
要么同时成功,要么同时失败,原子性!
Redis单条命令保证原子性,但是事务是不保证原子性的
Redis事务的本质: 一组命令的集合 一个事务中所有的命令都会被序列化,会按照顺序执行! 一次性、顺序性、排他性! 执行一些列的命令!
Redis没有隔离级别的概念(不会出现脏读) 所有的命令不会被执行,只有发起执行的时候才会执行
redis事务三个阶段
1.开启事务(multi)
2.命令入队
3.执行事务(exec)
锁: 可以实现乐观锁 , 正常执行事务!
--- 队列 set set set ---
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> set k3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) "v2"
4) OK
# 放弃事务 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> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get k4
(nil)
# 事务里面有错误
1.编译性异常(命令有错, 事务中所有的命令都不会被执行)
127.0.0.1:6379> clear
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> flushdb
OK
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> exec
(error) EXECABORT Transaction discarded because of previous errors. # 执行事务会报错
127.0.0.1:6379> get k1 # 所有的命令都不会被执行
(nil)
2.运行时异常(如果事务队列中存在语法错误,执行命令的时候其他的命令是可以正常执行的 错误命令会抛出异常)
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 k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range # 下面的依然执行只有第一条命令报错了
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
监控 watch
悲观锁: 很悲观 认为什么时候都会出问题,无论做什么都会加锁
乐观锁: 很乐观 认为什么时候都不会出现问题 , 所以不会上锁,更新数据的时候去判断一下再次期间是否有人修改过数据 version
1.获取version
2.更新的时候比较version
正常执行
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money # 监视money
OK
127.0.0.1:6379> MULTI # 事务正常结束,数据期间没有发生变动,这个时候正常执行
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
测试多线程修改值 监视失败 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 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec # 执行之前另外一个线程修改了值,修改失败
(nil)
# 多线程的修改
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
执行事务失败:
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 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec # 对比监视的值是否发生变化,如果没有变化,那么执行成功,变化则执行失败
1) (integer) 990
2) (integer) 30
面试常问
Jeids
使用java操作Redis
什么是Jedis: 是官方推荐的java开发工具!使用java操作Redis的中间件!如果要用,一定要对Jedis十分熟悉!
1.导入对应的依赖
2.编码测试
测试连接
package com.timous;
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());
}
}
输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iCgp0j6U-1596736770781)(/upload/7月/ks-421f45cb4a144d338950ae9cf83322b3.png “jedis测试ping”)]
常用的API
String 的测试
package com.timous;
import redis.clients.jedis.Jedis;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class testString {
public static void main(String[] args) {
//1. new Jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
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","valuechange"));
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","value2-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 (InterruptedException 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));
}
}
set
package com.timous;
import com.sun.xml.internal.ws.model.wsdl.WSDLPortProperties;
import redis.clients.jedis.Jedis;
import java.util.Set;
public class testSet {
public static void main(String[] args) {
//1. new Jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
jedis.flushDB();
System.out.println("======向集合中添加元素(不重复)======"); System.out.println(jedis.sadd("eleSet","e1","e2","e3","e0","e8","e7","e5"));
System.out.println(jedis.sadd("eleSet","e6"));
System.out.println(jedis.sadd("eleSet","e6"));
System.out.println("eleSet的所有元素为: "+jedis.smembers("eleSet"));
System.out.println("删除一个元素e0: "+ jedis.srem("eleSet","e0"));
System.out.println("eleSet的所有元素为: "+jedis.smembers("eleSet"));
System.out.println("删除两个元素e7和e6: "+jedis.srem("collection","e6","e7"));
System.out.println("eleSet中的所有的元素为: "+ jedis.smembers("eleSet"));
System.out.println("随机移除集合中的一个元素: "+jedis.spop("eleSet"));
System.out.println("随机移除集合中的一个元素: "+jedis.spop("eleSet"));
System.out.println("eleSet的所有元素的个数: "+jedis.scard("eleSet"));
System.out.println("e3是否在eleSet中: "+jedis.sismember("eleSet","e3"));
System.out.println("e1是否在eleSet中: "+jedis.sismember("eleSet","e1"));
System.out.println("e5是否在eleSet中: "+jedis.sismember("eleSet","e5"));
System.out.println("============"); System.out.println(jedis.sadd("eleSet1","e1","e2","e4","e3","e0","e8","e7","e5"));
System.out.println(jedis.sadd("eleSet2","e1","e2","e4","e3","e0","e8"));
System.out.println("将eleSet1中删除e1并存入eleSet3中: "+jedis.smove("eleSet","eleSet","e1"));
System.out.println("eleSet1中的元素: "+ jedis.smembers("eleSet1"));
System.out.println("eleSet3中的元素: "+ jedis.smembers("eleSet3"));
System.out.println("======集合元素======");
System.out.println("eleSet1中的元素: "+ jedis.smembers("elesSet1"));
System.out.println("eleSet2的元素: "+jedis.smembers("eleSet2"));
System.out.println("eleSet1和eleSet2的交集: "+jedis.sinter("eleSet1","eleSet2"));
System.out.println("eleSet1和eleSet2的并集: "+jedis.sunion("eleSet1","eleSet2"));
System.out.println("eleSet1和eleSet2的差集: "+jedis.sdiff("eleSet1","eleSet2"));
jedis.sinterstore("eleSet4","eleSet1","eleSet2");//求交集,并将交集保存到dstkey集合中
System.out.println("eleSet4中的元素: "+jedis.smembers("eleSet4"));
}
}
list
package com.timous;
import redis.clients.jedis.Jedis;
import java.util.concurrent.TimeUnit;
public class testList {
public static void main(String[] args) {
//1. new Jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
jedis.flushDB();
System.out.println("======添加一个list======");
jedis.lpush("collections","Arraylist","vector","Stack","HashMap","WeakHashMap","linkList");
jedis.lpush("collections","HashSet");
jedis.lpush("collections","TreeSet");
jedis.lpush("collections","TreeMap");
System.out.println("collections的内容: " + jedis.lrange("collections",0,-1));
System.out.println("collections区间0-3的元素: " + jedis.lrange("collections",0,3));
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("col1ections"));
System.out.println(" collections的内容: "+jedis. lrange("collections", 0,-1));
System.out.println("collections添加元素,从列表右端,与1push相对应: "+jedis. rpush(" collections","EnumMap"));
System.out.println("collections的内容: "+jedis.lrange("collections",0,-1));
System.out.println(" collections列表出栈(右端) : "+jedis. rpop("collections"));
System.out.println(" collections的内容: "+jedis.lrange("collections",0,-1));
System.out.println("修改co1lections指定下标1的内容: "+jedis. lset("collections", 1, "LinkedArrayList"));
System.out.println(" collections的内容: "+jedis.lrange("collections",0,-1));
System.out.println("============================");
System.out.println(" collections的长度: "+jedis.llen(" collections"));
System.out.println("获取col1ections下标为2的元素: "+jedis. lindex(" collections", 2));
System.out.println("======================");
jedis.lpush("sortedList", "3" ,"6","2" ,"0","7" ,"4");
System.out.println("sortedList排序前: "+jedis.lrange("sortedList", 0, -1));
System.out.println(jedis.sort(" sortedList"));
System.out.println("sortedList排序后: "+jedis.lrange("sortedList", 0, -1));
}
}
zset
hash
package com.timous;
import redis.clients.jedis.Jedis;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class testHash {
public static void main(String[] args) {
//1. new Jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
jedis.flushDB();
Map<String , String> map = new HashMap<String, String>();
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("散列hash的所有键为: "+jedis.hkeys("hash"));
System.out.println("散列hash的所有值为: "+jedis.hvals("hash"));
System.out.println("将key6保存的值添加一个整数,若果key6不存在则添加key6: "+jedis.hincrBy("hash","key6",6));
System.out.println("散列hash的所有键值对为: "+jedis.hgetAll("hash"));
System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6: "+jedis.hincrBy("hash","key6",3));
System.out.println("散列hash的所有键值对为: "+jedis.hgetAll("hash"));
System.out.println("删除一个或者多个键值对: "+jedis.hdel("hash","key2"));
System.out.println("散列hash所有键值对为: "+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中的值: "+jedis.hget("hash","key3"));
System.out.println("获取hash中的值: "+jedis.hmget("hash","key3","key4"));
}
}
SpringBoot2.2.5整合
固定的RedisTemplate
package com.timous.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.std.StringSerializer;
import org.omg.CORBA.portable.UnknownException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.StringRedisConnection;
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 {
//固定的模板,公司可以用
//编写自己的redis配置类
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory)
throws UnknownException {
//我们为了自己开发方便,一般使用String , Object
RedisTemplate<String , Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//序列化配置
//jackson的序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
//hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value的序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
//hash的value也采用jackson的序列化方式
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
测试类
package com.timous;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.timous.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class Redis02SpringbootApplicationTests {
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
/* //RedisTemplate opsForValue 操作字符串 opsForList 操作list 其他的都类似 api和指令一样
//除了基本的操作,常用的方法都可以直接使用
//获取连接对象
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushDb();
connection.flushAll();*/
redisTemplate.opsForValue().set("key","关注我");
System.out.println(redisTemplate.opsForValue().get("key"));
}
@Test
public void test() throws JsonProcessingException {
User user = new User("chenliuhong", 12);
String jsonUser = new ObjectMapper().writeValueAsString(user);
//真实的开发一般使用json
//直接传递对象需要序列化 , 在企业中,我们所有的pojo都会序列化
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
}
在公司的开发中的帮助类
package com.timous.Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
//企业中都会使用这种进行保存
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String , Object> redisTemplate;
private CollectionUtils CollectionsUtils;
// =============================common============================
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
所有的redis操作,其实对于java程序员来讲都是十分简单的,更重要的是要理解redis的思想和每一种数据结构的用处和作用场景
注意
此文章为看狂神说java的视频写出来的,如有侵权,请联系