笔记三

110 阅读14分钟

一 redis

1.1 数据解构

Redis中基本的五种数据结构,分别是字符串string、哈希hash、列表list、集合set、有序集合zset

string 应用场景 :【对象缓存,Session共享,分布式锁,计数器】简单的 set

hash哈希应用场景:【购物车,对象缓存,当数据库】hset

list 列表应用场景:【栈,队列,阻塞队列,信息流】lpush rpush

set集合应用场景:【抽奖,点赞】sadd key member [member ...]:在集合中增加一个或多个元素;

zset有序集合应用场景:【附近的人】

简单的理解就是一个Key-Value数据库,即字典形式存储,归类于NoSql(通常理解为“Not Only SQL”)数据库;

为什么要用Redis?

  • 开源:除了使用没障碍,开源会促进其发展;
  • 快:操作数据快,性能高;
  • 扩展性好:根据需要很容易进行扩展,集群、主从复制等;
  • 比较活跃:文档详细,版本维护及时,社区讨论都很活跃;

端口默认6379

操作命令

redis-cli.exe -h 127.0.0.1 -p 6379
set mykey kkkk 设置
get mykey  取值
select 6  切换数据库
Flushdb清除当前库中的数据
Flushall清除所有库中的数据

list

着重分享以下命令:

lpush key value [value1 ...] :在指定key的列表左边插入一个或多个值;

rpush key value [value1 ...] :在指定key的列表右边插入一个或多个值;

lpop key :从指定key的列表左边取出第一个值;

rpop key:从指定key的列表右边取出第一个值;

lrange key start end:从指定key列表中获取指定区间内的数据;

blpop key [key1 ...] timeout:从指定key列表中左边取出第一个值,若列表中没有元素就等待timeout时间,如果timeout为0就一直等待。

brpop key [key1 ...] timeout:从指定key列表中右边取出第一个值,若列表中没有元素就等待timeout时间,如果timeout为0就一直等待。

lset key index value:将指定下标的值更新为value,

img

set

set命令一般以s开头,里面元素无序且不重复,着重分享以下命令:

sadd key member [member ...]:在集合中增加一个或多个元素;

srem key member [member ...]:从集合中删除一个或多个元素;

smembers key:获取集合中的所欲元素;

scard key:获取集合中的元素个数;

sismember key member:判断指定member是否在集合中;

srandmember key [count]:从集合中获取count个元素,不从集合中删除;

spop key [count]:从集合中获取count个元素,从集合中删除

sinter key [key1 ...]:指定多个集合进行交集运算;

sinterstore dest key [key1 ...]:指定多个集合进行交集运算,存入dest集合;

sunion key [key1 ...]:指定多个集合进行并集运算;

sunionstore dest key [key1 ...]:指定多个集合进行并集运算,存入dest集合;

sdiff key [key1 ...]:指定多个集合进行差集运算;

sdiffstore dest key [key1 ...]:指定多个集合进行差集运算,并存入dest集

zset

zset的命令一般以z开头,里面元素是有序不可重复的。和Set用法基本一样,只是每个元素中多了一个分值,用于元素排序。

zadd key score member [(score member)...]:往有序集合中添加带分值的元素;

zrem key member [member...]:从有序集合中删除成员;

zscore key member:返回集合中指定成员的分值;

zcard key:统计集合中元素个数;

zrange key start stop [withscores]:返回指定范围的元素,withscores代表返回的元素包含对应的分值。

zreverange key start stop [withscores]:返回指定范围的倒序元素,withscores代表返回的元素包含对应的分值。

同set一样也可以进行交集、并集、差集的集合运算。

www.cnblogs.com/zoe-zyq/p/1… 命令操作

参考:www.cnblogs.com/zoe-zyq/p/1…

参考:redisdoc.com/string/

www.runoob.com/redis/redis…

事务特性(ACID)

  • 原子性(Atomicity)

    指事务内所有操作要么一起执行成功,要么都一起失败(或者说是回滚);如事务经典转账案例:A给B转账,A把钱扣了,但B没有收到;可见这种错误是不能接受的,最终会回滚,这也是原子性的重要性。

  • 一致性(Consistency)

    指事务执行前后的状态一致,如事务经典转账案例:A给B互相转账,不管怎么转,最终两者钱的总和还是不变;

  • 持久性(Durability)

    指事务一旦提交,数据就已经永久保存了,不能再回滚;

  • 隔离性(Isolation)

    指多个并发事务之间的操作互不干扰,但是事务的并发可能会导致数据脏读、不可重复读、幻读问题,根据业务情况,采用事务隔离级别进行对应数据读问题处理。

事务隔离级别

  • 读未提交(Read uncommitted)

    指一个事务读取到其他未提交事务的数据。可能导致数据脏读

    转账案例:A正在给B转账,本来转的1000,A多输入了个0,变成10000,但此事务还未提交,但此时B查询到转入的是10000,但A取消事务回滚之后,B又查询不到转入的数据。这种情况就是脏读

  • 读已提交(Read committed)

    指一个事务只能读取到其他事务已提交的数据,从而解决了脏读的问题。但可能导致数据不可重复读

    转账案例:A要给B转账1000,A先查看了一下余额,有1000,然后开始给B转钱,但此时A家里电费通过开启的自动缴费功能,自动从A账户扣除200缴纳电费,并提交;当A转账准备提交,再次确认余额时,钱少了200。这样就导致同一个事务中多次查询的结果不一致,这种情况就是不可重复读

  • 可重复读(Repeatable read)

    指事务只要一开启,就不允许其他事务进行修改操作,从而解决了不可重复读问题。但可能导致数据幻读

    转账案例:A经常给B转账,到年底了,需要查账,然后开启了一个事务进行查询统计,刚开始查询只是10条转账记录,正准备统计时,因为紧急情况A需要给B转一笔钱应急,从而新增了一条新记录,并提交;而查账事务正在统计中,最后发现转账额和看到的10条转账记录不匹配。这种情况就是幻读

  • 序列化(Serializable )

    指事务之间只能串行话执行,就像队列一样,排队进行,这样就解决了幻读的问题,但是这种级别的并发性能不高,非特殊需求,这种级别一般不用。

Redis事务优缺点

优点

  • 一次性按顺序执行多个Redis命令,不受其他客户端命令请求影响;
  • 事务中的命令要么都执行(命令间执行失败互相不影响),要么都不执行(比如中间有命令语法错误);

缺点

  • 事务执行时,不能保证原子性;
  • 命令入队每次都需要和服务器进行交互,增加带宽;

注意

  • 当事务中命令语法使用错误时,最终会导致事务执行不成功,即事务内所有命令都不执行;
  • 当事务中命令知识逻辑错误,就比如给字符串做加减乘除操作时,只能在执行过程中发现错误,这种事务执行中失败的命令不影响其他命令的执行。

www.cnblogs.com/zoe-zyq/p/1… redis 事务

redis 持久化

第一种 是快照形式,rdb

第二种是日志形式,aof

参考:www.cnblogs.com/zoe-zyq/p/1…

redis 集群

集群:多搞几台服务器,让请求/命令平均分发到各个服务器,避免单台服务器承载过大压力;对于Redis集群来说,为了实现自动故障转移,还需要在每个主节点上增加一个或多个从节点,当主节点发生故障时,从节点自动补上,实现高可用。

总的来说,Redis集群有以下作用:

  • 多主节点的实现可以应对高并发场景,并发量增大,节点可以随时扩展满足需求;
  • 多主节点的实现可以存储更多的数据,因为数据均匀分布到各个节点;
  • 多主节点搭配多从节点的实现让高可用更加稳定,即当有主节点发生故障时,对应下面的从节点会升级为主节点,正常提供功能;

Redis集群中最少需要3个主节点,再加上为了实现高可用,每个主节点至少得跟一个从节点,不然一个主节点挂了,找不到完整的数据,整个集群就不能用了;至于为什么会找不到完整的数据,下面会聊到。

接下来要搭建的集群环境如下:

image-20210202112246300

简要说明:

  • 6370为主节点,6381为6370的从节点;
  • 6380为主节点,6391为6380的从节点;
  • 6390为主节点,6371为6390的从节点;
  • 在集群环境中主节点之间是相互通讯的(这里没有哨兵),每一个节点都是数据节点;

参考:www.cnblogs.com/zoe-zyq/p/1…

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

1. 缓存穿透

1.1 简要描述

缓存穿透是指查找的数据在缓存和数据库中都不存在,导致每一次请求数据从缓存中都获取不到,而将请求打到数据库服务器,但数据库中也没有对应的数据,最后每一次请求都到数据库;如果在高并发场景或有人恶意攻击,就会导致后台数据库服务器压力增大,最终系统可能崩掉。来个直接点的图:

image-20210226101420703

简要说明:

缓存Redis服务器颜色说明:绿色块代表有缓存数据,粉色块代表缓存中没有数据;绿色箭头代表直接从缓存中获取数据;黄色箭头代表穿过缓存从数据库中查数据,但不一定有。

流程大概如下:

  1. 大量客户端发起大量请求到服务器;
  2. 服务器代码逻辑将先经过缓存,如果有缓存数据(绿色部分),直接从缓存中获取数据数据返回;如果缓存中没有数据(粉色部分),请求就会直接打到数据库服务器(如黄色箭头)。
  3. 如果存在大量无缓存数据的请求,最终数据库将因为过大压力而崩掉,导致系统不可用。

解决:缓存空值,布隆过滤器

2. 缓存雪崩

1.1 简要描述

缓存雪崩是指突然缓存层不可用,导致大量请求直接打到数据库,最终由于数据库压力过大可能导致系统崩掉。缓存层不可用指以下两方面:

  • 缓存服务器宕机,系统将请求打到数据库;
  • 缓存数据突然大范围集中过期失效,导致大量请求打到数据库重新加载数据;

如图:

image-20210226122232085

简要说明:

缓存Redis服务器颜色说明:绿色块代表有缓存数据,粉色块代表缓存中没有数据;白色块代表大范围失效的缓存数据,绿色箭头代表直接从缓存中获取数据;黄色箭头代表穿过缓存从数据库中查数据。

流程大概如下:

  1. 大量客户端发起大量请求到服务器;
  2. 服务器代码逻辑将先经过缓存,如果有缓存数据(绿色部分),直接从缓存中获取数据数据返回;如果缓存过期(白色块部分),请求就会直接打到数据库服务器**(如黄色箭头)**。
  3. 如果存在大量热数据的请求,但热数据又大范围过期,最终数据库将因为过大压力崩掉,导致系统不可用。

解决办法:缓存预热,均匀设置过期时间,多级缓存,限流,降级,加互斥锁

3. 缓存击穿

1.1 简要描述

缓存击穿是指在超级热点数据突然过期,导致针对超级热点的数据请求在过期期间直接打到数据库,这样数据库服务器会因为某一超热数据导致压力过大而崩掉。

超热数据:比如秒杀时的数据,某宝、某东、某多多这种平台的数据如果在秒杀时间段失效,请求量足矣让数据库崩掉。

如图:

image-20210226123740394

简要说明:

缓存Redis服务器颜色说明:绿色块代表有缓存数据,粉色块代表缓存中没有数据;白色圈代表超级热点缓存数据过期失效,绿色箭头代表直接从缓存中获取数据;黄色箭头代表穿过缓存从数据库中查数据。

流程大概如下:

  1. 大量客户端发起大量请求到服务器;
  2. 服务器代码逻辑将先经过缓存,如果有缓存数据(绿色部分),直接从缓存中获取数据数据返回;如果超热缓存数据过期(白色圈部分),请求就会直接打到数据库服务器**(如黄色箭头)**。
  3. 超级热点数据过期失效,如秒杀数据,如果在秒杀时段失效,最终数据库将因为过大压力崩掉,导致系统不可用。

注:这个只是针对超热点数据,而不是大范围数据。

解决办法:热点数据不过期,加互斥锁

缓存穿透、缓存雪崩、缓存击穿不管是哪个问题,其主要原因还是在缓存层没有命中,将请求直接打到数据库啦,最终导致数据库压力过大,系统不可用。小伙伴根据系统需要进行问题处理,没有完美的解决方案,但总会有一种适合需求的方案,解决业务问题才是真正目的。

参考:www.cnblogs.com/zoe-zyq/p/1…

二 Docker

2.1 安装

参考:www.cnblogs.com/xiaohuasan/…

www.cnblogs.com/zoe-zyq/p/1…

netcore 部署 参考:blog.csdn.net/qq_21275565…

mp.weixin.qq.com/s?__biz=MzU…

打包镜像

docker build -t mydemo .

后面的点不要忘了

docker build -t myimage:v1.0 .解析

  • -t:指定镜像的名字及标签,通常 name:tag 或者 name 格式,myimage就是镜像名字,v1.0就是tag;
  • -f :指定要使用的Dockerfile路径,这里由于Dockerfile在当前路径,所以不用指定;
  • 最后面的点官方称为构建上下文,点表示指定为当前目录。 会把指定的这个目录下的文件发送给docker daemon进行构建,所以千万不要指定/(斜杠代表根目录,有很多文件的)。
  • 其他选项参数小伙伴可以根据需要使用,以上是比较重要的。

运行

docker run -d --name xdemo -p 9999:80 mydemo

-d:后台模式运行;

--name:给运行中的容器指定一个名字;

-p:指定端口映射, 主机的端口9999映射到容器的端80,因为在容器里面我们的项目是以80 端口启动的;

最后一个参数是上一步生成的镜像名称, 即根据此镜像启动一个容器实例。

遇到的问题解决方案

Error response from daemon: driver failed programming external connectivity on endpoint XXX(端口映射或启动容器时报错)

blog.csdn.net/shz_123/art…

www.cnblogs.com/pywen/p/161…

docker 容器一些删除命令

#所有镜像和容器都删除的命令
docker system prune -a

#查看镜像
docker images 

#删除单个镜像
docker rmi -f  <镜像id>

#删除所有镜像,不删除容器
docker rmi $(docker images -q)

删除容器
首先需要停止所有的容器(只停止单个时把后面的变量改为image id即可)
docker stop $(docker ps -a -q)

删除所有的容器(只删除单个时把后面的变量改为image id即可)
docker rm $(docker ps -a -q)

#查看镜像
docker images

#删除镜像名称
docker rmi 镜像ID

#查看运行中的容器
docker ps 

#查看所有容器
docker ps -a

#强制删除容器
docker rm -f 容器ID

参考:blog.csdn.net/qq_46429858…

2.2 docker 常用命令

docker version 查看版本
docker info 查看信息
docker images -a 列出镜像信息 
docker iamges -ap 只显示镜像ID
docker pull 拉取远程仓储中的镜像
docker pull 镜像名:tag:指定版本拉取;
docker rmi:删除指定镜像,后面可以跟名称或镜像ID

启动和停止容器

 docker start 容器id  # 启动被停止的容器
 docker stop 容器id  # 停止运行中的容器
 docker restart 容器id # 重启容器
 docker kill 容器id  # 强制停止容器

www.cnblogs.com/zoe-zyq/p/1…

2.3 丰富化Dockefile文件内容并查看构建之后的细节

 # 指定基础镜像,在此基础上构建自己的项目镜像
 FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
 # 指定维护人
 MAINTAINER CodeZYQ<1137533407@qq.com>
 # 打标签
 LABEL createname="CodeZYQ"
 # 指定自己的工作目录,进入容器时目录 app
 WORKDIR /myapp
 # 将构建上下文目录下的文件拷贝到容器中的工作目录中
 COPY . .
 # 定义变量
 ARG myPort=8080
 # 使用环境变量方式改变启动端口,拼接用到了定义的变量
 ENV ASPNETCORE_URLS=http://+:$myPort
 # 通过RUN 执行相关命令,根据需要执行相关命令
 RUN mkdir testDir
 # 挂载数据卷,这里模拟挂载日志目录
 VOLUME /Logs
 # 容器向外暴露端口,项目以什么端口启动就暴露对应的端口
 EXPOSE $myPort
 # 执行命令,这里默认是以80端口启动的
 # 就类似于在Linux系统的项目目录下执行 dotnet DockerfileDemo.dll 是一样的
 ENTRYPOINT ["dotnet", "DockerfileDemo.dll"]

参考:www.cnblogs.com/zoe-zyq/p/1…

2.4 Docker Compose 使用

参考:www.cnblogs.com/zoe-zyq/p/1…