1)单机MySQL的年代
APP----->DAL------>MySQL
90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够!
那个时候,更多的去使用静态网页Html,服务器根本没有太大的压力
这个时候,网站的瓶颈:
2)Memcached (缓存) + MySQL + 垂直拆分 (读写分离)
读写分离
把服务器拆分,垂直拆分
发展过程:
优化数据结构和索引—>文件缓存(IO)—>Memcached(当时最热门的技术)
3)分库分表 + 水平拆分 +MySQL集群
MySQL集群
早些年MyISAM:表锁,十分影响效率!高并发下就会出现严重的锁问题
Innodb:行锁,效率增加
慢慢的开始使用分库分表来解决写的压力(不同业务使用单独数据库管理,不同字段使用不同的表来管理)
最近的年代
技术爆炸,世界发生了翻天覆地的变化;(定位,音乐,热榜等数据)
MySQL等关系数据库就不够用了,数据量很多而且变化很快!
用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式生长。这时候我们就需要使用NoSQL数据库的,NoSQL可以很好的处理以上的情况
什么是NoSQL
Not Only SQL :不仅仅是sql,泛指非关系型数据库。随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区。随着访问量增大,暴露出来很多难以克服的问题。NoSQL在当今大数据环境下发展的十分迅速,redis。
redis现在是很多公司招聘的必要求的技能。是我们当下必须要掌握的一个技术!
很多的数据类型如用户的个人信息,社交网络,地理位置等,这些数据类型的存储不需要一个固定的格式,不需要多余的操作就可以横向扩展的。Map
传统的RDBMS:
NoSQL:
大数据时代的3V :主要是描述问题的
大数据时代的3高: 主要是对程序的要求
**真正在公司中的实践:NoSQL +RDBMS 一起使用才是最强的。
1999 第一代网站架构 Perl ,CGL ,Oracle
2000 进入java时代 java,Servlet
2001-2004 EJB ,DAO,DTO
2005-2007 Without EJB重构 Spring+iBatis +Webx ,Antx ,底层架构:iSearch ,MQ+ESB ,数据挖掘 ,CMS
2008-2009 海量数据 :Memached集群,Mysql+数据切分=Cobar,分布式存储,Hadoop,KV,CDN
2010 安全、镜像 :安全,镜像,应用服务器升级,秒杀,NoSQL,SSD
2011 第五代网络架构 : 敏捷开发,开放,体验
推荐文章:阿里云的这些疯子https://www.jiqizhixin.com/articles/2018-10-18-8
Redis(Remote Dictionary Server) ,即远程服务字典
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、key-value数据库,并提供多种语言的API。
Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
免费和开源!是当下最热门的NoSQL技术之一!也被人们称之为结构化数据库!
…
Redis推荐都是在Linux服务器上搭建的,我们是基于Linux学习
Windows下安装
127.0.0.1:6379> ping //测试连接
PONG
127.0.0.1:6379> set name xqh //键值对存储
OK
127.0.0.1:6379> get name
"xqh"
127.0.0.1:6379>
Window下使用确实简单,但是redis推荐我们使用Linux去开发使用
Linux下安装
redis-benchmark是一个官方的性能测试
简单测试:
#测试:100个并发连接 100000个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
127.0.0.1:6379[3]>flushdb #清除当前数据库
ok
清除全部数据库的内容 flushAll
Redis是单线程的!
明白Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了。
Redis是C语言写的,官方提供的数据为 100000+的QPS,完全不比同时使用key-value的Memecache差
CPU>内存>硬盘的速度要有所了解
核心:redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!)对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案。
Redis是一个开源的,内存中的数据结构存储系统,它可以用作数据库、缓存和信息中间件MQ。它支持多种类型的数据结构,如 字符串,散列,列表,集合,有序集合与范围查询和地理空间索引半径查询。Reids内置了复制,LUA脚本,LRU驱动事件,事务和不同级别的磁盘持久化,并通过Redis哨兵和自动分区提供高可用性。
127.0.0.1:6379> ping #测试连接
PONG
127.0.0.1:6379> FlushALL #清空所有数据库
OK
127.0.0.1:6379> keys * #查看所有key
(empty list or set)
127.0.0.1:6379> set name xqh #新建一个key
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> set age 1
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> EXISTS name #判断当前的key是否存在
(integer) 1
127.0.0.1:6379> move name 1 #移除当前的key
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> get age
"1"
127.0.0.1:6379> EXPIRE age 10 #使这个key十秒后过期,单位是秒
(integer) 1
127.0.0.1:6379> ttl age #查看当前key的剩余时间
(integer) -2
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> type name #查看当前key的类型
string
127.0.0.1:6379> type age
string
127.0.0.1:6379> set key1 v1 #设置值
OK
127.0.0.1:6379> keys * #查看所有key
1) "key1"
127.0.0.1:6379> APPEND key1 "hello" # 拼接字符串,如果当前字符串不存在就相当于新建一个key
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> STRLEN key1 # 获取字符串的长度
(integer) 7
127.0.0.1:6379> APPEND key1 ",xqh"
(integer) 11
127.0.0.1:6379> STRLEN KEY1
(integer) 0
127.0.0.1:6379> STRLEN key1
(integer) 11
127.0.0.1:6379> get key1
"v1hello,xqh"
127.0.0.1:6379>
------------------------------------------------------------------------------------------------
127.0.0.1:6379> set views 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> incr views
(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> decr views
(integer) 0
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> INCRBY views 10 #设置步长,指定增量
(integer) 10
127.0.0.1:6379> INCRBY views 10
(integer) 20
127.0.0.1:6379>DECRBY views 5
(integer) 15
------------------------------------------------------------------------------------------------
127.0.0.1:6379> get key1
"hello,xqh"
127.0.0.1:6379> GETRANGE key1 0 3 #截取字符串 【0,3】闭区间
"hell"
127.0.0.1:6379> GETRANGE key1 0 -1 #截取所有字符串和 get key 是一样的
"hello,xqh"
127.0.0.1:6379> set key2 abcdefg
OK
127.0.0.1:6379> SETRANGE key2 1 xx # 从下标1开始替换为xx
(integer) 7
127.0.0.1:6379> get key2
"axxdefg"
------------------------------------------------------------------------------------------------
127.0.0.1:6379> setex key3 30 "hello" #设置过期时间
OK
127.0.0.1:6379> ttl key3
(integer) 24
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> setnx mykey "redis" #不存在这个key的话才设置这个值(在分布锁中会常常使用)
(integer) 1 #设置成功
127.0.0.1:6379> keys *
1) "key2"
2) "mykey"
3) "key1"
127.0.0.1:6379> setnx mykey "mongdb"
(integer) 0 #设置失败,因为已经存在mykey
127.0.0.1:6379>
------------------------------------------------------------------------------------------------
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同时设置多个值
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
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 #设置失败,k1存在,k4不存在,设置失败,说明msetnx是一个原子性操作,要么都成功,要么都失败,不能k1创建失败,而k4创建成功
(integer) 0
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379>
------------------------------------------------------------------------------------------------
127.0.0.1:6379> mset user:1:name zhangshan user:1:age 2 #对象作为值
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangshan"
2) "2"
127.0.0.1:6379>
------------------------------------------------------------------------------------------------
#getset :先获取再设置。先获取,如果不存在就返回nil并创建
127.0.0.1:6379> getset db redis #获取db,db不存在所以返回nil,并创建一个db:redis
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongdb # 获取db,db存在先获取原来的值,再赋新的值覆盖
"redis"
127.0.0.1:6379> get db
"mongdb"
127.0.0.1:6379>
------------------------------------------------------------------------------------------------
在redis里面,我们可以把list玩成栈、队列、阻塞队列
redis不区分大小写命令
127.0.0.1:6379> ping #测试连接
PONG
127.0.0.1:6379> keys *
(empty list or set)
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 righr #将一个值或者多个值 插入到列表的尾部
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "righr"
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "righr"
127.0.0.1:6379> lpop list #移除列表list第一个元素
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "righr"
127.0.0.1:6379> rpop list #移除列表list最后一个元素
"righr"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1 #通过下标获取list中某一个值
"one"
127.0.0.1:6379> llen list #获取列表长度
(integer) 2
#移除指定元素
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"
127.0.0.1:6379> lrem list 1 one #移除列表中1一个one
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "zero"
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) "zero"
127.0.0.1:6379> lrem list 2 three #移除列表中两个three
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "zero"
#trim:截取,保留部分元素
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> rpush mylist "hello3"
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello2"
4) "hello3"
127.0.0.1:6379> ltrim mylist 1 2 #通过下标截取指定长度 只保留下标1到2
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
#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 #移除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"
127.0.0.1:6379> exists list #列表是否存在
(integer) 0
127.0.0.1:6379> lpush list value1
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "value1"
127.0.0.1:6379> lset list 0 item # 将列表中指定下标的值替换为另一个值,更新操作。
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
127.0.0.1:6379> lset list 1 other #列表不存在下标1,所以报错
(error) ERR index out of range
#linsert 将某个值插入到列表中某个值的前面或者后面
127.0.0.1:6379> lpush mylist "hello"
(integer) 1
127.0.0.1:6379> lpush mylist "hello1"
(integer) 2
127.0.0.1:6379> linsert mylist before "hello1" "other" #在hello1的前面插入other
(integer) 3
127.0.0.1:6379> lrange list 0 -1
(empty list or set)
127.0.0.1:6379> lrange mylist 0 -1
1) "other"
2) "hello1"
3) "hello"
127.0.0.1:6379> linsert mylist after "hello1" "other" #在hello1的后面插入other
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "other"
2) "hello1"
3) "other"
4) "hello"
127.0.0.1:6379> sadd myset "hello" #set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset "xqh"
(integer) 1
127.0.0.1:6379> sadd myset "love"
(integer) 1
127.0.0.1:6379> smembers myset #查看指定set的所有值(无序、不重复的集合)
1) "xqh"
2) "love"
3) "hello"
127.0.0.1:6379> sismember myset hello #判断一个元素是否在set集合中,有---1 ,没有---0
(integer) 1
127.0.0.1:6379> sismember myset xxx
(integer) 0
127.0.0.1:6379> scard myset #获取set中值的个数
(integer) 3
127.0.0.1:6379> srem myset "hello" #移除集合中某一个元素
(integer) 1
127.0.0.1:6379> smembers myset
1) "xqh"
2) "love"
#随机抽出指定个数的元素
127.0.0.1:6379> smembers myset
1) "love"
2) "hello"
3) "hello3"
4) "xqh"
5) "hello2"
6) "hello1"
127.0.0.1:6379> srandmember myset
"hello1"
127.0.0.1:6379> srandmember myset
"hello3"
127.0.0.1:6379> srandmember myset
"hello"
127.0.0.1:6379> srandmember myset 2
1) "hello"
2) "hello2"
127.0.0.1:6379> srandmember myset 2
1) "hello1"
2) "hello2"
#随机删除
127.0.0.1:6379> smembers myset
1) "hello3"
2) "xqh"
3) "love"
4) "hello2"
5) "hello1"
6) "hello"
127.0.0.1:6379> spop myset #随机删除集合中的一个元素
"love"
127.0.0.1:6379> smembers myset
1) "hello3"
2) "xqh"
3) "hello2"
4) "hello1"
5) "hello"
#将一个指定的值移除并且添加到另一个集合中
127.0.0.1:6379> sadd myset2 "set2"
(integer) 1
127.0.0.1:6379> smove myset myset2 "xqh"
(integer) 1
127.0.0.1:6379> smembers myset2
1) "xqh"
2) "set2"
#交集,并集,差集(不同)
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key2 e
(integer) 1
127.0.0.1:6379> smembers key1
1) "c"
2) "a"
3) "b"
127.0.0.1:6379> smembers key2
1) "d"
2) "c"
3) "e"
127.0.0.1:6379> sdiff key1 key2 #差集,不同的元素
1) "a"
2) "b"
127.0.0.1:6379> sinter key1 key2 #交集 相同的部分 ,共同的好友可以这样实现
1) "c"
127.0.0.1:6379> sunion key1 key2 #并集
1) "b"
2) "a"
3) "d"
4) "e"
5) "c"
127.0.0.1:6379> hset myhash field1 xqh #set一个具体的key-value
(integer) 1
127.0.0.1:6379> hget myhash field1 # 取出值
"xqh"
127.0.0.1:6379> hmset myhash field1 hello field2 world #set多个key-value
OK
127.0.0.1:6379> hmget myhash field1 field2 #取出多个值
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash #查看所有key-value
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> hdel myhash field1 #删除hash指定key字段,对应的value值也消失了
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
#获取长度
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
3) "field1"
4) "hello"
5) "field3"
6) "xqh"
7) "field"
8) "xqhx"
127.0.0.1:6379> hlen myhash #获取哈希表的字段数量
(integer) 4
127.0.0.1:6379> hexists myhash field1 #判断指定字段是否存在
(integer) 1
#获取所有的key,获取所有的value
127.0.0.1:6379> hkeys myhash
1) "field2"
2) "field1"
3) "field3"
4) "field"
127.0.0.1:6379> hvals myhash
1) "world"
2) "hello"
3) "xqh"
4) "xqhx"
127.0.0.1:6379> hgetall myhash
1) "field1"
2) "4"
127.0.0.1:6379> hincrby myhash field1 -2 # hincrby自增,正是就增加,负数就减少
(integer) 2
127.0.0.1:6379> hsetnx myhash field4 hello #如果field4不存在就添加这个元素,所以这里添加成功
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 xqh #因为field4已经存在了,所以这里添加失败
(integer) 0
127.0.0.1:6379> zadd myset 1 one #添加一个值 (中间要加一个数字,代表优先级,从1到后面,排序越后)
(integer) 1
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zadd myset 3 three 4 four
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
4) "four"
127.0.0.1:6379> zrange salary 0 -1 #从小到大
1) "xiaohong"
2) "zhangshan"
3) "xiaolan"
127.0.0.1:6379> zrevrange salary 0 -1 #从大到小
1) "xiaolan"
2) "zhangshan"
3) "xiaohong"
127.0.0.1:6379> zrange salary 0 -1 withscores #升序排列显示全部的用户并带上成绩值
1) "xiaohong"
2) "2500"
3) "zhangshan"
4) "5000"
5) "xiaolan"
6) "20000"
127.0.0.1:6379> zrevrange salary 0 -1 withscores #降序排列显示全部的用户并带上成绩值
1) "xiaolan"
2) "20000"
3) "zhangshan"
4) "5000"
5) "xiaohong"
6) "2500"
127.0.0.1:6379> zrangebyscore salary -inf 10000 withscores #小于10000工资的用户升序排列
1) "xiaohong"
2) "2500"
3) "zhangshan"
4) "5000"
127.0.0.1:6379> zrevrangebyscore salary +inf 3000 withscores #大于3000工资的降序排列
1) "xiaolan"
2) "20000"
3) "zhangshan"
4) "5000"
#删除指定元素
127.0.0.1:6379> zrange salary 0 -1
1) "xiaohong"
2) "zhangshan"
3) "xiaolan"
127.0.0.1:6379> zrem salary xiaohong #删除xiaohong
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "zhangshan"
2) "xiaolan"
#获取有序集合中的个数
127.0.0.1:6379> zcard salary
(integer) 2
#获取指定区间的成员数量
127.0.0.1:6379> zrange salary 0 -1 withscores
1) "zhangshan"
2) "5000"
3) "xiaochen"
4) "10000"
5) "xiaolan"
6) "20000"
127.0.0.1:6379> zcount salary 5000 20000
(integer) 3
案例思路:带权重进行判断。set 排序 存储班级成绩表 ,工资表排序
排行榜应用实现。
Redis的Geo 这个功能可以推算地理位置的信息,两地之间的距离等
可以查询一些测试数据
################################################################################################
#geoadd 添加地理位置
#规则:两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
#有效的经度从-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 chongqi 114.05 22.52 shenzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
################################################################################################
# 获取当前定位:一定是一个坐标值
127.0.0.1:6379> geopos china:city beijing #获取指定城市的经度和纬度
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city beijing shanghai
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "121.47000163793563843"
2) "31.22999903975783553"
################################################################################################
#两人之间的距离
#单位
#m 表示单位为米。
#km 表示单位为千米。
#mi 表示单位为英里
#ft 表示单位为英尺。
#如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。
127.0.0.1:6379> geodist china:city beijing shanghai km #查看上海到北京的直线距离,单位km
"1067.3788"
#################################################################################################
#查找附近的人(需要获得所有附近的人的地址,定位!)通过半径来查询
#获取指定数量的人
#前提:所有数据应该都录入:china:city
127.0.0.1:6379> georadius china:city 110 30 1000 km #以110,30这个经纬度为中心,寻找方圆1000km内的城市
1) "chongqi"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 500 km #以110,30这个经纬度为中心,寻找方圆500km内的城市
1) "chongqi"
2) "xian"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist #以110,30这个经纬度为中心,寻找方圆500km内的城市,顺带输出离中心距离(110,30)的位置
1) 1) "chongqi"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord #以110,30这个经纬度为中心,寻找方圆500km内的城市,顺带输出他人的定位信息(主要是经纬度)
1) 1) "chongqi"
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 withcoord count 1 #以110,30这个经纬度为中心,寻找方圆500km内的城市,筛选出指定数量的数据
1) 1) "chongqi"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
################################################################################################
#找出位于指定元素周围的其他元素
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km #查找北京附近1000km的城市
1) "beijing"
2) "xian"
#################################################################################################
#geohash该命令将返回11个字符的geohash字符串
#将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近
127.0.0.1:6379> geohash china:city beijing chongqi
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
#################################################################################################
#GEO底层的实现原理其实就是zset!我们可以使用zset命令来操作geo
127.0.0.1:6379> zrange china:city 0 -1 #查看地图中全部的元素,之前通过geoadd添加进去的
1) "chongqi"
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) "chongqi"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
A{1,3,5,7,8,7}
B{1,3,5,7,8}
基数(不重复的元素) =5
基数统计的算法!网页的UV(一个人访问一个网站多次,但是还是算作一个人)
传统的方式,set保存用户的id ,然后就可以统计set中的元素数量作为标准判断
这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id
优点 :占用的内存是固定,2^64 不同的元素的技术,只需要费 12kb内存,所以如果要从内存角度来比较的话 Hyperloglog 首选
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 l m n b v
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 8
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 #将第一组和第二组合并到第三组中,不能有重复的元素,所以一共是15个
OK
127.0.0.1:6379> pfcount mykey3
(integer) 15
统计用户信息,活跃、不活跃!登录、未登录!打开等,两个状态的都可以使用bitmaps
Bitmaps 位图,数据结构 !都是操作二进制来进行记录,就只有0和1两个状态
#记录周一到周日的打卡情况。下标0-6代表周一至周日,0代表未打卡,1代表打卡
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0
#查看某一天是否打卡
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 5
(integer) 0
#统计打卡天数
127.0.0.1:6379> bitcount sign #统计这周打卡记录
(integer) 5
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
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)
#编译型异常(命令有错,代码有问题)
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 k3
(nil)
#运行时异常
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 "v1"
QUEUED
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) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) OK
5) "v3"
127.0.0.1:6379> get k2 #其他的命令行成功执行
"v2"
悲观锁:认为什么时候都会出问题,无论做什么都会加锁
乐观锁:认为什么时候都不会出问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人修改过和这个数据,version
获取version
更新的时候比较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乐观锁操作!
unwath 先解除监视,再重新监视 watch money
如果修改失败,获取最新的值就行。
我们要使用Java来操作Redis
<dependencies>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>4.2.3version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>2.0.2version>
dependency>
dependencies>
package com.qian;
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());//测试连接
}
}
//输出:pong
package com.qian;
import redis.clients.jedis.Jedis;
import java.util.Set;
public class TestKey {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println("清空数据:"+jedis.flushDB());
System.out.println("判断某个键是否存在:"+jedis.exists("username"));
System.out.println("新增键值对:"+jedis.set("username","xqh"));
System.out.println("新增键值对:"+jedis.set("password","123456"));
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("新增键值对:"+jedis.set("project","chinese"));
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.flushDB());
System.out.println("删除所有数据库中的所有key:"+jedis.flushAll());
}
}
...
//和之前的命令一模一样,需要使用可以直接翻前面的笔记喔
package com.qian;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.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","xqh");
//开启事务
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(); //关闭连接
}
}
}
//代码错误,事务执行失败,get不到,输出null
SpringBoot操作数据
jedis:采用的是直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool 连接池,BIO模式
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数据,更像NIO模式
默认的RedisTemplate 没有过多的设置,redis对象都是需要序列化
两个泛型都是 object ,object的类型,我们后使用需要强制转换
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
# 配置redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
package com.example;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class Redis02SpringbootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//redisTemplate 操作不同的数据类型,api和我们的命令是一样的
//opsForValue:操作字符串 类似String
//opsForList :操作List 类似List
//opsForSet
//opsForHash()
//opsForGeo()
//opsForZSet()
//opsForHyperLogLog()
//除了基本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务和基本的增删改查
//获取redis的连接对象
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// connection.flushDb();
// connection.flushAll();
redisTemplate.opsForValue().set("mukey","xqh");
System.out.println(redisTemplate.opsForValue().get("mukey"));
}
}
package com.example.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Component
@AllArgsConstructor
@NoArgsConstructor
@Data
//在企业中,我们的所有pojo都会序列化
public class User implements Serializable {
private String name;
private int age;
}
//test
@Test
public void test() throws JsonProcessingException {
//真实的开发一般都使用json来传递对象
User user = new User("java", 3);
//String jsonUser = new ObjectMapper().writeValueAsString(user);
redisTemplate.opsForValue().set("user",user);
Object user1 = redisTemplate.opsForValue().get("user"); //直接传递对象会报错,所有的对象传递要先序列化!
System.out.println(user1);
}
@Configuration
public class RedisConfig {
//固定模板,企业中可以拿去直接使用
//自定义一个RedisTemplate
@Bean
@SuppressWarnings("all")
public RedisTemplate<String,Object>redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//我们为了自己开发方便,一般直接使用
RedisTemplate<String,Object>template=new RedisTemplate<String,Object>();
//连接工厂
template.setConnectionFactory(redisConnectionFactory);
//配置具体的序列化方式
//将任意对象解析成序列化 用json
//json的序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//Stirng的序列化
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.example.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.Collection;
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;
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
// =============================common============================
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
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
* @return
*/
public boolean isExpire(String key) {
return getExpire(key) > 1 ? false : true;
}
/**
* 根据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((Collection<String>) 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 value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, timeUnit);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几
* @return
*/
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
* @return 值
*/
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 对应多个键值
* @return true 成功 false 失败
*/
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)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
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 键
* @return
*/
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代表所有值
* @return
*/
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 键
* @return
*/
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倒数第二个元素,依次类推
* @return
*/
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 值
* @return
*/
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 时间(秒)
* @return
*/
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;
}
}
}
在我们真实的开发中,或者你们在公司,一般都可以看到一个公司自己封装RedisUtil
测试,有了工具类后使用更加方便
@Autowired
private RedisUtils redisUtils;
@Test
public void test1(){
redisUtils.set("name","xqh");
System.out.println(redisUtils.get("name"));
}