笔记
Redis入门Remote Dictionary Server:远程字典服务
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
redis目前提供四种数据类型:string,list,set及zset(sorted set)。
1.内存存储,持久化,内存汇总是断电即失的,(RDB.AOF)
2.效率高
3.发布订阅系统
4.地图信息分析
5.计时器,计数器(浏览量,点赞)
redis默认有16个库0-15
1.基本命令
选择2号数据库 select 2 查看DB大小 DBSIZE 查看数据库所有的key keys * 清除全部数据库的内容 flushall 清除当前数据库 flushdb
redis是单线程的
为什么单线程还是那么快?
redis将所有的数据全部放到内存中,所以说单线程去操作效率是最高的(多线程CPU上下文会切换,耗时),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上. redis命令
查看键的类型 type key
五大数据类型 1.键 Key 1.1 get set设置获取键值
127.0.0.1:6379> set key1 v1 set设置值 OK 127.0.0.1:6379> get key1 get获取值 "v1" 127.0.0.1:6379> keys * keys * 获得该库的所有值 "key1" 127.0.0.1:6379> exists key1 exists判断是否存在键 (integer) 1 127.0.0.1:6379> append key1 key2 append追加字符串 append 键 值 (integer) 6 127.0.0.1:6379> get key1 "v1key2" 127.0.0.1:6379> strlen key1 strlen返回键的长度 (integer) 6
1.2.增量(增减) incr decr incrby decrby
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 (integer) 1 127.0.0.1:6379> get views "1" 127.0.0.1:6379> decr views 自减1 (integer) 0 127.0.0.1:6379> get views "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 设置指定增量 incrby 键 增量 (integer) 9 127.0.0.1:6379> get views "9" 127.0.0.1:6379> decrby views 7 设置指定减量 (integer) 2 127.0.0.1:6379> get views "2"
1.3字符串切片 getrange
127.0.0.1:6379> get key1 "nameoplokijuhygt" 127.0.0.1:6379> getrange key1 0 3 截取字符串 0,1,2,3 getrange 键 开始索引 结束索引 "name" 127.0.0.1:6379> getrange key1 0 -1 获取全部的字符串 "nameoplokijuhygt"
1.4替换字符串 setrange
127.0.0.1:6379> set key2 abcdefg OK 127.0.0.1:6379> get key2 "abcdefg" 127.0.0.1:6379> setrange key2 2 xx 替换指定位置开始的字符串 (integer) 7 127.0.0.1:6379> get key2 "abxxefg"
1.5过期时间 setex
127.0.0.1:6379> setex key3 30 "nihao" setex 键 过期时间 值 设置过期时间 OK 127.0.0.1:6379> get key3 "nihao" 127.0.0.1:6379> ttl key3 查看ttl时间 (integer) 23 127.0.0.1:6379> setnx mykey "redis" setnx如果不存在则设置,成功返回1,失败返回0 (integer) 1 127.0.0.1:6379> ttl key3 (integer) 0 127.0.0.1:6379> setnx mykey "mysql" setnx,返回0,设置失败 (integer) 0 127.0.0.1:6379> get mykey 没有修改mykey的值 "redis"
1.6 mset mget设置多个值
127.0.0.1:6379> mset name "gg" age 89 type "student" mset 键 值 键 值... OK 127.0.0.1:6379> keys * 1) "name" 2) "type" 3) "age" 127.0.0.1:6379> mget name age type mget 键 键 键... 1) "gg" 2) "89" 3) "studen 127.0.0.1:6379> msetnx address qin name oo oop java msetnx是一个原子操作,name存在,全部设置失败 (integer) 0 127.0.0.1:6379> get oop 则oop设置失败查询不到 (nil) 127.0.0.1:6379> get name "gg"
1.7对象的巧妙设计
127.0.0.1:6379> mset user:1:name gg user:1:age 78 OK 127.0.0.1:6379> mget user:1:name user:1:age 1) "gg" 2) "78"
1.8 getset先get 再set 获去旧值,设置新值
127.0.0.1:6379> getset db redis 没有旧值,get为nil,set执行 db=redis (nil) 127.0.0.1:6379> get db "redis" 127.0.0.1:6379> getset db mongdb 有旧值,getdb=redis,然后执行setdb=mongdb "redis" 127.0.0.1:6379> get db "mongdb"
String类似的使用场景:value除了是我们还可以是我们的数字
127.0.0.1:6379> lpush list one 将一个值或者多个值插入到列表的头部 (integer) 1 (0.70s) 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 获取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 获取list全部的值 1) "three" 2) "two" 3) "one" 4) "right"
2.2 移除 lpop
127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "two" 3) "one" 4) "right" 127.0.0.1:6379> lpop list 移除列表的第一个元素 "three" 127.0.0.1:6379> rpop list 移除列表的最后一个元素 "right" 127.0.0.1:6379> lrange list 0 -1 1) "two" 2) "one"
移除指定的值 lrem
127.0.0.1:6379> lrange list 0 -1 1) "three1" 2) "three2" 3) "three2" 4) "three1" 5) "three" 6) "three" 7) "two" 8) "one" 127.0.0.1:6379> lrem list 1 three1 lrem 键 数量 值 (integer) 1 127.0.0.1:6379> lrange list 0 -1 1) "three2" 2) "three2" 3) "three1" 4) "three" 5) "three" 6) "two" 7) "one"
2.3 通过下标获取值 lindex
127.0.0.1:6379> lrange list 0 -1 1) "two" 2) "one" 127.0.0.1:6379> lindex list 1 获取下标为1的值 "one" 127.0.0.1:6379> lindex list 0 获取下标为0的值 "two" 127.0.0.1:6379> llen list 获取列表的长度 (integer) 2
2.4 截取指定的长度 ltrim
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 ltrim 键 开始索引 结束索引 OK 127.0.0.1:6379> lrange mylist 0 -1 1) "hello1" 2) "hello2"
2.5 移动 rpoplpush
127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "hello1" 3) "hello2" 127.0.0.1:6379> rpoplpush mylist myother 移除最后一个元素,移动到其他列表 "hello2" 127.0.0.1:6379> lrange myother 0 -1 1) "hello2" 127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "hello1" 127.0.0.1:6379>
2.6 lset
127.0.0.1:6379> lpush list "hello" (integer) 1 127.0.0.1:6379> lrange list 0 0 1) "hello" 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> lset list 1 item1 不存在的会报错 (error) ERR index out of range
3.Set 集合 集合中没有重复的元素 3.1 添加 查看 sadd smembers sisimember
127.0.0.1:6379> sadd myset "hello" set集合中添加元素 (integer) 1 127.0.0.1:6379> sadd myset "gg" (integer) 1 127.0.0.1:6379> sadd myset "nihao " (integer) 1 127.0.0.1:6379> smembers myset 查看set集合中的值 1) "nihao " 2) "gg" 3) "hello" 127.0.0.1:6379> sismember myset nihao 查看set集合中是否存在某个值 (integer) 0 127.0.0.1:6379> sismember myset nihao (integer) 0 127.0.0.1:6379> sismember myset gg (integer) 1 127.0.0.1:6379> sismember myset "nihao " (integer) 1
3.2 指定移除 srem scard
127.0.0.1:6379> srem myset "nihao " (integer) 1 127.0.0.1:6379> scard myset 获取集合的长度 (integer) 2 127.0.0.1:6379> smembers myset 1) "gg" 2) "hello"
3.3 随机获取元素 srandmember myset count 多个元素
127.0.0.1:6379> srandmember myset "pop" 127.0.0.1:6379> srandmember myset "gg"
3.4.随机删除key spop
127.0.0.1:6379> spop myset 随机删除set集合中的元素 "gg" 127.0.0.1:6379> spop myset "hello" 127.0.0.1:6379> spop myset "pop" 127.0.0.1:6379> spop myset
3.5.移动集合元素 smove 集合1 集合2 值
127.0.0.1:6379> smove myset myset1 "hell1" 将一个集合中的值移动到另一个集合 (integer) 1 127.0.0.1:6379> smembers myset 1) "heopop" 2) "hello" 127.0.0.1:6379> smembers myset1 1) "ss" 2) "hell1"
3.6 集合 交差并补
key1 a b c key2 b c e 127.0.0.1:6379> sdiff key1 key2 key1与key2的差集 1) "a" 127.0.0.1:6379> sdiff key2 key1 key12与key1的差集 1) "e" 127.0.0.1:6379> sinter key1 key2 key1与key2的交集 1) "c" 2) "b" 127.0.0.1:6379> sinter key2 key1 1) "c" 2) "b" 127.0.0.1:6379> sunion key1 key2 key1与key2的并集 1) "b" 2) "e" 3) "c" 4) "a"
4.Hash 哈希 4.1 设置-获取 hget mset hmget hmset hgetall
127.0.0.1:6379> hset myhash name1 "gg" hset 键 字段 值 (integer) 1 127.0.0.1:6379> hget myhash name1 "gg" hmset 键 字段1 值1 字段2 值2... 127.0.0.1:6379> hmset myhash name1 "hello" name2 gg name3 woow OK 127.0.0.1:6379> hmget myhash name1 name2 name3 1) "hello" 2) "gg" 3) "woow" 127.0.0.1:6379> hgetall myhash 1) "name1" 2) "hello" 3) "name2" 4) "gg" 5) "name3" 6) "woow"
4.2 删除指定的字段 hdel
127.0.0.1:6379> hdel myhash name1 hdel 键 字段 (integer) 1 127.0.0.1:6379> hgetall myhash 1) "name2" 2) "gg" 3) "name3" 4) "woow"
4.3 获取hash的长度
127.0.0.1:6379> hlen myhash (integer) 2
4.4 判断hash中的指定字段是否存在 hexists
127.0.0.1:6379> hexists myhash name2 (integer) 1 127.0.0.1:6379> hexists myhash name1 (integer) 0 127.0.0.1:6379>
4.5 只获取键或只获取值 hkeys hvals
127.0.0.1:6379> hkeys myhash 1) "name2" 2) "name3" 127.0.0.1:6379> hvalues myhash (error) ERR unknown command 'hvalues' 127.0.0.1:6379> hvals myhash 1) "gg" 2) "woow"
4.6 hash增量 hincrby 键 字段 增量
127.0.0.1:6379> hset myhash name4 5 (integer) 1 127.0.0.1:6379> hincrby myhash name4 1 (integer) 6 127.0.0.1:6379> hget myhash name4 "6" 127.0.0.1:6379> hincrby myhash name4 -1 (integer) 5
4.7 hsetnx 存在则设置,不存在设置失败
127.0.0.1:6379> hsetnx myhash name5 op (integer) 1 127.0.0.1:6379> hsetnx myhash name5 op (integer) 0
5.Zset 有序集合 5.1 添加 查询 zadd zrange[有序集合的遍历相当于列表]
127.0.0.1:6379> zadd myset 1 one zadd 键 权重 成员 (integer) 1 (0.70s) 127.0.0.1:6379> zadd myset 2 two 3 three zadd 键 权重 成员 权重 成员... (integer) 2 127.0.0.1:6379> zrange myset 0 -1 zrange 键 开始索引 结束索引 1) "one" 2) "two" 3) "three"
5.2 按集合权重排序 http://doc.redisfans.com/sorted_set/zrangebyscore.html
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 288 gg (integer) 1 127.0.0.1:6379> zrangebyscore salary -inf +inf 1) "gg" 2) "xiaohong" 3) "zhangsan"
拓展:
ZRANGEBYSCORE zset (1 5 返回所有符合条件 1 < score <= 5 的成员,而 ZRANGEBYSCORE zset (5 (10 则返回所有符合条件 5 < score < 10 的成员。 区间及无限 min 和 max 可以是 -inf 和 +inf ,这样一来,你就可以在不知道有序集的最低和最高 score 值的情况下,使用 ZRANGEBYSCORE 这类命令。 可选的 LIMIT 参数指定返回结果的数量及区间(就像SQL中的 SELECT LIMIT offset, count ),注意当 offset 很大时,定位 offset 的操作可能需要遍历整个有序集,此过程最坏复杂度为 O(N) 时间。 ZRANGEBYSCORE salary -inf +inf WITHSCORES # 显示整个有序集及成员的 score 值 1) "jack" 2) "2500" 3) "tom" 4) "5000" 5) "peter" 6) "12000" redis> ZRANGEBYSCORE salary -inf 5000 WITHSCORES # 显示工资 <=5000 的所有成员 1) "jack" 2) "2500" 3) "tom" 4) "5000" redis> ZRANGEBYSCORE salary (5000 400000 # 显示工资大于 5000 小于等于 400000 的成员 1) "peter"
5.3 移除zrem 获取长度 zcard
127.0.0.1:6379> zrange salary 0 -1 1) "gg" 2) "xiaohong" 3) "zhangsan" 127.0.0.1:6379> zrem salary xiaohong (integer) 1 127.0.0.1:6379> zrange salary 0 -1 1) "gg" 2) "zhangsan" 127.0.0.1:6379> zcard salary (integer) 2
5.4 升序与降序 zrangebyscore zrevrange
redis> ZRANGE salary 0 -1 WITHSCORES # 递增排列 1) "peter" 2) "3500" 3) "tom" 4) "4000" 5) "jack" 6) "5000" redis> ZREVRANGE salary 0 -1 WITHSCORES # 递减排列 1) "jack" 2) "5000" 3) "tom" 4) "4000" 5) "peter" 6) "3500"
5.5 计算返回成员数量 zcount 包括min和max值 大于等于,小于等于
redis> ZRANGE salary 0 -1 WITHSCORES # 测试数据 1) "jack" 2) "2000" 3) "peter" 4) "3500" 5) "tom" 6) "5000" redis> ZCOUNT salary 2000 5000 # 计算薪水在 2000-5000 之间的人数 (integer) 3 redis> ZCOUNT salary 3000 5000 # 计算薪水在 3000-5000 之间的人数 (integer) 2
三种特殊数据类型 1.geospatial 地理位置 朋友的定位,附近的人,打车的距离 6个命令
127.0.0.1:6379> PFadd mykey a b c d e f g h i j k 创建第一组元素 (integer) 1 127.0.0.1:6379> PFcount mykey 统计元素 (integer) 11 127.0.0.1:6379> PFadd mykey2 p o i u y t r e w 创建第二组元素 (integer) 1 127.0.0.1:6379> PFcount mykey2 (integer) 9 127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 合并两组数据(并集) OK 127.0.0.1:6379> pfcount mykey3 (integer) 17
127.0.0.1:6379> PFadd mykey a b c d e f g h i j k 创建第一组元素 (integer) 1 127.0.0.1:6379> PFcount mykey 统计元素 (integer) 11 127.0.0.1:6379> PFadd mykey2 p o i u y t r e w 创建第二组元素 (integer) 1 127.0.0.1:6379> PFcount mykey2 (integer) 9 127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 合并两组数据(并集) OK 127.0.0.1:6379> pfcount mykey3 (integer) 17 如果允许容错,就使用 如果不允许容错,就别用吧~ 3.Bitmap 位存储 可以用来存储两个状态的值,还有0和1两个值 统计疫情感染人数: 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 1 0(感染与否) 统计用户信息: 活跃 不活跃 登录 未登录 打卡 未打卡:例子
127.0.0.1:6379> setbit sign 0 1 (integer) 0 127.0.0.1:6379> setbit sign 1 1 (integer) 0 127.0.0.1:6379> setbit sign 2 1 (integer) 0 127.0.0.1:6379> setbit sign 3 0 (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 0 (integer) 0 127.0.0.1:6379> getbit sign 4 (integer) 1 127.0.0.1:6379> getbit sign 3 (integer) 0 127.0.0.1:6379>
统计:返回bitmap为1的值的总数
127.0.0.1:6379> bitcount sign (integer) 4
事务 Redis事务本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按顺序执行 一次性,顺序性,排他性. Redis事务没有隔离级别的概念 所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行. Redis单条命令是保证原子性的,但是事务不保证原子性 redis的事务:
127.0.0.1:6379> multi 开启事务 (error) ERR MULTI calls can not be nested 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) OK 4) "v2" 5) OK (0.75s) 127.0.0.1:6379>
2.放弃事务 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> discard 取消事务 OK 127.0.0.1:6379> get k2 (nil)
3.编译型异常(代码有问题!命令有错!),事务中的所有的命令都不会被执行
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> exec 执行事务报错,所有的命令都不会执行 (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k2 (nil)
运行时异常(1/0),如果事务队列中存在语法型,那么执行命令的时候,其他命令是可以正常执行,错误命令抛出异常
127.0.0.1:6379> set k1 "v1" OK 127.0.0.1:6379> get k1 "v1" 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 v4 执行成功 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) "v4"
监控 悲观锁:
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
线程1
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 (nil)
线程2:线程1没完成之前线程2线改变了money的值,执行失败,返回nil
127.0.0.1:6379> get money "80" 127.0.0.1:6379> set money 100 OK 127.0.0.1:6379> get money "100"
解决方案: 1.如果发现事务执行失败,就先解锁(unwatch) 2.获取最新的值,再次监视,select version 3.对比监视的值是否发生了变化,如果没有发生变化,那么执行成功,如果变化了就执行失败
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) 90 2) (integer) 30
锁:redis可以实现乐观锁
SpringBoot整合 SpringBoot操作数据:spring-data JPA JDBC Mongdb redis SpringData也是和SpringBoot齐名的项目 在SpringBoot2.x之后,原来使用的jedis被替换为了lettuce jedis与lettuce的区别 jedis:采用直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池!像BIO模式 lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况,可以减少线程数量,像NIO模式 1.导入依赖 2.配置连接 3.测试 redis传递对象需要序列化 1.方式1:使用json序列化方法
User user = new User("序列化", 3); String jsonUser = new ObjectMapper().writeValueAsString(user); redisTemplate.opsForValue().set("user",jsonUser); System.out.println(redisTemplate.opsForValue().get("user"));
2.方式2:实体类实现Serializable接口
public class User implements Serializable {
3.方式3:自定义redisTemplate
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> myRedisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(redisConnectionFactory); //配置具体的序列化方式 Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //2021-02-03 官方不建议使用 // objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //2021-02-03 使用这个 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); template.setKeySerializer(stringRedisSerializer); template.setValueSerializer(objectJackson2JsonRedisSerializer); template.setHashKeySerializer(stringRedisSerializer); template.setHashValueSerializer(objectJackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
//注入 @Autowired @Qualifier("myRedisTemplate") private RedisTemplate redisTemplate;