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

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

阿里巴巴框架演进

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

  1. 127.0.0.16379> ping ----> 测试是否连接成功
  2. PONG
  3. 127.0.0.16379> set name changan -----> 设置 key value
  4. OK
  5. 127.0.0.16379> get name ------> key 去寻找 value
  6. "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

  1. 127.0.0.16379> shutdown --->关闭redis
  2. not 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 -k 1=keep alive 0=reconnect 1
8 -r SET/GET/INCR 使用随机 key, SADD 使用随机值  
9 -P 通过管道传输 <numreq> 请求 1
10 -q 强制退出 redis。仅显示 query/sec 值  
11 —csv 以 CSV 格式输出  
12 -l 生成循环,永久执行测试  
13 -t 仅运行以逗号分隔的测试命令列表。  
14 -I Idle 模式。仅打开 N 个 idle 连接并等待。

测试 使用 redis-benchmark

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

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

  1. ====== PING_INLINE ======
  2. 100000 requests completed in 2.31 seconds ----> 对我们10w请求进行测试
  3. 100 parallel clients -----> 100个并发的客服端
  4. 3 bytes payload ----> 每次写入3个字节
  5. keep alive 1 ---> 只有一台服务器来处理这些请求,单机性能
  6. host configuration "save" 3600 1 300 100 60 10000
  7. host configuration "appendonly" no
  8. multi-thread no
  9. ====== SET ======
  10. 100000 requests completed in 2.31 seconds
  11. 100 parallel clients
  12. 3 bytes payload
  13. keep alive 1
  14. host configuration "save" 3600 1 300 100 60 10000
  15. host configuration "appendonly" no
  16. multi-thread no
  17. Latency by percentile distribution
  18. 0.000% <= 0.935 milliseconds (cumulative count 1)
  19. 50.000% <= 1.599 milliseconds (cumulative count 50402)
  20. 75.000% <= 1.895 milliseconds (cumulative count 75422)
  21. 87.500% <= 2.039 milliseconds (cumulative count 87552)
  22. 93.750% <= 2.143 milliseconds (cumulative count 93953)
  23. 96.875% <= 2.487 milliseconds (cumulative count 96893)
  24. 98.438% <= 3.607 milliseconds (cumulative count 98439)
  25. 99.219% <= 6.535 milliseconds (cumulative count 99219)
  26. 99.609% <= 12.039 milliseconds (cumulative count 99613)
  27. 99.805% <= 12.583 milliseconds (cumulative count 99805)
  28. 99.902% <= 13.031 milliseconds (cumulative count 99903)
  29. 99.951% <= 13.479 milliseconds (cumulative count 99952)
  30. 99.976% <= 13.775 milliseconds (cumulative count 99976)
  31. 99.988% <= 13.927 milliseconds (cumulative count 99988)
  32. 99.994% <= 14.007 milliseconds (cumulative count 99994)
  33. 99.997% <= 14.039 milliseconds (cumulative count 99997)
  34. 99.998% <= 14.071 milliseconds (cumulative count 99999)
  35. 99.999% <= 14.087 milliseconds (cumulative count 100000)
  36. 100.000% <= 14.087 milliseconds (cumulative count 100000)

基础知识

image-20210318154701720

Redis默认数据库

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

  • select 2 ——> 更换数据库
  • dbsize ——-> 查看数据库大小
  • keys * ———> 查看数据库所有的key
  • flushdb ————> 清空当前数据库
  • flushall ———> 清空所有库
  1. [root@VM-0-5-centos changanconfig]# redis-cli -p 6379
  2. 127.0.0.16379> ping
  3. PONG
  4. 127.0.0.16379> select 2 ----> 更换数据库
  5. OK
  6. 127.0.0.16379[2]> dbsize -----> 查看数据库大小
  7. (integer) 0
  8. 127.0.0.16379[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 追加字符串

  1. 127.0.0.16379> keys *
  2. 1) "name"
  3. 2) "age"
  4. 127.0.0.16379> get name
  5. "changan"
  6. 127.0.0.16379> append name love
  7. (integer) 11
  8. 127.0.0.16379> get name
  9. "changanlove"
  10. 127.0.0.16379> exists name
  11. (integer) 1

incr views 给 views 加1

decr views 给 views 减1

incrby views 给 views 加10

decrby views 给 views 减10

  1. 127.0.0.16379> set views 0
  2. OK
  3. 127.0.0.16379> get views
  4. "0"
  5. 127.0.0.16379> incr views
  6. (integer) 1
  7. 127.0.0.16379> decr views
  8. (integer) 0
  9. 127.0.0.16379> incrby views 10
  10. (integer) 10
  11. 127.0.0.16379> decrby views 10

getrange 范围查询数据

  1. 127.0.0.16379> getrange key1 0 3
  2. "hell"
  3. 127.0.0.16379> getrange key1 0 -1
  4. "hello,word"
  5. 127.0.0.16379> getrange key1 5 -1
  6. ",word"
  7. 127.0.0.16379> getrange key1 5 0
  8. ""
  9. 127.0.0.16379> getrange key1 1 -1
  10. "ello,word"
  11. 127.0.0.16379> getrange key1 0 -1

setrange 范围修改

  1. 127.0.0.16379> setrange key1 1 xx
  2. (integer) 10
  3. 127.0.0.16379> keys *
  4. 1) "key1"
  5. 127.0.0.16379> get key1
  6. "hxxlo,word"

常在分布式锁使用

  1. 127.0.0.16379> setex key2 30 "hello" ---- > 设置过期时间
  2. OK
  3. 127.0.0.16379> ttl key2 ---- > 查看剩余时间
  4. (integer) 28
  5. 127.0.0.16379> setnx mykey "redis" ---- > 如果 mykey 不存在,创建 mykey 返回1
  6. (integer) 1
  7. 127.0.0.16379> keys *
  8. 1) "key2"
  9. 2) "key1"
  10. 3) "mykey"
  11. 127.0.0.16379> ttl key2
  12. (integer) -2
  13. 127.0.0.16379> setnx mykey "MongDB" ---- > 如果 mykey 存在,就创建失败 返回0
  14. (integer) 0
  15. 127.0.0.16379> get mykey
  16. "redis"

批量获取值和设置值

mset 批量设置

  1. 127.0.0.16379> mset k1 v1 k2 v2 k3 v3
  2. OK
  3. 127.0.0.16379> keys *
  4. 1) "k3"
  5. 2) "k2"
  6. 3) "k1"

mget 同时获取多个值

  1. 127.0.0.16379> mget k1 k2 k3
  2. 1) "v1"
  3. 2) "v2"
  4. 3) "v3"

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

  1. 127.0.0.16379> msetnx k1 v1 k5 v5
  2. (integer) 0

getset 先获取在设置

  1. 127.0.0.16379> getset db redis --> 不存在返回nil并创建
  2. (nil)
  3. 127.0.0.16379> get db
  4. "redis"
  5. 127.0.0.16379> getset db mongDb --> 如果存在,返回当前的值,并设置新的值
  6. "redis"
  7. 127.0.0.16379> get db
  8. "mongDb"
应用场景
  • 计数器
  • 统计多单位数量
  • 粉丝数
  • 对象缓存存储
List

基本数据类型,列表

lpush 储存 《左存储》

rpush 储存 《右存储》

lrange 读取

  1. 127.0.0.16379> lpush list one ---> 给左push
  2. (integer) 1
  3. 127.0.0.16379> lpush list two
  4. (integer) 2
  5. 127.0.0.16379> lpush list three
  6. (integer) 3
  7. 127.0.0.16379> lrange list 0 -1
  8. 1) "three"
  9. 2) "two"
  10. 3) "one"
  11. 127.0.0.16379> lrange list 0 1
  12. 1) "three"
  13. 2) "two"
  14. #####################################################################
  15. 127.0.0.16379> rpush list yss
  16. (integer) 4
  17. 127.0.0.16379> lrange list 0 -1
  18. 1) "three"
  19. 2) "two"
  20. 3) "one"
  21. 4) "yss"

lpop : 左移除

rpop : 右移除

  1. 127.0.0.16379> lpop list ----> 移除list第一个元素
  2. "three"
  3. 127.0.0.16379> rpop list ----> 移除list最后一个元素
  4. "yss"
  5. 127.0.0.16379> lrange list 0 -1
  6. 1) "two"
  7. 2) "one"

lindex 通过下标获取值

  1. 127.0.0.16379> lindex list 0
  2. "two"
  3. 127.0.0.16379> lindex list 1
  4. "one"

llen 获取list的长度

  1. 127.0.0.16379> llen list
  2. (integer) 4

lrem 删除 ‘精确匹配’

  1. 127.0.0.16379> lrange list 0 -1
  2. 1) "two"
  3. 2) "one"
  4. 3) "asdfa"
  5. 4) "9889"
  6. 5) "9889"
  7. 127.0.0.16379> lrem list 1 asdfa ----- > 删除 1 asdfa
  8. (integer) 1
  9. 127.0.0.16379> lrange list 0 -1
  10. 1) "two"
  11. 2) "one"
  12. 3) "9889"
  13. 4) "9889"
  14. 127.0.0.16379> rpush list 9889
  15. (integer) 4
  16. 127.0.0.16379> lrange list 0 -1
  17. 1) "two"
  18. 2) "one"
  19. 3) "9889"
  20. 4) "9889"
  21. 127.0.0.16379> lrem list 2 9889 ------ > 删除 2 9889
  22. (integer) 2
  23. 127.0.0.16379> lrange list 0 -1
  24. 1) "two"
  25. 2) "one"

ltrim 修剪

  1. 127.0.0.16379> rpush mylist "hello"
  2. (integer) 1
  3. 127.0.0.16379> rpush mylist "hello1"
  4. (integer) 2
  5. 127.0.0.16379> rpush mylist "hello2"
  6. (integer) 3
  7. 127.0.0.16379> rpush mylist "hello3"
  8. (integer) 4
  9. 127.0.0.16379> ltrim mylist 1 2 ----- > 通过下标截取指定长度
  10. OK
  11. 127.0.0.16379> lrange mylist 0 -1
  12. 1) "hello1"
  13. 2) "hello2"

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

  1. 127.0.0.16379> lrange mylist 0 -1
  2. 1) "hello1"
  3. 2) "hello2"
  4. 3) "hello3"
  5. 127.0.0.16379> rpoplpush mylist myotherlist
  6. "hello3"
  7. 127.0.0.16379> lrange mylist 0 -1
  8. 1) "hello1"
  9. 2) "hello2"
  10. 127.0.0.16379> lrange myotherlist 0 -1
  11. 1) "hello3"

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

  1. 127.0.0.16379> exists list --- > 判断是否有这个集合
  2. (integer) 0
  3. 127.0.0.16379> lset list 0 item ----- > list的第0个下标添加 item
  4. (error) ERR no such key ---> 报错,添加失败 没有这个list
  5. 127.0.0.16379> lpush list value1 ------ > 创建 list
  6. (integer) 1
  7. 127.0.0.16379> lrange list 0 -1
  8. 1) "value1"
  9. 127.0.0.16379> lset list 0 item ----- > 再次用 lset 覆盖 刚创建那个list的第0个下标
  10. OK
  11. 127.0.0.16379> lrange list 0 -1
  12. 1) "item"
  13. 127.0.0.16379> lset list 1 item -----> 如果给list添加值 这个下标不存在那么就会报错
  14. (error) ERR index out of range

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

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

  1. 127.0.0.16379> rpush mylist "hello"
  2. (integer) 1
  3. 127.0.0.16379> rpush mylist "word"
  4. (integer) 2
  5. // before
  6. 127.0.0.16379> linsert mylist before "word" "other"
  7. (integer) 3
  8. 127.0.0.16379> lrange mylist 0 -1
  9. 1) "hello"
  10. 2) "other"
  11. 3) "word"
  12. // after
  13. 127.0.0.16379> linsert mylist after "word" "king"
  14. (integer) 4
  15. 127.0.0.16379> lrange mylist 0 -1
  16. 1) "hello"
  17. 2) "other"
  18. 3) "word"
  19. 4) "king"
小结
  • set实际是一个链表,before Node after,left ,right 都可以插入值

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

  • 如果key 存在,新增内容

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

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

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

Set

set中的值不能重复

sadd

smembers

sismember

scard

  1. 127.0.0.16379> sadd myset hello ----> 给集合添加值
  2. (integer) 1
  3. 127.0.0.16379> sadd myset changan
  4. (integer) 1
  5. 127.0.0.16379> sadd myset king
  6. (integer) 1
  7. 127.0.0.16379> SMEMBERS myset -----> 查看当前集合元素
  8. 1) "king"
  9. 2) "changan"
  10. 3) "hello"
  11. 127.0.0.16379> SISMEMBER myset hello -----> 查看集合是否有hello 有的话返回1 没有就是0
  12. (integer) 1
  13. 127.0.0.16379> SISMEMBER myset word
  14. (integer) 0
  15. 127.0.0.16379> scard myset ------> 获取当前集合的个数
  16. (integer) 3
  17. 127.0.0.16379> srem myset hello -----> 删除集合指定的值
  18. (integer) 1
  19. 127.0.0.16379> SMEMBERS myset
  20. 1) "king"
  21. 2) "changan"

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

  1. 127.0.0.16379> SRANDMEMBER myset
  2. "king"
  3. 127.0.0.16379> SRANDMEMBER myset
  4. "king"
  5. 127.0.0.16379> SRANDMEMBER myset
  6. "king"
  7. 127.0.0.16379> SRANDMEMBER myset
  8. "king"
  9. 127.0.0.16379> SRANDMEMBER myset
  10. "changan"

spop 随机删除一个元素

  1. 127.0.0.16379> SMEMBERS myset
  2. 1) "king"
  3. 2) "changan"
  4. 127.0.0.16379> spop myset
  5. "king"
  6. 127.0.0.16379> SMEMBERS myset
  7. 1) "changan"

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

  1. 127.0.0.16379> sadd myset hello
  2. (integer) 1
  3. 127.0.0.16379> sadd myset word
  4. (integer) 1
  5. 127.0.0.16379> sadd myset changanking
  6. (integer) 1
  7. 127.0.0.16379> sadd myset king
  8. (integer) 1
  9. 127.0.0.16379> sadd myset2 set2
  10. (integer) 1
  11. 127.0.0.16379> smove myset myset2 changanking
  12. (integer) 1
  13. 127.0.0.16379> SMEMBERS myset
  14. 1) "word"
  15. 2) "hello"
  16. 3) "changan"
  17. 4) "king"
  18. 127.0.0.16379> SMEMBERS myset2
  19. 1) "changanking"
  20. 2) "set2"

SDIFF 查看2个集合的差集

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

SUNION 查看2个集合的

  1. 127.0.0.16379> sadd key1 a
  2. (integer) 1
  3. 127.0.0.16379> sadd key1 b
  4. (integer) 1
  5. 127.0.0.16379> sadd key1 c
  6. (integer) 1
  7. 127.0.0.16379> sadd key2 c
  8. (integer) 1
  9. 127.0.0.16379> sadd key2 d
  10. (integer) 1
  11. 127.0.0.16379> sadd key2 e
  12. (integer) 1
  13. 127.0.0.16379> SDIFF key1 key2
  14. 1) "a"
  15. 2) "b"
  16. 127.0.0.16379> SINTER key1 key2
  17. 1) "c"
  18. 127.0.0.16379> SUNION key1 key2
  19. 1) "c"
  20. 2) "b"
  21. 3) "a"
  22. 4) "d"
  23. 5) "e"
应用场景

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

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

Hash

Map集合,Key—Value

  1. 127.0.0.16379> hset map k changan ----> map存一个k-v
  2. (integer) 1
  3. 127.0.0.16379> hget map k ----> 通过k取值
  4. "changan"
  5. 127.0.0.16379> hmset map y king u anuyn ----> map存多个值
  6. OK
  7. 127.0.0.16379> hmget map y u ----> 获取map多个值
  8. 1) "king"
  9. 2) "anuyn"
  10. 127.0.0.16379> hgetall map ------> 查看map所有的kv
  11. 1) "k"
  12. 2) "changan"
  13. 3) "y"
  14. 4) "king"
  15. 5) "u"
  16. 6) "anuyn"

hdel 删除指定的kv

  1. 127.0.0.16379> hdel map u -----> 删除指定的 k
  2. (integer) 1
  3. 127.0.0.16379> hgetall map
  4. 1) "k"
  5. 2) "changan"
  6. 3) "y"
  7. 4) "king"

hlen 查询集合有几组kv

  1. 127.0.0.16379> hlen map
  2. (integer) 2

HEXISTS 查看集合的k是否存在

  1. 127.0.0.16379> HEXISTS map k
  2. (integer) 1
  3. 127.0.0.16379> HEXISTS map u
  4. (integer) 0

hkeys 查看所有的key

hvals 查看所有的value

  1. 127.0.0.16379> hkeys map
  2. 1) "k"
  3. 2) "y"
  4. 127.0.0.16379> hvals map
  5. 1) "changan"
  6. 2) "king"

HINCRBY 指定增量

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

  1. 127.0.0.16379> hset myhash field3 5 ----- >>> 指定增量
  2. (integer) 1
  3. 127.0.0.16379> HINCRBY myhash field3 1 ------>>> 给数据加一
  4. (integer) 6
  5. 127.0.0.16379> HINCRBY myhash field3 -1 ------>>>> 给数据减一
  6. (integer) 5
  7. 127.0.0.16379> hsetnx myhash field4 hello -------> 创建一个集合 如果存在则不能设置 返回1
  8. (integer) 1
  9. 127.0.0.16379> hsetnx myhash field4 world ---->> 创建一个集合 如果存在则不能设置 返回0
  10. (integer) 0

hash可以用于存储对象

  1. 127.0.0.16379> hset user1 name changan
  2. (integer) 1
  3. 127.0.0.16379> hget user1 name
  4. "changan"
  5. 127.0.0.16379> hset user1 age 23
  6. (integer) 1
  7. 127.0.0.16379> hget user1 age
  8. "23"
应用场景

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

Zset(有序集合)

在set的基础上加了一个值

api:
  1. 127.0.0.16379> zadd myset 1 one -----> 创建一个值
  2. (integer) 1
  3. 127.0.0.16379> zadd myset 2 two 3 three -----> 创建2个值
  4. (integer) 2
  5. 127.0.0.16379> zrange myset 0 -1 ------> 获取myset 里的数据
  6. 1) "one"
  7. 2) "two"
  8. 3) "three"

zrangebyscore : 从低到高 进行排序

zrevrange : 从高到低 进行排序

  1. 127.0.0.16379> zadd salary 5000 xiaohong
  2. (integer) 1
  3. 127.0.0.16379> zadd salary 2500 xiaoming
  4. (integer) 1
  5. 127.0.0.16379> zadd salary 500 zhangsan
  6. (integer) 1
  7. 127.0.0.16379> zrangebyscore salary -inf +inf ---- > 通过集合salary 钱数排序 -inf 负无穷 +inf 正无穷
  8. 从低到高
  9. 1) "zhangsan"
  10. 2) "xiaoming"
  11. 3) "xiaohong"
  12. 127.0.0.16379> zrevrange salary 0 -1
  13. 1) "xiaoming"
  14. 2) "zhangsan"
  15. 127.0.0.16379> zrangebyscore salary -inf +inf withscores -->> 查询出来所有数据 WITHSCORES会返回元素和其分数
  16. 1) "zhangsan"
  17. 2) "500"
  18. 3) "xiaoming"
  19. 4) "2500"
  20. 5) "xiaohong"
  21. 6) "5000"
  22. 127.0.0.16379> zrangebyscore salary -inf 2500 withscores
  23. 1) "zhangsan"
  24. 2) "500"
  25. 3) "xiaoming"
  26. 4) "2500"

zrem 移除

  1. 127.0.0.16379> zrange salary 0 -1
  2. 1) "zhangsan"
  3. 2) "xiaoming"
  4. 3) "xiaohong"
  5. 127.0.0.16379> zrem salary xiaohong
  6. (integer) 1

zcard 获取集合中的个数

  1. 127.0.0.16379> zcard salary
  2. (integer) 2

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

  1. 127.0.0.16379> zadd myset 1 "hello" 2 "word" 3 "changan"
  2. (integer) 3
  3. 127.0.0.16379> zcount myset 1 2
  4. (integer) 2
  5. 127.0.0.16379> zcount myset 1 3
  6. (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度。
  1. 127.0.0.16379> geoadd chinacity 116.40 39.90 beijing
  2. (integer) 1
  3. 127.0.0.16379> geoadd chinacity 121.47 31.23 shanghai
  4. (integer) 1
  5. 127.0.0.16379> geoadd chinacity 106.50 29.53 chongqi 114.05 22.52 shenzhen
  6. (integer) 2
  7. 127.0.0.16379> geoadd chinacity 120.16 30.24 hangzhou 108.96 34.26 xian
  8. (integer) 2

geopos 查询地理位置

  1. 127.0.0.16379> geopos chinacity beijing
  2. 1) 1) "116.39999896287918091"
  3. 2) "39.90000009167092543"
  4. 127.0.0.16379> geopos chinacity xian
  5. 1) 1) "108.96000176668167114"
  6. 2) "34.25999964418929977"

geodist

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

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

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

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。
  1. 127.0.0.16379> geodist chinacity beijing xian km 北京到西安的直线距离
  2. "910.0565"
  3. 127.0.0.16379> geodist chinacity beijing shanghai km 北京带上海的直线距离
  4. "1067.3788"

附近的人

半径搜索

  1. 127.0.0.16379> georadius chinacity 110 30 1000 km 11030的地理坐标 查询 1000km 内的城市
  2. 1) "chongqi"
  3. 2) "xian"
  4. 3) "shenzhen"
  5. 4) "hangzhou"
  6. 127.0.0.16379> georadius chinacity 110 30 500 km 11030的地理坐标 查询 500km 内的城市
  7. 1) "chongqi"
  8. 2) "xian"
  9. 127.0.0.16379> georadius chinacity 110 30 500 km withdist 到中心的直线距离
  10. 1) 1) "chongqi"
  11. 2) "341.9374"
  12. 2) 1) "xian"
  13. 2) "483.8340"
  14. 127.0.0.16379> georadius chinacity 110 30 500 km withcoord 经纬度
  15. 1) 1) "chongqi"
  16. 2) 1) "106.49999767541885376"
  17. 2) "29.52999957900659211"
  18. 2) 1) "xian"
  19. 2) 1) "108.96000176668167114"
  20. 2) "34.25999964418929977"
  21. 127.0.0.16379> georadius chinacity 110 30 500 km withcoord count 1 查询指定数量的
  22. 1) 1) "chongqi"
  23. 2) 1) "106.49999767541885376"
  24. 2) "29.52999957900659211"

GEORADIUSBYMEMBER

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

  1. 127.0.0.16379> GEORADIUSBYMEMBER chinacity beijing 1000 km
  2. 1) "beijing"
  3. 2) "xian"
  4. 127.0.0.16379> GEORADIUSBYMEMBER chinacity beijing 500 km
  5. 1) "beijing"

删除、查看

  1. 127.0.0.16379> zrange chinacity 0 -1 查看所有
  2. 1) "chongqi"
  3. 2) "xian"
  4. 3) "shenzhen"
  5. 4) "hangzhou"
  6. 5) "shanghai"
  7. 6) "beijing"
  8. 127.0.0.16379> zrem chinacity beijing 删除
  9. (integer) 1
  10. 127.0.0.16379> zrange chinacity 0 -1
  11. 1) "chongqi"
  12. 2) "xian"
  13. 3) "shenzhen"
  14. 4) "hangzhou"
  15. 5) "shanghai"
Hyperloglog 基数统计

什么是基数!

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

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

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

简介

pfadd :添加数据

pfcount :查询数据

pfmerge :合并数据

  1. 127.0.0.16379> pfadd mykey a b c d e f g h i j k
  2. (integer) 1
  3. 127.0.0.16379> pfcount mykey
  4. (integer) 11
  5. 127.0.0.16379> pfadd mykey2 b c d t y o p
  6. (integer) 1
  7. 127.0.0.16379> pfcount mykey2
  8. (integer) 7
  9. 127.0.0.16379> pfmerge mykey3 mykey mykey2
  10. OK
  11. 127.0.0.16379> pfcount mykey3
  12. (integer) 14
Bitmaps(位图) 位存储

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

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

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

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

测试

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

setbit 存值

getbit 取值

bitcount 查询

  1. 127.0.0.16379> setbit sign 20210415 0
  2. (integer) 0
  3. 127.0.0.16379> setbit sign 20210416 1
  4. (integer) 0
  5. 127.0.0.16379> setbit sign 20210414 0
  6. (integer) 0
  7. 127.0.0.16379> setbit sign 20210413 1
  8. (integer) 0
  9. 127.0.0.16379> setbit sign 20210412 1
  10. (integer) 0
  11. 127.0.0.16379> setbit sign 20210417 1
  12. (integer) 0
  13. 127.0.0.16379> getbit sign 20210416
  14. (integer) 1
  15. 127.0.0.16379> getbit sign 20210415
  16. (integer) 0
  17. 127.0.0.16379> bitcount sign
  18. (integer) 4

事务

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

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

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

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

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

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

Redis的事务:

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

测试:

  1. 127.0.0.16379> multi // > 开启事务
  2. OK
  3. 127.0.0.16379(TX)> set k1 1 // 命令入队
  4. QUEUED
  5. 127.0.0.16379(TX)> set k1 2
  6. QUEUED
  7. 127.0.0.16379(TX)> set k1 3
  8. QUEUED
  9. 127.0.0.16379(TX)> set k2 2
  10. QUEUED
  11. 127.0.0.16379(TX)> set k3 4
  12. QUEUED
  13. 127.0.0.16379(TX)> set k4 6
  14. QUEUED
  15. 127.0.0.16379(TX)> get k4
  16. QUEUED
  17. 127.0.0.16379(TX)> set k7 9
  18. QUEUED
  19. 127.0.0.16379(TX)> exec // 执行事务
  20. 1) OK
  21. 2) OK
  22. 3) OK
  23. 4) OK
  24. 5) OK
  25. 6) OK
  26. 7) "6"
  27. 8) OK

放弃事务:

  1. 127.0.0.16379> multi // 开启事务
  2. OK
  3. 127.0.0.16379(TX)> set key7 7
  4. QUEUED
  5. 127.0.0.16379(TX)> discard // 取消事务
  6. OK
  7. 127.0.0.16379> get key7
  8. (nil)

编译型异常: 命令错误

  1. 127.0.0.16379> multi
  2. OK
  3. 127.0.0.16379(TX)> set k1 2
  4. QUEUED
  5. 127.0.0.16379(TX)> set k2 4
  6. QUEUED
  7. 127.0.0.16379(TX)> set k4 5
  8. QUEUED
  9. 127.0.0.16379(TX)> getset k7 8
  10. QUEUED
  11. 127.0.0.16379(TX)> getset k9 //错误语法
  12. (error) ERR wrong number of arguments for 'getset' command
  13. 127.0.0.16379(TX)> set k8 9
  14. QUEUED
  15. 127.0.0.16379(TX)> exec
  16. (error) EXECABORT Transaction discarded because of previous errors.
  17. /*提示语法错误,所有命令都不执行 **/
  18. 127.0.0.16379> get k4
  19. (nil)

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

  1. 127.0.0.16379> set k1 7
  2. OK
  3. 127.0.0.16379> set k2 "j9"
  4. OK
  5. 127.0.0.16379> multi
  6. OK
  7. 127.0.0.16379(TX)> incr k2 // 会执行失败
  8. QUEUED
  9. 127.0.0.16379(TX)> set k3 3
  10. QUEUED
  11. 127.0.0.16379(TX)> set k4 5
  12. QUEUED
  13. 127.0.0.16379(TX)> get k4
  14. QUEUED
  15. 127.0.0.16379(TX)> exec
  16. 1) (error) ERR value is not an integer or out of range
  17. // 虽然第一条命令执行失败了后面的命令会继续执行
  18. 2) OK
  19. 3) OK
  20. 4) "5"
  21. 127.0.0.16379> get k3
  22. "3"
  23. 127.0.0.16379> get k4
  24. "5"

监控

悲观锁:

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

乐观锁:

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

  • 获取 version

  • 更新的时候比较 version

数据测试:

单线程

  1. 127.0.0.16379> set money 100 // 设置金钱是100
  2. OK
  3. 127.0.0.16379> set out 0 // 设置金钱是0
  4. OK
  5. 127.0.0.16379> watch money // 给money上锁
  6. OK
  7. 127.0.0.16379> multi // 开启事务
  8. OK
  9. 127.0.0.16379(TX)> decrby money 20 // money金钱减 20
  10. QUEUED
  11. 127.0.0.16379(TX)> incrby out 20 // out 加 220
  12. QUEUED
  13. 127.0.0.16379(TX)> exec
  14. 1) (integer) 80
  15. 2) (integer) 20

多线程

使用 watch 来当做Redis的乐观锁

  1. // 线程 1
  2. 127.0.0.16379> watch money // 开启监控
  3. OK
  4. 127.0.0.16379> multi // 开启事务
  5. OK
  6. 127.0.0.16379(TX)> decrby money 10 // 金币减10
  7. QUEUED
  8. 127.0.0.16379(TX)> incrby out 10 // 金币加10
  9. QUEUED
  10. // 线程 2
  11. 127.0.0.16379> incrby money 1000 // 线程2 给这个用户 充值 1000
  12. (integer) 1080
  13. // 线程 1
  14. 127.0.0.16379(TX)> exec // 线程 1 继续执行 然后就会执行失败
  15. (nil)

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

解决办法:

  1. 127.0.0.16379> unwatch // 放弃旧的锁
  2. OK
  3. 127.0.0.16379> watch money // 加新锁
  4. OK
  5. 127.0.0.16379> multi // 开启事务
  6. OK
  7. 127.0.0.16379(TX)> decrby money 100
  8. QUEUED
  9. 127.0.0.16379(TX)> incrby out 100
  10. QUEUED
  11. 127.0.0.16379(TX)> exec
  12. 1) (integer) 1000
  13. 2) (integer) 120

Jedis

什么是Jedis

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

测试

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

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

输出 PONG 连接成功

操作命令

……

事务:

  1. RedisApi api = new RedisApi();
  2. Jedis jedis = api.connection();
  3. jedis.flushDB();
  4. JSONObject jsonObject = new JSONObject();
  5. jsonObject.put("hello""word");
  6. jsonObject.put("name""changan");
  7. Transaction multi = jedis.multi();
  8. String toString = jsonObject.toJSONString();
  9. try {
  10. multi.set("user1"toString); //注意事务执行的话要用事务 multi.set 而不是jedis.set
  11. multi.set("user2"toString);
  12. // int i = 1/0;
  13. multi.exec();
  14. }catch (Exception e){
  15. multi.discard();
  16. e.printStackTrace();
  17. }finally {
  18. System.out.println(jedis.get("user1"));
  19. System.out.println(jedis.get("user2"));
  20. }
断开连接

jedis.close()

SpringBoot整合

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

区别:

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

整合

  1. 添加依赖

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

    1. spring
    2. redis
    3. host 服务器地址
    4. port 6379
    5. password redis密码
  1. 测试

    1. @Resource
    2. private RedisTemplate redisTemplate;
    3. @Test
    4. void contextLoads() {
    5. RedisConnection connection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection();
    6. System.out.println(connection.ping()); // 测试链接是否成功
    7. redisTemplate.opsForValue().set("key""changan"); // 操纵字符串 set 一个值
    8. Object key = redisTemplate.opsForValue().get("key"); // 获取值
    9. System.out.println(key);
    10. }
    • redisTemplate.opsForValue() 操作字符串

    • redisTemplate.opsForList() 操作list

      1. connection.close(); // 关闭链接
      2. connection.flushAll(); // 清空所有数据库的所有 key
      3. connection.flushDb(); // 清空

测试

  1. @Test
  2. public void UserTest() throws JsonProcessingException {
  3. RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
  4. connection.flushDb();
  5. User user = new User("长安"23);
  6. String s = new ObjectMapper().writeValueAsString(user); // 转化为Json 数据
  7. redisTemplate.opsForValue().set("user"s);
  8. System.out.println(redisTemplate.opsForValue().get("user"));
  9. }
  10. // 输出结果 {"name":"长安","age":23}

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

  1. @Test
  2. public void UserTest() throws JsonProcessingException {
  3. RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
  4. connection.flushDb();
  5. User user = new User("长安"23);
  6. redisTemplate.opsForValue().set("user"user);
  7. System.out.println(redisTemplate.opsForValue().get("user"));
  8. }
  9. //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]

如果要传递对象要序列化

  1. public class User implements Serializable {
  2. private String name;
  3. private int age;
  4. }
  5. // 结果 User(name=长安, age=23)

编写一个自己的 Redis 序列化

  1. @Configuration
  2. public class RedisConfig {
  3. // 配置自己的Redis 固定模板
  4. @Bean
  5. public RedisTemplate<String Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  6. // 我们为了自己开发方便,一般直接使用<String,Object>
  7. RedisTemplate<String Object> template = new RedisTemplate<>();
  8. template.setConnectionFactory(redisConnectionFactory);
  9. // Json序列化配置
  10. Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  11. ObjectMapper objectMapper = new ObjectMapper();
  12. objectMapper.setVisibility(PropertyAccessor.ALL JsonAutoDetect.Visibility.ANY);
  13. objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  14. jsonRedisSerializer.setObjectMapper(objectMapper);
  15. // String 的序列化
  16. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  17. // key 采用String 方式序列化
  18. template.setKeySerializer(stringRedisSerializer);
  19. // hash 的key 采用String 方式序列化
  20. template.setHashKeySerializer(stringRedisSerializer);
  21. // Value 采用Json 序列化
  22. template.setValueSerializer(jsonRedisSerializer);
  23. // hash 的 Value 采用Json 序列化
  24. template.setHashValueSerializer(jsonRedisSerializer);
  25. template.afterPropertiesSet();
  26. return template;
  27. }
  28. }

Redis.conf详解

单位

image-20210419092419685

包含 INCLUDES

image-20210420092826846

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

  1. # Specify the server verbosity level.
  2. # This can be one of:
  3. # debug (a lot of information, useful for development/testing)
  4. # verbose (many rarely useful info, but not a mess like the debug level)
  5. # notice (moderately verbose, what you want in production probably)
  6. # warning (only very important / critical messages are logged)
  7. loglevel notice
  8. #指定服务器详细级别。
  9. #这个可以是:
  10. # debug(大量信息,对开发/测试有用)
  11. # verbose(很多很少有用的信息,但不像调试级别那样混乱)
  12. # notice(有点冗长,可能是在生产中需要的内容)
  13. # warning(只记录非常重要/关键的消息)

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

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

databases 默认的数据库数量 16 个

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

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

快照 SNAPSHOTTING

image-20210420095513960

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

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

image-20210420095830031

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

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

rdbcompression yes 是否压缩 rdb文件

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

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

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

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

rdb 文件保存的目录

复制 REPLICATION

安全 SECURITY

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

requirepass xxxxx 设置redis 登录密码

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

限制 CLIENTS

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

maxmemory <bytes> 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" 持久化文件的名字

  1. # appendfsync always // 每修改一个key都会执行 sync,消耗性能
  2. appendfsync everysec // 每一秒执行一次 sync,可能会丢失这1s的数据
  3. # 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 发布订阅常用命令:

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

订阅信息:

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

推送信息:

  1. 127.0.0.16379> publish king "hello,yss" // 发布信息到指定的频道
  2. (integer) 1
  3. 127.0.0.16379> publish king "我爱你"
  4. (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高可用的基础

环境配置

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

查看当前库的信息

  1. 127.0.0.16379> info replication
  2. # Replication
  3. rolemaster // 角色
  4. connected_slaves0 // 连接的丛机
  5. master_failover_stateno-failover
  6. master_replidc52bb664e09e9e76e9c995fdd15215578e38250f
  7. master_replid20000000000000000000000000000000000000000
  8. master_repl_offset0
  9. second_repl_offset:-1
  10. repl_backlog_active0
  11. repl_backlog_size1048576
  12. repl_backlog_first_byte_offset0
  13. repl_backlog_histlen0

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

给 6320 认 6310 是老大

6320的信息

image-20210427153811589

同样给 6330 配置一下

6310的信息

  1. 127.0.0.1:6310> info replication
  2. # Replication
  3. role:master // 主机标识
  4. connected_slaves:1 // 他有一个从机
  5. slave0:ip=127.0.0.1 // 从机地址
  6. ,port=6320 // 从机端口
  7. ,state=online // 从机状态
  8. ,offset=252,lag=1
  9. master_failover_state:no-failover
  10. master_replid:1c382c2d63dcacaabab6ce01942c24c1b1d3fbb1
  11. master_replid2:0000000000000000000000000000000000000000
  12. master_repl_offset:252
  13. second_repl_offset:-1
  14. repl_backlog_active:1
  15. repl_backlog_size:1048576
  16. repl_backlog_first_byte_offset:1
  17. repl_backlog_histlen:252

2个都配置完成

image-20210427154330727

细节

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

主机可以读写 :

image-20210427154957290

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

image-20210427155036738

==测试==

1、主机宕机了 从机状态

  1. 127.0.0.1:6310> SHUTDOWN
  2. not connected> exit
  3. [root@King bin]# redis-cli -p 6310
  4. Could not connect to Redis at 127.0.0.1:6310: Connection refused
  5. not connected> ping
  6. Could not connect to Redis at 127.0.0.1:6310: Connection refused

主机关闭

image-20210427160524806

image-20210427160413157

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

2、主机从新工作

  1. [root@King bin]# redis-server KingConfig/redis-10.conf
  2. [root@King bin]# redis-cli -p 6310
  3. 127.0.0.1:6310> ping
  4. PONG
  5. 127.0.0.1:6310> set k2 v2
  6. OK
  7. 127.0.0.1:6310> get k2
  8. "v2"

从机状态

image-20210427160810556

也可以查询值

  1. 127.0.0.1:6320> get k2
  2. "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地址端口
Redis 192.168.11.128 6379
Redis 192.168.11.129 6379
Redis 192.168.11.130 6379
Sentinel - 192.168.11.128 26379
Sentinel - 192.168.11.129 26379
Sentinel - 192.168.11.130 26379

2

多哨兵监控Redis

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

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

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

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

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

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

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

  1. # 启动Redis服务器进程
  2. ./redis-server ../redis.conf
  3. # 启动哨兵进程
  4. ./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

这个时候如果主机宕机了

image-20210427223236066

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

选举 6320 来做为主机

image-20210427223441603

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

image-20210427223921295

6320 主机

image-20210427224011068

哨兵集群配置

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

Redis缓存穿透和雪崩

缓存穿透 (查不到)

概念

缓存穿透的概念很简单,用户想要查询一个数据,发现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,设置不同的过期时间,让缓存失效的时间点尽量均匀。

posted @ 2021-07-02 17:49  键盘_书生  阅读(106)  评论(0编辑  收藏  举报