Redis

  • NoSql简述
  • Nosql概念
  • Nosql的四大分类
  • Redis概述
  • Redis的安装
  • 安装文件
  • Window下安装
  • Linux下安装
  • redis-benchmark 压力测试工具
  • 五大数据类型
  • Redis-key
  • String
  • List
  • Set
  • Hash
  • Zset
  • 三种特殊数据类型
  • geospatial
  • GEOADD
  • GEODIST
  • GEOHASH
  • GEOPOS
  • GEORADIUS
  • GEORADIUSBYMEMBER
  • hyperloglog
  • bitmaps
  • 事务
  • Jedis
  • redis持久化
  • RDB机制(Redis Database)
  • AOF机制(Append Only File)
  • redis发布订阅
  • redis主从复制
  • 环境配置
  • 一从二主
  • 哨兵模式(自动选举老大的模式)
  • Redis缓存穿透和雪崩
  • 缓存穿透(查询不到)
  • 缓存击穿(量太大,缓存过期)
  • 缓存雪崩


NoSql简述

Nosql概念

NOsql

Not only sql 不仅仅是sql
关系型数据库:表格,行,列
泛指非关系型数据库的,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社
区!暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的,而且是我们当下必须要掌握的一个技术!
很多的数据类型用户的个人信息,社交网络.地理位查。这些数据类型的存储不需要-一个固定的格式!不需要多月的操作就可以横
向扩展的! Map<String,0bject> 使用键值对来控制!

Nosql特点

1、方便扩展(数据之间没有关系,很好扩展! )
2、大数据:量高性能( Redis一秒写8万次 ,读取11万,NoSQL的缓存记录级,是-种细粒度的缓存,性能会比较高! )
3、数据类型是多样型的! (不需要事先设计数据库!随取随用! 如果是数据量十分大的表,很多人就无法设计了! )
4、传统RDBMS和NoSQL

  • 传统的RDBMS
  • 结构化组织
  • SQL
  • 数据和关系都存在单独的表中操作
  • 数据定义语言
  • 严格的一致性
  • 基础的事务

  • Nosql
  • 不仅仅是数据
  • 没有固定的查询语言
  • 键值对存储,列存储,文档存储,图形数据库(社交关系)
  • 最终一致
  • CAP定理利BASE
  • 高性能,高可用,高可扩

Nosql的四大分类

KV键值对:

键值存储,即Key-Value存储,简称KV存储。它是NoSQL存储的一种方式。它的数据按照键值对的形式进行组织,索引和存储。KV存储非常适合不涉及过多数据关系业务关系的业务数据,同时能有效减少读写磁盘的次数,比SQL数据库存储拥有更好的读写性能。

  • 新浪: Redis
  • 美团: Redis + Tair
  • 阿里、百度: Redis + memecache

文档型数据库( bson格式和json一样) :

文档数据库区别于传统的其它数据库,它是用来管理文档。在传统的数据库中,信息被分割成离散的数据段,而在文档数据库中,文档是处理信息的基本单位。一文档可以很长、很复杂、可以无结构,与字处理文档类似。一个文档相当于关系数据库中的一条记录。

  • MongoDB ( 一般必须要掌握)
  • MongoDB是一个基于分布式文件存储的数据库 ,C++编写,主要用来处理大量的文档!
  • MongoDB是一个介于关系型数据库和非关系型数据中中间的产品! MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的!

列存储数据库

列式数据库是以列相关存储架构进行数据存储的数据库,主要适合于批量数据处理和即时查询。相对应的是行式数据库,数据以行相关的存储体系架构进行空间分配,主要适合于大批量的数据处理,常用于联机事务型数据处理。

. HBase
.分布式文件系统
图关系数据库
存的不是图,放的是关系。比如:朋友圈社交网络。

redisson 缓存api redis缓存用法_数据库

Redis概述

什么是redis?

redis(remote dictionary server)远程字典服务。是一个开源的使用ANSI C语言编写的,支持网络、可基于内存一颗持久化的日志型,key-value 数据库。并提供多种语言的API。
被称之为:结构化数据库

Redis能干嘛?

1、内存存储、持久化,内存中是断电即失、所以说持久化很重要( rdb、aof )
2、效率高,可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量! )
6. …

特性

1、多样的数据类型
2、持久化
3、集群
4、事务

Redis的安装

安装文件

可以在Redis官网中下载http://www.redis.cn/即可,或者在我分享的下面链接中下载。
window下载文件链接:
链接:https://pan.baidu.com/s/1xYnBc5ewTxNzfY4nvQ2Nhw
提取码:xjh8
Linux下载文件连链接:
链接:https://pan.baidu.com/s/1p5Lkto6YzZHl0xg3KlXJ4w
提取码:l615

redis虽然在window下安装较简单,但我们通常在Linux下使用

Window下安装

在window下安装比较简单,安装文件后,进行解压,目录如下:

redisson 缓存api redis缓存用法_java_02

1.首先打开redis-server,不要关闭:

redisson 缓存api redis缓存用法_java_03

2.打开redis-cli

redisson 缓存api redis缓存用法_数据库_04

Linux下安装

我使用的是Ubuntu,所以下文演示是在Ubuntu下使用的。

1.首先将下载好的Ubuntu放在共享文件夹中,在Ubuntu中才能获取。
2. 在共享文件中找到我们上传的文件,并将它移到/opt路径下: mv redis-5.0.8.tar.gz /opt

redisson 缓存api redis缓存用法_redis_05


3. 解压文件:tar -zxvf redis-5.0.8.tar.gz

redisson 缓存api redis缓存用法_redis_06

4.进入redis-5.0.8文件下,找到 redis.cof文件

redisson 缓存api redis缓存用法_缓存_07


5.使用命令: sudo apt-get install gcc c++进行基本环境安装 需要在有网的条件下进行。

redisson 缓存api redis缓存用法_数据库_08

6.使用make命令,对需要的文件进行配置,需要些许时间,请耐心等待。配置完成后再次输入make出现下面内容则完成。

redisson 缓存api redis缓存用法_redisson 缓存api_09


7.输入make install 出现下面内容

redisson 缓存api redis缓存用法_缓存_10


8.使用命令:cd /usr/local/bin 下载的文件全在此处

redisson 缓存api redis缓存用法_java_11

9.创建一个文件 eg:xconfig mkdir xconfig 将redis.conf文件复制过来 cp /opt/redis-5.0.7.redis.conf xconfig。

redisson 缓存api redis缓存用法_java_12


10.redis默认不是后台启动的,我们进行修改。进入 redis.conf: vim redis.conf 修改

redisson 缓存api redis缓存用法_redis_13


修改后按esc:wq即可

11.启动redis-server 命令: redis-server xconfig/redis.conf. 启动redis-cli命令:redis-cli -p 6379.出现下面情况 则连接成功。

redisson 缓存api redis缓存用法_java_14


12.测试

redisson 缓存api redis缓存用法_redis_15


至此redis即可完成安装。

redis-benchmark 压力测试工具

1.redis-benchmark 压力测试工具

redisson 缓存api redis缓存用法_redis_16


eg: 测试:100个并发连接 100000请求

redis-benchmark -h localhost -p 6379 -c 100 -n 100000

redisson 缓存api redis缓存用法_数据库_17

五大数据类型

Redis-key

127.0.0.1:6379> keys *    #查看所有的key
(empty list or set)
127.0.0.1:6379> set name xiaoxu  # 设置key -vale
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(name) 是否存在
(integer) 1
127.0.0.1:6379> exists name1
(integer) 0
127.0.0.1:6379> expire name 3     #设置消亡时间
(integer) 1
127.0.0.1:6379> ttl name     #查看当前剩余时间  为负则表示消亡
(integer) -2
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> type age   #查看key的类型
string

String

**********************************************************
127.0.0.1:6379> append age "hello"  #在key(age)后面添加值
(integer) 6
127.0.0.1:6379> get age
"1hello"
127.0.0.1:6379> append huan "hhhhh" # 如果不存在key 则直接创建key
(integer) 5
127.0.0.1:6379> keys *
1) "age"
2) "huan"
127.0.0.1:6379> strlen age   #获取key(age)的长度
(integer) 6
**********************************************************
127.0.0.1:6379> set view 0   
OK
127.0.0.1:6379> incr view   #给view的值增加1
(integer) 1
127.0.0.1:6379> incr view
(integer) 2
127.0.0.1:6379> get view
"2"
127.0.0.1:6379> decr view    #给view的值减少1
(integer) 1
127.0.0.1:6379> get view
"1"
127.0.0.1:6379> incrby view 10   #给view的值增加若干步
(integer) 11
127.0.0.1:6379> get view
"11"
127.0.0.1:6379> decrby view 2   #给view的值减少若干步
(integer) 9
127.0.0.1:6379> get view
"9"
127.0.0.1:6379> set key1 "hello,xixxx"
OK
127.0.0.1:6379> getrange key1 1 3  #根据范围获取key的值  从下标1-3
"ell"
127.0.0.1:6379> getrange key1 0 -1  #获取该key的全部值
"hello,xixxx"
127.0.0.1:6379> setrange key1 1 xiaoxu  #从下标1开hi设置替换key1的值
(integer) 11
127.0.0.1:6379> getrange key1 0 -1
"hxiaoxuixxx"
**********************************************************
# setex(set with expire) 设置消亡时间
#setnx(set if not exit)  不存在设置...

127.0.0.1:6379> get hhh
"xxx"
127.0.0.1:6379> setex hhh 30 "huan" #设置hhh的值为huan同时消亡时间为30
OK
127.0.0.1:6379> get hhh
"huan"
127.0.0.1:6379> keys *
1) "huan"
2) "key1"
3) "view"
4) "hhh"
127.0.0.1:6379> keys *
1) "huan"
2) "key1"
3) "view"
127.0.0.1:6379> setnx hhh   "xxx" #如果hhh不存在 则添加 hhh-xxx
(integer) 1
127.0.0.1:6379> keys *
1) "huan"
2) "key1"
3) "view"
4) "hhh"    #设置成功
127.0.0.1:6379> setnx hhh "nihao"  #此时hhh存在 则设置失败
(integer) 0
**********************************************************
127.0.0.1:6379> mset k1 q k2 w k3 e  #批量添加key-value
OK
127.0.0.1:6379> mget k1 k2   #批量获取key-value
1) "q"
2) "w"
**********************************************************
127.0.0.1:6379> mset k1 2 k6 x
OK
127.0.0.1:6379> msetnx k1 2 key8 8  #如果不存在则设置 是原子操作 如果其中有存在的 则全部无法设置
(integer) 0

**********************************************************
#json对象
set user:1{name:zhangsan,age:3}
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2  #批量设置  user:1 user的名字为1 :分别设置他的name和age的key-value
OK
127.0.0.1:6379> mget user:1:name user:1:age #获取到1的name和age的值
1) "zhangsan"
2) "2"
**********************************************************
# getset   ##表示先get后set
127.0.0.1:6379> getset db redis  ##表示先get后set
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db hello
"redis"
127.0.0.1:6379> get db
"hello"

List

在redis中,我们可以把list使用成:栈,队列,阻塞队列等。

**********************************************************
127.0.0.1:6379> lpush list 1 #将1插入列表的头部  列表插入前面有l
(integer) 1
127.0.0.1:6379> lpush list 2
(integer) 2
127.0.0.1:6379> lpush list 3
(integer) 3
127.0.0.1:6379> lrange list 0 -1   #查看整个列表的值
1) "3"
2) "2"
3) "1"
127.0.0.1:6379> rpush list 8  #从列表尾部插入8  使用rpush
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "2"
3) "1"
4) "8"
127.0.0.1:6379> lpop list  #从头部移除元素  #lpop
"3"
127.0.0.1:6379> rpop list  #从尾部移除元素  #rpop
"8"
127.0.0.1:6379> lrange list 0 -1
1) "2"
2) "1"
**********************************************************
127.0.0.1:6379> lindex list 0  #获取list列表的某个下标
"2"
127.0.0.1:6379> lindex list 1
"1"
127.0.0.1:6379> llen list   #获取list的长度
**********************************************************
(integer) 2
127.0.0.1:6379> lpush list 3
(integer) 3
127.0.0.1:6379> lpush list 2
(integer) 4
127.0.0.1:6379> lrem list 1  3   #删除list列表中的一个 3 即删除列表中某个固定的值 及其个数
(integer) 1
127.0.0.1:6379> lrem list 2 2   #删除list列表中的2个 2
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "1"
**********************************************************
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello2"
127.0.0.1:6379> ltrim mylist 0 1   #只获取从0-1的值  trim修剪
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
**********************************************************
##rpoplpush  将列表中的最后一位元素移除并插入到另一个列表中,如果该列表不存在则创建
127.0.0.1:6379> lpush mylist hel
(integer) 3
127.0.0.1:6379> rpoplpush mylist otherlist
"hello1"
127.0.0.1:6379> lrange mylist 0 -1
1) "hel"
2) "hello"
127.0.0.1:6379> lrange otherlist 0 -1
1) "hello1"
**********************************************************
127.0.0.1:6379> exists list #判断list是否存在
(integer) 0
127.0.0.1:6379> lset list 0 item   将list中第0号下标设置为item 如果list的第0号下表不存在 则设置失败
(error) ERR no such key
127.0.0.1:6379> lpush list value
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "value"
127.0.0.1:6379> lset list 0 item   #存在则设置成功
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
**********************************************************
#linsert 选择在列表中某个值的前面还是后面插入某值
127.0.0.1:6379> linsert list before item hi  #在list列表中的value前面插入hi
(integer) 2
127.0.0.1:6379> linsert list after item hhh #在list列表中的value后面插入hhh
(integer) 3
127.0.0.1:6379>  lrange list 0 -1
1) "hi"
2) "item"
3) "hhh"

Set

**********************************************************
127.0.0.1:6379> sadd mylist hello  #向列表中添加值 可以添加多个   set中不含重复元素  如果添加重复元素则添加失败
(integer) 1
127.0.0.1:6379> sadd mylist nihao hhh
(integer) 2
127.0.0.1:6379> smembers mylist  # 查看列表的全部成员信息
1) "nihao"
2) "hhh"
3) "hello"
127.0.0.1:6379> sismember mylist  huanhuan  #判断某信息是否包含在列表中   不存在返回0  存在返回1
(integer) 0
127.0.0.1:6379> sismember mylist hhh
(integer) 1

127.0.0.1:6379> scard mylist  #查看列表中的元素个数
(integer) 3

127.0.0.1:6379> srem mylist hhh  #移除元素中的某值
(integer) 1
127.0.0.1:6379> smembers mylist
1) "nihao"
2) "hello"
**********************************************************
set 是无序不重复的集合 可以随机取值
127.0.0.1:6379> srandmember mylist  #随机取例表中的值
"nihao"
127.0.0.1:6379>  srandmember mylist 2  #随机取列表中的两个值
1) "nihao"
2) "hello"
127.0.0.1:6379> spop mylist  #移除随机元素
"nihao"
**********************************************************
# smove  移除元素  
127.0.0.1:6379> sadd mylist2 xxx
(integer) 1
127.0.0.1:6379> smove mylist mylist2 hello  #将 源文件移动到目的文件 移动的值为hello
(integer) 1
127.0.0.1:6379> smembers mylist
(empty list or set)
127.0.0.1:6379> smembers mylist2
1) "hello"
2) "xxx"
**********************************************************
#交(sinter)差(sdiff)并(sunion)集合
127.0.0.1:6379> sadd key b c
(integer) 2
127.0.0.1:6379> sadd key a
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d e
(integer) 2
127.0.0.1:6379> sdiff key key2  # 查找key key2的差集  即 key中不同于key2中的数
1) "a"
2) "b"
127.0.0.1:6379> sinter key key2  # 查找key key2的交集  即 key中与key2相同的数
1) "c"
127.0.0.1:6379> sunion key1 key2 # 查找key key2的并集  即 key中与key2所有的数
1) "d"
2) "c"
3) "e"
4) "a"

Hash

hash是以键值对的形式存在的:key-value

127.0.0.1:6379> hset myhash f1 v1   #设置hash的键值对   set一个具体的key-value
(integer) 1
127.0.0.1:6379> hget myhash f1  #获取myhash的f1的值
"v1"
127.0.0.1:6379> hmset myhash f1 v2 f2 v2  #批量设置 f1 f2
OK
127.0.0.1:6379> hmget myhash f1 f2   #批量获取f1 f2
1) "v2"
2) "v2"
127.0.0.1:6379> hgetall myhash  #获取所有的键值信息
1) "f1"
2) "v2"
3) "f2"
4) "v2"
127.0.0.1:6379> hdel myhash f1  #删除指定的hash字段
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "f2"
2) "v2"
**********************************************************
127.0.0.1:6379> hlen myhash   #获取myhash的长度
(integer) 1
127.0.0.1:6379> hexists myhash f2  #判断hash是否存在
(integer) 1
127.0.0.1:6379> hexists myhash f3
(integer) 0
**********************************************************
# 只获得所有的filed   hkeys
#只获得所有的value    hvals
127.0.0.1:6379> hkeys myhash
1) "f2"
2) "f3"
127.0.0.1:6379> hvals myhash
1) "v2"
2) "v3"
**********************************************************
127.0.0.1:6379> hset myhash f4 1
(integer) 1
127.0.0.1:6379> hincrby myhash f4 1  #给某个hash的值增加1
(integer) 2
127.0.0.1:6379> hincrby myhash f4 3  #给某个hash的值增加若干
(integer) 5
127.0.0.1:6379> hincrby myhash f4 -2 #给某个hash的值减少若干
(integer) 3
127.0.0.1:6379> hget myhash f4
"3"

Zset

与set相似,在set基础上实现有序添加值。

127.0.0.1:6379> zadd myhash 1 ni  #添加一个值 序号为1
(integer) 1
127.0.0.1:6379> zadd myhash 2 hi 3 hhh  #添加多个值
(integer) 2
127.0.0.1:6379> zrange myhash 0 -1
1) "ni"
2) "hi"
3) "hhh"
**********************************************************
127.0.0.1:6379> zadd salary 2500 xiaoming
(integer) 1
127.0.0.1:6379> zadd salary 5000 xiaohua
(integer) 1
127.0.0.1:6379> zadd salary 500 huanhuan
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf  #按照salary从小到大排序 从负无穷到正无穷
1) "huanhuan"
2) "xiaoming"
3) "xiaohua"
127.0.0.1:6379> zrangebyscore salary -inf 2500 #按照salary从小到大排序 从负无穷到任意值
1) "huanhuan"
2) "xiaoming"
**********************************************************
127.0.0.1:6379>  zrangebyscore salary -inf 2500 withscores    #显示所有用户并附带成绩
1) "huanhuan"
2) "500"
3) "xiaoming"
4) "2500"

127.0.0.1:6379> zrem salary huanhuan  #移除某个值
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "xiaoming"
2) "xiaohua"
**********************************************************
127.0.0.1:6379> zcard salary #获取有序集合中的个数
(integer) 2
#zcount获取指定区间的数量
127.0.0.1:6379> zadd myset 1 h1 2 h2 3 h3
(integer) 3
127.0.0.1:6379> zcount myset 1 +inf  #zcount获取指定区间的数量
(integer) 3
127.0.0.1:6379> zcount myset 0 2
(integer) 2

三种特殊数据类型

geospatial

意思:地理空间的

朋友的定位,附近的人,打车距离计算,Redis的geo在redis3.2后就推出 可以推算出地理位置的信息,两地之间的距离,方圆几里的人等信息。

相关命令:

redisson 缓存api redis缓存用法_缓存_18

GEOADD

geoadd添加地理位置
规则:两级无法直接添加,我们一般会下载城市数据,直接通过Java程序一次性导入!
经纬度查询:https://jingweidu.bmcx.com/
#参数key值 (经度、纬度、名称)

127.0.0.1:6379> geoadd china:city 116.397128 39.916527 beijing    #添加城市的经纬度
(integer) 1
127.0.0.1:6379> geoadd china:city 121.48941 31.40527 shanghai
(integer) 1
127.0.0.1:6379
GEODIST

返回两个地区之间的距离
单位 :m 米 km 千米 mi 英里 ft 英尺

127.0.0.1:6379>  geodist china:city shenzhen beijing  #获取深圳到北京的距离
"1945754.4147"
127.0.0.1:6379> geodist china:city shenzhen beijing km  #获取深圳到北京的距离 拿km表示
"1945.7544"
GEOHASH

该命令返回11个字符-返回一个或者多个位置元素的geohash表示
将二维的经纬度转为以为的字符串,如果两个字符串越接近,则距离越近

127.0.0.1:6379> geohash china:city beijing xian
1) "wx4g0dtf9e0"
2) "wqj6wz7x210"

geo底层的实现原理是 Zset,则我们可以使用zset命令来操作geo 例如:geo中无删除元素 我们可以用zset中的删除

127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "shanghai"
5) "beijing"
127.0.0.1:6379> zrem china:city chongqing  #使用zrem删除
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "xian"
2) "shenzhen"
3) "shanghai"
4) "beijing"
GEOPOS

根据城市获取经纬度

127.0.0.1:6379> geopos china:city shenzhen beijing
1) 1) "113.88307839632034"
   2) "22.553291115657139"
2) 1) "116.39712899923325"
   2) "39.916526473629808"
GEORADIUS

GEORADIUS:以给定的经纬度为中心,找出某一半径内的元素。
我附近的人:获得所有附近的人的地址定位。通过半径来查询。

127.0.0.1:6379> georadius china:city 110 30 1000 km   #获取以经纬度(110,30)为圆心,1000km为半径 进行查询周围的人
1) "chongqing"
2) "xian"
3) "shenzhen"
127.0.0.1:6379>  georadius china:city 110 30 500 km withcoord withdist   ##获取以经纬度(110,30)为圆心,500km为半径 进行查询周围的人 同时获取到圆心之间的距离和其经纬度
1) 1) "chongqing"
   2) "340.7667"
   3) 1) "106.54040783643723"
      2) "29.402680535172998"
2) 1) "xian"
   2) "481.1278"
   3) 1) "108.9342525601387"
      2) "34.230530975990824"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord withdist count 1  # ##获取以经纬度(110,30)为圆心,500km为半径 进行查询周围的人 同时获取到圆心之间的距离和其经纬度  只要1个
1) 1) "chongqing"
   2) "340.7667"
   3) 1) "106.54040783643723"
      2) "29.402680535172998"
GEORADIUSBYMEMBER

以给定的城市为中心,找出某一半径内的元素。

127.0.0.1:6379> georadiusbymember china:city beijing 1000 km  ##获取以某城市为圆心,1000km为半径 进行查询周围的人
1) "beijing"
2) "xian"
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km withcoord withdist ##获取以某城市为圆心,1000km为半径 进行查询周围的人 同时获取其经纬度 及到圆心的半径
1) 1) "beijing"
   2) "0.0000"
   3) 1) "116.39712899923325"
      2) "39.916526473629808"
2) 1) "xian"
   2) "915.0863"
   3) 1) "108.9342525601387"
      2) "34.230530975990824"

hyperloglog

什么是基数?

A{1,3,5,7,8,7}
B{1,3,5,7,8}
基数(不重复的元素)=5 ,可以接受误差

简介

Redis 2.8.9版本就更新了Hyperloglog数据结构!
Redis Hyperloglog基数统计的算法!
优点∶占用的内存是固定,2*64不同的元素的技术,只需要花费12KB内存!
如果要从内存角度来比较的话Hyperloglog 首选!
网页的UV (一个人访问一个网站多次,但是还是算作一个人!)
传统的方式,set保存用户的id ,然后就可以统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id ;0.81%错误率!统计UV任务,可以忽略不计的!

127.0.0.1:6379> pfadd mykey a b c d e f g  #创建key并赋值
(integer) 1
127.0.0.1:6379> pfcount mykey  #计算mykey的基数数量  自动去重
(integer) 7
127.0.0.1:6379> pfadd mykey2 e f g h i g k
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 6
127.0.0.1:6379> pfmerge mykey3 mykey mykey2  #将key1和key2的值合并为key3
OK
127.0.0.1:6379> pfcount mykey3  #去重
(integer) 10

bitmaps

位存储

统计用户信息,活跃,不活跃!登录,未登录,打卡,未打卡
,只有两个状态的都可以用。都是操作二进制位来记录。只有0 1 两个状态
eg:使用bitmap来记录周一到周日的打卡。 0表示未打 1表示打卡
1: 0 2:1 3:1 4:1 5:0 6:1

127.0.0.1:6379> setbit key 0 0
(integer) 0
127.0.0.1:6379> stbit key 1 1
(error) ERR unknown command 'stbit'
127.0.0.1:6379> setbit key 1 1
(integer) 0
127.0.0.1:6379> setbit key 2 1
(integer) 0
127.0.0.1:6379> setbit key 3 0
(integer) 0
127.0.0.1:6379> setbit key 4 1
(integer) 0
127.0.0.1:6379> setbit key 5 0
(integer) 0
127.0.0.1:6379> setbit key 6 1
(integer) 0

获取某天打卡情况:

127.0.0.1:6379> getbit key 0    #获取第1天的打卡情况
(integer) 0
127.0.0.1:6379> getbit key 6
(integer) 1

获取一共有多少天打卡:

127.0.0.1:6379> bitcount key
(integer) 4

事务

redis事务本质:一组命令的集合。一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行。
一次性、顺序性、排他性。执行一些命令
redis单命令式保存原子性的,但是事务不保证原子性!

redis的事务:

  • 开启事务(multi)
  • 命令入队(…)
  • 执行事务(exec)

正常执行事务 exec

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set key 1
QUEUED
127.0.0.1:6379> set key1 2
QUEUED
127.0.0.1:6379> get key
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "1"

放弃执行 discard

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set hhh  23
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get hhh
(nil)

编译型异常(代码有问题,命令有错),事务中所有命令都不会被执行!

127.0.0.1:6379> set k1 nihu
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1    #k1 对应的值不能+1 则此代码无法运行  但其他可以运行
QUEUED
127.0.0.1:6379> set k2 3
QUEUED
127.0.0.1:6379> set k3 3
QUEUED
127.0.0.1:6379> exec  #k1 对应的值不能+1 则此代码无法运行  但其他可以运行
1) (error) ERR value is not an integer or out of range
2) OK
3) OK

运行时异常(1/0),如果事务中存在语法性,那么执行命令的时候,其他命令是可以正确执行的。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
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.

监控 watch 乐观锁

悲观锁:
很悲观,认为什么都会出问题,无论做什么都会加锁。
乐观锁:

  • 很乐观,人为什么时候都不会出现问题,所以不会上锁。更新数据的时候去判断下,在此期间是否有人修改了这个数据。
  • 获取version
  • 更新的时候比较version

redis测监视测试

首先打开一个 redis.cli 执行下面操作

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> watch money     
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby money 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 100

此时只有一个线程,没有任何问题
若此时 重新打开一个新的线程 执行下面操作:

127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby money 20
QUEUED

此时还没有执行 exec 此时有一个新的线程执行 对money进行了修改

127.0.0.1:6379> incrby money 3
(integer) 103

回到刚才线程 执行 exec 则会返回空

127.0.0.1:6379> exec
(nil)

解决办法: 输入unwatch先释放锁后重新watch 便可以重新对锁进行操作

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> incr money
QUEUED
127.0.0.1:6379> exec
1) (integer) 101

Jedis

jedis是官方推荐的java连接开发工具,使用Java操作redis中间件,则一定要对jedis非常熟悉
步骤
1.创建项目 引入依赖

<dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>3.4.1</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.62</version>
    </dependency>

2.编码测试

redisson 缓存api redis缓存用法_数据库_19

redisson 缓存api redis缓存用法_数据库_20

事务举例:

正常情况下:

public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        //对事务进行操作
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "word");
        jsonObject.put("name", "huanhuan");
        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        try {
            multi.set("user1", result);
            multi.set("user2", result);
            multi.exec();
        }
        catch (Exception e){
            multi.discard();
            e.printStackTrace();
        }
        finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }
    }

结果:

redisson 缓存api redis缓存用法_缓存_21

异常情况:

redisson 缓存api redis缓存用法_java_22

redis持久化

redis是内存数据库,如果不将内存中的数据库状态保存到磁盘中,那么服务器进程一旦退出,服务器中的数据库状态也会消失。所以提供持久化功能。

RDB机制(Redis Database)

什么是rdb

redisson 缓存api redis缓存用法_缓存_23

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。Redis会单独创建( fork )一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的一般不需要修改该配置。

rdb保存的文件为:dump.rdb 在redis.conf文件中

redisson 缓存api redis缓存用法_java_24

RDB持久化(以快照的方式) 策略(默认):

save 900 1 (15分钟变更一次)
  save 300 10 (5分钟变更10次)
  save 60 10000 (1分钟变更1万次)
触发机制:

  1. save 满足的条件下 ,会自动触发rdb规则
  2. 执行flush all命令。
  3. 退出redis
    备份就会自动生成一个 dump.rdb文件

优点:
1.适合大规模的数据恢复
2.对数据完整性要求不高。

缺点:
1.需要一定时间间隔进程操作。如果redis意外宕机了,最后一个修改的数据可能没有。
2.fork进程的时候,会占用一定的内存空间。

AOF机制(Append Only File)

将我们的所有命令记录下来,history。恢复的时候就把这个文件再执行一遍。

什么是AOF

以日志的形式来记录每一个写操作,将redis执行的所有指令记录下来。只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日子文件的内容将写指令从从前到后执行一次以完成数据恢复工作。

aof 保存的是appendonly.aof文件

打开redis.conf文件 默认是不开启的

redisson 缓存api redis缓存用法_redis_25


重启之后就可以生效了。

如果这个aof有错位,或者被修改。这时候redis是无法启动的,我们需要修复。

redis-check-aof --fix

redisson 缓存api redis缓存用法_数据库_26

便可以正常进行操作

优点:
1.每一次修改都同步,文件的完整性会更好。
2.从不同步,效率最高。
缺点:
1.相对于数据文件来说,aof远远大于rdb,修复的速度也慢。
2.aof运行效率要比rdb慢,所以我们redis默认的配置就是rdb持久化。
3.每秒同步一次,可能那个会丢失一秒的数据。

redis发布订阅

redis发布订阅(pub/sub)是一种消息通信模式:发送者发送消息,订阅者接收消息

图示:

redisson 缓存api redis缓存用法_数据库_27

用法:

redisson 缓存api redis缓存用法_java_28

redisson 缓存api redis缓存用法_数据库_29

测试:
订阅端:

127.0.0.1:6379> subscribe xiaoxu  #订阅xiaoxu的频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "xiaoxu"
3) (integer) 1
1) "message"    #接收到订阅的信息
2) "xiaoxu"  
3) "hello"

发送端:

127.0.0.1:6379> publish xiaoxu hello  #向xiaoxu的频道 发送消息hello
(integer) 1

原理

Redis是使用C实现的,通过分析Redis源码里的pubsud.c文件,了解发布和订阅机制的底层实现,籍此加深对Redis的理解。Redis 通过PUBLISH、SUBSCRIBE 和PSUBSCRIBE等命令实现发布和订阅功能。
通过 SUBSCRIBE命令订阅某频道后,redis-server里维护了一个字典,字典的键就是一个个channel,而字典的值则是一个链表,链表中保存了所有订阅这个channel的客户端。SUBSCRIBE 命令的关键,就是将客户端添加到给定channel的订阅链表中。通过 PUBLISH命令向订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
Pub/Sub 从字面上理解就是发布( Publish )与订阅(Subscribe ) ,在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。

redis主从复制

概念

主从复制,是指将一台Redis服务器的数据 ,复制到其他的Redis服务器。前者称为主节点(master/leader) ,后者称为从节点

(salevfollower) ;数据的复制是单向的,只能由主节点到从节点。Master以写为主 ,Slave以读为主。

默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但-一个从节点只能有一个主节点。

主从复制的作用主要包括:

1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复; 实际上是一种服务的冗余。

3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接

主节点,读Redis数据时应用连接从节点), 分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大

大提高Redis服务器的并发量。

4、高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

一般来说 ,要将Redis运用于工程项目中,只使用一台Redis是万万不能的,原因如下:

1、从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;

2、从容量上,单个Redis服务器内存容最有限,就算-一台Redis服务器内存容最 为256G,也不能将所有内存用作Redis存储内存,

一般来说 ,单台Redis最大使用内存不应该超过20G。

电商网站.上的商品,一般都是一次 上传,无数次浏览的,说专业点也就是’多读少写”。

我们通常使用如下架构:

redisson 缓存api redis缓存用法_redisson 缓存api_30

主从复制,读写分离。多数都在进行读操作。减轻服务器的压力

环境配置

只需要配置从库 不需要配置主库

127.0.0.1:6379> info replication  #查看当前库信息
# Replication
role:master  # 主机
connected_slaves:0  #0从机
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

复制三个端口号,然后修改对应的信息

redisson 缓存api redis缓存用法_redisson 缓存api_31

1.端口

redisson 缓存api redis缓存用法_redis_32

2.pid名字

redisson 缓存api redis缓存用法_redis_33

3.log文件名称

redisson 缓存api redis缓存用法_缓存_34

4.dump.rdb文件

redisson 缓存api redis缓存用法_数据库_35


开启成功

redisson 缓存api redis缓存用法_java_36

一从二主

默认情况下每台redis服务器都是主节点,我们只需要配置从机就好。

slaveof 主机 端口

redisson 缓存api redis缓存用法_redis_37

redisson 缓存api redis缓存用法_数据库_38

主机可以进行读写操作:

redisson 缓存api redis缓存用法_数据库_39


从机只能进行读:

redisson 缓存api redis缓存用法_redis_40

主机断开连接后,从机一九连接到主机,但是不能进行写操作。这个时候,主机如果回来,便可以接着读取。

复制原理

Slave启动成功连接到master后会发送一个sync命令,Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执,行完毕之后,master将传送整个数据文件到slave,并完成-一次完全同步。
全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制: Master 继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要是車新连接master,一次完全同步(全量复制)将被自动执行

层层链路

redisson 缓存api redis缓存用法_redisson 缓存api_41

这个时候也能完成主从复制。

如果没有了老大,这个时候能不能选择一个老大出来?手动?

谋朝篡位
如果主机断开了连接,我们可以使用

slaveof no one

自立为王。

redisson 缓存api redis缓存用法_数据库_42

哨兵模式(自动选举老大的模式)

概述

主从切换技术的方法是:当主服务器宕机后, 需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。 这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel (哨

兵)架构来解决这个问题。谋朝篡位的自动版.能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应。从而监控运行的多个Redis实例。

redisson 缓存api redis缓存用法_java_43


redisson 缓存api redis缓存用法_redisson 缓存api_44


假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结

果由一个哨兵发起,进行failover故障转移操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切

换主机,这个过程称为客观下线

使用哨兵模式具体步骤:

1.创建sentinel.conf文件

vim sentinel.conf

2.配置sentinel.conf文件

sentinel monitor  被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6390 1

1代表主机挂了,slave投票看让谁成为主机,票数最多的,就是主机。
3.启动哨兵模式

redis-sentinel xconfig/sentinel.conf

redisson 缓存api redis缓存用法_java_45

这个时候如果将6390断开,则哨兵,经过一段时间后,会选中新的主机

redisson 缓存api redis缓存用法_redisson 缓存api_46


此时6392为新的主机。

redisson 缓存api redis缓存用法_redisson 缓存api_47

哨兵模式

优点:
1、哨兵集群,基于主从复制模式,所有的主从配置优点,它全有
2、主从可以切换 ,故障可以转移,系统的可用性就会更好
3、哨兵模式就是主从模式的升级,手动到自动,更加健壮!
缺点:
1、Redis 不好啊在线扩容的,集群容量一旦到达上限,在线扩容就-十分麻烦!
2、实现哨兵模式的配置其实是很麻烦的,里面有很多选择!

Redis缓存穿透和雪崩

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。 其中,最要害的问题,就是数据的一致性问题 ,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。
另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也都有比较流行的解决方案。

缓存穿透(查询不到)

缓存穿透的概念很简单,用户想要查询一个数据, 发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很
大的压力,这时候就相当于出现了缓存穿透。

redisson 缓存api redis缓存用法_redisson 缓存api_48

解决方案

布隆过滤器

布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。从而避免了对底层存储系统的查询压力。

redisson 缓存api redis缓存用法_redis_49


缓存空对象

当存储层不命中时,即使返回的空对象也将其缓存起来,同时设置一个过期时间,之后在访问这个数据时将会从换从中获取,保护了后端数据源。

redisson 缓存api redis缓存用法_数据库_50

该方法存在的问题:

1.如果空值能够被缓存起来,这就意味着需要更多的空间和更多的键,因为这当中可能会有很多的空值的键。

2.即使对空值设置了过期时间,还是会存在缓存层和存储层的数据有一段时间窗口不一致,这对于需要保持一致性的业务会有影响。

缓存击穿(量太大,缓存过期)

概述

这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点 ,在不停的扛着大并发,大并发集中对这一个点进行访问 ,当这个key在失效的瞬间,持续的大并发就穿破缓存, 直接请求数据库,就像在一个屏障 上凿开了一个洞。
当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数
据,并且回写缓存,会导使数据库瞬间压力过大。

解决方法

设置热点数据永不过期
从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题。
加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要
等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。

缓存雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis 宕机!

产生雪崩的原因之一, 比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购 ,这波商品时间比较集中的放入了缓存,假设缓存一个小时。 那么到了凌晨一点钟的时候 ,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

redisson 缓存api redis缓存用法_数据库_51

其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在

某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。

而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。

解决方案

redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之 后其他的还可以继续工作,其实就是搭建的集群。
限流降级
在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一 个线程查询数据和写缓存,其他线程等待。
数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。 在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。