Redis学习笔记

Posted new一个对象777

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis学习笔记相关的知识,希望对你有一定的参考价值。

Redis学习

主要知识点

  • nosql介绍
  • nosql数据类型
  • nosql 四大分类
  • CAP
  • BASE
  • Redis入门
  • 五大基本数据类型
    • String
    • List
    • Set
    • Hash
    • Zset
  • 三种特殊的数据类型
    • geo
    • hyperloglog
    • bitmap
  • Redis配置详解
  • Redis持久化
    • RDB
    • AOF
  • Redis事务操作
  • Redis实现订阅发布
  • Redis实现主从复制
  • Redis哨兵模式(重点的重点)
  • 缓存穿透及解决方案(重点的重点)
  • 缓存击穿及解决方案(重点的重点)
  • 缓存雪崩及解决方案(重点的重点)
  • 基础API之Jedis详解
  • Springboot集成Redis
  • Redis实战分析

为什么要用NoSQL

由于现在是大数据时代,我们单靠mysql和MySQL集群已经解决不了现在的海量数据存储了,不论对索引优化的多好,也很那解决现在百亿条数据的读写,所以NoSQL出现了。

纵观互联网历史

第一个时代:单机MySql的年代

在这里插入图片描述

在当时那个年代,基本上都是静态的网站,访问量很少,所以一个单机mysql就可以了。DAL-数据库访问层

但是这样的网站有瓶颈:

  • 数据量太大,一个机器放不下

  • 数据的索引,单个数据库超过300万就必须建立索引,但是索引太大,单机也放不下 索引(B+ tree)

  • 访问量,一个服务器承受不了

第二个时代:有了缓存的存在Memcached(高速缓存插件) + MySQL + 垂直拆分 读写分离

一个网站基本都是在做读操作,每次都去操作数据库的时候就很浪费很麻烦,所以我们做一个缓存,将第一次查到的内容放到缓存里面,第二次查的时候直接从缓存里面拿,这样就快很多,减少了数据库服务器的压力。
在这里插入图片描述

分库分表 + Mysql集群 + 水平拆分 集群主从复制

MySQL的存储引擎:

早些年是:MyISAM:表锁,即当我们访问一条记录的时候会把整个表锁住,当我们的表中有一百万条数据的时候十分影响效率,高并发下会出现严重的锁问题。

现在是InnoDB:行锁,每次查询只锁一行

慢慢的就是分库分表来解决写的压力

在这里插入图片描述
BOSN就是JSON的二进制格式

这时节nosql就可以很好地解决上面的问题。处理海量数据

NoSQL不仅仅是sql!!!

Map就是典型的nosql的特点因为Map<String,Object>键值对的形势,可以存储任何形式的值。

NoSQL的特点

  • 方便解耦
  • 方便扩展 因为数据之间没有关系,
  • 大数据量的高性能 (redis 一秒可以写8万次,读取11万)
  • 数据类型是多样的 不需要事先设置表结构 因为是键值对的形式,随取随用

CAP定理 和 BASE理论 – 异地多活 搞明白这些就是初级架构师了!!

3V 和 3H

大数据时代的3V:主要是描述问题的

  • 海量 volume
  • 实时 variety
  • 多样 velocity

大数据时代的3H 即三高:主要是对程序的要求

  • 高并发
  • 高性能
  • 高可扩 随时拆分,机器不够了可以随时增加机器来提高性能

现在是Nosql和rdbms一起使用
在这里插入图片描述
在这里插入图片描述

一个网站的信息存储架构

  • 商品的基本信息
    • 名称,价格,商家信息,这些都是存储在sql中的,也就是关系型数据库,阿里云用的是mysql,但是他这个MySQL和我们的MySQL并不一样,他们将MySQL底层重构了~~
  • 商品的评论,描述(文字较多)
    • 文档数据库中 mongoDB
  • 图片
    • 分布式存储系统 FastDFS
    • 淘宝自己的 TFS
    • Google的 GFS
    • Hadoop HDFS
    • 阿里云的 oss
  • 商品的关系字 即搜素引擎
    • 阿里云用的是自己研发的 ISerach: 是多隆大佬一高兴就研发出来的。
  • 商品热门的波段信息
    • 内存数据库
    • redis
  • 商品的交易 支付接口
    • 第三方接口 支付宝 微信等API

一个网页的背后很复杂。

在这里插入图片描述

NoSQL的四大分类

Redis是单线程的

  • 键值对存储
    • 美团:redis + Tair
    • 新浪:redis
  • 文档存储
    • MongoDB是非关系型数据库中功能最丰富的,最像关系型数据库的一种nosql。
  • 列存储
    • HBase
    • 分布式文件系统
  • 图形关系数据库
    • 他不是用来存储图形的,它是用来存储拓扑关系的。
    • 社交推荐,广告推荐
      在这里插入图片描述

追求幸福,探索未知

Redis入门

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。

Redis能干吗?

  • 内存存储,数据持久化
  • 效率高,可用作高速缓存
  • 简单的发布订阅系统–可实现简单的消息队列的功能
  • 地图信息分析
  • 计时器,计数器(浏览量)

redis默认端口号:6379
在这里插入图片描述

Redis 性能测试

Redis 性能测试是通过同时执行多个命令实现的。

语法

redis 性能测试的基本命令如下:

redis-benchmark [option] [option value]

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
每秒处理87642条请求,速度不畏不快!!!

Redis的基础知识

默认的16个数据库

在这里插入图片描述
在这里插入图片描述

# 切换数据库
select 数字(0-15)
# 查询此数据库中所有的键的名称
keys * 
# 清除当前数据库 
flushdb 
# 清除全部的数据库的内容 16个数据库全部清除
flushall
# 判断键是否存在
exists 键名
# 迁移键 将本数据库的键迁移到其他的数据库 最后面的数字指的就是要迁到的数据库名
move 键名 1
# 设置过期时间 键名+时间(秒)
expire name 10
# 查看一个键还有多少秒过期
ttl 键名
# 查看类型
type 键名

Redis是单线程的

redis的瓶颈不是cpu而是服务器的内存和网络带宽

Redis是单线程的,为什么还这么快?

  • 高性能的服务器不一定是多线程的
  • 多线程不一定比单线程快 多线程有上下文切换,比较耗时
  • Redis是将所有的数据放到内存中的

五大数据类型

在这里插入图片描述

String (用的最多)

###########################################################################

# 给String追加字符串  如果键名不存在则新建一个 相当于set key
append 键名 "hello"
# 字符串长度
strlen 键名
# 自增1
incr 键名
# 自减1
decr 键名
# 增加任意数值
incrby 键名 数值
# 减去任意数值
decrby 键名 数值

###########################################################################

# 截取字符串的某个范围内的字符
getrange 键名 start end
getrange 键名 0 -1 # 查看全部的字符串
# 替换某个字符串
setrange 键名 开始替换的位置 替换的内容
setrange address 3 wangjiatun

###########################################################################

# 设置键值对,顺便设置过期时间
setex 键名 过期时间 内容
# 如果该值存在则返回0,不覆盖原来的内容 如果不存在则创建 分布式锁中会用到
setnx 键名 内容

###########################################################################

# 一次性创建多个键值对 
mset k1 v1 k2 v2 k3 v3
# 原子性的操作,要们都成功 要么都失败
msetnx ..

# 类似于对象的操作
mset user:1:name zhansgan user:1:age 21 # 存储
mget user:1:name user:1:age # 读取

# 如果不存在值 则返回nil  如果存在值,获取原来的值,设置新的值
getset 键名 内容

##########################################################################

List (可做消息队列)

在redis里面我们可以将list做成栈,队列等。

所有的list命令都是以l开头的

##########################################################################

lpush list one  # 将一个值放入list 放在头部
(integer) 1
127.0.0.1:6379[3]> lpush list two
(integer) 2
127.0.0.1:6379[3]> lpush list two
(integer) 3
127.0.0.1:6379[3]> lpush list two
(integer) 4
127.0.0.1:6379[3]> lrange list 0 -1 # 将list中的所有的值输出
1) "two"
2) "two"
3) "two"
4) "one"
127.0.0.1:6379[3]> lrange list 1 2 # 输出指定的范围的list
1) "two"
2) "two"
127.0.0.1:6379[3]> rpush list ahhh # 放在尾部
(integer) 5
127.0.0.1:6379[3]> lrange list 0 -1
1) "two"
2) "two"
3) "two"
4) "one"
5) "ahhh"
127.0.0.1:6379[3]> lrange list 0 -1 
1) "wangge"
2) "two"
3) "two"
4) "two"
5) "one"
6) "ahhh"
127.0.0.1:6379[3]> lpop list # 在左边移除 也就是第一个元素 头元素
"wangge"
127.0.0.1:6379[3]> lrange list 0 -1
1) "two"
2) "two"
3) "two"
4) "one"
5) "ahhh"
127.0.0.1:6379[3]> rpop list # 在右边移除 也就是最后一个元素 尾元素
"ahhh"
127.0.0.1:6379[3]> lrange list 0 -1
1) "two"
2) "two"
3) "two"
4) "one"
127.0.0.1:6379[3]> lindex list 0 # 取出list中的某一个位置的元素
"two"

# 获取list的长度
llen list
# 移除指定的值
lrem key 移除几个 移除的内容
lrem list 3 two

127.0.0.1:6379[3]> lrange list 0 -1
1) "hello4"
2) "hello3"
3) "hello2"
4) "hello1"
127.0.0.1:6379[3]> ltrim list 1 2 # 截取第二个到第三个 其余的删除
OK
127.0.0.1:6379[3]> lrange list 0 -1
1) "hello3"
2) "hello2"

##########################################################################

# rpoplpush 将这个集合的元素移到另一个集合中 移除列表的最后一个元素并将他添加到一个集合的第一个元素的位置
# 将指定下标的值替换为另外一个值 相当于更新操作  如果不存在列表就会报错,,存在则更新
lset list 0 "dads"

##########################################################################

# 在某个值的前面或者是后面插入一个值
linsert list before "world" "hhh"
linsert list after "world" "kkk" # 后面

##########################################################################

list实际上是一个链表,左边右边都可以插入

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

如果key存在 新增内容

在两边插入或者是改动值效率最高,如果链表很长,执行中间元素效率很低。

消息队列,Lpush,Rpop ,栈,

Set (无序不重复)

set的值是不可以重复的。

##########################################################################

127.0.0.1:6379[3]> sadd set "hello" # set集合中添加元素
(integer) 1 
127.0.0.1:6379[3]> sadd set "world" 
(integer) 1
127.0.0.1:6379[3]> sadd set "and"
(integer) 1
127.0.0.1:6379[3]> smembers set # 查看set的所有的内容
1) "and"
2) "world"
3) "hello"
127.0.0.1:6379[3]> sismember set add
(integer) 0
127.0.0.1:6379[3]> sismember set and # 查看该key是否在该set中存在 ismember判断 1存在 0不存在在
(integer) 1
127.0.0.1:6379[3]> scard set # 获取该集合的元素个数
(integer) 3

##########################################################################

127.0.0.1:6379[3]> srem set and # 移除指定的元素
(integer) 1
127.0.0.1:6379[3]> smembers set
1) "world"
2) "hello"
127.0.0.1:6379[3]> srandmember set # 随机输出一个元素
"world"
127.0.0.1:6379[3]> srandmember set # 随机输出一个元素
"hello"
127.0.0.1:6379[3]> srandmember set 2 # 随机输出指定个数的元素
1) "hello"
2) "world"

##########################################################################

127.0.0.1:6379[3]> smembers set 
1) "and"
2) "world"
3) "hello"
127.0.0.1:6379[3]> spop set # 随机删除一个元素
"world"
127.0.0.1:6379[3]> spop set
"hello"
127.0.0.1:6379[3]> smembers set
1) "and"

##########################################################################

# 将一个指定的值移动到另一个set集合中
127.0.0.1:6379[3]> smembers set2
1) "zhe"
127.0.0.1:6379[3]> smove set set2 kuang # 移动到另一个集合中
(integer) 1
127.0.0.1:6379[3]> smembers set2
1) "kuang"
2) "zhe"
127.0.0.1:6379[3]> smembers set
1) "and"
2) "world"
3) "hello"

##########################################################################
- 交集
- 并集
- 差集
127.0.0.1:6379[3]> sadd k1 a
(integer) 1
127.0.0.1:6379[3]> sadd k1 b
(integer) 1
127.0.0.1:6379[3]> sadd k1 c
(integer) 1
127.0.0.1:6379[3]> sadd k2 c
(integer) 1
127.0.0.1:6379[3]> sadd k2 d
(integer) 1
127.0.0.1:6379[3]> sadd k2 e
(integer) 1
127.0.0.1:6379[3]> sdiff k1 k2 # 取k1中和k2不同的元素
1) "b"
2) "a"
127.0.0.1:6379[3]> sdiff k2 k1
1) "d"
2) "e"
127.0.0.1:6379[3]> sinter k1 k2 # 取交集  微博中的共同关注,微信QQ的共同好友就是这样实现的
1) "c"
127.0.0.1:6379[3]> sinter k2 k1
1) "c"
127.0.0.1:6379[3]> sunion k1 k2 # 取并集
1) "c"
2) "d"
3) "b"
4) "a"
5) "e"

##########################################################################

QQ中的共同好友是怎么实现的呢?

就是用set,你所有的好友都放在一个set里面,因为QQ号是唯一的,刚好能发挥set的作用,然后将你的好友的所有的好友也放在一个set里面,然后对这两个set集合取交集,取得的交集就是你们两个的共同好友!!

六度分隔(Six Degrees of Separation)理论。简单地说:“你和任何一个陌生人之间所间隔的人不会超五个,也就是说,最多通过六个人你就能够认识任何一个陌生人。

Hash (哈希)

想象成Map集合,本质和string类型没有太大区别,只是多加了一层,key是哈希表,里面是字段和value。

##########################################################################

127.0.0.1:6379[3]> hset myhash k1 shi # 设置一个kv键值对放入哈希 类似于向Map集合添加一个键值对
(integer) 1
127.0.0.1:6379[3]> hget myhash k1 # 取出一个哈希表中一个键的值
"shi"
127.0.0.1:6379[3]> hmset myhash k1 wang k2 han # 同时设置多个键值对
OK
127.0.0.1:6379[3]> hmget myhash k1 k2 # 同时取出多个键的值
1) "wang"
2) "han"
127.0.0.1:6379[3]> hgetall myhash # 取出哈希表中所有的键值对
1) "k1"
2) "wang"
3) "k2"
4) "han"

##########################################################################

127.0.0.1:6379[3]> hdel myhash k1 # 删除hash指定的字段
(integer) 1
127.0.0.1:6379[3]> hlen myhash # 获取hash表的字段的数量,即哈希表长度
(integer) 3
127.0.0.1:6379[3]> hexists myhash k2 # 判断某个字段是否存在
(integer) 1

##########################################################################

127.0.0.1:6379[3]> hkeys myhash # 获取所有的字段名
1) "k2"
2) "k1"
3) "k3"
127.0.0.1:6379[3]> hvals myhash # 获取所有的值
1) "han"
2) "kuang"
3) "jing"

##########################################################################

127.0.0.1:6379[3]> hset myhash count 0
(integer) 1
127.0.0.1:6379[3]> hget myhash count
"0"
127.0.0.1:6379[3]> hincrby myhash count 2 # 自增2
(integer) 2
127.0.0.1:6379[3]> hincrby myhash count 2
(integer) 4
127.0.0.1:6379[3]> hget myhash count
"4"
127.0.0.1:6379[3]> hincrby myhash count -3 # 自减3
(integer) 1
127.0.0.1:6379[3]> hget myhash count
"1"

##########################################################################

127.0.0.1:6379[3]> hsetnx myhash k2 ss # 如果存在不可以设置 存在不可以设置 分布式锁
(integer) 0
127.0.0.1:6379[3]> hsetnx myhash k5 ss
(integer) 1

##########################################################################

Hash更适合存储对象,String更加适合存储字符串

Zset (有序集合)

在set的基础上增加了一个值,set k1 v1 zset k1 score1 v1

##########################################################################

127.0.0.1:6379[3]> zadd set 1 one # 添加一个元素 指定位置 有序
(integer) 1
127.0.0.1:6379[3]> zadd set 2 two 3 three
(integer) 2
127.0.0.1:6379[3]> zrange set 0 -1 # 输出所有的元素 都是按插入的顺序排列好的
1) "one"
2) "two"
3) "three"

##########################################################################
127.0.0.1:6379[3]> zadd salary 2500 wang
(integer) 1
127.0.0.1:6379[3]> zadd salary 5000 liu
(integer) 1
127.0.0.1:6379[3]> zadd salary 12000 shi
(integer) 1
127.0.0.1:6379[3]> zadd salary 3000 zheng
(integer) 1
127.0.0.1:6379[3]> zadd salary 800 han
(integer) 1
127.0.0.1:6379[3]> zrange salary 0 -1
1) "han"
2) "wang"
3) "zheng"
4) "liu"
5) "shi"
127.0.0.1:6379[3]> zrangebyscore salary -inf +inf # 按score字段排序后输出
1) "han"
2) "wang"
3) "zheng"
4) "liu"
5) "shi"
127.0.0.1:6379[3]> zrevrange salary 0 -1 # 从大到小排序
1) "shi"
2) "liu"
3) "zheng"
4) "han"
127.0.0.1:6379[3]> zrevrange salary 0 -1 withscores
1) "shi"
2) "12000"
3) "liu"
4) "5000"
5) "zheng"
6) "3000"
7) "han"
8) "800"
127.0.0.1:6379[3]> zrangebyscore salary -inf +inf withscores # 带上字段的值输出
 1) "han"
 2) "800"
 3) "wang"
 4) "2500"
 5) "zheng"
 6) "3000"
 7) "liu"
 8) "5000"
 9) "shi"
10) "12000"
127.0.0.1:6379[3]> zrangebyscore salary -inf 3000 withscores # 从最小的到3000范围内的人
1) "han"
2) "800"
3) "wang"
4) "2500"
5) "zheng"
6) "3000"
127.0.0.1:6379[3]> zrem salary wang # 删除指定的字段
(integer) 1
127.0.0.1:6379[3]> zcard salary # 获取集合的长度
(integer) 4
127.0.0.1:6379[3]> zcount salary 1 2 # 获取指定区间内的成员数量

##########################################################################

set可以做排行榜,将一些信息放到redis里面然后每天或者是某个时间根据一些权重,放在set里面,定期的刷新一下,so easy~

三种特殊的数据类型 用这些数据结构可以优化程序效率

geospatial 地图位置

应用场景:

  • 附近的朋友
  • 朋友的定位
  • 打车距离计算

查看经纬度的网址: https://jingweidu.bmcx.com/

Redis的Geo可以推算地理位置的信息,两地之间的距离,方圆多少公里之内的人。
在这里插入图片描述
他只有如上六个命令。

GEOADD 添加某个城市的经纬度

# 添加一些城市的经纬度坐标 (经度,维度,名称)  longitude : 精度  latitude : 维度
127.0.0.1:6379[3]> geoadd china:city 120.39 36.30 qingdao
(integer) 1
127.0.0.1:6379[3]> geoadd china:city 118.89 31.32 nanjing
(integer) 1
127.0.0.1:6379[3]> geoadd china:city 120.21 30.20 hangzhou 121.48 31.40 shanghai
(integer) 2
127.0.0.1:6379[3]> geoadd china:city 117.86 36.49 zibo
(integer) 1

GEODIST 获取指定城市的经纬度

# 获取指定的城市的经度和维度 取到的一定是一个坐标值
127.0.0.1:6379[3]> geopos china:city shanghai
1) 1) "121.48000091314315796"
   2) "31.40000025319353938"
127.0.0.1:6379[3]> geopos china:city nanjing hangzhou
1) 1) "118.89000087976455688"
   2) "31.3199993839624824"
2) 1) "120.21000176668167114"
   2) "30.19999988833350102"

GEOPOS 返回两个给定位置之间的距离

单位必须是以下之一,默认为米:

  • 为米。
  • 公里为公里。
  • 英里英里。
  • 英尺为英尺。
# 表示从你定义的城市当中取出来计算他们之间的距离 可以指定距离单位
127.0.0.1:6379[3]> geodist china:city nanjing qingdao km
"570.9649"
127.0.0.1:6379[3]> geodist china:city qingdao zibo km # 青岛到淄博的距离
"227.4906"

GEORADIUS 查找附近的人

以给定的经纬度为中心,找出某一半径内的所有的元素微信中附近的人就是这么做的!只不过他们的经纬度很精确!!但是有一点我们附近的朋友不可能全部查询出来,比如说附近有一万个,我们只需要200个就够了。

127.0.0.1:6379[3]> georadius china:city 110 30 1000 km # 查找经纬度为110 30 半径为 1000 km范围之内的所有的城市
1) "hangzhou"
2) "nanjing"
127.0.0.1:6379[3]> georadius china:city 110 30 1000 km withcoord # 返回值带城市的坐标
1) 1) "hangzhou"
   2) 1) "120.21000176668167114"
      2) "30.19999988833350102"
2) 1) "nanjing"
   2) 1) "118.89000087976455688"
      2) "31.3199993839624824"
127.0.0.1:6379[3]> georadius china:city 110 30 1000 km withcoord withdist # 带坐标和距离
1) 1) "hangzhou"
   2) "982.4071"
   3) 1) "120.21000176668167114"
      2) "30.19999988833350102"
2) 1) "nanjing"
   2) "862.8969"
   3) 1) "118.89000087976455688"
      2) "31.3199993839624824"
127.0.0.1:6379[3]> georadius china:city 110 30 1000 km withcoord withdist withhash
1) 1) "hangzhou"
   2) "982.4071"
   3) (integer) 4054122592018769
   4) 1) "120.21000176668167114"
      2) "30.19999988833350102"
2) 1) "nanjing"
   2) "862.8969"
   3) (integer) 4054278551831736
   4) 1) "118.89000087976455688"
      2) "31.3199993839624824"
127.0.0.1:6379[3]> georadius china:city 110 30 1000 km count 2 # 查询出2个来
1) "nanjing"
2) "hangzhou"

GEORADIUSBYMEMBER 查找某个坐标附近的坐标

127.0.0.1:6379[3]> GEORADIUSBYMEMBER china:city nanjing 100 km
1) "nanjing"

# 以北京这个已经存入的点来查找方圆800km之内的所有的城市
127.0.0.1:6379[3]> GEORADIUSBYMEMBER china:city nanjing 800 km 
1) "hangzhou"
2) "nanjing"
3) "shanghai"
4) "zibo"
5) "qingdao"

GEOHASH

将二维的经纬度转化为一维的字符串,字符串长得越像,说明两个坐标越接近!

127.0.0.1:6379[3]> geohash china:city nanjing hangzhou
1) "wtsd1mth1z0"
2) "wtm7z3wrb00"

GEO是基于Zset实现的,我们可以用Zset命令操作GEO

127.0.0.1:6379[3]> zrange china:city 0 -1
1) "hangzhou"
2) "nanjing"
3) "shanghai"
4) "zibo"
5) "qingdao"
127.0.0.1:6379[3]> zrem china:city hangzhou
(integer) 1
127.0.0.1:6379[3]> zrange china:city 0 -1
1) "nanjing"
2) "shanghai"
3) "zibo"
4) "qingdao"

hyperloglog 基数–不重复的元素

什么是基数?即不重复的元素,可以接受误差!

做基数统计,占用固定内存12kb,

127.0.0.1:6379[3]> pfadd key2 i j k o h y r e t y b v f  f # 创建第一组元素
(integer) 1
127.0.0.1:6379[3]> pfcount key2 # 统计元素
(integer) 12
127.0.0.1:6379[3]> pfcount key
(integer) 1
127.0.0.1:6379[3]> pfmerge key3 key2 key # 合并两组元素的基数数量
OK
127.0.0.1:6379[3]> pfcount key3
(以上是关于Redis学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

Redis学习笔记5:JedisRedisTemplate

Redis学习笔记5:JedisRedisTemplate

redis学习笔记: ae

redis学习笔记: replication

Redis学习笔记jedis(JedisCluster)操作Redis集群 redis-cluster

DOM探索之基础详解——学习笔记