NOSQL概述
一、单机部署时代方式
一个应用+一层数据访问方式+数据库
缺点:
注释:比如第二点,mysql的单表数据超过300W就要搞索引,但是索引太大,一个机器的内存也放不下。
二、memcached(缓存)+mysql+垂直拆分(读写分离)时代
百分之80%都是在做读操作,所以用缓存加快读取速度,减轻数据压力。另外加数据库,存更多的数据,为了提高性能,数据库做读写分离。
三、分库分表(单个库单个表的数据太多了)+水平拆分+mysql集群
比如说:集群1、2、3各放3分之1的数据,它们组合起来就是一个完整的数据库。 当请求在cache里找不到数据时,就会根据一些集群机制,找到对应的集群去取数据。
本质就是:解决读写的问题。
- 早些年的Mysql使用Myisam引擎,查询某条数据的时候,就会产生表锁,其他进程要等它查完才能操作表。高并发下,影响效率。
- 转战Innodb引擎,行锁。
- 分库分表解决写的压力。(使用集群)
四、现在基本项目的架构
五、为什么要用NOSQL
用户个人的信息,社交网络,地理位置,用户日志,用户自己产生的数据,爆发式增长。 不可预知。
3V+3高
- 大数据时代的3V:描述问题
- 海量的
- 多样的
- 实时的
- 大数据时代的3高:对程序的要求
- 高并发
- 高可扩
- 高性能
真正在公司实践:NOSQL+RDBMS一起使用
六、4种NOSQL数据库对比
Redis学习提纲
一、redis概述
二、redis测试性能
redis-benchmark是一个官方自带的压力测试工具 www.runoob.com/redis/redis…
测试截图:
参数解析:
三、基础知识
一共有16个数据库,默认在0号数据库
redis是单线程
redis为什么单线程还这么快?:
- 基于内存操作,CPU不是性能瓶颈,机器的内存和网络带宽才是
- 单线程不像多线程,需要CPU上下文切换,节省时间
- 使用了I/O多路复用技术
误区:
- 高性能服务器一定是多线程的?不一定
- 多线程(CPU上下文会切换)一定比单线程效率高?不一定
四、redis事务
- 原子性:要么同时成功,要么同时失败。
- redis单条命令是保证原子性的,但是事务不保证原子性。
- redis事务没有隔离级别的概念。(所有命令存在事务中,并没有直接被执行,只有发起执行命令的时候才会执行)
- redis事务本质:一组命令的集合。一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行(顺序性),且不可被打断(排他性)。一次性,顺序性,排他性!
redis事务:
- 开启事务(multi)
- 命令入队(正常执行命令) #有点类似剧本
- 执行事务(exec)
正常执行事务
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k1 v1 #命令入队
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) "v2"
4) OK
放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> discard #放弃事务
OK
127.0.0.1:6379> get k1 #事务队列中命令都不会被执行
(nil)
编译型异常(命令使用有问题),事务中所有命令都不会被执行
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> getset k3 #错误命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec #执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1 #所有的命令都不会被执行
(nil)
运行时异常,若事务中有一条命令运行时出错,会抛出异常,但不影响其他命令正常执行
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 #给字符串值自增1,运行时肯定会报错
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
五、监控(watch)
悲观锁
- 认为什么时候都可以可能出问题,无论做什么都加锁
乐观锁(redis使用watch,仅对事务有效)
- 认为什么时候都不会出问题,所以不会主动上锁。但可以使用watch命令,提前监控,并在真正更新数据之前判断一下watch的对象是否有改动过,若有,则更新数据的提交失效。
redis监控测试
正常执行
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监视money对象,事务成功后,监控自动取消。unwatch手动解除监控
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec #事务正常结束,事务期间数据没有被其他人改动,所以成功
1) (integer) 80
2) (integer) 20
事务期间,监控对象数据被改动
#客户端1
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监视money对象
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> incrby out 20 #敲完这个后转到客户端2,修改监控对象的值
QUEUED
127.0.0.1:6379> exec #客户端2修改完值后,返回客户端1,提交执行。执行失败,返回nil。exec和discard会自动解除监控。
(nil)
#客户端2
127.0.0.1:6379> get money
"100"
127.0.0.1:6379> set money 120 #在客户端1的事务还没提交前,修改监控对象的值
OK
六、redis.conf参数解析
#单位:配置文件对单位的大小写不敏感
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
#可以包含其他redis.conf的配置文件
################################## INCLUDES ###################################
# Include one or more other config files here. This is useful if you
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# include .\path\to\local.conf
# include c:\path\to\other.conf
# 网络配置
################################ GENERAL #####################################
# 如果想让其他主机访问,这里要改为0.0.0.0 或者指定网段,可以使用通配符
bind 127.0.0.1
# 监听端口
port 6379
# 以守护进程的方式运行,默认是no,需要改的
daemonize yes
# 如果是以后台方式运行,那么这个文件会记录redis进程的ID
pidfile /var/run/redis_6379.pid
)
# 设置日志级别,默认notice,生产就是用这个
loglevel notice
# 设置日志记录文件,默认空就是stdout
logfile ""
# 数据库数量,默认16
databases 16
# 快照,在规定的时间内,执行了多少次操作,则会持久化到文件里。一个是.rdb,一个是.aof
################################ SNAPSHOTTING ################################
# 如果900s内,如果至少有1个Key进行了修改,那就进行持久化操作
save 900 1
# 如果300s内,如果至少有10个Key进行了修改,那就进行持久化操作
save 300 10
# 如果60s内,如果至少有10000个Key进行了修改,那就进行持久化操作
save 60 10000
# 持久化如果出错了,是否还需要继续工作
stop-writes-on-bgsave-error yes
# 是否压缩rdb文件,需要消耗一些cpu资源
rdbcompression yes
# 保存rdb文件的时候,进行错误检查校验
rdbchecksum yes
# RDB保存的持久化文件名
dbfilename dump.rdb
# rdb文件保存目录
dir ./
#复制,主从复制需要用到
################################# REPLICATION #################################
# slaveof <主服务器ip> <主服务器端口>
# slaveof <masterip> <masterport>
# masterauth <主服务器Redis密码>
# masterauth <master-password>
# 当slave丢失master或者同步正在进行时,如果发生对slave的服务请求
# yes则slave依然正常提供服务
# no则slave返回client错误:"SYNC with master in progress"
slave-serve-stale-data yes
# 指定slave是否只读
slave-read-only yes
# 无硬盘复制功能
repl-diskless-sync no
# 无硬盘复制功能间隔时间
repl-diskless-sync-delay 5
# 从服务器发送PING命令给主服务器的周期
# repl-ping-slave-period 10
# 超时时间
# repl-timeout 60
# 是否禁用socket的NO_DELAY选项
repl-disable-tcp-nodelay no
# 设置主从复制容量大小,这个backlog 是一个用来在 slaves 被断开连接时存放 slave 数据的 buffer
# repl-backlog-size 1mb
# master 不再连接 slave时backlog的存活时间。
# repl-backlog-ttl 3600
# slave的优先级
slave-priority 100
# 未达到下面两个条件时,写操作就不会被执行
# 最少包含的从服务器
# min-slaves-to-write 3
# 延迟值
# min-slaves-max-lag 10
#安全
################################## SECURITY ###################################
# 可以在配置文件里设置redis数据库密码
# requirepass foobared
#限制客户端
################################### LIMITS ####################################
# 最多同时有10000个客户端连接过来
# maxclients 10000
# 最大的内存容量
# maxmemory <bytes>
# 内存到达上限后,采取的策略。LRU算法,最近最少使用算法
# maxmemory-policy noeviction
# 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 ###############################
# 默认不开启aof模式,默认是rdb方式持久化,因为大部分情况下,rdb完全够用。
appendonly no
# 持久化文件的名字
appendfilename "appendonly.aof"
# appendfsync always #每次修改都会sync,消耗性能
appendfsync everysec #每秒都执行一次sync,可能会丢失这1s的数据
# appendfsync no #不执行sync,操作系统自己同步数据,速度最快
# no: don't fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log . Slow, Safest.
# everysec: fsync only one time every second. Compromise.
七、持久化
redis是内存数据库,如果不将内存中的数据保存到磁盘,一旦服务器进程退出或者断电,那么内存数据库中保存的数据就将丢失。所以redis提供持久化功能。
rdb & aof 合集参考文章: juejin.im/post/684490…
RDB(redis database)
什么是RDB
在指定的时间间隔内将内存中的数据写入磁盘。也就是snapshot快照,它恢复时是将快照文件直接读到内存里。
过程
(bgsave命令或配置文件触发条件后的做法)Redis会单独创建(fork)一个子进程来进行持久化,会将数据写入到一个临时文件中(cowpy-on-write机制),等持久化过程结束后,再用这个临时文件替换上次持久化好的文件。整个过程,主进程是不参与任何IO操作的。确保了redis的可用。如果需要进行大规模数据的备份,且对数据完整性不是非常敏感,那么RDB方式比AOF方式要高效。redis默认就是RDB的持久方式。 注:生产环境下,我们需要备份下这个dump.rdb持久化文件,以保证不可预知的数据丢失情况。
rdb保存的持久化文件默认名,可在配置文件中修改
如何恢复rdb文件
- 只需要将dump.rdb文件放在配置文件中定义好的,rdb文件默认放置目录下即可。redis启动时会自动检查dump.rdb文件,并恢复其中的数据。
- 查看需要存放的位置,也是redis默认放置目录。
127.0.0.1:6379> config get dir
1) "dir"
2) "D:\\redis"
参考文章: juejin.im/post/684490…
AOF(Append Only File)
默认保存文件为:appendonly.aof
参考文章(重要): juejin.im/post/684490…
八、redis订阅发布
- redis的发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。例如:微信公众号、微博、关注系统等
- redis客户端可以订阅任意数量的频道。
订阅发布消息图:
命令(来自菜鸟教程)
这些命令被广泛用于构建即时通信应用,比如网络聊天室、实时广播、实时提醒等。
#以下实例演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 redisChat:
redis 127.0.0.1:6379> SUBSCRIBE redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
现在,我们先重新开启个 redis 客户端,然后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。
redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"
(integer) 1
redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"
(integer) 1
# 订阅者的客户端会显示如下消息
1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by runoob.com"
九、主从复制
一般来说,生产上只使用一台redis服务器是不可取的,原因如下:
对于多读少写的场景可以使用如下架构:
建议:主从复制,集群最少使用3台,一主二从。
环境配置
只配置从库,不用配置主库。
#info 可以查看所有redis当前的状态信息
#info Replication 可以查看当前redis当前的主从复制信息
127.0.0.1:6379> info Replication
# Replication
role:master #角色 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
#在从机上配置master信息
slaveof 192.168.10.1 7379
#选举自己成为master
#slaveof no one
#适用模型[master-slave(master)-slave,第一个master宕机后,它的slave自己选举自己成为master]
(临时命令)配置方法:
(永久)配置方法:
通过配置文件,见上面配置文件参数解析处。
特点
- 在master-redis上写数据,slave-redis会自动复制数据。
- 在slave-redis上写数据,会被拒绝,因为slave-redis只能读。
- master-redis宕机后,slave-redis依旧可以读,但也只能读,不能写;master-redis恢复后,一切照常。
主从复制参考:
juejin.im/post/684490…
十、哨兵模式
哨兵模式 简单测试
优点:
- 哨兵集群,基于主从复制模式
- 主从自动切换,故障自动转移,可用性高,健壮性高
缺点:
- 在线扩容困难
- 哨兵模式配置复杂
哨兵模式全部配置
十一、缓存穿透和雪崩
redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的是数据一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,就不能使用缓存。 另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也有比较流行的解决方案。
缓存穿透(查不到)
概念
用户想要查询一个数据,发现redis数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这就会给持久层数据库造成极大的压力,就出现缓存穿透。
缓存空对象的问题:
- 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键。
- 即使对空值设置了过期时间,还是会导致缓存层和存储层的数据有一段时间窗口的不一致。
缓存击穿(查太多,缓存过期)
概述
- 注意和缓存穿透的区别。缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就会穿透缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
- 当某个Key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并回写缓存,会导致数据库瞬间压力过大。
解决方案
- 设置热点数据永不过期 从缓存层面来看,没有设置过期时间就不会有后续的问题。
- 加互斥锁 分布式锁:使用分布式锁,保证对于每个Key同时只有一个线程去 查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁上,因此对分布式锁的考验很大。
缓存雪崩
概念
解决方案
大部分资料来源:www.bilibili.com/video/BV1S5…