Redis学习笔记

2021/5/20 2:29:25

本文主要是介绍Redis学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Redis学习笔记

nosql

Nosql概述:

为什么要用Nosql
  1. 单机MySQL的年代!

    app —> dal —>Mysql

  2. Memcached(缓存)+ MySql + 垂直拆分(读写分离)

    优化数据结构和索引 —> 文件缓存(IO)—> Memcached(当时最热门的技术!)

  3. 分库分表 + 水平拆分 + MySQL集群

    本质:数据库(读、写)

    早些年MyISAM:表锁,十分影响效率!高并发下就会出现严重的锁问题

    转战Innodb : 行锁

    慢慢就开始了分库分表来缓解压力!

什么是Nosql

NoSQL = Not Only Sql (不仅仅是sql)

泛指非关系型数据库

NoSql特点:

解耦!

  1. 方便扩展!(数据之间没有关系,很好扩展!)

  2. 大数据量高性能!(Redis 一秒写8万次,读取11万次)

  3. 数据类型是多样型的!(不需要实现设计数据库!随取随用!)

  4. 传统RDBMS 和 NoSQL

    传统的 RDBMS- 结构化组织- SQL- 数据和关系都存在单独的表中- 操作数据,数据定义语言- 严格的一致性- 基础的事务- .......
    
    NoSql- 不仅仅是数据-  没有固定的查询语言-  键值对存储,列存储,文档储存,图形化存储-  最终一致性-  CAP定理BASE-  高性能、高可用、高可扩展-  .......
    

阿里巴巴框架演进

nosql 数据模型

KV键值对:
  • 新浪:Redis
  • 美团:Redis + Tair
  • 阿里、百度:Redis + memecache
文档型数据库:
  • MongoDB
    • MonoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档
    • MonoDB是一个介于关系型数据库和非关系型数据库中中间的产品 (MonoDB是非关系型数据库功能最丰富的,最想关系型数据库)
  • ConthDB
列存储:
  • HBase
  • 分布式文件系统
图形化数据库:
  • 他不是存图形的,放的是关系,比如:朋友圈社交网络、广告推荐!
  • Neo4J、InfoGrid

Nosql 四大分类

CAP

BASE

Redis入门

Redis是什么!

Redis(Remote Dictionary Server ),即远程字典服务

是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

免费和开源!是当下最热门的NoSql 技术之一!也被人们称为架构化数据库

Redis能干什么!
  1. 内存储存、持久化、内存中的数据是断电及失、所以说持久化很重要!(rdb、aof)
  2. 效率高、可以用于告诉缓存
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器、计数器、(数据浏览量)
  6. ……
Redis特性!
  1. 持久化
  2. 多样化数据库
  3. 集群
  4. 事务
  5. ……

安装Redis

Windows安装

windows :下载地址

下载完成解压

image-20210312190659183

启动Redis服务器:

双击运行服务 redis-server.exe

image-20210312191028923

运行成功

再次运行redis客户端 启动 redis-cli.exe

127.0.0.1:6379> ping  ----> 测试是否连接成功PONG 127.0.0.1:6379> set name changan  -----> 设置 key valueOK127.0.0.1:6379> get name  ------> 用 key 去寻找 value"changan"

Linux安装

官网下载:地址

下载完成之后导入到 Liunx 的 opt 目录下面

执行 tar -zxvf redis... 进行解压

解压完成只要需要安装c++ 环境 yum -y install gcc-c++

安装完成执行 make

image-20210313130749997

执行完成之后在执行一遍

然后执行 make install

image-20210313130858601

redis 的默认路径在 usr/local/bin目录下面

image-20210313131120947

移动 redis.config 到 本目录下

image-20210313131349900

redis默认不是后台启动,我们需要修改配置文件

找到这个修改为 yes

image-20210313131611230

启动redis 服务

image-20210313131826979

测试连接

image-20210313132042196

查看redis服务信息

image-20210313132339207

关闭redis

127.0.0.1:6379> shutdown   --->关闭redisnot connected> exit   ---> 退出

redis性能测试

序号选项描述默认值
1-h指定服务器主机名127.0.0.1
2-p指定服务器端口6379
3-s指定服务器 socket
4-c指定并发连接数50
5-n指定请求数10000
6-d以字节的形式指定 SET/GET 值的数据大小2
7-k1=keep alive 0=reconnect1
8-rSET/GET/INCR 使用随机 key, SADD 使用随机值
9-P通过管道传输 请求1
10-q强制退出 redis。仅显示 query/sec 值
11—csv以 CSV 格式输出
12-l生成循环,永久执行测试
13-t仅运行以逗号分隔的测试命令列表。
14-IIdle 模式。仅打开 N 个 idle 连接并等待。

测试 使用 redis-benchmark

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

redis-benchmark -h 127.0.0.0 -p 6379 -c 100 -n 100000
====== PING_INLINE ======                                                     100000 requests completed in 2.31 seconds    ----> 对我们10w请求进行测试  100 parallel clients    -----> 100个并发的客服端  3 bytes payload    ----> 每次写入3个字节  keep alive: 1   ---> 只有一台服务器来处理这些请求,单机性能  host configuration "save": 3600 1 300 100 60 10000  host configuration "appendonly": no  multi-thread: no====== SET ======                                                     100000 requests completed in 2.31 seconds  100 parallel clients  3 bytes payload  keep alive: 1  host configuration "save": 3600 1 300 100 60 10000  host configuration "appendonly": no  multi-thread: noLatency by percentile distribution:0.000% <= 0.935 milliseconds (cumulative count 1)50.000% <= 1.599 milliseconds (cumulative count 50402)75.000% <= 1.895 milliseconds (cumulative count 75422)87.500% <= 2.039 milliseconds (cumulative count 87552)93.750% <= 2.143 milliseconds (cumulative count 93953)96.875% <= 2.487 milliseconds (cumulative count 96893)98.438% <= 3.607 milliseconds (cumulative count 98439)99.219% <= 6.535 milliseconds (cumulative count 99219)99.609% <= 12.039 milliseconds (cumulative count 99613)99.805% <= 12.583 milliseconds (cumulative count 99805)99.902% <= 13.031 milliseconds (cumulative count 99903)99.951% <= 13.479 milliseconds (cumulative count 99952)99.976% <= 13.775 milliseconds (cumulative count 99976)99.988% <= 13.927 milliseconds (cumulative count 99988)99.994% <= 14.007 milliseconds (cumulative count 99994)99.997% <= 14.039 milliseconds (cumulative count 99997)99.998% <= 14.071 milliseconds (cumulative count 99999)99.999% <= 14.087 milliseconds (cumulative count 100000)100.000% <= 14.087 milliseconds (cumulative count 100000)

基础知识

image-20210318154701720

Redis默认数据库

Redis默认有16个数据库,默认使用第0个

  • select 2 ——> 更换数据库
  • dbsize ——-> 查看数据库大小
  • keys * ———> 查看数据库所有的key
  • flushdb ————> 清空当前数据库
  • flushall ———> 清空所有库
[root@VM-0-5-centos changanconfig]# redis-cli -p 6379127.0.0.1:6379> pingPONG127.0.0.1:6379> select 2   ---->  更换数据库OK127.0.0.1:6379[2]> dbsize  -----> 查看数据库大小(integer) 0127.0.0.1:6379[2]>

Redis数据类型

五种基本数据类型

  • Redis-key
  • String
  • List
  • Set
  • Hash
  • Zset
Redis-key

keys * 查看当前库所有的key

exists name 判断namekey 是否存在

move name 1` 移除 `name

expire name 10 设置name 10秒钟之后过期

ttl name 查看过期时间

type name 查看当前 key 的类型

String

append 追加字符串

127.0.0.1:6379> keys *1) "name"2) "age"127.0.0.1:6379> get name"changan"127.0.0.1:6379> append name love    (integer) 11127.0.0.1:6379> get name"changanlove"127.0.0.1:6379> exists name(integer) 1

incr views 给 views 加1

decr views 给 views 减1

incrby views 给 views 加10

decrby views 给 views 减10

127.0.0.1:6379> set views 0OK127.0.0.1:6379> get views"0"127.0.0.1:6379> incr views(integer) 1127.0.0.1:6379> decr views(integer) 0127.0.0.1:6379> incrby views 10(integer) 10127.0.0.1:6379> decrby views 10

getrange 范围查询数据

127.0.0.1:6379> getrange key1 0 3"hell"127.0.0.1:6379> getrange key1 0 -1"hello,word"127.0.0.1:6379> getrange key1 5 -1",word"127.0.0.1:6379> getrange key1 5 0""127.0.0.1:6379> getrange key1 1 -1"ello,word"127.0.0.1:6379> getrange key1 0 -1

setrange 范围修改

127.0.0.1:6379> setrange key1 1 xx(integer) 10127.0.0.1:6379> keys *1) "key1"127.0.0.1:6379> get key1"hxxlo,word"

常在分布式锁使用

127.0.0.1:6379> setex key2 30 "hello"   ---- > 设置过期时间OK127.0.0.1:6379> ttl key2                ---- > 查看剩余时间(integer) 28127.0.0.1:6379> setnx mykey "redis"     ---- > 如果 mykey 不存在,创建 mykey 返回1 (integer) 1127.0.0.1:6379> keys *   1) "key2"2) "key1"3) "mykey"127.0.0.1:6379> ttl key2 (integer) -2127.0.0.1:6379> setnx mykey "MongDB"    ---- > 如果 mykey 存在,就创建失败 返回0(integer) 0127.0.0.1:6379> get mykey"redis"

批量获取值和设置值

mset 批量设置

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3OK127.0.0.1:6379> keys *1) "k3"2) "k2"3) "k1"

mget 同时获取多个值

127.0.0.1:6379> mget k1 k2 k31) "v1"2) "v2"3) "v3"

msetnx 设置多个值 如果一个值设置失败,那么全部失败 (原子性)

127.0.0.1:6379> msetnx k1 v1 k5 v5(integer) 0

getset 先获取在设置

127.0.0.1:6379> getset db redis   --> 不存在返回nil并创建(nil)127.0.0.1:6379> get db"redis"127.0.0.1:6379> getset db mongDb  --> 如果存在,返回当前的值,并设置新的值"redis"127.0.0.1:6379> get db"mongDb"
应用场景
  • 计数器
  • 统计多单位数量
  • 粉丝数
  • 对象缓存存储
List

基本数据类型,列表

lpush 储存 《左存储》

rpush 储存 《右存储》

lrange 读取

127.0.0.1:6379> lpush list one  ---> 给左push(integer) 1127.0.0.1:6379> lpush list two(integer) 2127.0.0.1:6379> lpush list three(integer) 3127.0.0.1:6379> lrange list 0 -11) "three"2) "two"3) "one"127.0.0.1:6379> lrange list 0 11) "three"2) "two"#####################################################################127.0.0.1:6379> rpush list yss(integer) 4127.0.0.1:6379> lrange list 0 -11) "three"2) "two"3) "one"4) "yss"

lpop : 左移除

rpop : 右移除

127.0.0.1:6379> lpop list ----> 移除list第一个元素"three"127.0.0.1:6379> rpop list ----> 移除list最后一个元素"yss"127.0.0.1:6379> lrange list 0 -11) "two"2) "one"

lindex 通过下标获取值

127.0.0.1:6379> lindex list 0"two"127.0.0.1:6379> lindex list 1"one"

llen 获取list的长度

127.0.0.1:6379> llen list(integer) 4

lrem 删除 ‘精确匹配’

127.0.0.1:6379> lrange list 0 -11) "two"2) "one"3) "asdfa"4) "9889"5) "9889"127.0.0.1:6379> lrem list 1 asdfa  ----- > 删除 1个 asdfa(integer) 1127.0.0.1:6379> lrange list 0 -11) "two"2) "one"3) "9889"4) "9889"127.0.0.1:6379> rpush list 9889(integer) 4127.0.0.1:6379> lrange list 0 -11) "two"2) "one"3) "9889"4) "9889"127.0.0.1:6379> lrem list 2 9889   ------ > 删除 2个 9889(integer) 2127.0.0.1:6379> lrange list 0 -11) "two"2) "one"

ltrim 修剪

127.0.0.1:6379> rpush mylist "hello"(integer) 1127.0.0.1:6379> rpush mylist "hello1"(integer) 2127.0.0.1:6379> rpush mylist "hello2"(integer) 3127.0.0.1:6379> rpush mylist "hello3"(integer) 4127.0.0.1:6379> ltrim mylist 1 2   ----- > 通过下标截取指定长度OK127.0.0.1:6379> lrange mylist 0 -11) "hello1"2) "hello2"

rpoplpush 移除列表的最后一个元素 并移动到新的列表中

127.0.0.1:6379> lrange mylist 0 -11) "hello1"2) "hello2"3) "hello3"127.0.0.1:6379> rpoplpush mylist myotherlist"hello3"127.0.0.1:6379> lrange mylist 0 -11) "hello1"2) "hello2"127.0.0.1:6379> lrange myotherlist 0 -11) "hello3"

lset 将列表中指定下标的值替换为另外一个

127.0.0.1:6379> exists list   --- >  判断是否有这个集合(integer) 0127.0.0.1:6379> lset list 0 item   ----- > 给list的第0个下标添加 item(error) ERR no such key    ---> 报错,添加失败 没有这个list127.0.0.1:6379> lpush list value1  ------ > 创建 list(integer) 1127.0.0.1:6379> lrange list 0 -11) "value1"127.0.0.1:6379> lset list 0 item   ----- > 再次用 lset 覆盖 刚创建那个list的第0个下标OK127.0.0.1:6379> lrange list 0 -11) "item"127.0.0.1:6379> lset list 1 item  -----> 如果给list添加值 这个下标不存在那么就会报错(error) ERR index out of range

linsert 将某一个具体的值插入列表某个元素的前后

参数:``before左、前after` 右、后

127.0.0.1:6379> rpush mylist "hello"(integer) 1127.0.0.1:6379> rpush mylist "word"(integer) 2// before127.0.0.1:6379> linsert mylist before "word" "other"  (integer) 3127.0.0.1:6379> lrange mylist 0 -11) "hello"2) "other"3) "word"// after127.0.0.1:6379> linsert mylist after "word" "king"(integer) 4127.0.0.1:6379> lrange mylist 0 -11) "hello"2) "other"3) "word"4) "king"
小结
  • set实际是一个链表,before Node after,left ,right 都可以插入值

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

  • 如果key 存在,新增内容

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

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

    可以用来 消息排队! 消息队列(Lpush Rpop) 左进右出 ,栈(Lpush Lpop)左进左出

Set

set中的值不能重复

sadd
smembers
sismember
scard
127.0.0.1:6379> sadd myset hello   ----> 给集合添加值(integer) 1127.0.0.1:6379> sadd myset changan  (integer) 1127.0.0.1:6379> sadd myset king(integer) 1127.0.0.1:6379> SMEMBERS myset    -----> 查看当前集合元素1) "king"2) "changan"3) "hello"127.0.0.1:6379> SISMEMBER myset hello   -----> 查看集合是否有hello  有的话返回1 没有就是0(integer) 1127.0.0.1:6379> SISMEMBER myset word(integer) 0127.0.0.1:6379> scard myset   ------> 获取当前集合的个数(integer) 3127.0.0.1:6379> srem myset hello   -----> 删除集合指定的值(integer) 1127.0.0.1:6379> SMEMBERS myset1) "king"2) "changan"

SRANDMEMBER 随机从集合中筛选一个数据

127.0.0.1:6379> SRANDMEMBER myset"king"127.0.0.1:6379> SRANDMEMBER myset"king"127.0.0.1:6379> SRANDMEMBER myset"king"127.0.0.1:6379> SRANDMEMBER myset"king"127.0.0.1:6379> SRANDMEMBER myset"changan"

spop 随机删除一个元素

127.0.0.1:6379> SMEMBERS myset1) "king"2) "changan"127.0.0.1:6379> spop myset"king"127.0.0.1:6379> SMEMBERS myset1) "changan"

smove将一个指定的值移动到另外一个set集合中

127.0.0.1:6379> sadd myset hello(integer) 1127.0.0.1:6379> sadd myset word(integer) 1127.0.0.1:6379> sadd myset changanking(integer) 1127.0.0.1:6379> sadd myset king(integer) 1127.0.0.1:6379> sadd myset2 set2(integer) 1127.0.0.1:6379> smove myset myset2 changanking    (integer) 1127.0.0.1:6379> SMEMBERS myset1) "word"2) "hello"3) "changan"4) "king"127.0.0.1:6379> SMEMBERS myset21) "changanking"2) "set2"

SDIFF 查看2个集合的差集

SINTER 查看2个集合的交集 共同好友就可以实现

SUNION 查看2个集合的

127.0.0.1:6379> sadd key1 a(integer) 1127.0.0.1:6379> sadd key1 b(integer) 1127.0.0.1:6379> sadd key1 c(integer) 1127.0.0.1:6379> sadd key2 c(integer) 1127.0.0.1:6379> sadd key2 d(integer) 1127.0.0.1:6379> sadd key2 e(integer) 1127.0.0.1:6379> SDIFF key1 key21) "a"2) "b"127.0.0.1:6379> SINTER key1 key21) "c"127.0.0.1:6379> SUNION key1 key21) "c"2) "b"3) "a"4) "d"5) "e"
应用场景

微博 ,A用户将所有的关注的人放在一个set集合中,将他的粉丝也放在一个集合中

共同关注,共同爱好,二度好友,推荐好友!

Hash

Map集合,Key—Value

127.0.0.1:6379> hset map k changan   ---->  给map存一个k-v(integer) 1127.0.0.1:6379> hget map k           ---->  通过k取值"changan"127.0.0.1:6379> hmset map y king u anuyn  ---->  给map存多个值OK 127.0.0.1:6379> hmget map y u           ---->  获取map多个值1) "king"2) "anuyn"127.0.0.1:6379> hgetall map              ------> 查看map所有的kv1) "k"2) "changan"3) "y"4) "king"5) "u"6) "anuyn"

hdel 删除指定的kv

127.0.0.1:6379> hdel map u     -----> 删除指定的 k(integer) 1127.0.0.1:6379> hgetall map1) "k"2) "changan"3) "y"4) "king"

hlen 查询集合有几组kv

127.0.0.1:6379> hlen map(integer) 2

HEXISTS 查看集合的k是否存在

127.0.0.1:6379> HEXISTS map k(integer) 1127.0.0.1:6379> HEXISTS map u(integer) 0

hkeys 查看所有的key

hvals 查看所有的value

127.0.0.1:6379> hkeys map1) "k"2) "y"127.0.0.1:6379> hvals map1) "changan"2) "king"

HINCRBY 指定增量

hsetnx 创建一个集合 如果这个集合没有数据 就放入数据 返回1 如果有数据 那么返回0

127.0.0.1:6379> hset myhash field3 5    ----- >>> 指定增量(integer) 1127.0.0.1:6379> HINCRBY myhash field3 1   ------>>> 给数据加一(integer) 6127.0.0.1:6379> HINCRBY myhash field3 -1  ------>>>> 给数据减一(integer) 5127.0.0.1:6379> hsetnx myhash field4 hello  -------> 创建一个集合 如果存在则不能设置 返回1(integer) 1127.0.0.1:6379> hsetnx myhash field4 world   ---->> 创建一个集合 如果存在则不能设置 返回0(integer) 0

hash可以用于存储对象

127.0.0.1:6379> hset user:1 name changan(integer) 1127.0.0.1:6379> hget user:1 name"changan"127.0.0.1:6379> hset user:1 age 23(integer) 1127.0.0.1:6379> hget user:1 age"23"
应用场景

hash 用来处理变更的数据 user name age ,尤其是用户信息之类,经常变动的信息! hash 更适合与对象的存储,String 适合字符串的存储!

Zset(有序集合)

在set的基础上加了一个值

api:
127.0.0.1:6379> zadd myset 1 one     -----> 创建一个值(integer) 1127.0.0.1:6379> zadd myset 2 two 3 three   ----->  创建2个值(integer) 2127.0.0.1:6379> zrange myset 0 -1   ------> 获取myset 里的数据1) "one"2) "two"3) "three"

zrangebyscore : 从低到高 进行排序

zrevrange : 从高到低 进行排序

127.0.0.1:6379> zadd salary 5000 xiaohong(integer) 1127.0.0.1:6379> zadd salary 2500 xiaoming(integer) 1127.0.0.1:6379> zadd salary 500 zhangsan(integer) 1127.0.0.1:6379> zrangebyscore salary -inf +inf   ---- > 通过集合salary 的 钱数排序 -inf 负无穷  +inf 正无穷    从低到高1) "zhangsan"2) "xiaoming"3) "xiaohong"127.0.0.1:6379> zrevrange salary 0 -11) "xiaoming"2) "zhangsan"127.0.0.1:6379> zrangebyscore salary -inf +inf withscores  -->> 查询出来所有数据 WITHSCORES会返回元素和其分数1) "zhangsan"2) "500"3) "xiaoming"4) "2500"5) "xiaohong"6) "5000"127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores1) "zhangsan"2) "500"3) "xiaoming"4) "2500"

zrem 移除

127.0.0.1:6379> zrange salary 0 -11) "zhangsan"2) "xiaoming"3) "xiaohong"127.0.0.1:6379> zrem salary xiaohong(integer) 1

zcard 获取集合中的个数

127.0.0.1:6379> zcard salary(integer) 2

zcount 获取指定区间的成员数量

127.0.0.1:6379> zadd myset 1 "hello" 2 "word" 3 "changan"(integer) 3127.0.0.1:6379> zcount myset 1 2(integer) 2127.0.0.1:6379> zcount myset 1 3(integer) 3
使用场景

set 排序 存储班级成绩,工资排序

普通消息 1、重要消息 2 带权重进行判断

排行榜应用实现 取 Top N测试

三种特殊数据类型

geospatial 地理位置

Redis 的 Geo 可以去实现 推算2地之间的距离,推算地理位置信息 …… !

测试数据网站:http://www.toolzl.com/tools/gps.html

image-20210415171238885

Geoadd 添加地理位置
2极地区无法直接添加,我们一般会下载城市数据,用Java程序导入

将指定的地理空间位置(纬度、经度、名称)添加到指定的key

  • 有效经度为-180至180度。
  • 有效纬度为-85.05112878至85.05112878度。
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing(integer) 1127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai(integer) 1127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqi 114.05 22.52 shenzhen(integer) 2127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian (integer) 2

geopos 查询地理位置

127.0.0.1:6379> geopos china:city beijing1) 1) "116.39999896287918091"   2) "39.90000009167092543"127.0.0.1:6379> geopos china:city xian1) 1) "108.96000176668167114"   2) "34.25999964418929977"

geodist

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

如果两个位置之间的其中一个不存在, 那么命令返回空值。

指定单位的参数 unit 必须是以下单位的其中一个:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。
127.0.0.1:6379> geodist china:city beijing xian km   北京到西安的直线距离"910.0565"127.0.0.1:6379> geodist china:city beijing shanghai km  北京带上海的直线距离"1067.3788"

附近的人

半径搜索

127.0.0.1:6379> georadius china:city 110 30 1000 km  以 110,30的地理坐标 查询 1000km 内的城市1) "chongqi"2) "xian"3) "shenzhen"4) "hangzhou"127.0.0.1:6379> georadius china:city 110 30 500 km 以 110,30的地理坐标 查询 500km 内的城市1) "chongqi"2) "xian"127.0.0.1:6379> georadius china:city 110 30 500 km withdist   到中心的直线距离1) 1) "chongqi"   2) "341.9374"2) 1) "xian"   2) "483.8340"127.0.0.1:6379> georadius china:city 110 30 500 km withcoord   经纬度1) 1) "chongqi"   2) 1) "106.49999767541885376"      2) "29.52999957900659211"2) 1) "xian"   2) 1) "108.96000176668167114"      2) "34.25999964418929977"127.0.0.1:6379> georadius china:city 110 30 500 km withcoord count 1  查询指定数量的1) 1) "chongqi"   2) 1) "106.49999767541885376"      2) "29.52999957900659211"

GEORADIUSBYMEMBER

找出位于指定元素周围的其他元素

127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km1) "beijing"2) "xian"127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 500 km1) "beijing"

删除、查看

127.0.0.1:6379> zrange china:city 0 -1   查看所有1) "chongqi"2) "xian"3) "shenzhen"4) "hangzhou"5) "shanghai"6) "beijing"127.0.0.1:6379> zrem china:city beijing  删除(integer) 1127.0.0.1:6379> zrange china:city 0 -11) "chongqi"2) "xian"3) "shenzhen"4) "hangzhou"5) "shanghai"
Hyperloglog 基数统计

什么是基数!

A{ 1、3、5、7、8、7 }

B{ 1、3、5、7、8 }

基数 (不重复的元素)= 5,可以接收误差!

简介

pfadd :添加数据

pfcount :查询数据

pfmerge :合并数据

127.0.0.1:6379> pfadd mykey a b c d e f g h i j k(integer) 1127.0.0.1:6379> pfcount mykey(integer) 11127.0.0.1:6379> pfadd mykey2 b c d t y o p(integer) 1127.0.0.1:6379> pfcount mykey2(integer) 7127.0.0.1:6379> pfmerge mykey3 mykey mykey2OK127.0.0.1:6379> pfcount mykey3(integer) 14
Bitmaps(位图) 位存储

只要是2位数的 数据结构就可以用

如:登录、未登录 活跃、不活跃

设置或者清空key的value(字符串)在offset处的bit值。

那个位置的bit要么被设置,要么被清空,这个由value(只能是0或者1)来决定。当key不存在的时候,就创建一个新的字符串value

测试

测试打卡记录 在2021年4月15日没有打卡等

setbit 存值

getbit 取值

bitcount 查询

127.0.0.1:6379> setbit sign 20210415 0 (integer) 0127.0.0.1:6379> setbit sign 20210416 1(integer) 0127.0.0.1:6379> setbit sign 20210414 0(integer) 0127.0.0.1:6379> setbit sign 20210413 1(integer) 0127.0.0.1:6379> setbit sign 20210412 1(integer) 0127.0.0.1:6379> setbit sign 20210417 1(integer) 0127.0.0.1:6379> getbit sign 20210416(integer) 1127.0.0.1:6379> getbit sign 20210415(integer) 0127.0.0.1:6379> bitcount sign(integer) 4

事务

Redis 事务本质:一组命令的集合!

一个事务中所有的命令都会被序列化,在事务执行过程中,会按照顺序执行!

一次性、序列型、非它性、

Redis事务没有隔离级别的概念

Redis 单条命令是保存原子性的、但是事务不保证原子性

所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!—Exec

Redis的事务:

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

测试:

127.0.0.1:6379> multi     // > 开启事务OK127.0.0.1:6379(TX)> set k1 1    // 命令入队QUEUED127.0.0.1:6379(TX)> set k1 2QUEUED127.0.0.1:6379(TX)> set k1 3QUEUED127.0.0.1:6379(TX)> set k2 2QUEUED127.0.0.1:6379(TX)> set k3 4QUEUED127.0.0.1:6379(TX)> set k4 6QUEUED127.0.0.1:6379(TX)> get k4QUEUED127.0.0.1:6379(TX)> set k7 9QUEUED127.0.0.1:6379(TX)> exec      //  执行事务1) OK2) OK3) OK4) OK5) OK6) OK7) "6"8) OK

放弃事务:

127.0.0.1:6379> multi    // 开启事务OK127.0.0.1:6379(TX)> set key7 7QUEUED127.0.0.1:6379(TX)> discard    // 取消事务OK127.0.0.1:6379> get key7(nil)

编译型异常: 命令错误

127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> set k1 2QUEUED127.0.0.1:6379(TX)> set k2 4QUEUED127.0.0.1:6379(TX)> set k4 5QUEUED127.0.0.1:6379(TX)> getset k7 8QUEUED127.0.0.1:6379(TX)> getset k9       //错误语法(error) ERR wrong number of arguments for 'getset' command127.0.0.1:6379(TX)> set k8 9QUEUED127.0.0.1:6379(TX)> exec(error) EXECABORT Transaction discarded because of previous errors.     /*提示语法错误,所有命令都不执行 **/127.0.0.1:6379> get k4(nil)

运行时异常:如果命令中存在错误,不会影响其他命令执行

127.0.0.1:6379> set k1 7OK127.0.0.1:6379> set k2 "j9"OK127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> incr k2    // 会执行失败QUEUED127.0.0.1:6379(TX)> set k3 3QUEUED127.0.0.1:6379(TX)> set k4 5QUEUED127.0.0.1:6379(TX)> get k4QUEUED127.0.0.1:6379(TX)> exec1) (error) ERR value is not an integer or out of range        // 虽然第一条命令执行失败了后面的命令会继续执行2) OK3) OK4) "5"127.0.0.1:6379> get k3"3"127.0.0.1:6379> get k4"5"

监控

悲观锁:

认为什么时候都会出现问题,无论做什么都会加锁

乐观锁:

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

数据测试:

单线程

127.0.0.1:6379> set money 100    // 设置金钱是100OK127.0.0.1:6379> set out 0        // 设置金钱是0OK127.0.0.1:6379> watch money      // 给money上锁OK127.0.0.1:6379> multi     // 开启事务OK127.0.0.1:6379(TX)> decrby money 20    // money金钱减 20QUEUED127.0.0.1:6379(TX)> incrby out 20      // out 加 220QUEUED127.0.0.1:6379(TX)> exec1) (integer) 802) (integer) 20

多线程

使用 watch 来当做Redis的乐观锁

// 线程 1 127.0.0.1:6379> watch money      // 开启监控OK127.0.0.1:6379> multi            // 开启事务OK127.0.0.1:6379(TX)> decrby money 10    // 金币减10QUEUED127.0.0.1:6379(TX)> incrby out 10      // 金币加10QUEUED    // 线程 2    127.0.0.1:6379> incrby money 1000   // 线程2 给这个用户 充值 1000    (integer) 1080// 线程 1    127.0.0.1:6379(TX)> exec  // 线程 1 继续执行 然后就会执行失败(nil)

如果修改失败获取最新的值即可

解决办法:

127.0.0.1:6379> unwatch    // 放弃旧的锁OK127.0.0.1:6379> watch money   // 加新锁OK127.0.0.1:6379> multi    // 开启事务OK127.0.0.1:6379(TX)> decrby money 100QUEUED127.0.0.1:6379(TX)> incrby out 100QUEUED127.0.0.1:6379(TX)> exec1) (integer) 10002) (integer) 120

Jedis

什么是Jedis

Jedis是Redis 推荐使用的Java开发工具!使用Java操作中间件!

测试

1、导入依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency>    <groupId>redis.clients</groupId>    <artifactId>jedis</artifactId>    <version>3.5.2</version></dependency><!-- fastjson--><dependency>    <groupId>com.alibaba</groupId>    <artifactId>fastjson</artifactId>    <version>1.2.76</version></dependency>
2、编码测试
连接数据库
public static void main(String[] args) {        Jedis jedis = new Jedis("XXXXXXX",6379); // ip地址及端口号        jedis.auth("---"); //redis 密码        System.out.println(jedis.ping());}

本地访问 直接 Jedis jedis = new Jedis("127.0.0.1",6379);

输出 PONG 连接成功

操作命令

……

事务:

RedisApi api = new RedisApi();Jedis jedis = api.connection();jedis.flushDB();JSONObject jsonObject = new JSONObject();jsonObject.put("hello","word");jsonObject.put("name","changan");Transaction multi = jedis.multi();String toString = jsonObject.toJSONString();try {    multi.set("user1",toString);        //注意事务执行的话要用事务 multi.set 而不是jedis.set    multi.set("user2",toString);    // int i = 1/0;    multi.exec();}catch (Exception e){    multi.discard();    e.printStackTrace();}finally {    System.out.println(jedis.get("user1"));    System.out.println(jedis.get("user2"));}
断开连接
jedis.close()

SpringBoot整合

说明:在SpringBoot2.x 之后 Jedis 被替换成为了 lettuce

区别:

  • Jedis : 采用直连方式 、是不安全的,要避免安全隐患要采用 Jedis的pool连接池管理! 像BIO
  • lettuce : 采用netty 实例可以在多个进程**享、不存在线程不安全问题 像NIO

整合

  1. 添加依赖

    <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
    
  2. 配置Redis

    spring:  redis:    host: 服务器地址    port: 6379    password: redis密码
    
  3. 测试

    @Resourceprivate RedisTemplate redisTemplate;@Testvoid contextLoads() {    RedisConnection connection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection();    System.out.println(connection.ping());    // 测试链接是否成功    redisTemplate.opsForValue().set("key","changan");   //  操纵字符串 set 一个值    Object key = redisTemplate.opsForValue().get("key");  // 获取值    System.out.println(key);}
    
    • redisTemplate.opsForValue() 操作字符串

    • redisTemplate.opsForList() 操作list

    • connection.close();    // 关闭链接connection.flushAll(); // 清空所有数据库的所有 keyconnection.flushDb();  // 清空
      

测试

@Testpublic void UserTest() throws JsonProcessingException {    RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();    connection.flushDb();    User user = new User("长安",23);    String s = new ObjectMapper().writeValueAsString(user);   // 转化为Json 数据    redisTemplate.opsForValue().set("user",s);    System.out.println(redisTemplate.opsForValue().get("user"));}// 输出结果  {"name":"长安","age":23}

如果我们直接传对象没有序列化 ,会报错

@Testpublic void UserTest() throws JsonProcessingException {    RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();    connection.flushDb();    User user = new User("长安",23);    redisTemplate.opsForValue().set("user",user);    System.out.println(redisTemplate.opsForValue().get("user"));}//Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.changan.model.User]

如果要传递对象要序列化

public class User implements Serializable {    private String name;    private int age;}// 结果  User(name=长安, age=23)

编写一个自己的 Redis 序列化

@Configurationpublic class RedisConfig {    // 配置自己的Redis  固定模板    @Bean    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {        // 我们为了自己开发方便,一般直接使用<String,Object>        RedisTemplate<String, Object> template = new RedisTemplate<>();        template.setConnectionFactory(redisConnectionFactory);        // Json序列化配置        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);        ObjectMapper objectMapper = new ObjectMapper();        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jsonRedisSerializer.setObjectMapper(objectMapper);        // String 的序列化        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();        // key 采用String 方式序列化        template.setKeySerializer(stringRedisSerializer);        // hash 的key 采用String 方式序列化        template.setHashKeySerializer(stringRedisSerializer);        // Value 采用Json 序列化        template.setValueSerializer(jsonRedisSerializer);        // hash 的 Value 采用Json 序列化        template.setHashValueSerializer(jsonRedisSerializer);        template.afterPropertiesSet();        return template;    }}

Redis.conf详解

单位

image-20210419092419685

包含 INCLUDES

image-20210420092826846

这里包括一个或多个其他配置文件。这很有用,如果你有一个标准的模板,去所有的Redis服务器,但也需要自定义一些服务器设置。包括文件可以包括其他文件,所以明智地使用这个。注意选项“include”不会被命令“CONFIG REWRITE”重写从admin或Redis哨兵。因为Redis总是使用最后处理的line作为配置指令的值,你最好放包含在这个文件的开头,以避免在运行时覆盖配置更改。如果你有兴趣使用include覆盖配置options,最好使用include作为最后一行。include /path/to/local.confinclude /path/to/other.conf

网络 NETWORK

image-20210420093312397

bind 0.0.0.0 对所有人开放 可以指定单个或多个ip

指定多个ip 访问 ip 用空格 隔开

image-20210420093502139

protected-mode yes 开启受保护模式

prot 6379 默认端口号

通用 GENERAL

daemonize yes 以守护进程的方式运行,默认是 no 我们需要自己设置为yes

pidfile /www/server/redis/redis.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#指定服务器详细级别。#这个可以是:# debug(大量信息,对开发/测试有用)# verbose(很多很少有用的信息,但不像调试级别那样混乱)# notice(有点冗长,可能是在生产中需要的内容)# warning(只记录非常重要/关键的消息)

logfile "/www/server/redis/redis.log" 日志的文件位置

# Set the number of databases. The default database is DB 0, you can select# a different one on a per-connection basis using SELECT <dbid> where# dbid is a number between 0 and 'databases'-1databases 16#设置数据库个数。默认数据库为“DB 0”,可选择#在每个连接上使用SELECT where# dbid是一个介于0和'databases'-1之间的数字

databases 默认的数据库数量 16 个

# By default Redis shows an ASCII art logo only when started to log to the# standard output and if the standard output is a TTY. Basically this means# that normally a logo is displayed only in interactive sessions.## However it is possible to force the pre-4.0 behavior and always show a# ASCII art logo in startup logs by setting the following option to yes.always-show-logo yes

always-show-logo yes 是否显示log 默认为开启

快照 SNAPSHOTTING

image-20210420095513960

持久化数据 因为 Redis是内存数据库 如果断电等因素 会失去数据 所以我们需要在一定时间里 持久化数据

// 在 900s 内 有 1个key进行了操作 那么将会持久化一下save 900 1// 在 300s 内 有 10个key进行了操作 那么将会持久化一下save 300 10// 在 60s 内 有 1w个key进行了操作 那么将会持久化一下save 60 10000

image-20210420095830031

stop-writes-on-bgsave-error yes 持久化 出错了是否继续工作 默认继续

# Compress string objects using LZF when dump .rdb databases?# For default that's set to 'yes' as it's almost always a win.# If you want to save some CPU in the saving child set it to 'no' but# the dataset will likely be bigger if you have compressible values or keys.rdbcompression yes

rdbcompression yes 是否压缩 rdb文件

默认压缩 压缩会消耗cpu资源

# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.# This makes the format more resistant to corruption but there is a performance# hit to pay (around 10%) when saving and loading RDB files, so you can disable it# for maximum performances.## RDB files created with checksum disabled have a checksum of zero that will# tell the loading code to skip the check.rdbchecksum yes

rdbchecksum yes 保存 rdb 文件时进行错误的校验

# The working directory.## The DB will be written inside this directory, with the filename specified# above using the 'dbfilename' configuration directive.## The Append Only File will also be created inside this directory.## Note that you must specify a directory here, not a file name.dir /www/server/redis/

rdb 文件保存的目录

复制 REPLICATION

安全 SECURITY

# Require clients to issue AUTH <PASSWORD> before processing any other# commands.  This might be useful in environments in which you do not trust# others with access to the host running redis-server.## This should stay commented out for backward compatibility and because most# people do not need auth (e.g. they run their own servers).## Warning: since Redis is pretty fast an outside user can try up to# 150k passwords per second against a good box. This means that you should# use a very strong password otherwise it will be very easy to break.## requirepass foobared

requirepass xxxxx 设置redis 登录密码

#要求客户端在处理任何其他密码之前发出AUTH#命令。这在您不信任的环境中可能很有用#其他可以访问运行redis-server的主机。#这个应该被注释掉,以便向后兼容,因为大多数#人们不需要认证(例如,他们运行自己的服务器)#警告:由于Redis是相当快的外部用户可以尝试# 150k密码每秒对一个好的盒子。这意味着你应该这么做#使用一个非常强的密码,否则它会很容易被破解

限制 CLIENTS

maxclients 10000 默认有 1w 个用户可以同时连接redis 服务器

maxmemory redis 配置最大的内存容量

maxmemory-policy noeviction 内存到达上限的处理策略 6种

**1、volatile-lru:**只对设置了过期时间的key进行LRU(默认值)

2、allkeys-lru : 删除lru算法的key

**3、volatile-random:**随机删除即将过期key

**4、allkeys-random:**随机删除

5、volatile-ttl : 删除即将过期的

6、noeviction : 永不过期,返回错误

aof配置 APPEND ONLY MODE

基本配置

appendonly no 默认不开启 默认使用rdb持久化方式

appendfilename "appendonly.aof" 持久化文件的名字

# appendfsync always    // 每修改一个key都会执行 sync,消耗性能appendfsync everysec    // 每一秒执行一次 sync,可能会丢失这1s的数据# appendfsync no        // 不执行 sync,这个时候操作系统会自己同步数据速度是最快的
详细配置

Redis持久化

因为Redis是内存数据库,如果不把数据保存到磁盘中,那么如果机器出现断电等,数据就会丢失,

所以Redis提供了持久化功能

RDB

RDB (Redis DataBase)

什么是RDB

image-20210420173941820

就是在指定时间里把内存的数据存储到磁盘中

Rdb 默认保存的文件叫做 dump.rdb

image-20210420174644255

触发机制

image-20210420175238907

  1. save 的规则满足的情况下,会自动触发rdb规则
  2. 执行flushdb 命令,也会触发rdb文件规则
  3. 退出Redis ,也会产生rdb文件

备份就会生成一个 dump.rdb文件

如何回复rdb文件
  1. 只需要将rdb文件放到Redis启动目录下就可以,Redis启动的时候自动检查dump.rdb回复其数据
  2. 可以用 config get dir 查询我们需要将文件放在那个目录下
优点

适合大规模数据恢复

对数据的完成性数据不高可以使用

缺点

需要一定的时间间隔进行操作 ,如果在最后一次操作的时候当机了 那么最后修改的数据就没了

fork 一条进程的时候会占用一定的内存空间

AOF

AOF (Append Only File)

Aof是什么

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

AOf

以日志的形式记录每一个写的操作,将Redis执行过程中的所有命令指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取文件重新构建数据

aof 保存的文件是 appendonly.aof

image-20210422090840407

默认是没有开启的 我们需要的话需要手动开启 把 appendonly no 改为 appendonly yes

如果Aof文件受损了Redis启动不起来 我们可以通过 redis-check-aof --fix 来修复aof文件

image-20210422091808551

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

Redis发布与订阅

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息 ——— > 微信 、微博的 关注系统

Redis 客户端可以订阅任意数量的频道

订阅/发布消息图:

  • 消息发布者
  • 频道
  • 消息订阅者

image-20210423090614975

下图是频道 channel1 , 以及订阅这个频道的三个客户端 ——- cilent2,cilent5 和 cilent1 之间的关系

image-20210423091450391

当有消息通过Publish 命令发送给频道 channel1 时,这个消息就会被它发送给订阅它的三个客户端

image-20210423091506037

命令

redis 发布订阅常用命令:

序号命令及描述
1PSUBSCRIBE pattern [pattern …] 订阅一个或多个符合给定模式的频道。
2[PUBSUB subcommand argument [argument …]] 查看订阅与发布系统状态。
3PUBLISH channel message 将信息发送到指定的频道。
4[PUNSUBSCRIBE pattern [pattern …]] 退订所有给定模式的频道。
5SUBSCRIBE channel channel …] 订阅给定的一个或多个频道的信息。
6[UNSUBSCRIBE channel [channel …]] 指退订给定的频道。

订阅信息:

127.0.0.1:6379> subscribe king     // 订阅一个频道Reading messages... (press Ctrl-C to quit)1) "subscribe"2) "king"3) (integer) 1    // 等待推送的信息1) "message"   // 消息2) "king"      // 来自于那个频道3) "hello,yss"  // 来自哪个频道的内容1) "message"2) "king"3) "\xe6\x88\x91\xe7\x88\xb1\xe4\xbd\xa0"

推送信息:

127.0.0.1:6379> publish king "hello,yss"  // 发布信息到指定的频道(integer) 1127.0.0.1:6379> publish king "我爱你"(integer) 1

Redis主从复制

概念

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

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

2、从容量上,单个 Redis服务器内存容量有限就算一台 Redis服务器内存容量为266,也不能将所有内存用作 Redis?存储内存一般来说,单台 Redist最大使用内存不应该超过206

电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是”多读少写”。
对于这种场景,我们可以使如下这种架构主从复制,息指将一台Reds3服的数据,复制他的Reds眼努,前者称为主节点 master/leade,后者称为从节点( slave/ follower);数据的复制是单向的,只能由主节点到从节点。 Master以写为主,Save以读为主。

认情况下,每台 Redish服务器都是主节点:且ー个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点
主从复制的作用主要包括:

  1. 数据元余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  2. 故障恢复:当主节点出现可题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写 Redisa效据时应用连接主节点,读Reds数据时应用连接从节点),分担服务器负载;尤具是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis服务器的并发量
  4. 高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Reds高可用的基础

环境配置

只配置从库,不配置主库!

查看当前库的信息

127.0.0.1:6379> info replication# Replicationrole:master   // 角色connected_slaves:0  // 连接的丛机 master_failover_state:no-failovermaster_replid:c52bb664e09e9e76e9c995fdd15215578e38250fmaster_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0

image-20210424193553158

复制3个Redis.conf

然后修改配置文件:

  1. 端口号
  2. .pid 名字
  3. log 文件名字
  4. dump.rdb 名字

配置成功

启动3个redis

image-20210427152706030

一主二从

默认情况Redis每一个服务都是主节点

一般只配置从机

一主 (6310)二从 (6320,6330)

查看Redis当前库信息

6310的信息

image-20210427153212906

6320的信息

image-20210427153233963

6330的信息

image-20210427153312486

配置

命令slaveof 127.0.0.1 6310

63206310 是老大

6320的信息

image-20210427153811589

同样给 6330 配置一下

6310的信息

127.0.0.1:6310> info replication# Replicationrole:master     // 主机标识connected_slaves:1   // 他有一个从机slave0:ip=127.0.0.1  // 从机地址,port=6320  // 从机端口,state=online // 从机状态 ,offset=252,lag=1master_failover_state:no-failovermaster_replid:1c382c2d63dcacaabab6ce01942c24c1b1d3fbb1master_replid2:0000000000000000000000000000000000000000master_repl_offset:252second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:252

2个都配置完成

image-20210427154330727

细节

主机可以写,从机不可以写只能读!主机中的所有数据都会被从机保存

主机可以读写 :

image-20210427154957290

从机只能读不能写不然会报错:

image-20210427155036738

测试

1、主机宕机了 从机状态

127.0.0.1:6310> SHUTDOWNnot connected> exit[root@King bin]# redis-cli -p 6310Could not connect to Redis at 127.0.0.1:6310: Connection refusednot connected> pingCould not connect to Redis at 127.0.0.1:6310: Connection refused

主机关闭

image-20210427160524806

image-20210427160413157

主机宕机不会影响从机的读取操作

2、主机从新工作

[root@King bin]# redis-server KingConfig/redis-10.conf[root@King bin]# redis-cli -p 6310127.0.0.1:6310> pingPONG127.0.0.1:6310> set k2 v2OK127.0.0.1:6310> get k2"v2"

从机状态

image-20210427160810556

也可以查询值

127.0.0.1:6320> get k2"v2"

测试从机断开 :如果从机是使用命令连接的主机 那么从机断开之后 重新连接 他就会变为自己的主机,这样是取不到主机更新数据,但是可以取到以前的数据,我们重新让它成为从机 那么他还是可以读取到数据

复制原理

Slave启动成功连接到 master 后会发送一个sync命令

Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后, master将传送整个数据文件到 slave,井完成一次完全同步

全量复制:而 slave服务在接收到数据库文件数据后,将其存盘井加载到内存中

增量复制:Master继续将新的所有收集到的修改命令依次传给 slave,完成同步

但是只要是重新连接 master,一次完全同步(全量复制)将被自动执行

主机宕机

如果主机宕机了,从机想要变成主机 我们可以通过命令 slaveof no noe 让自己变成主机!其他节点就可以手动连接,这个时候主机连接了 还想要 这个主机当老大 那么我们需要手动配置

哨兵模式

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

哨兵模式概述

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

1

Redis哨兵

这里的哨兵有两个作用

  • 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。

然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

用文字描述一下故障切换(failover)的过程。假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。这样对于客户端而言,一切都是透明的。

Redis配置哨兵模式

配置3个哨兵和1主2从的Redis服务器来演示这个过程。

服务类型是否是主服务器IP地址端口
Redis192.168.11.1286379
Redis192.168.11.1296379
Redis192.168.11.1306379
Sentinel-192.168.11.12826379
Sentinel-192.168.11.12926379
Sentinel-192.168.11.13026379

2

多哨兵监控Redis

首先配置Redis的主从服务器,修改redis.conf文件如下

# 使得Redis服务器可以跨网络访问bind 0.0.0.0# 设置密码requirepass "123456"# 指定主服务器,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置slaveof 192.168.11.128 6379# 主服务器密码,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置masterauth 123456

上述内容主要是配置Redis服务器,从服务器比主服务器多一个slaveof的配置和密码。

配置3个哨兵,每个哨兵的配置都是一样的。在Redis安装目录下有一个sentinel.conf文件,copy一份进行修改

# 禁止保护模式protected-mode no# 配置监听的主服务器,这里sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,192.168.11.128代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作。sentinel monitor mymaster 192.168.11.128 6379 2# sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码# sentinel auth-pass <master-name> <password>sentinel auth-pass mymaster 123456

上述关闭了保护模式,便于测试。

有了上述的修改,我们可以进入Redis的安装目录的src目录,通过下面的命令启动服务器和哨兵

# 启动Redis服务器进程./redis-server ../redis.conf# 启动哨兵进程./redis-sentinel ../sentinel.conf

注意启动的顺序。首先是主机(192.168.11.128)的Redis服务进程,然后启动从机的服务进程,最后启动3个哨兵的服务进程。

哨兵模式的其他配置项

配置项参数类型作用
port整数启动哨兵进程端口
dir文件夹目录哨兵进程服务临时文件夹,默认为/tmp,要保证有可写入的权限
sentinel down-after-milliseconds<服务名称><毫秒数(整数)>指定哨兵在监控Redis服务时,当Redis服务在一个默认毫秒数内都无法回答时,单个哨兵认为的主观下线时间,默认为30000(30秒)
sentinel parallel-syncs<服务名称><服务器数(整数)>指定可以有多少个Redis服务同步新的主机,一般而言,这个数字越小同步时间越长,而越大,则对网络资源要求越高
sentinel failover-timeout<服务名称><毫秒数(整数)>指定故障切换允许的毫秒数,超过这个时间,就认为故障切换失败,默认为3分钟
sentinel notification-script<服务名称><脚本路径>指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,比较常用

sentinel down-after-milliseconds配置项只是一个哨兵在超过规定时间依旧没有得到响应后,会自己认为主机不可用。对于其他哨兵而言,并不是这样认为。哨兵会记录这个消息,当拥有认为主观下线的哨兵达到sentinel monitor所配置的数量时,就会发起一次投票,进行failover,此时哨兵会重写Redis的哨兵配置文件,以适应新场景的需要。

哨兵配置

配置哨兵配置文件 sentinel.conf

image-20210427221721022

sentinel monitor 被监控的名字自定义 ip地址 端口号 1
sentinel monitor myredis 127.0.0.1 6310 1

后面这个1代表 如果主机宕机了 slave 投票看让谁接替成为主机,票数最多的,就会成为主机

redis-sentinel 服务来启动 哨兵

image-20210427222948307

这个时候如果主机宕机了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yBY651Os-1621431540688)(file:///C:/Users/11608/AppData/Roaming/Typora/typora-user-images/image-20210427223236066.png)]

哨兵就会去检测 主机是否还有呼吸 如果检查没有了 那么他就是从从机里面选一个来作为主机

选举 6320 来做为主机

image-20210427223441603

如果 6310 回来 那么 他就会作为 现任老大的从机

image-20210427223921295

6320 主机

image-20210427224011068

哨兵集群配置

# Example sentinel.conf# port <sentinel-port> 哨兵的默认端口 默认是 26379 port 8001# 守护进程模式daemonize yes# 指明日志文件名logfile "./sentinel1.log"# 工作路径,sentinel一般指定/tmp比较简单dir ./# 哨兵监控这个master,在至少quorum个哨兵实例都认为master down后把master标记为odown# (objective down客观down;相对应的存在sdown,subjective down,主观down)状态。# slaves是自动发现,所以你没必要明确指定slaves。sentinel monitor MyMaster 127.0.0.1 7001 1# master或slave多长时间(默认30秒)不能使用后标记为s_down状态。sentinel down-after-milliseconds MyMaster 1500# 若sentinel在该配置值内未能完成failover操作(即故障时master/slave自动切换),则认为本次failover失败。sentinel failover-timeout TestMaster 10000# 设置master和slaves验证密码sentinel auth-pass TestMaster testmaster123sentinel config-epoch TestMaster 15#除了当前哨兵, 还有哪些在监控这个master的哨兵sentinel known-sentinel TestMaster 127.0.0.1 8002 0aca3a57038e2907c8a07be2b3c0d15171e44da5sentinel known-sentinel TestMaster 127.0.0.1 8003 ac1ef015411583d4b9f3d81cee830060b2f29862

Redis缓存穿透和雪崩

缓存穿透 (查不到)

img

概念

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

这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

解决方案

布隆过滤器

布隆过滤器是一种数据结构,垃圾网站和正常网站加起来全世界据统计也有几十亿个。网警要过滤这些垃圾网站,总不能到数据库里面一个一个去比较吧,这就可以使用布隆过滤器。假设我们存储一亿个垃圾网站地址。

可以先有一亿个二进制比特,然后网警用八个不同的随机数产生器(F1,F2, …,F8) 产生八个信息指纹(f1, f2, …, f8)。接下来用一个随机数产生器 G 把这八个信息指纹映射到 1 到1亿中的八个自然数 g1, g2, …,g8。最后把这八个位置的二进制全部设置为一。过程如下:

3

有一天网警查到了一个可疑的网站,想判断一下是否是XX网站,首先将可疑网站通过哈希映射到1亿个比特数组上的8个点。如果8个点的其中有一个点不为1,则可以判断该元素一定不存在集合中。

那这个布隆过滤器是如何解决redis中的缓存穿透呢?很简单首先也是对所有可能查询的参数以hash形式存储,当用户想要查询的时候,使用布隆过滤器发现不在集合中,就直接丢弃,不再对持久层查询。

1

这个形式很简单。

缓存空对象

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

2

但是这种方法会存在两个问题:

如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

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

描述:

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

解决方案:

  1. 设置热点数据永远不过期
  2. 接口限流与熔断,降级

重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些 服务 不可用时候,进行熔断,失败快速返回机制。

  1. 布隆过滤器

bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小

  1. 加互斥锁,互斥锁参考代码如下:

3

说明:

1)缓存中有数据,直接走上述代码13行后就返回结果了

2)缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。

3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。

缓存雪崩

概念

缓存雪崩是指,缓存层出现了错误,不能正常工作了。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

4

解决方案

(1)redis高可用

这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。

(2)限流降级

这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

(3)数据预热

数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。



这篇关于Redis学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程