Redis学习笔记

https://www.bilibili.com/video/BV1S54y1R7SB
未完待续…

前言

大数据的3v和3高

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

  1. 海量Volume
  2. 多样Variety
  3. 实时Velocity

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

  1. 高并发
  2. 高可拓
  3. 高性能

以淘宝为实例

  1. 商品的基本信息
  • 名称、价格、商家信息
  1. 商品的描述、评论
  • mongoDB
  1. 图片视频等静态资源
  • 分布式文件系统FastDFS
  • 淘宝自己的TFS
  • Google的GFS
  • Hadoop HDFS
  • 阿里云的oss
  1. 商品的关键字
  • 搜索引擎 solr
  • ISearch (阿里多隆)
  1. 商品热门的波段信息
  • 内存数据库
  • Redis
  1. 商品的交易、外部的支付接口
  • 三方应用

Nosql的四大分类

  1. kv键值对
  • 新浪:Redis
  • 美团:Redis + Tair
  • 阿里, 百度:Redis + memecache
  1. 文档型数据库(bson格式)
  • MongoDB(一般必须要掌握), 分布式文件存储的数据库, C++编写, 主要用来处理大量的文档;介于关系型数据库和非关系型数据库中间的产品, MongoDB是非关系型数据库中功能最丰富的, 最像关系型数据库
  • ConthDB
  1. 列存储数据库
  • HBase
  • 分布式文件系统
  1. 图关系数据库(朋友圈社交网络, 广告推荐)
  • Neo4j
  • InfoGrid

Redis简介

Redis能干嘛?

  1. 数据库, 内存存储、持久化(rdb、aof)
  2. 缓存
  3. 消息中间件MQ, 发布订阅系统
  4. 地图信息分析
  5. 计数器、计数器(浏览量)

特性

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

Redis安装

https://redis.io
https://download.redis.io/releases/redis-6.2.1.tar.gz


redis_server使用

  1. 启动
  • redis_server, 这种启动方式需要一直打开窗口, 不能进行其他操作, 不太方便。ctrl+c退出
  • 后台启动, 需要修改配置文件redis.conf
mkdir /etc/redis
cp redis.conf /etc/redis/6379.conf #以端口命名
#修改配置文件为daemonize yes
redis-server /etc/redis/6379.conf
  • 开机自启动, 以CentOS 7为例, 将redis加入到systemctl中
cat <<EOF > /usr/lib/systemd/system/redis.service
[Unit]
Description=redis
After=redis.service
 
[Service]
#Type=forking
#PIDFile=/var/run/redis_6379.pid
ExecStart=/usr/local/bin/redis-server /etc/redis/6379.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
 
[Install]
WantedBy=multi-user.target

EOF

systemctl daemon-reload
systemctl enable redis.service
systemctl start redis.service

redis_cli使用

redis-cli #默认本地6379端口
redis-cli -h 127.0.0.1 -p 6379 #无权限控制
redis-cli -a password #有权限控制, 需要修改配置文件的requirepass
auth xxx              #或者进入redis-cli后, 输入密码登录

#添加密码方法1
config set requirepass 123456  #添加密码
config rewrite                 #写回配置文件

#添加密码方法2
requirepass "123456"           #或者在配置文件中加, 需要""

redis性能测试

redis-benchmark

Redis基本数据结构


Redis基本操作

  1. 选择数据库dateabases, 默认0号数据库, 默认支持16个数据库, 通过配置文件修改上限
# 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'-1
databases 16

select 7       #选择数据库
DBSIZE         #数据库大小

flushdb        #清空当前数据库
flushall       #清空所有数据库
  1. key
keys pattern        #指定pattern的所有key, 通配符
DEL KEY             #删除key
DUMP KEY            #序列化key
EXISTS KEY          #是否存在
EXPIRE KEY          #设置过期时间,秒,过期后不再存在key
TTL KEY             #返回剩余过期时间, 秒
PERSIST KEY         #移除过期时间
RANDOMKEY           #返回随机key
RENAME KEY NEWKEY   #重命名
TYPE KEY            #key所存储的值的类型
MOVE KEY DB         #移动key到指定db
  1. 字符串value
SET KEY VALUE                   #设置value
GET KEY                         #获取key对应的value
GETRANGE KEY START END          #获取子字串,[start:end], 从下标0开始, 可用负数, -1表示最后一位
SETRANGE KEY START value        #从start开始设置
APPEND KEY VALUE                #如果是字符串, 则追加
STRLEN KEY                      #长度

SETEX KEY SECONDS VALUE         #设置key和value和到期时间
SETNX KEY VALUE                 #在key不存在的时候设置value,在分布式锁中会常常使用


MSET KEY1 VALUE1 KEY2 VALUE2 ...#设置多个key, value
MGET KEY1 KEY2 ...              #获取多个key的value
MSETNX                          #原子性, 要么一起成功, 要么一起失败

INCR KEY                        #+1, 比如是整型
INCRBY KEY INCREMENT            #增量
DECR KEY                        #-1
DECRBY KEY INCREMENT            #

GETSET KEY VALUE                #返回原来值, 并设置新值

设置对象常用方式

#方法一, 全都放一起
set student "{\"id\": 1,\"age\": 18}"
#方法二, 分开放, 方便扩展
mset student:id 1 student:age 18
  1. 列表, L,left,头;R,right,尾
LPUSH KEY ELEMENT1 ELEMENT2             #左边插入
LPOP KEY count                          #左边出
RPUSH KEY ELEMENT1 ELEMENT2             #右边插入
RPOP KEY count                          #右边出

LLEN KEY                                #长度
LRANGE KEY start stop                   #范围
LINDEX KEY index                        #获取对应下标的值
LTRIM KEY start stop                    #修剪
rpoplpush source destination            #将队列从source转移到destination
lset KEY INDEX ELEMENT                  #设置对应下标的值, 不能超过llen
linsert key before|after pivot element  #在对应的pivot前|后插入新的element
  1. 集合
SADD KEY member1 member2            #添加成员
SISMEMBER KEY member                #是否是成员
SMEMBERS KEY                        #输出所有成员
SCARD KEY                           #成员数
srem key member1 member2            #移除成员
SRANDMEMBER key count               #随机返回count个成员
SPOP key                            #移除并返回随机成员

smove source destination member     #移动source的member到destination


SDIFF KEY1 KEY2                     #差集 key1-key2
SDIFFSTORE destination key1 key2    #差集保存在destination
SINTER KEY1 KEY2                    #交集 key1^key2
SINTERSTORE destination KEY1 KEY2   #交集保存在destination
SUNION KEY1 KEY2                    #并集
SUNIONSTORE destination KEY1 KEY2   #并集保存在destination

  1. 哈希value
HSET KEY FIELD1 VALUE1 FIELD2 VALUE2     #添加字典
HGET KEY FILED1                          #获取值
HMGET KEY FILED1 FILED2                  #获取多个值
HDEL KEY FIELD1                          #删除指定field
HEXIST KEY FIELD1                        #判断是否存在
HGETALL KEY                              #获取所有field, value      
HKEYS KEY                                #获取所有field             
HLEN KEY                                 #获取所有field的个数       

HINCRBY KEY FIELD1 increment             #增加, 可以是负数
HSETNX KEY FIELD1 VALUE1                 #如果不存在

  1. 有序集合
ZADD KEY score1 member1 score2 member2  #添加, 以score进行单调递增排序
ZRANGE KEY start stop                   #范围, 索引范围, 从小到大取
ZRANGEBYSCORE key min max               #排序, score范围, -inf, +inf
ZRANGEBYSCORE key (min (max             #排序, 开区间
zrevrange key start stop                #倒序, 索引范围, 并按照倒序返回
zrevrangebyscore key max min            #倒序, score范围, +inf,-inf


zrem key member                         #移除成员
zcard key                               #成员数量
zcount key min max                      #min max内的数量, 一般都是闭区间


zdiff numkeys key1 key2 ...             #计算numkeys个key1, key2的member差集
zinter numkeys key1 key2 ...            #计算numkeys个key1, key2的member交集, score叠加
zunion numkeys key1 key2 ...            #计算numkeys个key1, key2的member并集, score叠加

zincrby key increment member            #
  1. geospatial地理位置
geoadd key longitude1 latitude1 member1 longitude2 latitude2 member2 #经度 纬度
geoadd china:city 113.265 23.108 guangzhou 114.109 22.544 shenzhen
geopos key member1 member2                                           #获取指定member的经纬度信息
geodist key member1 member2 m|km                                     #定位距离, 指定单位
georadius key longitude latitude radius m|km                         #指定位置的范围半径
georadiusbymember key member radius m|km                             #指定member的半径
geohash key member1 member2                                          #返回hash值
zrem key member1                                                     #底层用的是zset, 可以用zset来操作
  1. HyperLogLog 基数计数
pfadd key element1 element2 ...           #
pfcount key                               #计数
pfmerge destkey sourcekey1 sourcekey2 ... #合并
  1. bitmaps 二进制位, 只有两个状态
setbit key offset value                   #设置位为1或0, 以bit为单位
getbit key offset                         #设置位为1或0, 以bit为单位
bitcount key start end                    #start, end, 以byte为单位

事务, 一组命令的集合

要么同时成功, 要么同时失败–>原子性
redis的单条命令是原子性的, 但事务不保证原子性
redis的事务是全局的(排他), 只能有一个客户执行事务

mutil       #开启事务
set k1 v1   #入队
set k2 v2   #入队
get k2      #入队
exec        #结束事务
discard     #放弃事务, 事务中命令都不会执行

编译型异常, 所有命令都不会执行

multi
set k1 v1 
set k2 v2
getset k3
set k4 v4
exec

运行时异常, 除了异常的命令, 其他都能执行

multi
set k1 v1
incr k2
set k3 v3
exec

监视watch, 作为乐观锁

悲观锁, 认为什么时候都出问题, 无论做什么都加锁。悲观锁的实现, 往往依靠数据库提供的锁机制
乐观锁, 认为什么时候都不会出问题, 所以不会加锁。乐观锁, 就是利用版本号比较机制, 只是在读数据的时候, 将读到的数据的版本号一起读出来, 当对数据的操作结束后, 准备写数据的时候, 再进行一次数据版本号的比较, 若版本号没有变化, 即认为数据是一致的, 没有更改, 可以直接写入, 若版本号有变化, 则认为数据被更新, 不能写入, 防止脏写。

#线程1: redis-cli
watch money
multi 
incrby money 10
exec
unwatch     #好像自动释放?

#线程2:redis-cli, 在线程1执行watch后, 修改money值
set money 100

RDB持久化

  • 写时复制, 处于效率考虑, Linux引入了写时复制计数, 只有进程空间的各段内容要发生变化时, 才会将父进程的内容复制一份给子进程, fork之后, 父子进程的虚拟地址不同, 但指向同一段物理地址空间
  • redis单独创建子进程进行持久化, 会将数据写入一个临时文件, 待持久化结束后, 再用临时文件替换上一次持久化好的文件。如果最后一次持久化宕机, 那么最后一次持久化的数据可能丢失。
  • Redis的RDB文件是内存存储的二进制表示。这个二进制文件足以完全恢复Redis的状态。RDB文件格式, https://blog.csdn.net/damanchen/article/details/103326964
  • 配置文件
################################ SNAPSHOTTING  ################################
# save <seconds> <changes>
# 注销掉save, 默认不进行持久化
save 3600 1
save 300 100
save 60 10000

rdbcompression yes    #压缩
rdbchecksum yes       #校验
dbfilename dump.rdb   #文件名
dir ./                #保存路径
  • 触发规则
  save     #即时保存, 在主线程中工作, 会阻塞其他请求。
  bgsave   #即时保存, 生成子进程, 子进程将数据写入临时文件。配置文件的策略用的是这个
  flushall #删除所有
  shutdown #关机
  • 优点
  1. 适合大规模的数据快速恢复
  2. 对数据的完整性要求不高
  • 缺点
  1. 存储需要一定的时间间隔操作, 如果宕机, 会丢失最后一次数据
  2. fork子进程需要系统资源

AOF持久化 append only file

记录所有命令, 类似history
以日志的形式来记录每个写操作(不记录读操作), 追加文件, 启动之初会读取该文件重新构建数据。

############################## APPEND ONLY MODE ###############################
appendonly no                    #默认不开启
appendfilename "appendonly.aof"

# appendfsync always             #每次操作, 效率最低
appendfsync everysec             #每秒
# appendfsync no                 #不主动, 一般30s, 效率最高
  • 如果aof的配置文件有错误, redis是启动不起来的, 通过redis-check-aof修复aof文件
  • 优点
  1. 如果文件有错误, aof可以通过redis-check-aof进行修复, 丢弃损坏部分。
  • 缺点
  1. 相对于rdb文件来说, aof文件占用的空间大, 恢复比rdb慢

持久化拓展

  • 如果只做缓存, 那么可以不用持久化
  • 同时开启两种持久化方式, 优先载入aof。rdb更适合用于备份数据库, 快速重启
  • 建议, rdb只用于后备用途

发布与订阅

SUBSCRIBE channel
UNSUBSCRIBE channel
PUBLISH channel message
PUBSUB 
  • 使用场景
  1. 实时消息系统
  2. 群聊
  3. 订阅, 关注系统
  • 稍微复杂的场景会使用消息中间件MQ

主从复制

主从复制, 读写分离!80%的情况下都是进行读操作。基本是一主二从。

主从复制的作用

  • 数据冗余:主从复制实现数据的热备份, 是持久化之外的一种数据冗余方式
  • 故障恢复:当主节点出现问题, 可以有从节点提供服务, 实现快速的故障恢复;实际上是一种服务的冗余
  • 负载均衡:在主从复制的基础上, 配合读写分离, 可以由主节点提供写服务, 由从节点提供读服务, 分担服务器负载;尤其在写少读多的场景下, 通过多个从节点分担读负载, 可以大大提高redis服务器的并发量
  • 高可用集群的基石

不能用只使用Redis单机的原因

  • 从结构上, 单个Redis服务器会发生单点故障, 并且一台服务器需要处理所有的请求负载, 压力较大
  • 从容量上, 单个Redis服务器内存容量优先, 就算一台Redis服务器内存容量为256G, 也能将所有内存用作Redis存储, 一般来说, 单台Redis最大使用内存不应该超过20G
  • 电商的商品, 一般是一次上传, 无数次浏览, 专业点就是多读少写

配置文件,每个Redis服务器默认为主节点

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:44cb41f864f5e77cc95a5e7e56a06c689d58eeb4
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

配置从机

#如果从机和主机在同一台机器, 需要修改端口, 持久化文件等路径
slaveof host port #在redis-cli中设置主机的host port

#或者是修改配置文件
replicaof <masterip> <masterport> #设置主机的host port
masterauth <master-password>

第一个模型,一主多从

master处理写 ---> slave1 读操作
            |--> slave2 读操作
            ---> slave3 读操作

主机可以读写, 从机只读
场景

  • 主机宕机, 从机依旧连接到主机, 但是没有写操作;主机回来后, 从机依旧可以直接获取到主机的信息
  • 从机宕机, 重连回主机后, 立马从主机中获取值
    复制原理
    全量复制:master将整个数据文件(rdb)传送到slave
    增量复制:master将新修改的命令依次传给slave
    只要是重新连接master, 就会先执行全量复制

第二个模型,一主一从并链接

master处理写 ---> slave1 读操作, 仍然不能写 ---> slave2 读操作    

场景

  • master宕机, slave1如何变成主节点
slaveof no one #使自己变成主机, 需要手动
  • master回来,slave1重连回master, 数据会和master一样, slave2跟着同步成和master一样

哨兵模式(自动选老大)

哨兵配置文件

port 26379
daemonize no
pidfile /var/run/redis-sentinel.pid
logfile ""
dir /tmp

#Tells Sentinel to monitor this master, and to consider it in O_DOWN
# (Objectively Down) state only if at least <quorum> sentinels agree.
sentinel monitor <master-name> <ip> <redis-port> <quorum>                                                         
sentinel monitor mymaster 127.0.0.1 6379 2 #被监控的redis主机

场景

  • master宕机, 哨兵切换成其他slave为master
  • master回来, 哨兵将master作为新master的slave
    优点:
  1. 哨兵集群, 基于主从复制模式, 所有的主从配置有点, 它全有
  2. 主从可以切换, 故障可以转移, 系统的可用性会更好
  3. 哨兵模式就是主从模式的升级, 手动到自动, 更加健壮

缺点:

  1. Redis不好在线扩容
  2. 配置麻烦

主观下线(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断。
客观下线(Objectively Down, 简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。(一个 Sentinel 可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线。)


哨兵集群

拷贝多个配置文件即可(同一台机需要分配不同端口), 指向同一个master即可


缓存击穿, 缓存穿透, 缓存雪崩

1. 缓存击穿:Redis无, MySql有

  • 解决办法: 缓存的key不过期

2. 缓存穿透:Redis无, MySql无

  • 解决办法: 布隆过滤器
  • 解决办法: 在Redis缓存空对象。需要更多空间存储;缓存与存储层的数据不一致

3. 缓存雪崩:Redis大量数据集中到期(或者Redis宕机), 查询都落到MySql。比如双十一0点抢购, 停掉一些服务(比如退款)

  • 解决办法: Redis高可用, 多集群, 异地多活
  • 解决办法: 限流降级
  • 解决办法: 数据预热, 让缓存失效时间均匀

编程

redis-6.2.1/deps/hiredis, 编译库
-lhiredis

redisReply

  1. 判断指针是不是空的
  2. 判断type类型

面试题

1. redis为什么这么快?

  • c实现
  • 存储在内存
  • 数据结构简单
  • 多路I/O模型(这算在其中吗)

2. redis为什么用单线程?

https://redis.io/topics/faq

Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值