Redis:
目录
Redis全解下半部分---狂神Redis笔记_多 啦A夢的博客-CSDN博客
redis是非关系型数据库,存储的是k v结构,内存中的数据结构存储系统,它可以作为数据库,缓存和消息中间件.redis有多种类型数据结构,字符串(String) ,散列(hashes),列表(lists),集合(sets),有序集合 (sorted sets) 与范围查询,bitmaps,hyperloglog和地理空间(geospatial) 索引半径查询.redis内置了 复制(replication),LUA脚本(Lua scripting),LRU驱动事件(LRUeviction),事务(transactions)和不同级别的 磁盘持久化(persistence),并通过Redis哨兵(Sentinel) 和自动 分区(Cluster) 提供高可用性(highavailability).
NoSql的四大分类
kv键值
(1)新浪:BerkeleyDB+redis
(2)美团:redis+tair
(3)阿里、百度:memcache+redis
列存储数据库
(1)Cassandra, HBase
(2)分布式文件系统
图形数据库
(1)它不是放图形的,放的是关系比如:朋友圈社交网络、广告推荐系统
(2)社交网络,推荐系统等。专注于构建关系图谱
(3)Neo4J, InfoGrid
文档型数据库
(1)CouchDB
(2)MongoDB
四者对比
redis 的基本命令
redis-server /redis.conf(路径)启动redis
[root@localhost bin]# ls
dump.rdb redis-check-aof redis-cli redis-server
redis-benchmark redis-check-rdb redis-sentinel yconfig
[root@localhost bin]# redis-server yconfig/redis.conf ##启动
2525:C 01 Sep 2020 09:28:24.121 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
2525:C 01 Sep 2020 09:28:24.121 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=2525, just started
2525:C 01 Sep 2020 09:28:24.121 # Configuration loaded
redis-cli -p 6379 连接数据库
[root@localhost bin]# redis-cli -p 6379
127.0.0.1:6379> ping
PONG
set key value( 设置kv)
get key value(获取kv)
flushall (清空所有数据库-redis默认一共有十六个)
127.0.0.1:6379[2]> flushall
OK
127.0.0.1:6379[2]> keys *
(empty array)
flushdb (清空当前数据库)
exists key(判断当前key是否存在---按照查询k的数量来返回对应的integer ,如果不存在返回零)
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists nam
(integer) 0
select 2(跳转到3号数据库)
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]>
keys *(查找本数据库所有的key)
127.0.0.1:6379[2]> keys *
1) "age"
2) "name"
move k db(删除本库中的k,数据库从0开始,类似与下标,select 2就是3号数据库)
127.0.0.1:6379[2]> move name 3
(integer) 1
127.0.0.1:6379[2]> keys *
1) "age"
expire key 秒
127.0.0.1:6379[2]> set name yan
OK
127.0.0.1:6379[2]> EXPIRE name 20
(integer) 1
127.0.0.1:6379[2]> ttl name
(integer) 13
ttl key(时间过去会显示-2)
127.0.0.1:6379[2]> ttl name
(integer) -2
type key (查看k的类型)
127.0.0.1:6379[2]> TYPE name
string
五大基本类型
String(字符串)
127.0.0.1:6379> set k1 xxx # 设置值
OK
127.0.0.1:6379> get k1 #获得值
"xxx"
127.0.0.1:6379> keys * #获得所有的key
1) "k1"
127.0.0.1:6379> EXISTS k1 #判断某一个key是否存在
(integer) 1
127.0.0.1:6379> APPEND k1 "hello"#追加字符串,如果当前key不存在,就相当setkey
(integer) 8
127.0.0.1:6379> get k1
"xxxhello"
127.0.0.1:6379> APPEND k1 ",yan"
(integer) 12
127.0.0.1:6379> STRLEN k1 #获取字符串长度
(integer) 12
127.0.0.1:6379> get k1
"xxxhello,yan"
####################################################################
步长概念
主要用于各种网页浏览量,类似与java的自增自减
127.0.0.1:6379> set li 0 #初始化
OK
127.0.0.1:6379> incr li #自增1
(integer) 1
127.0.0.1:6379> incr li
(integer) 2
127.0.0.1:6379> incrby li 5 #可以设置步长,指定增量!
(integer) 7
127.0.0.1:6379> decr li
(integer) 6
127.0.0.1:6379> decr li #自减
(integer) 5
127.0.0.1:6379> decr li
(integer) 4
127.0.0.1:6379> decrby li 3 #设置步长,指定减量!
(integer) 1
获取字符串范围 #range
127.0.0.1:6379> set key1 buhuiyou
OK
127.0.0.1:6379> get key1
"buhuiyou"
127.0.0.1:6379> SETRANGE key1 0 yihouxxx #替换字符串从下标0个开始
(integer) 8
127.0.0.1:6379> get key1
"yihouxxx"
##########################################################
#serex (set with expire) #设置过期时间
#setnx(set if not exist) #不存在设置(分布式锁中会常常使用!)
127.0.0.1:6379> setex key1 40 "hello" #设置key1的值为hello,40秒后过期
OK
127.0.0.1:6379> ttl key1
(integer) 32
127.0.0.1:6379> get key1
"hello"
127.0.0.1:6379> setnx mykey "redis" # 如果mykey不存在则创建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "mykey"
127.0.0.1:6379> ttl key1
(integer) -2
127.0.0.1:6379> setnx mykey "MongDB" #如果mykey存在则创建失败!
(integer) 0
127.0.0.1:6379> get mykey
"redis"
同时设置多值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同时设置多个值
OK
127.0.0.1:6379> keys *
1) "mkkey"
2) "k2"
3) "k3"
4) "mykey"
5) "k1"
127.0.0.1:6379> mget k1 k2 k3 #同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> mget mkkey k2
1) "MongDB"
2) "v2"
127.0.0.1:6379> msetnx k1 v1 k4 v4 #msetnx是原子性操作,要么一起飞,要么一起死
(integer) 0
#对象
set user:1{name:yan,age:20} #设置一个user:1对象,值为json字符来保存一个对象
#这里的key是个巧妙的设计: user:{id}:{filed},如此设计及在redis中是完全OK的!
127.0.0.1:6379> mset user:1:name lisi user:1:age 4
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "lisi"
2) "4
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 mongodb #如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mongodb"
数据结构是相同的 !
String类似的应用场景:value除了是字符串还可以是数字 ! 计数器 统计定义单位数量 粉丝数
List
基本的数据类型,列表,在redis里面,我们可以把list理解成成,栈,队列,堵塞队列! 所有的list命令都是用l开头的,Redis不区分大小写命令
插入值:
##########################################################前面插入值 lpush 栈底插入值 Rpush
127.0.0.1:6379> lpush list y1
(integer) 1
127.0.0.1:6379> lpush list y2
(integer) 2
127.0.0.1:6379> lpush list y3
(integer) 3
127.0.0.1:6379> lpush list y4
(integer) 4
127.0.0.1:6379> LRANGE list 0 2 #通过区间获取具体的值!
1) "y4"
2) "y3"
3) "y2"
127.0.0.1:6379> LRANGE list 0 -2 #获取list(key)中的值!
1) "y4"
2) "y3"
3) "y2"
127.0.0.1:6379> LRANGE list 0 -1
1) "y4"
2) "y3"
3) "y2"
4) "y1"
127.0.0.1:6379> RPUSH list y5 #将一个值或者多个值插入到列表底部
(integer) 5
127.0.0.1:6379> lrange list 0 -1
1) "y4"
2) "y3"
3) "y2"
4) "y1"
5) "y5"
#################################################################
栈顶弹出LPOP和栈底弹出RPOP
127.0.0.1:6379> Lpop list #栈顶弹出
"y4"
127.0.0.1:6379> LRange list 0 -1
1) "y3"
2) "y2"
3) "y1"
4) "y5"
127.0.0.1:6379> Rpop y3
(nil)
127.0.0.1:6379> Rpop list #栈底弹出Rpop
"y5"
127.0.0.1:6379> LRange list 0 -1 #获取list(key中值)
1) "y3"
2) "y2"
3) "y1"
lindex 的使用
通过下标获取到list中的某一个值:
#lindex 通过下标获得list中的某一个值
127.0.0.1:6379> LRange list 0 -1
1) "y3"
2) "y2"
3) "y1"
127.0.0.1:6379> Lindex list 2
"y1"
127.0.0.1:6379> Lindex list 0
"y3"
127.0.0.1:6379> Lindex list 1
"y2"
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 指定值的移除
举个栗子 就比如 取关
trim 截取指定长度
rpoplpush
#移除列表的最后一个元素,将他移动到新的列表中!
lset
将列表中的指定下标的值替换为另外一个值,相当于一个更新操作,如果不存在列表更新的时候会报错,找不到当前下标
LINSERT :
可以从目标字符串前和后插入字符串,将某个具体的value插入到列表中某个元素的前面或者后面.
小结:
他其实是一个链表,before Node after,right都可以插入值
如果key不存在,创建新的链表
如果key存在,新增数据
如果移除了所有的值,空链表,也代表不存在!
在两边插入或者改动,效率最高!中间元素,相对来说效率会低一点
消息队列 ! 消息队列 ( Lpush Rpop) , 栈 ( Lpush Lpop)
set :(集合)
set中的值是不可重复的
sadd--添加
rem--#移除set集合中的指定元素
scard--获取set集合中的内容元素个数
sadd 添加值进set集合
SMEMBRRS 查看对应key的所有set值
SISMEMBER 判断一个值是否存在对应的set集合中
127.0.0.1:6379> scard myset #scard 获取set集合中的内容元素个数
(integer) 3
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379> srem myset hello #移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> scard myset
(integer) 2
127.0.0.1:6379> smembers myset
1) "love,yan"
2) "yan"
set随机性
无序不重复无序集合.随机抽 #SRANDMEMBER
随机删除一些set集合中的元素 #spop
127.0.0.1:6379> smembers myset #存入9个元素
1) "y8"
2) "y1"
3) "y5"
4) "y4"
5) "y6"
6) "y2"
7) "y3"
8) "y7"
9) "y9"
127.0.0.1:6379> SRANDMEMBER myset #随机抽取myset集合中的一个元素
"y2"
127.0.0.1:6379> SRANDMEMBER myset
"y4"
127.0.0.1:6379> SRANDMEMBER myset
"y9"
127.0.0.1:6379> SRANDMEMBER myset
"y9"
127.0.0.1:6379> SRANDMEMBER myset
"y7"
127.0.0.1:6379> SRANDMEMBER myset 3 #随机抽取myset集合中的多个元素
1) "y7"
2) "y6"
3) "y4"
127.0.0.1:6379> SRANDMEMBER myset 5
1) "y4"
2) "y6"
3) "y8"
4) "y1"
5) "y5"
127.0.0.1:6379> SMEMBERS myset
1) "y6"
2) "y4"
3) "y7"
4) "y1"
5) "y8"
6) "y5"
7) "y2"
8) "y3"
9) "y9"
127.0.0.1:6379> spop myset #随机删除一个
"y5"
127.0.0.1:6379> spop myset 3 #随机删除多个
1) "y6"
2) "y9"
3) "y2"
set集合移动key
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 y
(integer) 1
127.0.0.1:6379> sadd myset2 set2
(integer) 1
127.0.0.1:6379> smove myset myset2 y #将一个元素移动到另一个集合中
(integer) 1
127.0.0.1:6379> smembers myset
1) "world"
2) "hello"
127.0.0.1:6379> smembers myset2
1) "y"
2) "set2"
总结:
微博,A用户将所有关注的人放到一个set集合中 !将它的粉丝也放在一个集合中!共同关注,共同爱好,二度好友(六度分隔理论)
Hash(哈希):
Map集合 key-map! 这个时候值是一个map集合 set myhash field yan
127.0.0.1:6379> hset myhash field1 yan #set一个具体 key-vlue
(integer) 1
127.0.0.1:6379> hget myhash field1 #获取一个字段值
"yan"
127.0.0.1:6379> hmset myhash field1 hello field2 world #set设置多个k,v
OK
127.0.0.1:6379> hmget myhash field1 field2 #获取多个字段值
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash #获取所有的键和值
1) "field1"
2) "hello"
3) "field2"
4) "world"
删除---hdel:
127.0.0.1:6379> hdel myhash field1
#删除hash指定的key字段!对应的value的值也没了
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
获取所有的长度 lhen 获取hash表中的字段数量 :
127.0.0.1:6379> hmset myhash field1 hello field2 world
OK
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
3) "field1"
4) "hello"
127.0.0.1:6379> hlen myhash
(integer) 2
判断字段是否存在 hexists---判断 hash中指定的字段是否存在:
127.0.0.1:6379> hexists myhash field1
(integer) 1
127.0.0.1:6379> hexists myhash field4
(integer) 0
127.0.0.1:6379>
获取字段和值:
hkeys---获取所有的键
hvals---获取所有的值
127.0.0.1:6379> hkeys myhash
1) "field2"
2) "field1"
127.0.0.1:6379> hvals myhash
1) "world"
2) "hello"
hash增减:
incr 指定增量 和 减量
127.0.0.1:6379> hset myhash field1 4
(integer) 1
127.0.0.1:6379> hincrby myhash field1 1
(integer) 5
127.0.0.1:6379> hincrby myhash field1 -2
(integer) 3
127.0.0.1:6379> hincrby myhash field1 9
(integer) 12
127.0.0.1:6379> hincrby myhash field1 -7
(integer) 5
######################################################################
127.0.0.1:6379> hsetnx myhash field2 hello #如果不存在则设置
(integer) 1
127.0.0.1:6379> hsetnx myhash field2 h2 #如果存在则不能设置,可以用与分布式锁中
(integer) 0
小结:
hash可以存一些变更的数据,尤其是用户信息之类的经常变动的信息 !
hash更适合与对象存储String更适合存储字符串存储1!
Zset(有序集合)
在set的基础上,增加了一个值,set k1 v1 zset k1 score v1
增:
#增加
127.0.0.1:6379> zadd myset 1 one #添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three #添加多个值
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zRange myset 0 -1
1) "one"
2) "two"
3) "three"
zset 排序(有序集合):
ZRANGEBYSCORE(升序排列返回集合中指定的元素)
127.0.0.1:6379> zadd salary 2000 xiaoming
(integer) 1
127.0.0.1:6379> zadd salary 3000 zhangsan #添加用户
(integer) 1
127.0.0.1:6379> zadd salary 100 yan
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf
1) "yan" # 从小到大排序,只显示k
2) "xiaoming"
3) "zhangsan"
#####################################################################
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "yan" #从小到大排序,显示键值对
2) "100"
3) "yan"
4) "2000"
5) "zhangsan"
6) "3000"
####################################################################
127.0.0.1:6379> ZREVRANGE salary 0 -1 #从大到小进行排序
1) "zhangsan"
2) "yan"
127.0.0.1:6379> ZREVRANGE salary 0 -1 withscores
1) "zhangsan" #从大到小进行排序,显示kv
2) "3000"
3) "yan"
4) "100"
####################################################################
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2001 withscores
1) "yan" #小于2001的值升序排列
2) "100"
3) "xiaoming"
4) "2000
移除--rem--移除指定集合中的指定元素:
127.0.0.1:6379> zrem salary xiaoming
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1 #移除salary 中的 一个key
1) "yan"
2) "zhangsan"
zcard 获取有序集合的个数:
127.0.0.1:6379> zcard salary
(integer) 2
获取元素数量--zcount
127.0.0.1:6379> zadd myset 1 hello 2 world 3 yan #添加三个值
(integer) 3
127.0.0.1:6379> zcount myset 1 3 #获取1到3区间的元素数量
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2
上面的其余的APl ,是常用的,如果没有,可以去redis官网查看官方文档!
案例思路:set 排序 存储班级成绩单工资排序表!
普通信息,1,重要信息 2,带权重进行判断!
排行榜应用实现,取Top N测试!
三种特殊数据类型:
geospatial 地理位置:
朋友的定位,附近的人,打车距离计算,Redis的Geo在Redis3.2版本就推出了 ! 这个功能可以推算地理位置的信息,两地之间的距离 , 方圆几里的人
getadd 添加和 geopos(取出)
地理位置:
规则;两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
有效的经度从-180 度到180度,
- 有效的维度从-85.05112878度到85,05112878度
127.0.0.1:6379> geoadd china:city 33.97 113.47 wangluo
(error) ERR invalid longitude,latitude pair 33.970000,113.470000 #报错,维度超过85
存数据和取数据(维度,经度)
geoadd(添加)
geopos (输出)
参数 key 值 (维度 , 经度 , 名称)
获得当前定位:一定是一个坐标值!
127.0.0.1:6379> geoadd china:city 113.47 33.97 wl
(integer) 1
127.0.0.1:6379> geopos china:city wl # 获取指定城市的经度和纬度
1) "113.47000211477279663"
2) "33.9699996616281723"
GEODIST点与点之间距离!
单位:m 表示米,km 千米,mi 表示单位是英里,ft表示单位为英尺
直线距离:
127.0.0.1:6379> geoadd china:city 120.21 30.25 hangzhou
(integer) 1 #王洛到杭州的直线距离
127.0.0.1:6379> GEODIST china:city wl hangzhou km
"757.6070"
georadius以经纬度为中心,找出某一半径内的元素,我附近的人 ? (获得所有附近的人的地址,定位 !) 通过半径来查询 !
#经纬度以110 30 为点 查找半径为1000km内集合中的位置并输出
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
1) "hangzhou"
2) "wl"
#经纬度以110 30 为点 查找半径为600km内集合中的位置并输出
127.0.0.1:6379> GEORADIUS china:city 110 30 600 km
1) "wl" #输出的是值
#距离分布 遍历600km内集合中的k和v
127.0.0.1:6379> GEORADIUS china:city 110 30 600 km withdist
1) "wl"
2) "549.5876"
127.0.0.1:6379> GEORADIUS china:city 110 30 800 km withcoord
1) 1) "wl" #以110 30 为点获取周围800km内集合中的经纬度
2) 1) "113.47000211477279663" #withcoord 输出的是经纬度
2) "33.9699996616281723"
###################################################################
高级玩法 count 2(显示两个) ,count 1(显示一个)
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withcoord withdist count 1
1) 1) "wl"
2) "549.5876"
3) 1) "113.47000211477279663"
2) "33.9699996616281723"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withcoord withdist count 2
1) 1) "wl" #王洛距离 110 30 经纬度549.58km
2) "549.5876"
3) 1) "113.47000211477279663"
2) "33.9699996616281723"
2) 1) "hangzhou"
2) "982.2993" #杭州距离 110 30经纬度982.29km
3) 1) "120.21000176668167114"
2) "30.24999979792261939"
GEORADIUSBYMEMBER--以集合中的某个k查找周围的(人,城市,地标)(在集合内)集合外的找不着
上海经纬度,经度:121.493539,纬度:31.371277
127.0.0.1:6379> GEORADIUSBYMEMBER china:city hangzhou 400 km withcoord withdist
1) 1) "hangzhou" #以杭州为中心查找四百千米中的数据--上海符合
2) "0.0000" #距离
3) 1) "120.21000176668167114"
2) "30.24999979792261939"
2) 1) "shanghai"
2) "174.5538" #距离
3) 1) "121.49000018835067749"
2) "31.36999929355160788"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city hangzhou 1000 km withcoord withdist #以杭州为中心查找一千公里内的城市
1) 1) "hangzhou"
2) "0.0000"
3) 1) "120.21000176668167114"
2) "30.24999979792261939"
2) 1) "shanghai" #上海
2) "174.5538"
3) 1) "121.49000018835067749"
2) "31.36999929355160788"
3) 1) "wl" #王洛 757公里
2) "757.6070"
3) 1) "113.47000211477279663"
2) "33.9699996616281723"
GEOHASH--返回一个或多个位置元素的Geohash表示,将二维的经纬度转换为一维的字符串,GEO 底层的实现原理就是 Zset !我们可以使用Zset命令操作GEO !
127.0.0.1:6379> geohash china:city shanghai wl hangzhou
1) "wtw6kwdr6p0" #11位的哈希码值
2) "ww09q043dd0"
3) "wtmkp6y7000"
############################################################
127.0.0.1:6379> ZRANGE china:city 0 -1 #遍历GEO对应k的v
1) "hangzhou"
2) "shanghai"
3) "wl"
127.0.0.1:6379> zrem china:city wl #删除GEO元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "hangzhou"
2) "shanghai"
hyperloglog:
什么是基数? A{1,3,5,7,8} B{1,3,5} 基数(不重复的元素)=5,可以接受误差!
简介:Redis 2.8.9版本就更新了Hyperloglog数据结构!Redis Hyperloglog 基数统计的算法
优点:占用的内存是固定的,2^64不同的元素的基数,只需要12KB内存!,如果从内存角度比较的话Hyperloglog首选!
网页的UV(个人访问一个网站多次,但是还是算作一个人 !)
传统的方式,set集合(不允许重复)保护用户的id,然后就可以统计set中的元素数量作为标准判断!这个方式如果保存大量的用户就会比较麻烦,我们的目的是为了计数,而不是为了保存用户id.0.81%错误率 ! 统计UV任务,可以忽略不记
测试使用:三个命令,pfadd ,pfcount ,pfmerge
127.0.0.1:6379> pfadd mykey a b c d e f g h i j
(integer) 1 #创建一组元素 mykey
127.0.0.1:6379> pfcount mykey
(integer) 10 #统计mykey的基数数量
127.0.0.1:6379> pfadd mykey2 c d e m n
(integer) 1 # 创建低二组元素 mykey2
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2
OK #合并两组mykey-mykey2=>mykey3 并集
127.0.0.1:6379> pfcount mykey3
(integer) 12 #c d e 重复 所以值是12
如果允许容错,那么可以使用Hyperloglog !如果不允许容错,就使用 set 或者自己的数据类型即可 !
bit map:位存储
统计疫情感染人数: 0 1 0 1 !统计用户信息,活跃, 不活跃 !登录 , 未登录 ! 打卡 ,365打卡!user id 两个状态的,都可以使用Bitmaps,Bitmap 位图 ,数据结构 ! 都是操作二进制位来进行记录,就只有0和1两个状态! 365天 = 365 bit 1 字节=8bit 46个字节左右!
使用bitmap来记录 周一到周日的打卡 !
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 0
(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 1
(integer) 0
127.0.0.1:6379> SETBIT sign 6 0
#查看某一天是否有打卡
127.0.0.1:6379> getbit sign 6 #查看周日打卡了没
(integer) 0
127.0.0.1:6379> getbit sign 0 #周一已打卡
(integer) 1
#统计操作,统计 打卡!
127.0.0.1:6379> bitcount sign 0 6
(integer) 3 #一周打卡了三天
事务的基本操作:
Redis 事务本质 : 一组命令的集合 ! 一个事务中的所有的命令都会被序列化,在事务执行过程中,会按照顺序执行 !一次性, 顺序性 , 排他性 ! 执行一系列的命令 !
------------队列 set set set 执行--------------
Redis事务没有隔离级别的概念!所有的命令在事务中,并没有直接执行 !只有发行的时候才会执行,Redisds单条命令是保存原子性的,但是事务不保证原子性!
redis :开启事务( multi ) , 命令入队( ........ ),执行事务( exec )
正常开启事务
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 #因为放弃了事务,所有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> set k5 v5
QUEUED
127.0.0.1:6379> exec #执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 #所有命令都不会被执行!
(nil)
运行时异常 (1/0,数组下标越界异常...) ,如果事务队列中存在语法性,那么执行命令的时候,其他命令式都可以正常执行的,错误命令抛出异常!
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 #执行的时候失败
QUEUED
127.0.0.1:6379> set 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"
127.0.0.1:6379> get k3
"v3"
监控 ! Watch
放弃监视 :unwatch !
悲观锁 :很悲观,认为什么时候都会出现问题,无论做什么都会加锁 !
乐观锁 :很乐观,认为什么时候都不会出现问题,所以不会上锁 ! 跟新数据的时候判断一下,在此期间是否有人修改过这个数据
获取version ! 更新的时候比较version
Redis测监控测试:
正常执行成功 !
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监控 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的乐观锁操作!
所以就导致执行失败!
如果修改失败,获取最新数据即可!
Jedis:
我们要使用java 来操作Redis,什么是Jedis ?是Redis官方推荐的java连接开发工具!使用java操作Redis,那么一定对Jedis十分熟悉
1,导入对应的依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
2,编码测试:
连接数据库:
操作命令
断开连接
package com.yan.Test;
import redis.clients.jedis.Jedis;
public class TestX {
public static void main(String[] args) {
Jedis jedis= new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping());
}
}
常用的API:String,List,Set,Hash,Zset 所有的API命令,就是上面的命令,一个都没有变化 !
package com.yan.Test;
import redis.clients.jedis.Jedis;
import java.util.concurrent.TimeUnit;
public class TestX {
public static void main(String[] args) {
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(jedis.get("key2"));
System.out.println("修改key1"+jedis.set("key1", "valuexxxxx"));
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","key04","value04"));
System.out.println("获取多个值"+jedis.mget("key01", "key02", "key03"));
System.out.println("删除多个值"+jedis.del("key01", "key02"));
System.out.println("获取多个值"+jedis.mget("key03", "key04"));
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", "value1-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", "keyGetSet"));
System.out.println(jedis.get("key2"));
System.out.println("获得key的值的字符串"+jedis.getrange("key2", 2, 4));
}
}
正常执行:
抛出异常:
已经放弃了事务,没有get到所以输出null
Spring Boot进行整合
PringBoot 操作数据 :spring-data jpa jdbc mongodb redis
SpringData也是和SpringBoot齐名的项目 !
说明: 在springBoot2x之后,原来使用的jedis被替换为了lettuce?
jedis:采用直连的方式.多个线程操作的话,是不安全的,如果像啊哟避免不安全,使用jedis pool连接池 ! 更像BIO模式
Lettuce : 采用netty,实例可以再多个线程中进行共享,不存在不安全的情况!可以减少线程数据了
更像NIO模式
源码分析:
@Bean //我们可以自己定义一个redisTemplate来替换这个默认的!
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
//默认的RedisTemplte 没有过多的设置,redis对象是需要序列化!
//两个泛型都是Object,Object 的类型,之后使用需要强制转换<Strin,Object> >
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
//由于String是redis中最常用的类型,所以说单独提出来一个bean!
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
整合测试
1,导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
2,配置连接
spring:
redis:
host: 127.0.0.1
port: 6379
3,测试
Redis.com详解
启动的时候就是通过配置文件来启动的
单位:
配置文件unit单位对大小写不敏感
包含:
就是像Spring ,Improt ,include
#网络
bind 127.0.0.1 #绑定的ip
protected-mode yes #保护模式
port 6379 #端口
通用GENERAL
daemonize yes #以守护进程方式运行,默认是no,自己开启为yes!
pidfile /var/run/redis_6379.pid #如果以后台方式运行,我们就需要指定一个pid文件
#日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
#生产环境
# warning (only very important / critical messages are logged)
loglevel notice
logfile "" #日志的文件位置名
databases 16 #数据库的数量 , 默认是 16 个数据库
always-show-logo yes #是否总是显示LOGE
快照:--持久化,在规定的时间内执行了多少次操作,则会持久化到文件.rdb.aof redis是内存数据库,如果没有持久化那么数据断电及失 !
#如果900内,至少有一个key进行了修改,我们及进行持久化操作
save 900 1
#如果300内,至少有十个key进行了修改,我们及进行持久化操作
save 300 10
#如果60内,至少有一万个key进行了修改,我们及进行持久化操作
save 60 10000
stop-writes-on-bgsave-error yes #持久化如果出错,是否需要继续工作 !
rdbcompression yes #是否压缩 rdb 文件,需要消耗一些cpu资源 !
rdbchecksum yes #保存rdb文件的时候,进行错误的检查校验 !
dir ./ #rdb 文件保存的目录 !
security安全:
127.0.0.1:6379> config get requirepass #获取redis密码
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass "123456" #设置redis的密码
OK
127.0.0.1:6379> auth 123456 #使用密码进行登录 !
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"
限制 CLIENTS:
maxclients 10000 #设置能连接上redis的最大客户端的数量
maxmemory <bytes> #redis 配置最大的内存容量
mamemory-policy noeviction #内存达到上限之后的处理策略
maxmemory-policy 六种方式
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
APPEND ONLY模式 aof配置
appendonly no #默认是不开启aof模式的,默认是使用rdb方式持久化,大部分所有情况下,rdb完全够用
appendfilename "appendonly.aof" #持久化的文件的名字
# appendfsync always #每秒修改都会sync消耗性能
appendfsync everysec #每秒执行一次sync,可能会丢失之1秒的数据
# appendfsync no # 不执行 sync ,这个时候操作系统自己同步数据,速度最快!