Redis:

目录

Redis:

NoSql的四大分类

kv键值

列存储数据库

图形数据库

文档型数据库

四者对比

redis 的基本命令

五大基本类型

String(字符串)

List

set :(集合)

Hash(哈希):

Zset(有序集合)

三种特殊数据类型:

geospatial 地理位置:

hyperloglog:

bit map:位存储

事务的基本操作:

Jedis:

Redis.com详解

Redis全解下半部分


       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

四者对比

Redisson 发送心跳的默认间隔是多少毫秒_windows

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)

Redisson 发送心跳的默认间隔是多少毫秒_数据库_02

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不区分大小写命令

Redisson 发送心跳的默认间隔是多少毫秒_redis_03

插入值:

##########################################################前面插入值   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 指定值的移除

举个栗子 就比如 取关

Redisson 发送心跳的默认间隔是多少毫秒_java_04

trim  截取指定长度

Redisson 发送心跳的默认间隔是多少毫秒_字符串_05

rpoplpush

#移除列表的最后一个元素,将他移动到新的列表中!

Redisson 发送心跳的默认间隔是多少毫秒_字符串_06

lset

      将列表中的指定下标的值替换为另外一个值,相当于一个更新操作,如果不存在列表更新的时候会报错,找不到当前下标

Redisson 发送心跳的默认间隔是多少毫秒_字符串_07

LINSERT :

       可以从目标字符串前和后插入字符串,将某个具体的value插入到列表中某个元素的前面或者后面.

Redisson 发送心跳的默认间隔是多少毫秒_字符串_08

小结:

他其实是一个链表,before Node after,right都可以插入值

如果key不存在,创建新的链表

如果key存在,新增数据

如果移除了所有的值,空链表,也代表不存在!

在两边插入或者改动,效率最高!中间元素,相对来说效率会低一点

消息队列 ! 消息队列 ( Lpush Rpop) , 栈 ( Lpush Lpop)

set :(集合)

set中的值是不可重复的

sadd--添加

rem--#移除set集合中的指定元素

scard--获取set集合中的内容元素个数

sadd 添加值进set集合
SMEMBRRS 查看对应key的所有set值
SISMEMBER  判断一个值是否存在对应的set集合中

Redisson 发送心跳的默认间隔是多少毫秒_java_09

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测试!

Redisson 发送心跳的默认间隔是多少毫秒_字符串_10

三种特殊数据类型:

    geospatial 地理位置:

朋友的定位,附近的人,打车距离计算,Redis的Geo在Redis3.2版本就推出了 ! 这个功能可以推算地理位置的信息,两地之间的距离 , 方圆几里的人

Redisson 发送心跳的默认间隔是多少毫秒_java_11

getadd 添加和 geopos(取出)

地理位置:

规则;两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!

有效的经度从-180 度到180度,

  1. 有效的维度从-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个字节左右!

Redisson 发送心跳的默认间隔是多少毫秒_redis_12

使用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的乐观锁操作!

Redisson 发送心跳的默认间隔是多少毫秒_redis_13

所以就导致执行失败!

Redisson 发送心跳的默认间隔是多少毫秒_字符串_14

如果修改失败,获取最新数据即可!

Redisson 发送心跳的默认间隔是多少毫秒_数据库_15

Jedis:

Redisson 发送心跳的默认间隔是多少毫秒_windows_16

     我们要使用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());
    }

}

Redisson 发送心跳的默认间隔是多少毫秒_java_17

常用的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));
    }

}

正常执行:

Redisson 发送心跳的默认间隔是多少毫秒_redis_18

抛出异常:

Redisson 发送心跳的默认间隔是多少毫秒_windows_19

已经放弃了事务,没有get到所以输出null

Spring Boot进行整合

    PringBoot 操作数据 :spring-data jpa jdbc mongodb redis

SpringData也是和SpringBoot齐名的项目 !

说明: 在springBoot2x之后,原来使用的jedis被替换为了lettuce?

jedis:采用直连的方式.多个线程操作的话,是不安全的,如果像啊哟避免不安全,使用jedis pool连接池 ! 更像BIO模式

Lettuce : 采用netty,实例可以再多个线程中进行共享,不存在不安全的情况!可以减少线程数据了

更像NIO模式

Redisson 发送心跳的默认间隔是多少毫秒_数据库_20

源码分析:

@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,测试

Redisson 发送心跳的默认间隔是多少毫秒_数据库_21

Redis.com详解

启动的时候就是通过配置文件来启动的

单位:

Redisson 发送心跳的默认间隔是多少毫秒_字符串_22

配置文件unit单位对大小写不敏感

包含:

Redisson 发送心跳的默认间隔是多少毫秒_windows_23

就是像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 ,这个时候操作系统自己同步数据,速度最快!