Redis从入门到高可用分布式实践(一)

初识

nosql介绍

  • 泛指非关系型的数据库
  • 不支持SQL语法
  • 数据类型k-v
  • 无通用的语言,每种nosql数据库都有自己的api和语法,以及擅长的业务场景
  • NoSQL中的产品种类相当多
    • redis
    • mogodb
    • hadoop

NoSQL和SQL数据库的比较

  • 适用场景不同:sql数据库适合用于关系特别复杂的数据查询场景,nosql反之
    -事务 特性的支持:sql对事务的支持非常完善,而nosql基本不支持事务
  • 两者在不断地取长补短,呈现融合趋势

redis简介

  • Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
  • 属于NoSQL,通过多种键值数据类型来适应不同场景下的存储需求,借助一些高层级的接口使用其可以胜任,如缓存、队列系统的不同角色
  • 默认端口:6379

特性目录

1. 速度快

  • 底层用C开发
  • 内存型数据库
  • 单线程

2. 持久化

  • 提供了持久化存储方案——RDB\AOF

3. 支持多种数据类型

  • string
  • hash
  • list
  • set
  • zset

衍生类型:

  • BitMaps:位图——用很小的内存,实现高效存储。(本质是字符串)
  • 2.8.19提供HyperLogLog:超小内存(12k)唯一值计数——超小内存完成上亿级技术。
    缺点:计数不太准确,有一定的误差率。(本质是字符串)
    GEO:地理信息定位——比如算经纬度——用于地图中。
  • 不依赖外部库
  • 单线程模型:无论是服务端、开发端都很方便。

4. 备份
主从复制——主服务器的数据可同步到从服务器:为高可用和分布式提供很好的基础。

5. 高可用和分布式

  • Redis_Sentinel(V2.8)支持高可用
  • Redis_Cluster(v3.0)支持分布式

使用场景

  • 缓存系统 :

    1. APP Server
    2. Cache(缓存)
    3. Storage(数据源)
      用户访问App Server —>Cache(有数据直接返回给用户,若不存在则访问Storage)—>Storage
  • 计数器 : 提供 incr命令,可在单线程下进行非常高效的计数,且不会出现计数错误的问题。适用于微博评论,转发计数,这种场景。

  • 消息队列系统 : rabitmq…

  • 排行榜 : 提供有序集合

  • 社交网络 : 天然吻合

  • 实时系统:消息队列缓冲…位图:垃圾邮件处理,实时过滤…


修改常用redis配置

在Linux系统中,redis的配置信息在

三种启动方式

  1. redis-server
    验证方法:
    • ps -ef | grep redis
    • netstat -antpl | grep redis
    • redis-cli -h ip -p port ping
  2. redis-server --port 6380 # 指定端口启动
  3. redis-server configPath # 配置文件启动——生产环境,单机多实例配置文件可以用端口区分。

API

通用命令

  • keys : * 、补齐
    keys 命令一般不再生产环境使用: 1.键值对比较多,该命令比较重o(n)——慢!2.本身也没什么意义。
    若要查出所有的key,可选用热备从节点(从一般不再生产环境使用吗?)、scan
  • dbsize : 计算key的总数。o(1) (Redis内置一个计算器,会自动计数key-value值)
  • exists key : 检查key是否存在 。存在返回1,不存在返回0。
  • del key # 成功删除返回1,
  • expire key seconde :默认以秒为单位。
  • ttl key : 查看key剩余过期时间(-1—>存在且没有设置过期时间)
  • persist key # 去掉key的过期时间
  • type key # 返回key的类型(5种)
    通配符的使用必须掌握
    时间复杂度:
    redis通用命令时间复杂度

数据结构内部编码

学习目标:知道编码设计的目的
数据结构内部编码
redis 源码内部有个RedisObject: 包含两个重要的属性

  • 对外数据类型
  • 编码方式

单线程架构

单车道的高速公路
每个瞬间只允许一辆车通过
单线程速度还快的原因:

  1. 纯内存 (本质)
  2. 非阻塞IO 不在IO上浪费过多的时间(辅)
  3. 避免线程切换和竞态消耗(要知道对多线成使用不合理时,甚至会比单线程慢。)(辅)

使用单线程的注意事项

  • 一次只运行一条命令
  • 拒绝长(慢)命令
    keys ,flushall,flushdb,slow lua script,mutil/exec, operate big value(collection)
  • 其实不是纯粹单线程
    • fysnc file descriptor
    • close file descriptor
      以上两个由独立线程执行。

数据结构

一、字符串

对于redis来说,所有的 key都是字符串, value可以是多种类型
字符串: 本质上都是二进制,not Up to 512MB (其实数据大了对单线程不利,考虑到并发和流量,一般都建议在100k内)
场景:

  • 缓存
  • 计数器
  • 分布式锁

命令:

  • get
  • set
  • del

当字符串中存储的数据为’123‘这种类型时,还可以使用以下API:

  • incr --------------------------->key自增1,key不存在时,会创建一个key 值为 0 ----->incr之后:get(key) = 1
  • decr
  • incrby key increment ---->key 按increment增加
  • decrby key decrement

set、setnx、setxx
set key value : 无论key是否存在 o(1)
setnx : key不存在
setxx :key必须存在

mget 、mset
批量、原子操作 o(n)
优:可省去网络时间,注意使用mget也要有节制。
getset
getset key newvalue # 设置新值,并返回旧值o(1)
append
append key value # 追加value
strlen
strlen key # 返回字符串长度 (注意中文长度:utf8下一个汉字俩字节)

二、hash(small redis)

keyfieldvalue
usernamexyx
age18
genderman

Mapmap
Small redis
field不能相同,value可以相同。

hget 、hset、hdel
hget key field
hgetall key # 获取所有属性

hexists 、hlen
hexists # 判断hash键是否有field
hlen # 获取hash key field的数量

hmset、hmget
hmset key field1 value1 field2 value2 field3 value3…
hmget key field1 field2 field3…

应用:

  1. 记录网站每个用户个人主页的访问量

    hincrby user1 pageview count

  2. 缓存视频基本信息(数据在mysql)


hgetall、hvals、hkeys o(n)
hgetall key # 获取key所有的field 和value.(小心使用,返回所有的field和value)
hvals key # 获取key所有的值
hkeys # 获取所有的field
符合关系型数据库的特点。

三、list

keyelements

列表:有序、可重复、索引取值

rpush key value1 value2 …valueN # 从右往左插
rpush list 1,2,3,4,5
lpush
lpush list1 a,b,c,d
linsert :
linsert key before|after value newvalue # 在list指定值前后插入
linsert list before 1 a


lpop # 从左边弹出一个元素。
lpop list------>2,3,4,5
rpop # 右边弹

lrem # 删除指定元素
redis lrem
lrem key count value # 删除列表中count个值为value的元素。count:zheng’fu
a c a c b f
lrem list 0 a # 把list中所有为a的元素都删点
lren list -1 c # 从右开始删除,一个
ltrim
ltrim key start end # 按照索引范围修剪列表
在这里插入图片描述

改:lset
lset key index newvalue
查:
lrange # 按照范围取
lrange key start end (闭区间)
lindex # 按照索引取
lindex key index
注意点:

  • 下标从零开始
  • 取值范围为闭区间
  • 新建list时,元素之间以空格区分
    llen key

微博timeline:

weibo更新时间轴
weibo6
weibo5
weibo4
weibo3微博对象3
weibo2微博对象2
weibo1微博对象1

when 关注的人更新了微博 -------lpush

blpop、brpop
blpop # lpop阻塞版本,timeout阻塞超时时间,timeout=0永不阻塞。

127.0.0.1:6379> blpop a 10
(nil)
(10.07s)
127.0.0.1:6379> lpush a 1 2 3
(integer) 3
127.0.0.1:6379> blpop a 10
1) "a"
2) "3"
127.0.0.1:6379> blpop a 10
1) "a"
2) "2"
127.0.0.1:6379> blpop a 10
1) "a"
2) "1"

TIPS:

  1. LPUSH + LPOP =Stack # 栈
  2. LPUSH +RPOP = Queue # 队列
  3. LPUSH +LTRIM =Capped Collection # 控制列表数量
  4. LPUSH + BRPOP = Message Queue # 消息队列系统

四、set

user1_like
特点:

  • 无序
  • 不重复
  • 集合间的交叉并集

sadd 、 srem

sadd : 添加失败返回结果为0
sadd key element
srem key element

scard、sismember、srandmenber、smembers
在这里插入图片描述
smembers
-无序

  • 返回所有

srandmember 和spop

  • srandmember随机返回多个元素不会破坏原本数据
  • spop 从集合中弹出

应用:

  1. 抽奖系统
    srandmenbe弹出
  2. like 、赞、踩 ---->放在一个用户对应的集合中。
  3. 标签
    给用户添加标签,用户关注的标签。

集合间的API
sdiff 、sinter、 sunion
在这里插入图片描述
应用:共同关注
在这里插入图片描述


五、zset


keyscorevalue
user1like
100name
100gender
  • 有序 (分值越大越靠前)
  • 不重复
  • 同分值,按字母顺序排序。

zadd key score element o(n)

zadd user 1 name 2 age 3 gender 4 id

zrem key element # 可以是多个element

zrem user name age

zscore key element

zincrby key increScore element

zrank # 查看排名
zrem
zrange key start end

zremrangebyrank # 按排名删除范围
zremrangebyrank key start end
zremrangebyscore # 按分值删除

应用:排行榜


Redis其它功能

慢查询

  1. 生命周期
    • 客户端发送命令
    • redis中排队
    • 执行命令
    • 返回结果

慢查询发生在第3 阶段
客户端超时不一定有慢查询

  1. 两个配置
  • slowlog-max-len
    1. 先进先出队列
    2. 固定长度
    3. 保存在内存中
  • slowlog-log-slower-than
    1. 慢查询阈值(单位:秒)
    2. slowlog-log-slower-than=0,记录所有命令。
    3. slowlog-log-slower-than<0,不记录任何命令。
  • 默认值
    1. config get slowlog-max-len = 128
    2. config get slowlog-log-slower-than = 10000
  • 修改配置文件重启
  • 动态配置
    1. config set slowlog-max-len 1000
    2. config set slowlog-log-slower-than 1000
  1. 三个命令
    1. slowlog get[n] : 获取慢查询队列,指定n条命令
    2. slowlog len : 获取慢查询队列长度
    3. slowlog reset : 清空慢查询队列
  • 运维经验
    1. slowlog-max-len 不要设置过大,默认10ms,通常设置1ms
    2. slow-log-slower-than不要设置过小,通常设置1000左右。
    3. 定期持久化慢查询

pipline

  1. 什么是流水线
    一次网络通信模型:
    一次通信时间 = 一次网络时间(发送+返回)+ 一次命令时间
    n命令 = n网络时间 + n命令时间
    命令时间非常快,网络时间受网络影响

流水线:
将一批命令批量打包,在服务端进行一个批量计算,最后一次性返回。
总时间 = 一次网络时间 + n次命令时间

  1. redis命令时间是微妙级别
  2. pipeline每次条数需要控制
    流水线作用

notes:
3. 注意每次pipline携带数据量
4. pipline每次只能作用在一个redis节点上
5. M操作与pipline区别


发布订阅

  • 角色

    1. 发布者
    2. 订阅者
    3. 频道
    • 模型redis提供的发布订阅功能
      注意:新的订阅者接收不到发布者发布的之前的消息。——不提供消息堆积。
  • API

    1. publish
      publish channel message
      publish keji ‘shijiebei fhrjrg’
      (integer) 3 # 返回结果为订阅者个数
    2. subscribe
      subscribe channnel # 订阅频道,一个或多个
      返回订阅频道信息+发布的消息。
    3. unsubscribe channel # 取消订阅 一个或多个

其它API:
在这里插入图片描述
消息队列:
消息队列
redis本身不提供消息队列,而是通过列表实现。
消息队列与发布订阅的区别在于:消息队列中的资源只用一个客户端获得。而发布订阅消息是所有订阅者均能获得。

bitmap

位图:
set hello big
getbit hello 0 # b的二进制存储的一个bit

setbit
setbit key offset value # offset 偏移量,即将key的第offset位设为value。—原本的value肯定会改变。
返回结果:之前对应位的值。
setbit hello 0 1 # 将hello的第1位设为1
getbit
getbit key offset
bitcount
bitcount key [start end] # 返回指定范围值为1 的个数
bitop
bitop op destkey key [key…]
做多个Bitmap 的and(交集)、or(并集)、not,并将操作的结果保存到destkey中。
bitpos
bitpos key targetBit[start] [end]
计算位图指定范围第一个 偏移量对应的值等于targetBit的位置

使用:独立用户统计

  1. 使用set和Bitmap
  2. 1亿用户,5千万用户

使用经验:

  1. type = string ,最大512MB
  2. 注意setbit时的偏移量,可能有较大耗时。
  3. 位图不是绝对的好。

hyperloglog

新的数据结构?
极小空间完成独立用户统计。——算法
type hyperloglog_key ——string
三个命令
pfadd key element [element] # 添加独立用户
pfcount key [key…] # 统计独立用户
pfmerge destkey sourcekey [key…] #

内存消耗

geo

持久化的取舍和选择(重要)

什么是持久化?
redis 数据存储在内存中,会将更新数据异步保存到磁盘。

持久化的方式:

  1. 快照——把某个时间点的数据记录下来
    MySQLdump、redis RDB
  2. 写日志 ——恢复时重新执行下日志文件
    MySQL Binlog 、Hbase HLog、Redis AOF

RDB

redis -------->命令---------->RDB文件(二进制、硬盘)
重启(恢复redis)------------------>载入RDB文件
生成RDB
save(同步):该命令执行完前,其它命令处于等待状态。——数据量大时会造成redis阻塞。
文件策略——新替老
复杂度o(n)

bgsave(异步):单独线程执行,立即返回ok.

bgsave -->fork()子进程---->createRDB----->告诉redis bgsave successfully
因为fork()快,极少情况下会出现慢的情况阻塞redis
此时可正常访问redis-cli
对比:
save bgsave
自动
自动生成RDB的策略
在的多长时间内,改变了多少条数据------>内部执行bgsave

配置secondschanges
save9001
save9010
save601000

900s 改了1条数据,90s改了10条数据,60s改了1000条数据,都执行bgsave.
——并不是很好的策略,因为数据写入量不受控制,所以生成的规则也不好控制。频繁的操作会对硬盘造成一定压力。
最佳配置:

save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir ,/
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes #是否采用校验和

文件名:dump.rdb
默认保存:dir ./

触发机制
  1. 全量复制(主从复制时,主自动触发–>RDB)
  2. debug reload
  3. shutdown (shutdown save)

RDB总结

  1. RDB是Redis内存到硬盘的快照,用于支持化。
  2. save通常会阻塞Redis.
  3. bgsave不会阻塞Redis,但是会fork新进程。
  4. save自动配置满足任一就会被执行
  5. 有些触发机制不容忽视

RDB问题:
耗时、耗性能

  • o(n)
  • fork() :消耗内存,copy-on-write策略
  • disk I/O :把数据写到硬盘中

不可控、数据丢失

  • 因为不是同步创建

AOF

解决RDB数据丢失问题。——日志,记录每次操作命令。
redis宕机---->重启之后将AOF载入数据库。

AOF的三种策略

  • always
    redis写命令---->缓冲区-- 每条命令fsync —>硬盘(AOF)
  • everysec
    redis写命令---->缓冲区-- 每秒把缓冲区fsync —>硬盘(AOF)——优点:数据量巨大的时候能既减轻硬盘压力。缺点:发生故障会丢失一秒的数据。——默认策略。
  • no
    redis写命令---->缓冲区-- 操作系统决定 —>硬盘(AOF)
    策略选择:
    在这里插入图片描述
    一般使用默认配置。
    问题:
    随着时间的推移,并发量的增大…——AOF文件会增大。
    AOF重写
    AOF重写
    把过期的,无用的以及可以优化的命令进行化简成一个很小的AOF文件。

目的:

  • 减少磁盘占用量 (想象incr 一亿次)
  • 加速恢复速度
    AOF重写实现两种方式:
  1. bgrewriteaof
    client-----> redis(redis立马响应ok)----->fork —>redis子进程—>AOF重写
    AOF将redis中的数据进行一次回朔——>生成AOF文件。
  2. AOF重写配置
    AOF重置
  • AOF重写的最小尺寸。
  • AOF重写的增长速率。比如当前数据为100,设置重写增长率为100%,当数据为200时进行重写。

自动触发时机

  • aof_current_size > auto-aof-rewrite-min-size
  • aof_current_size-aof_base_size/aof_base_size> auto-aof-rewrite-percentage

AOF重写流程
AOF重写流程

RDB&AOF

Redis持久化开发运维常见问题

1.fork操作

  • 同步操作(快 若卡则阻塞redis主进程)
  • 与内存息息相关:内存越大耗时越长(redis使用的内存越大,数据量越多)
  • info: latest_fork_usec 查看持久化执行的时间

改善fork

  • 有限使用物理机,or 高效支持fork的虚拟化技术
  • 控制redis可用最大内存:maxmemory
  • 合理配置Liunx 内存分配策略:vm.overcommit_memory = 1(默认为0,当内存很小的时候则不会执行fork,造成redis阻塞。)
  • 降低fork频率。放宽AOF重写 自动触发机制,不必要的全量复制

2. 进程外的开销和优化

1. cpu

  • 开销 :RDB和AOF文件的生成,属于CPU密集型
  • 优化: 不做CPU绑定(不把redis和cpu绑定)不要和cou密集型应用部署在一起。在单机多部署的情况下,不要发生大量的aof,重写AOF,bgsave…

2. 内存

  • 开销::fork内存==(liux 提供copy-on-write 子父进程共用内存件,只有父进程执行写入请求时,会创建副本,这才消耗较多内存,而在整个期间子父进程会共享父进程内存值快照)==
  • 优化:不允许单机多部署时大量产生重写,在主进程写入量较少的时候重写,在liunx上做一些优化——内存页分配(禁止echo never)

3. 硬盘

  • 开销:RDB和AOF文件写入
  • 优化:
    • 不要和高硬盘负载服务部署到一起:存储服务,消息队列等
    • no-appedfsync-on-rewrite = yes
    • 根据数据量决定磁盘类型:如,数据量很大的时候可以考虑ssd
    • 单机多实例持久化文件目录可以考虑分盘

3.AOF追加阻塞

AOF追加阻塞
每秒刷盘

主从复制

单机有什么问题

若在一台机器上部署redis节点

  1. 机器故障
    客户端无法连接redis。只能把redis迁移到别的机器,此时需要考虑数据书否丢失的问题。节点坏了,redis可以重启。——高可用
  2. 容量瓶颈
    内存不够,加内存钱不够——分布式
  3. QPS瓶颈——分布式。(redis号称可以提供十万QPS,若我们需要100万该怎么做呢?)

主从复制的作用

总:

  1. 一个master可以有多个slave,slave还可以有slave
  2. 一个slave只能由一个master
  3. 数据流向是单向的:master—>slave
    主从之间的复制是通过网络传输的。
    master:
    slave:
    一主一从
    一主多从
    读写分离:
    作用: 数据副本(高可用,分布式基础)、扩展读性能
    方式
    1.命令 : 在从节点执行:slaveof 127.0.0.1 (主的IP)6379(假设希望6380成为6379的从节点,此处127.0.0.1只是演示,主从在一台机器上是没有任何价值的。)
    取消复制:在6380执行:slaveof no one (6380并不会清除之前从6379同步来的数据)
    6380: slaveof 一个新的主,需要把6380上的所有数据删除。
    在这里插入图片描述
    2.配置
    slaveof ip port
    slave-read-only yes : 主从复制之读写分离。
    daemonize yes : 是否已守护进程方式启动
    对比:
    命令: 无需重启、不便于管理
    配置:统一配置、需要重启
    dbfilename dump-6379.rdb:最好给不同的主从设置不同的rdb.

redis-server redis-6379.config : 指定配置文件启动
redis-cli
info replication : 查看主从信息。

127.0.0.1:6379> info replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379>

从:

127.0.0.1:6380> info replication
# Replication
role:slave
master_host:
master_port:6379
master_link_status:up
master_last_io_seconds_age:3
slave_repl_offset:43
slave-read-only:1
connected_slaves:0
.....
127.0.0.1:6380>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值