Bläddra i källkod

Merge branch 'lab' of https://gitee.com/sun1040084806/notes into lab

seamew 3 år sedan
förälder
incheckning
38fb69c95c
77 ändrade filer med 3310 tillägg och 11 borttagningar
  1. 2 0
      linux/linux服务器配置/centos网络配置.md
  2. 23 4
      云原生/K8S/16.强制删除ns.md
  3. BIN
      前端/前端部署问题/assets/image-20220422184600670.png
  4. BIN
      前端/前端部署问题/assets/image-20220422192911885.png
  5. BIN
      前端/前端部署问题/assets/image-20220422193518653.png
  6. BIN
      前端/前端部署问题/assets/image-20220422200013867.png
  7. BIN
      前端/前端部署问题/assets/image-20220422201018474.png
  8. BIN
      前端/前端部署问题/assets/image-20220422203623997.png
  9. BIN
      前端/前端部署问题/assets/image-20220422203636691.png
  10. BIN
      前端/前端部署问题/assets/image-20220422203653688.png
  11. BIN
      前端/前端部署问题/assets/image-20220428112117780.png
  12. BIN
      前端/前端部署问题/assets/image-20220428112143281.png
  13. BIN
      前端/前端部署问题/assets/image-20220428112325615.png
  14. BIN
      前端/前端部署问题/assets/image-20220428112442031.png
  15. 189 0
      前端/前端部署问题/自动化部署.md
  16. 51 0
      后端/Java/java23种设计模式/java23种设计模式.md
  17. 30 0
      后端/Java/java23种设计模式/工厂模式.md
  18. 45 0
      后端/Java/java23种设计模式/模版模式.md
  19. 37 0
      后端/Java/java23种设计模式/策略模式.md
  20. 64 0
      后端/Java/java23种设计模式/责任链模式.md
  21. 1 1
      后端/Spring/SpringBoot/springboot.md
  22. 51 0
      后端/redis/1.redis数据结构原理.md
  23. 89 0
      后端/redis/2.redis持久化与集群机制.md
  24. 96 0
      后端/redis/3.redis淘汰策略和事务.md
  25. 47 0
      后端/redis/4.基于redis实现分布式锁.md
  26. 96 0
      后端/redis/5.redis哨兵模式.md
  27. 86 0
      后端/redis/6.Redis Cluster集群.md
  28. 45 0
      后端/redis/7.布隆过滤器.md
  29. BIN
      后端/redis/assets/1363376-20210622212732123-1297981656.png
  30. BIN
      后端/redis/assets/20200513215523258.png
  31. BIN
      后端/redis/assets/copycode.gif
  32. BIN
      后端/redis/assets/image-20220517095951464.png
  33. BIN
      后端/redis/assets/image-20220517100347511.png
  34. BIN
      后端/redis/assets/image-20220517101228747.png
  35. BIN
      后端/redis/assets/image-20220517101336924.png
  36. BIN
      后端/redis/assets/image-20220517111032308.png
  37. BIN
      后端/redis/assets/image-20220517145307564.png
  38. BIN
      后端/redis/assets/image-20220517145312078.png
  39. BIN
      后端/redis/assets/image-20220518101146275.png
  40. BIN
      后端/redis/assets/image-20220518102710826.png
  41. BIN
      后端/redis/assets/image-20220518103755786.png
  42. BIN
      后端/redis/assets/image-20220518103759533.png
  43. BIN
      后端/redis/assets/image-20220518103925266.png
  44. BIN
      后端/redis/assets/image-20220518104417240.png
  45. BIN
      后端/redis/assets/image-20220519150111752.png
  46. BIN
      后端/redis/assets/image-20220519150141092.png
  47. BIN
      后端/redis/assets/image-20220519150149697.png
  48. BIN
      后端/redis/assets/image-20220519161122696.png
  49. BIN
      后端/redis/assets/image-20220519161138514.png
  50. BIN
      后端/redis/assets/image-20220520151507939.png
  51. BIN
      后端/redis/assets/image-20220520151518945.png
  52. BIN
      后端/redis/assets/image-20220520151521608.png
  53. BIN
      后端/redis/assets/image-20220520152007648.png
  54. BIN
      后端/redis/assets/image-20220520152042159.png
  55. BIN
      后端/redis/assets/image-20220520153051951.png
  56. BIN
      后端/redis/assets/image-20220520153115865.png
  57. BIN
      后端/redis/assets/image-20220520164337367.png
  58. BIN
      后端/redis/assets/image-20220520164434639.png
  59. BIN
      后端/redis/assets/image-20220520164601716.png
  60. BIN
      后端/redis/assets/image-20220520164627927.png
  61. BIN
      后端/redis/assets/image-20220522144243791.png
  62. BIN
      后端/redis/assets/image-20220522155302372.png
  63. BIN
      后端/redis/assets/image-20220522161015869.png
  64. BIN
      后端/redis/assets/image-20220522162257224.png
  65. BIN
      后端/redis/assets/image-20220522162300489.png
  66. BIN
      后端/redis/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1MjIzMTU5NTc0Mg==,size_16,color_FFFFFF,t_70.png
  67. 1914 0
      后端/redis/redis.md
  68. 2 2
      大数据/kafka/1.kafka笔记.md
  69. BIN
      大数据/zookeeper/assets/image-20220424134137986.png
  70. BIN
      大数据/zookeeper/assets/image-20220424134506741.png
  71. BIN
      大数据/zookeeper/assets/image-20220509154618465.png
  72. BIN
      大数据/zookeeper/assets/image-20220510102509795.png
  73. BIN
      大数据/zookeeper/assets/image-20220510102714102.png
  74. 264 0
      大数据/zookeeper/zookeeper.md
  75. 13 0
      煤矿项目问题汇总/华为云.md
  76. 7 4
      部署文档/大数据平台/大数据平台环境搭建.md
  77. 158 0
      部署文档/大数据平台/脚本.md

+ 2 - 0
linux/linux服务器配置/centos网络配置.md

@@ -58,8 +58,10 @@ yum install wget -y
 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
 # 2.下载
 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
+yum install -y epel-release
 # 或者
 curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
+yum install -y epel-release
 # 3.清理缓存
 yum clean all     # 清除系统所有的yum缓存
 yum makecache     # 生成yum缓存

+ 23 - 4
云原生/K8S/16.强制删除ns.md

@@ -1,10 +1,29 @@
-```
+```shell
 kubectl delete ns --grace-period=0 –force 
 
-kubectl get namespace <terminating-namespace> -o json >tmp.json
+kubectl get namespace kube-system -o json >tmp.json
 
 kubectl proxy
 
-curl -k -H "Content-Type: application/json" -X PUT --data-binary @tmp.json http://127.0.0.1:8001/api/v1/namespaces/istio-system/finalize
+curl -k -H "Content-Type: application/json" -X PUT --data-binary @tmp.json http://127.0.0.1:8001/api/v1/namespaces/kube-system/finalize
+
+kubectl -n kube-system  get pods | grep Evicted |awk '{print$1}'|xargs kubectl -n argo delete pods
+```
+
+```shell
+kubeadm reset -f
+modprobe -r ipip
+lsmod
+rm -rf ~/.kube/
+rm -rf /etc/kubernetes/
+rm -rf /etc/systemd/system/kubelet.service.d
+rm -rf /etc/systemd/system/kubelet.service
+rm -rf /usr/bin/kube*
+rm -rf /etc/cni
+rm -rf /opt/cni
+rm -rf /var/lib/etcd
+rm -rf /var/etcd
+yum clean all
+yum remove kube*
+```
 
-```

BIN
前端/前端部署问题/assets/image-20220422184600670.png


BIN
前端/前端部署问题/assets/image-20220422192911885.png


BIN
前端/前端部署问题/assets/image-20220422193518653.png


BIN
前端/前端部署问题/assets/image-20220422200013867.png


BIN
前端/前端部署问题/assets/image-20220422201018474.png


BIN
前端/前端部署问题/assets/image-20220422203623997.png


BIN
前端/前端部署问题/assets/image-20220422203636691.png


BIN
前端/前端部署问题/assets/image-20220422203653688.png


BIN
前端/前端部署问题/assets/image-20220428112117780.png


BIN
前端/前端部署问题/assets/image-20220428112143281.png


BIN
前端/前端部署问题/assets/image-20220428112325615.png


BIN
前端/前端部署问题/assets/image-20220428112442031.png


+ 189 - 0
前端/前端部署问题/自动化部署.md

@@ -0,0 +1,189 @@
+## 1、启动docker容器
+
+![image-20220422184600670](assets/image-20220422184600670.png)
+
+1. 首先创建一个容器,导出默认的配置文件
+
+```shell
+docker run -d --name=test nginx
+
+docker cp test:/etc/nginx ~/conf
+```
+
+2. 删除刚刚的容器,并且建立项目路径
+
+```shell
+docker rm -f test 
+
+mv conf  ./front/blog/
+
+mkdir front/blog/html
+```
+
+3. 启动容器挂载目录
+
+```shell
+docker run -d -p 80:80 -p 443:443 --restart=on-failure:5 --name=blog -v /root/front/blog/html:/usr/share/nginx/html -v /root/front/blog/conf:/etc/nginx nginx
+```
+
+## 2、全自动部署
+
+这里会涉及的配置有:
+
+- 我们开发机需要配置连接服务器的免密登录
+- 设置 github 仓库的私钥为本地开发机的私钥
+- 项目根目录下创建`.github/workflows/publish.yml`
+- 服务器配置 nginx 或者配置 docker
+
+### 为什么要配置免密登录?
+
+这里有个细节,这里的免密登录并不是代表我们在所有的电脑连接服务器都不需要输入密码,而是我们开发机连接服务器不需要输入密码,当我们连接的时候会自动的以我们开发机的 ssh 私钥作为密码进行匹配,如果匹配成功就会登录成功,因为这个过程是自动的,所以看起来跟不用输入密码一样,实际上是需要的。
+
+### github 仓库私钥设置了干什么?
+
+github 会以我们开发机的私钥作为密码去连接我们的服务器,服务器只看私钥,它发现私钥是正确的,就会给予通过,我们就能访问成功连接服务器,就可以传输文件了。
+
+### 示例
+
+项目结构
+
+![image-20220422192911885](assets/image-20220422192911885.png)
+
+.github/workflows/publish.yml
+
+```yml
+name: 打包vuepress博客
+
+on:
+  push:
+    # push 代码的时候 哪个分支会受到影响 这里是 main 主分支
+    branches:
+      - main
+
+# 推送之后执行一系列的任务
+jobs:
+  build:
+    # 运行 ubuntu虚拟机系统
+    runs-on: ubuntu-latest
+    steps:
+      # 获取代码
+      - name: 迁出代码
+        # 使用action库
+        uses: actions/checkout@master
+      # 安装Node10
+
+      - name: 安装node.js
+        # 使用action库  actions/setup-node安装node
+        uses: actions/setup-node@v1
+        with:
+          node-version: 14.18.0
+
+      - name: 安装yarn
+        run: npm install -g yarn
+
+      # 安装依赖
+      - name: 安装依赖
+        run: yarn
+
+      # 打包
+      - name: 打包
+        run: yarn docs:build
+
+      # 上传到自己的服务器
+      - name: 发布到百度云
+        uses: easingthemes/ssh-deploy@v2.1.1
+        env:
+          # 私钥 PRIVATE_KEY 要和 仓库的私钥名一致 也就是私钥名也要叫 PRIVATE_KEY
+          SSH_PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
+          # SCP参数
+          ARGS: "-avzr --delete"
+          # 源目录 -- 打包后的文件目录,也就是这个文件会被传到服务器上
+          SOURCE: "docs/.vuepress/dist/*"
+          # 服务器ip
+          REMOTE_HOST: "你的IP"
+          # 用户
+          REMOTE_USER: "root"
+          # 目标地址 -- 上传到服务器的地址
+          TARGET: "/root/front/blog/html"
+
+```
+
+在仓库中设置私钥
+
+![image-20220422193518653](assets/image-20220422193518653.png)
+
+配置好以上的操作就可以,当我们代码写好了之后执行`git push`就可以前往 github 仓库下的`Actions`栏目下看进度了,这是个图形化的优雅界面,非常的 NICE
+
+![image-20220422200013867](assets/image-20220422200013867.png)
+
+之后就可以前往服务器中查看指定目录下是否含有我们打包后的文件了,如果前面的步骤都没有报错,那么是肯定会有的。
+
+![image-20220422203623997](assets/image-20220422203623997.png)
+
+最后通过浏览器访问
+
+![image-20220422203653688](assets/image-20220422203653688.png)
+
+## 3、配置https访问
+
+1. 申请证书
+
+![image-20220428112143281](assets/image-20220428112143281.png)
+
+2. 这里购买阿里云的证书,填写好个人信息后就可以申请成功了
+
+![image-20220428112325615](assets/image-20220428112325615.png)
+
+然后选择下载对应的证书,我这里选择的是nginx
+
+![image-20220428112442031](assets/image-20220428112442031.png)
+
+3. 将下载的证书上传到服务器
+
+```shell
+mkdir cert
+
+# 将下载好的证书上传到服务器
+
+# 配置default.conf
+server {
+    listen       80;
+    listen  [::]:80;
+    server_name  www.seamew.top;
+	rewrite ^(.*)$ https://$host$1; #将所有HTTP请求通过rewrite指令重定向到HTTPS。
+    location / {
+        root   /usr/share/nginx/html;
+        index  index.html index.htm;
+    }
+    error_page   500 502 503 504  /50x.html;
+    location = /50x.html {
+        root   /usr/share/nginx/html;
+    }
+}
+server {
+    listen 443 ssl;
+    server_name www.seamew.top; #需要将yourdomain替换成证书绑定的域名。
+    root html;
+    index index.html index.htm;
+    ssl_certificate cert/7688799_www.seamew.top.pem;  #需要将cert-file-name.pem替换成已上传的证书文件的名称。
+    ssl_certificate_key cert/7688799_www.seamew.top.key; #需要将cert-file-name.key替换成已上传的证书私钥文件的名称。
+    ssl_session_timeout 5m;
+    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
+    #表示使用的加密套件的类型。
+    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; #表示使用的TLS协议的类型。
+    ssl_prefer_server_ciphers on;
+    location / {
+        root   /usr/share/nginx/html;
+        index  index.html index.htm;
+    }
+}
+
+# 最后重启容器即可访问成功
+```
+
+4. 如果无法正常访问,请检查docker是否映射端口,并且检查服务器防火墙是否开启端口。
+
+## 总结
+
+通过**github action**是CICD的一种实现方式。但如果是前端的一些小网站,还是优选**github action**。
+

+ 51 - 0
后端/Java/java23种设计模式/java23种设计模式.md

@@ -0,0 +1,51 @@
+[TOC]
+
+## 1、为什么需要使用设计模式
+
+使用设计模式可以重构整体架构代码、提交代码复用性、扩展性、减少代码冗余问题。
+
+Java高级工程师必备的技能!
+
+ 
+
+## 2、设计模式六大原则
+
+### 2.1、开闭原则(Open Close Principle)
+
+开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
+
+### 2.2、里氏代换原则(Liskov Substitution Principle)
+
+里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
+
+封装 继承 多态 重写(模版方法设计模式中) 接口、抽象类
+
+## 2.3、依赖倒转原则(Dependence Inversion Principle)
+
+这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
+
+### 2.4、接口隔离原则(Interface Segregation Principle)
+
+这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
+
+### 2.5、迪米特法则(最少知道原则)(Demeter Principle)
+
+为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
+
+### 2.6、合成复用原则(Composite Reuse Principle)
+
+原则是尽量使用合成/聚合的方式,而不是使用继承。
+
+## 3、设计模式的分类
+
+### 3.1、创建型模式
+
+工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
+
+### 3.2、结构型模式
+
+适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
+
+### 3.3、行为模式
+
+策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

+ 30 - 0
后端/Java/java23种设计模式/工厂模式.md

@@ -0,0 +1,30 @@
+## 工厂模式
+
+1.工厂模式是为了解耦:把对象的创建和使用的过程分开。就是Class A 想调用Class B,那么只是调用B的方法,而至于B的实例化,就交给工厂类。
+
+2.工厂模式可以降低代码重复。如果创建B过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。可以把这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的维护。
+
+3.工厂模式可以减少错误,因为工厂管理了对象的创建逻辑,使用者不需要知道具体的创建过程,只管使用即可,减少了使用者因为创建逻辑导致的错误。
+
+ 
+
+工厂模式可以分为简单工厂、工厂方法、抽象工厂、静态工厂模式
+
+## 工厂模式优缺点
+
+优点:
+
+代码结构简单。
+
+获取产品的过程更加简单。
+
+满足了开闭原则,即对拓展开放,对修改关闭。
+
+ 
+
+缺点:
+
+拓展较繁琐,要拓展时,需同时改动抽象工厂和工厂实现类。
+
+
+ 

+ 45 - 0
后端/Java/java23种设计模式/模版模式.md

@@ -0,0 +1,45 @@
+# 模版模式
+
+ 
+
+## 什么是模版方法
+
+1.定义了一个操作中的算法的骨架,而将部分步骤的实现在子类中完成。
+
+模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
+
+2.模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术,没有关联关系。 因此,在模板方法模式的类结构图中,只有继承关系。
+
+ 
+
+核心设计要点:
+
+AbstractClass : 抽象类,定义并实现一个模板方法。这个模板方法定义了算法的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类去实现
+
+ConcreteClass : 实现实现父类所定义的一个或多个抽象方法。
+
+ 
+
+## 模版方法应用场景
+
+1. 比如聚合支付平台中系统回调代码重构
+
+2. Servlet 请求
+
+
+
+## 模式模式优缺点
+
+1.)优点
+
+模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。子类实现算法的某些细节,有助于算法的扩展。通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
+
+2.)缺点
+
+每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。
+
+3.)适用场景
+
+在某些类的算法中,用了相同的方法,造成代码的重复。控制子类扩展,子类必须遵守算法规则。
+
+ 

+ 37 - 0
后端/Java/java23种设计模式/策略模式.md

@@ -0,0 +1,37 @@
+### 1、什么是策略模式
+
+策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。
+
+ 
+
+1.环境(Context)角色:持有一个Strategy的引用。
+
+2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
+
+3.具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
+
+ 
+
+定义策略接口->实现不同的策略类->利用多态或其他方式调用策略
+
+### 2、为什么叫做策略模式
+
+每个if判断都可以理解为就是一个策略。
+
+ 
+
+### 3、策略模式优缺点
+
+#### 3.1、优点
+
+算法可以自由切换(高层屏蔽算法,角色自由切换)
+
+避免使用多重条件判断(如果算法过多就会出现很多种相同的判断,很难维护)
+
+扩展性好(可自由添加取消算法 而不影响整个功能)
+
+#### 3.2、缺点
+
+策略类数量增多(每一个策略类复用性很小,如果需要增加算法,就只能新增类)
+
+所有的策略类都需要对外暴露(使用的人必须了解使用策略,这个就需要其它模式来补充,比如工厂模式、代理模式)

+ 64 - 0
后端/Java/java23种设计模式/责任链模式.md

@@ -0,0 +1,64 @@
+## 什么是责任链模式
+
+ 
+
+**客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象**。这样就实现了请求者和接受者之间的解耦,并且在客户端可以实现动态的组合职责链。使编程更有灵活性。
+
+ 
+
+定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。其过程实际上是一个递归调用。
+
+ 
+
+要点主要是:
+
+1、有多个对象共同对一个任务进行处理。
+
+2、这些对象使用链式存储结构,形成一个链,每个对象知道自己的下一个对象。
+
+3、一个对象对任务进行处理,可以添加一些操作后将对象传递个下一个任务。也可以在此对象上结束任务的处理,并结束任务。
+
+4、客户端负责组装链式结构,但是客户端不需要关心最终是谁来处理了任务。
+
+ 多个对象指的是什么意思?
+
+## 责任链模式类结构图
+
+ 
+
+ 1.抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。
+
+ 2.具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家
+
+## 责任链模式优缺点
+
+优点:
+
+职责链模式的最主要功能就是:动态组合,请求者和接受者解耦。
+
+请求者和接受者松散耦合:请求者不需要知道接受者,也不需要知道如何处理。每个职责对象只负责自己的职责范围,其他的交给后继者。各个组件间完全解耦。
+
+动态组合职责:职责链模式会把功能分散到单独的职责对象中,然后在使用时动态的组合形成链,从而可以灵活的分配职责对象,也可以灵活的添加改变对象职责。
+
+ 
+
+缺点:
+
+产生很多细粒度的对象:因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。
+
+不一定能处理:每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。
+
+## 责任链模式应用场景
+
+1. 多条件流程判断 权限控制
+
+2. ERP系统 流程审批 总经理、人事经理、项目经理
+
+3. Java过滤器的底层实现Filter 
+
+比如:在Java过滤器中客户端发送请求到服务器端,过滤会经过参数过滤、session过滤、表单过滤、隐藏过滤、检测请求头过滤
+
+## 责任链设计模式如何保证顺序问题? 
+
+使用链表数据结构,只需要获取链表头结点,就可以获取到所有的handler
+

+ 1 - 1
后端/Spring/SpringBoot/springboot.md

@@ -1441,7 +1441,7 @@ If you want to take complete control of Spring MVC, you can add your own @Config
 
 我们来仔细对照,看一下它怎么实现的,它告诉我们SpringBoot已经帮我们自动配置好了SpringMVC,然后自动配置了哪些东西呢?
 
-## 7.2、ContentNegotiatingViewResolver 内容协商视图解析器**
+## 7.2、ContentNegotiatingViewResolver 内容协商视图解析器
 
 自动配置了ViewResolver,就是我们之前学习的SpringMVC的视图解析器;
 

+ 51 - 0
后端/redis/1.redis数据结构原理.md

@@ -0,0 +1,51 @@
+## Redis缓存框架基本介绍
+
+Redis 是完全开源免费的,是一个高性能的key-value数据库,目前市面上主流的数据库
+
+Redis、Memcache、Tair(淘宝自研发)
+
+Redis的官网:https://redis.io/
+
+内存数据库(nosql数据库)、mysql、sqlserver
+
+关系数据库存放在硬盘中 查询实现io操作
+
+非关系数据库 Redis 持久化机制 淘汰策略
+
+Jvm内置缓存框架 ECACH os cache
+
+## Redis的应用场景
+
+1. Token令牌的生成
+
+2. 短信验证码Code
+
+3. 缓存查询数据
+
+4. 网页计数器
+
+5. 分布式锁
+
+6. 延迟操作
+
+## Redis单线线程模型
+
+首先Redis官方是没有windows版本的,只有redis版本
+
+Redis的底层采用Nio中的多路IO复用的机制,能够非常好的支持这样的并发,从而保证线程安全问题;
+
+Redis单线程,也就是底层采用一个线程维护多个不同的客户端io操作。
+
+但是Nio在不同的操作系统上实现的方式有所不同,在我们windows操作系统使用select实现轮训时间复杂度是为o(n),而且还存在空轮训的情况,效率非常低, 其次是默认对我们轮训的数据有一定限制,所以支持上万的tcp连接是非常难。
+
+所以在linux操作系统采用epoll实现事件驱动回调,不会存在空轮训的情况,只对活跃的 socket连接实现主动回调这样在性能上有大大的提升,所以时间复杂度是为o(1)
+
+注意:windows操作系统是没有epoll,只有linux系统才有epoll
+
+所以为什么nginx、redis都能够非常高支持高并发,最终都是linux中的IO多路复用机制epoll
+
+Redis底层采用nio epoll实现
+
+## redis默认情况下分成16个库
+
+**总结**:Redis实例默认建立了16个db,由于不支持自主进行数据库命名所以以dbX的方式命名。默认数据库数量可以修改配置文件的database值来设定。对于db正确的理解应为“命名空间”,多个应用程序不应使用同一个Redis不同库,而应一个应用程序对应一个Redis实例,不同的数据库可用于存储不同环境的数据。最后要注意,Redis集群下只有db0,不支持多db。

+ 89 - 0
后端/redis/2.redis持久化与集群机制.md

@@ -0,0 +1,89 @@
+## Redis持久化机制
+
+ Redis因为某种原因的情况下宕机之后,数据是不会丢失的。
+
+原理就是持久化
+
+EHCACHE
+
+大部分的缓存框架都会基本功能淘汰策略、持久机制
+
+Redis的持久化的机制有两种:aof、rdb(默认)
+
+## 全量同步与增量同步的区别
+
+全量同步:就是每天定时(避开高峰期)或者采用一个周期实现将数据拷贝到一个地方也就是Rdb存储。
+
+增量同步:比如采用对行为的操作实现对数据的同步,也就是AOF。
+
+全量与增量的比较:增量同步比全量同步更加消耗服务器的内存,但是能够更加的保证数据的同步。
+
+ 
+
+ 
+
+## RDB与AOF实现持久化的区别
+
+Redis提供了两种持久化的机制,分别为RDB、AOF实现,RDB采用定时(全量)持久化机制,但是服务器因为某种原因宕机后可能数据会丢失,AOF是基于数据日志操作实现的持久化,所以AOF采用增量同步的方案。
+
+ 
+
+Redis已经帮助我默认开启了rdb存储。
+
+ 
+
+## Redis的RDB与AOF同步配置
+
+## RDB
+
+Redis默认采用rdb方式实现数据的持久化,以快照的形式将数据持久化到磁盘的是一个二进制的文件dump.rdb, 在redis.conf文件中搜索“dump.rdb “。
+
+![image-20220519150111752](assets/image-20220519150111752.png)
+
+Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:
+
+save 900 1  #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
+
+save 300 10  #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
+
+save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
+
+![image-20220519150141092](assets/image-20220519150141092.png)
+
+如果直接替RDB 文件的时候就可能会出现突然断电等问题而导致RDB 文件还么有保存完整就突然关机,从而导致数据的丢失,可以手动将每次生成的RDB 文件进行备份,这样可以最大化保存历史数据。
+
+ 
+
+## Aof
+
+在Redis的配置文件中存在三种同步方式,它们分别是:
+
+appendfsync always   #每次有数据修改发生时都会写入AOF文件,能够保证数据不丢失,但是效率非常低。
+
+appendfsync everysec #每秒钟同步一次,可能会丢失1s内的数据,但是效率非常高。
+
+appendfsync no     #从不同步。高效但是数据不会被持久化。
+
+直接修改redis.conf中 appendonly yes
+
+建议最好还是使用everysec 既能够保证数据的同步、效率也还可以。
+
+everysec有缓冲区操作,效率偏高
+
+ 
+
+Aof是以执行命令的形式实现同步
+
+![image-20220519150149697](assets/image-20220519150149697.png)
+
+## MySQL与Redis一致性解决同步问题
+
+方式1:直接清除Redis的缓存,重新读取数据库即可
+
+方式2:使用mq异步订阅mysql binlog实现增量同步
+
+方式3:使用alibaba的canal
+
+ 
+
+ 

+ 96 - 0
后端/redis/3.redis淘汰策略和事务.md

@@ -0,0 +1,96 @@
+## Redis内存淘汰策略
+
+将Redis用作缓存时,如果内存空间用满,就会自动驱逐老的数据。
+
+### Redis六种淘汰策略
+
+- `noeviction`:当内存使用达到阈值的时候,执行命令直接报错 
+
+- `allkeys-lru`:在所有的key中,优先移除最近未使用的key。(推荐)
+
+- `volatile-lru`:在设置了过期时间的键空间中,优先移除最近未使用的key。
+
+- `allkeys-random`:在所有的key中,随机移除某个key。
+
+- `volatile-random`:在设置了过期时间的键空间中,随机移除某个key。
+
+- `volatile-ttl`:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。
+
+### 如何配置Redis淘汰策略
+
+在redis.conf文件中,                                
+
+设置Redis 内存大小的限制,我们可以设置maxmemory <bytes>,
+
+![image-20220519161122696](assets/image-20220519161122696.png)
+
+当数据达到限定大小后,会选择配置的策略淘汰数据
+
+比如:maxmemory 300mb。
+
+通过配置  设置Redis的淘汰策略。
+
+比如:maxmemory-policy volatile-lru
+
+ ![image-20220519161138514](assets/image-20220519161138514.png)
+
+## Redis中的自动过期机制
+
+实现需求:处理订单过期自动取消,比如下单30分钟未支付自动更改订单状态
+
+实现方案1:
+
+1. 使用Redis Key自动过期出发事件通知
+
+```shell
+原理:
+1. 创建订单的时候绑定一个订单的token, 存放在redis中(有效期30分钟)
+2. key=token value就是订单id
+3. 对该key绑定过期事件回调,执行回调方法:传递key
+```
+
+2. 使用定时任务30分钟后检查
+
+3. 按照每分钟轮训检查
+
+### 使用Redis Key自动过期机制
+
+当我们的key失效时,可以执行我们的客户端回调监听的方法。
+
+需要在Redis中配置:
+
+```shell
+notify-keyspace-events "Ex"
+```
+
+## Redis事务操作
+
+```shell
+# watch 可以监听一个或者多个key,在提交事务之前是否有发生了变化 如果发生边了变化就不会提交事务,没有发生变化才可以提交事务 版本号码 乐观锁
+watch name
+# Multi开启事务
+multi
+set name xiaoxiao
+# EXEC提交事务
+exec
+# 取消提交事务
+Discard
+```
+
+>  注意:Redis官方是没有提供回滚方法,值提供了取消事务。
+>
+>  Redis中本身就是单线程的能够保证线程安全问题。
+
+## 思考:Redis与Mysql中的事务有那些区别?
+
+**mysql:** 
+
+* mysql实现事务,是基于undo/redo日志
+* undo记录修改前状态,rollback基于undo日志实现
+* redo记录修改后的状态,commit基于redo日志实现
+* 在mysql中无论是否开启事务,sql都会被立即执行并返回执行结果,只是事务开启后执行后的状态只是记录在redo日志,执行commit之后,数据才会被写入磁盘
+
+ **redis:**
+* redis实现事务,是基于commands队列
+* 如果没有开启事务,command将会被立即执行并返回执行结果,并且直接写入磁盘
+* 如果事务开启,command不会被立即执行,而是排入队列,并返回排队状态(具体依赖于客户端(例如:spring-data-redis)自身实现)。调用exec才会执行commands队列

+ 47 - 0
后端/redis/4.基于redis实现分布式锁.md

@@ -0,0 +1,47 @@
+# Redis实现分布式锁
+
+***
+
+## 什么是分布式锁?
+
+本地锁:在多个线程中,保证一个线程执行
+
+分布锁:在分布式jvm中,保证只有一个线程执行
+
+## 分布式锁实现方案
+
+* 基于数据库方式实现
+* 基于zk方式实现
+* 基于redis方式实现
+
+## 分布式锁的应用场景有那些
+
+1. 分布式任务调度平台保证任务的幂等性。
+
+2. 分布式全局id的生成
+
+## Redis分布式锁实现思路---单机
+
+Redis实现分布式锁基于SetNx命令,因为在Redis中key是保证是唯一的。所以当多个线程同时的创建setNx时,只要谁能够创建成功谁就能够获取到锁。
+
+Set 命令 每次set时,可以修改原来旧值;
+
+SetNx命令 每次SetNx检查该 key是否已经存在,如果已经存在的话不会执行任何操作。返回为0 如果已经不存在的话直接新增该key。
+
+1:新增key成功 0 失败
+
+获取锁的时候:当多个线程同时创建SetNx k,只要谁能够创建成功谁就能够获取到锁。
+
+释放锁:可以对该key设置一个有效期可以避免死锁的现象。
+
+> 在提交事务的时候可以检查锁是否已经超时(就是endtime-strattime),进行手动回滚
+
+## Zookeeper实现分布式锁思路
+
+Zookeeper实现分布式锁核心采用临时节点+事件通知,因为zk节点路径是保证全局唯一的,当多个线程同时创建该临时节点,只要谁能够创建成功谁就能够获取到锁。 
+
+获取锁:当多个线程同时创建该临时节点,只要谁能够创建成功谁就能够获取到锁。
+
+释放锁:关闭当前Session连接,自动的删除当前的zk节点路径,其他线程重新进入到获取锁阶段。
+
+ 

+ 96 - 0
后端/redis/5.redis哨兵模式.md

@@ -0,0 +1,96 @@
+# Redis集群高可用环境
+
+***
+
+## Redis主从复制
+
+基本概念:
+
+单个Redis如果因为某种原因宕机的话,可能会导致Redis服务不可用,可以使用主从复制实现一主多从,主节点负责写的操作,从节点负责读的操作,主节点会定期将数据同步到从节点中,保证数据一致性的问题。
+
+![image-20220520151507939](assets/image-20220520151507939.png)
+
+![image-20220520151521608](assets/image-20220520151521608.png)
+
+## 主从复制数据同步的过程--首次全量,后面增量
+
+1. Redis从节点向主节点建立socket长连接
+
+2. Redis采用全量或者增量的形式将数据同步给从节点
+
+ 从Redis2.8版本以后  过程采用增量和全量同步
+
+全量复制:一般用于在初次的复制场景(从节点与主节点一次建立),发送二进制rdb文件
+
+增量复制:网络出现问题,从节点再次连接主节点时,主节点补发缺少的数据,每次数据增量同步,使用aof日志文件进行同步
+
+
+
+## 主从复制存在那些缺陷 
+
+传统的一主多从效率偏低,建议使用树状模型
+
+如果主节点存在了问题,整个Redis环境是不可以实现写的操作,需要人工更改配置变为主操作
+
+如何解决该问题:使用哨兵机制可以帮助解决Redis集群主从选举策略。
+
+
+
+## 哨兵机制原理--哨兵之监听master,不监听slave
+
+1. 哨兵机制每隔10s(可以配置)时间只需要配置监听我们的主节点就可以获取当前整个Redis集群的环境列表,采用info 命令形式。因为这个info可以知道你的从节点信息,递归调用直到没有从节点为止
+2. 哨兵不建议是单机的,最好每个Redis节点都需要配置哨兵监听。
+3. 哨兵集群原理是如何:多个哨兵都执行同一个主的master节点,订阅到相同都通道,有新的哨兵加入都会向通道中发送自己服务的信息,该通道的订阅者可以发现新哨兵的加入,随后相互建立长连接。
+4. Master的故障发现:单个哨兵会向主的master节点发送ping的命令,如果master节点没有及时的响应,哨兵会认为该master节点为“主观不可用状态”会发送给其他都哨兵确认该Master节点是否不可用,当前确认的哨兵节点数(别人确定他,自己不能投给自己)>=quorum(可配置),会实现重新选举。
+
+> 只能解决选举问题,不能解决主从复制问题 
+>
+> **redis哨兵会修改配置文件,底层原理**
+>
+> 挂掉的节点,他不会修改redis.conf配置文件。但是也会修改哨兵的配置文件
+
+# Redis安全控制
+
+## 缓存穿透--没有key
+
+产生的背景:
+
+缓存穿透是指使用不存在的key进行大量的高并发查询,导致缓存无法命中,每次请求都要都要穿透到后端数据库查询,使得数据库的压力非常大,甚至导致数据库服务压死;
+
+解决方案:
+
+1. 接口层实现api限流、用户授权、id检查等;
+2. 从缓存和数据库都取不到数据的话,一样将数据库空值放入缓存中,设置30s有效期避免使用同一个id对数据库攻击压力大;
+3. 布隆过滤器
+
+## 缓存击穿--单个key
+
+产生背景:
+
+在高并发的情况下,当一个缓存key过期时,因为访问该key请求较大,多个请求同时发现缓存过期,因此对多个请求同时数据库查询、同时向Redis写入缓存数据,这样会导致数据库的压力非常大;
+
+解决方案:
+
+1. 使用分布式锁
+
+保证在分布式情况下,使用分布式锁保证对于每个key同时只允许只有一个线程查询到后端服务,其他没有获取到锁的权限,只需要等待即可;这种高并发压力直接转移到分布式锁上,对分布式锁的压力非常大。
+
+2. 使用本地锁
+
+使用本地锁与分布式锁机制一样,只不过分布式锁适应于服务集群、本地锁仅限于单个服务使用。
+
+3. 软过过期 
+
+设置热点数据永不过期或者异步延长过期时间;
+
+4. 布隆过滤器
+
+ 
+
+## 缓存雪崩--多个key
+
+缓存雪崩指缓存服务器重启或者大量的缓存集中在某个时间段失效,突然给数据库产生了巨大的压力,甚至击垮数据库的情况。
+
+解决思路:对不用的数据使用不同的失效时间,加上随机数
+
+ 

+ 86 - 0
后端/redis/6.Redis Cluster集群.md

@@ -0,0 +1,86 @@
+# Redis Cluster集群
+
+***
+
+## 传统Redis集群存在那些问题
+
+1. Redis哨兵集群模式,每个节点都保存全量同步数据,冗余的数据比较多;
+2. 只能允许一个主节点
+3. 中心化模式
+
+而在Redis Cluster模式中集群中采用分片集群模式,可以减少冗余数据,缺点就是构建该集群模式成本非常高。
+
+* 去中心化模式
+
+ 
+
+## 传统RedisCluster集群的原理
+
+ 
+
+Redis3.0开始官方推出了集群模式 RedisCluster,原理采用hash槽的概念,预先分配16384个卡槽,并且将该卡槽分配给具体服务的节点;
+
+通过key进行**crc16(key)%16384**获取余数,余数就是对应的卡槽的位置,一个卡槽可以存放多个不同的key,从而将读或者写转发到该卡槽的服务的节点。 最大的有点:动态扩容、缩容。
+
+1. 为每个server节点部署从节点,保证高可用
+2. 一个卡槽可以分配不同的key
+3. 可以通过重定向来找到对应的server
+4. 卡槽(数据库的具体分表)其实就是服务器位置的标识符,从而实现数据均摊存放
+5. 动态的实现扩容和缩容
+
+```shell
+# 自动分配卡槽
+redis-cli --cluster create  192.168.245.20:7000  192.168.245.20:7001  192.168.245.20:7002  192.168.245.20:7003  192.168.245.20:7004  192.168.245.20:7005  --cluster-replicas 1
+# 默认不转发,应该使用集群模式连接
+# 修改为Redis的集群方式连接
+redis-cli -h 192.168.245.20 -p 7000 -c
+```
+
+
+
+![image-20220522144243791](assets/image-20220522144243791.png)
+
+## 扩容
+
+如果我们新增一个节点,我们需要重新分配卡槽。数据会一起迁移到主节点
+
+```shell
+redis-cli --cluster add-node  新增集群地址:端口  集群中已经存在的任意一台连接地址:端口 
+redis-cli --cluster add-node 192.168.245.20:7006   192.168.245.20:7000  
+```
+
+![image-20220522155302372](assets/image-20220522155302372.png)
+
+> **原因:redis配置文件复用,但是数据不同步**
+>
+> **解决办法:将rdb文件和aof文件改个名字**
+
+![image-20220522161015869](assets/image-20220522161015869.png)
+
+**分配卡槽**
+
+```shell
+# 扩容从节点:
+redis-cli --cluster add-node   192.168.245.20:7007  192.168.245.20:7000  --cluster-slave   --cluster-master-id  583ad82a8582733f278d1afb740dd3a5ca89c6ef
+# 如何分配卡槽节点
+redis-cli --cluster reshard  192.168.245.20:7000
+# 输入需要接受的id
+
+```
+
+![image-20220522162300489](assets/image-20220522162300489.png)
+
+## RedisCluster master宕机效果
+
+如果master宕机之后,会自动的在从节点会顺利的选为主节点;当原来的主节点在恢复启动的时候会变为从节点;
+
+## RedisCluster集群模式缩容节点
+
+```shell
+redis-cli --cluster  reshard  192.168.245.20:7000  --cluster-from   需要缩容的卡槽节点  --cluster-to  接受卡槽的节点  --cluster-slots 4096
+
+
+redis-cli --cluster  reshard  192.168.245.20:7000  --cluster-from   9299bfa1bb47dc7d53ed737f32bd0630ff5c8136  --cluster-to bdd488f3e42b2b05649856c9d59e2fc8acec7ed1  --cluster-slots 4096
+
+```
+

+ 45 - 0
后端/redis/7.布隆过滤器.md

@@ -0,0 +1,45 @@
+# 基于布隆过滤器解决缓存穿透问题
+
+## 缓存穿透
+
+产生的背景:
+
+缓存穿透是指使用不存在的key进行大量的高并发查询,导致缓存无法命中,每次请求都要都要穿透到后端数据库查询,使得数据库的压力非常大,甚至导致数据库服务压死;
+
+解决方案:
+
+1. 接口层实现api限流、用户授权、id检查等;
+
+2. 从缓存和数据库都取不到数据的话,一样将数据库空值放入缓存中,设置30s有效期避免使用同一个id对数据库攻击压力大;
+
+## 布隆过滤器基本介绍
+
+布隆过滤器适用于判断某个数据是否在集合中存在,不一定百分百准备, Bloom Filter基本实现原理采用位数组与联合函数一起实现;
+
+### 什么是布隆过滤器呢?
+
+ 
+
+HashMap? 检测该key是否存在 原理:先计算对应的key存放到数组的
+
+Index? O(1)
+
+就是使用hashmap存放redis的key
+
+ 
+
+### 布隆过滤器 作用:
+
+ 
+
+布隆过滤器适用于判断一个元素在集合中是否存在,但是可能会存在误判的问题。
+
+实现的原理采用二进制向量数组和随机映射hash函数。
+
+ 
+
+ 
+
+布隆过滤器为什么会产生冲突 ,会根据key计算hash值,可能与布隆过滤器中存放的元素hash产生冲突都是为1,布隆可能会产生误判可能存在。
+
+如何解决这个问题:二进制数组长度设置比较大,可以减少布隆误判的概率。

BIN
后端/redis/assets/1363376-20210622212732123-1297981656.png


BIN
后端/redis/assets/20200513215523258.png


BIN
后端/redis/assets/copycode.gif


BIN
后端/redis/assets/image-20220517095951464.png


BIN
后端/redis/assets/image-20220517100347511.png


BIN
后端/redis/assets/image-20220517101228747.png


BIN
后端/redis/assets/image-20220517101336924.png


BIN
后端/redis/assets/image-20220517111032308.png


BIN
后端/redis/assets/image-20220517145307564.png


BIN
后端/redis/assets/image-20220517145312078.png


BIN
后端/redis/assets/image-20220518101146275.png


BIN
后端/redis/assets/image-20220518102710826.png


BIN
后端/redis/assets/image-20220518103755786.png


BIN
后端/redis/assets/image-20220518103759533.png


BIN
后端/redis/assets/image-20220518103925266.png


BIN
后端/redis/assets/image-20220518104417240.png


BIN
后端/redis/assets/image-20220519150111752.png


BIN
后端/redis/assets/image-20220519150141092.png


BIN
后端/redis/assets/image-20220519150149697.png


BIN
后端/redis/assets/image-20220519161122696.png


BIN
后端/redis/assets/image-20220519161138514.png


BIN
后端/redis/assets/image-20220520151507939.png


BIN
后端/redis/assets/image-20220520151518945.png


BIN
后端/redis/assets/image-20220520151521608.png


BIN
后端/redis/assets/image-20220520152007648.png


BIN
后端/redis/assets/image-20220520152042159.png


BIN
后端/redis/assets/image-20220520153051951.png


BIN
后端/redis/assets/image-20220520153115865.png


BIN
后端/redis/assets/image-20220520164337367.png


BIN
后端/redis/assets/image-20220520164434639.png


BIN
后端/redis/assets/image-20220520164601716.png


BIN
后端/redis/assets/image-20220520164627927.png


BIN
后端/redis/assets/image-20220522144243791.png


BIN
后端/redis/assets/image-20220522155302372.png


BIN
后端/redis/assets/image-20220522161015869.png


BIN
后端/redis/assets/image-20220522162257224.png


BIN
后端/redis/assets/image-20220522162300489.png


BIN
后端/redis/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1MjIzMTU5NTc0Mg==,size_16,color_FFFFFF,t_70.png


+ 1914 - 0
后端/redis/redis.md

@@ -0,0 +1,1914 @@
+> [TOC]
+
+# Nosql概述
+
+***
+
+## 为什么要使用Nosql
+
+### 1、单机MySQL的年代
+
+90年代,一个网站的访问量一般不会太大,单个数据库完全够用。随着用户增多,网站出现以下问题
+
+1. 数据量增加到一定程度,单机数据库就放不下了
+2. 数据的索引(B+ Tree),一个机器内存也存放不下
+3. 访问量变大后(读写混合),一台服务器承受不住。
+
+![image-20220517095951464](assets/image-20220517095951464.png)
+
+### 2、Memcached(缓存) + MySQL + 垂直拆分 (读写分离)
+
+网站80%的情况都是在读,每次都要去查询数据库的话就十分的麻烦!所以说我们希望减轻数据的压 力,我们可以使用缓存来保证效率! 
+
+发展过程: 优化数据结构和索引--> 文件缓存(IO)---> Memcached(当时最热门的技术!)
+
+![image-20220517100347511](assets/image-20220517100347511.png)
+
+### 3、分库分表 + 水平拆分 + MySQL集群
+
+技术和业务在发展的同时,对人的要求也越来越高!
+
+本质:数据库(读,写) 
+
+早些年MyISAM: 表锁,十分影响效率!高并发下就会出现严重的锁问题 
+
+转战Innodb:行锁 
+
+慢慢的就开始使用分库分表来解决写的压力! MySQL 在哪个年代推出 了表分区!这个并没有多少公司 使用! 
+
+MySQL 的 集群,很好满足哪个年代的所有需求!
+
+![image-20220517101228747](assets/image-20220517101228747.png)
+
+### 4、如今最近的年代
+
+2010--2020 十年之间,世界已经发生了翻天覆地的变化;(定位,也是一种数据,音乐,热榜!) 
+
+MySQL 等关系型数据库就不够用了!数据量很多,变化很快~!
+
+ MySQL 有的使用它来村粗一些比较大的文件,博客,图片!数据库表很大,效率就低了!如果有一种数 据库来专门处理这种数据,
+
+ MySQL压力就变得十分小(研究如何处理这些问题!)大数据的IO压力下,表几乎没法更大!
+
+![image-20220517101336924](assets/image-20220517101336924.png)
+
+### 5、为什么要用NoSQL!
+
+用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!
+
+这时候我们就需要使用NoSQL数据库的,Nosql 可以很好的处理以上的情况
+
+
+
+## 什么是NoSQL
+
+NoSQL = Not Only SQL(不仅仅是SQL)
+
+Not Only Structured Query Language
+
+关系型数据库:列+行,同一个表下数据的结构是一样的。
+
+非关系型数据库:数据存储没有固定的格式,并且可以进行横向扩展。
+
+NoSQL泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区,暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的。
+
+### 1、Nosql特点
+
+1. 方便扩展(数据之间没有关系,很好扩展!)
+
+2. 大数据量高性能(Redis一秒可以写8万次,读11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高!)
+
+3. 数据类型是多样型的!(不需要事先设计数据库,随取随用)
+
+4. 传统的 RDBMS 和 NoSQL
+
+   ```diff
+   传统的 RDBMS(关系型数据库)
+   - 结构化组织
+   - SQL
+   - 数据和关系都存在单独的表中 row col
+   - 操作,数据定义语言
+   - 严格的一致性
+   - 基础的事务
+   ```
+
+   ```diff
+   Nosql
+   - 不仅仅是数据
+   - 没有固定的查询语言
+   - 键值对存储,列存储,文档存储,图形数据库(社交关系)
+   - 最终一致性
+   - CAP定理和BASE
+   - 高性能,高可用,高扩展
+   - ..
+   ```
+
+### 2、了解:3V + 3高
+
+大数据时代的3V :主要是描述问题的
+
+1. 海量Velume
+2. 多样Variety
+3. 实时Velocity
+
+大数据时代的3高 : 主要是对程序的要求
+
+1. 高并发
+2. 高可扩
+3. 高性能
+
+真正在公司中的实践:NoSQL + RDBMS 一起使用才是最强的。
+
+## Nosql的四大分类
+
+> **KV键值对**
+
+- 新浪:Redis
+- 美团:Redis + Tair
+- 阿里、百度:Redis + Memcache
+
+> **文档型数据库(bson数据格式):**
+
+- MongoDB(掌握)
+  - 基于分布式文件存储的数据库。C++编写,用于处理大量文档。
+  - MongoDB是RDBMS和NoSQL的中间产品。MongoDB是非关系型数据库中功能最丰富的,NoSQL中最像关系型数据库的数据库。
+- ConthDB
+
+> **列存储数据库**
+
+- HBase(大数据必学)
+- 分布式文件系统
+
+> **图关系数据库**
+
+用于广告推荐,社交网络
+
+- Neo4j、InfoGrid
+
+| 分类                | Examples举例                                       | 典型应用场景                                                 | 数据模型                                        | 优点                                                         | 缺点                                                         |
+| ------------------- | -------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
+| 键值对(key-value) | Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB | 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 | Key 指向 Value 的键值对,通常用hash table来实现 | 查找速度快                                                   | 数据无结构化,通常只被当作字符串或者二进制数据               |
+| 列存储数据库        | Cassandra, HBase, Riak                             | 分布式的文件系统                                             | 以列簇式存储,将同一列数据存在一起              | 查找速度快,可扩展性强,更容易进行分布式扩展                 | 功能相对局限                                                 |
+| 文档型数据库        | CouchDB, MongoDb                                   | Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) | Key-Value对应的键值对,Value为结构化数据        | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 | 查询性能不高,而且缺乏统一的查询语法。                       |
+| 图形(Graph)数据库   | Neo4J, InfoGrid, Infinite Graph                    | 社交网络,推荐系统等。专注于构建关系图谱                     | 图结构                                          | 利用图结构相关算法。比如最短路径寻址,N度关系查找等          | 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群 |
+
+# Redis入门
+
+## 概述
+
+> Redis是什么?
+
+Redis(Remote Dictionary Server ),即远程字典服务。
+
+是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
+
+与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
+
+> Redis能干什么?
+
+1. 内存存储、持久化,内存是断电即失的,所以需要持久化(RDB、AOF)
+2. 高效率、用于高速缓冲
+3. 发布订阅系统
+4. 地图信息分析
+5. 计时器、计数器(eg:浏览量)
+6. 。。。
+
+> 特性
+
+1. 多样的数据类型
+2. 持久化
+3. 集群
+4. 事务
+
+## Linux安装
+
+1. 基本安装
+
+```shell
+# 1.下载安装包!
+redis-7.0.0.tar.gz
+# 2.解压Redis的安装包!
+tar -zxvf redis-7.0.0.tar.gz
+yum install gcc-c++ -y
+# 然后进入redis目录下执行
+make && make install
+```
+
+2.  redis默认安装路径 `/usr/local/bin`
+3. 将redis的配置文件复制到 程序安装目录 `/usr/local/bin/redis-config`下
+4. redis默认不是后台启动的,需要修改配置文件!
+
+![image-20220517111032308](assets/image-20220517111032308.png)
+
+5. 通过制定的配置文件启动redis服务
+
+   ```shell
+   redis-server redis-config/redis.conf
+   ```
+
+   
+
+6. 关闭Redis服务 `shutdown`
+
+## 基础知识
+
+redis默认有16个数据库
+
+```shell
+默认使用的第0个;
+
+16个数据库为:DB 0~DB 15
+默认使用DB 0 ,可以使用select n切换到DB n,dbsize可以查看当前数据库的大小,与key数量相关。
+```
+
+`keys *` :查看当前数据库中所有的key。
+
+`flushdb`:清空当前数据库中的键值对。
+
+`flushall`:清空所有数据库的键值对。
+
+> Redis是单线程的,Redis是基于内存操作的。
+
+所以Redis的性能瓶颈不是CPU,而是机器内存和网络带宽。
+
+那么为什么Redis的速度如此快呢,性能这么高呢?QPS达到10W+
+
+> Redis为什么单线程还这么快?
+
+- 误区1:高性能的服务器一定是多线程的?
+- 误区2:多线程(CPU上下文会切换!)一定比单线程效率高!
+
+核心:Redis是将所有的数据放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存存储数据情况下,单线程就是最佳的方案。
+
+# 五大数据类型
+
+***
+
+Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持[字符串](https://www.redis.net.cn/tutorial/3508.html)、[哈希表](https://www.redis.net.cn/tutorial/3509.html)、[列表](https://www.redis.net.cn/tutorial/3510.html)、[集合](https://www.redis.net.cn/tutorial/3511.html)、[有序集合](https://www.redis.net.cn/tutorial/3512.html),[位图](https://www.redis.net.cn/tutorial/3508.html),[hyperloglogs](https://www.redis.net.cn/tutorial/3513.html)等数据类型。内置复制、[Lua脚本](https://www.redis.net.cn/tutorial/3516.html)、LRU收回、[事务](https://www.redis.net.cn/tutorial/3515.html)以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动[分区](https://www.redis.net.cn/tutorial/3524.html)。
+
+## Redis-key
+
+> 在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。
+
+下面学习的命令:
+
+- `exists key`:判断键是否存在
+- `del key`:删除键值对
+- `move key db`:将键值对移动到指定数据库
+- `expire key second`:设置键值对的过期时间
+- `type key`:查看value的数据类型
+- `expire key time`: 设置键值对的过期时间
+- `ttl key`:查看key的过期剩余时间
+
+关于`TTL`命令
+
+Redis的key,通过TTL命令返回key的过期时间,一般来说有3种:
+
+1. 当前key没有设置过期时间,所以会返回-1.
+2. 当前key有设置过期时间,而且key已经过期,所以会返回-2.
+3. 当前key有设置过期时间,且key还没有过期,故会返回key的正常剩余时间.
+
+关于重命名`RENAME`和`RENAMENX`
+
+- `RENAME key newkey`修改 key 的名称
+- `RENAMENX key newkey`仅当 newkey 不存在时,将 key 改名为 newkey 。
+
+[更多命令学习](https://www.redis.net.cn/order)
+
+## String(字符串)
+
+普通的set、get直接略过。
+
+| 命令                                 | 描述                                                         | 示例                                                         |
+| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
+| `APPEND key value`                   | 向指定的key的value后追加字符串                               | 127.0.0.1:6379> set msg hello OK127.0.0.1:6379> append msg " world" (integer) 11 127.0.0.1:6379> get msg “hello world” |
+| `DECR/INCR key`                      | 将指定key的value数值进行+1/-1(仅对于数字)                    | 127.0.0.1:6379> set age 20 OK 127.0.0.1:6379> incr age (integer) 21 127.0.0.1:6379> decr age (integer) 20 |
+| `INCRBY/DECRBY key n`                | 按指定的步长对数值进行加减                                   | 127.0.0.1:6379> INCRBY age 5 (integer) 25 127.0.0.1:6379> DECRBY age 10 (integer) 15 |
+| `INCRBYFLOAT key n`                  | 为数值加上浮点型数值                                         | 127.0.0.1:6379> INCRBYFLOAT age 5.2 “20.2”                   |
+| `STRLEN key`                         | 获取key保存值的字符串长度                                    | 127.0.0.1:6379> get msg “hello world” 127.0.0.1:6379> STRLEN msg (integer) 11 |
+| `GETRANGE key start end`             | 按起止位置获取字符串(闭区间,起止位置都取)                 | 127.0.0.1:6379> get msg “hello world” 127.0.0.1:6379> GETRANGE msg 3 9 “lo worl” |
+| `SETRANGE key offset value`          | 用指定的value 替换key中 offset开始的值                       | 127.0.0.1:6379> SETRANGE msg 2 hello (integer) 7 127.0.0.1:6379> get msg “tehello” |
+| `GETSET key value`                   | 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。   | 127.0.0.1:6379> GETSET msg test “hello world”                |
+| `SETNX key value`                    | 仅当key不存在时进行set                                       | 127.0.0.1:6379> SETNX msg test (integer) 0 127.0.0.1:6379> SETNX name sakura (integer) 1 |
+| `SETEX key seconds value`            | set 键值对并设置过期时间                                     | 127.0.0.1:6379> setex name 10 root OK 127.0.0.1:6379> get name (nil) |
+| `MSET key1 value1 [key2 value2..]`   | 批量set键值对,原子操作                                      | 127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 OK                    |
+| `MSETNX key1 value1 [key2 value2..]` | 批量设置键值对,仅当参数中所有的key都不存在时执行            | 127.0.0.1:6379> MSETNX k1 v1 k4 v4 (integer) 0               |
+| `MGET key1 [key2..]`                 | 批量获取多个key保存的值                                      | 127.0.0.1:6379> MGET k1 k2 k3 1) “v1” 2) “v2” 3) “v3”        |
+| `PSETEX key milliseconds value`      | 和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,     |                                                              |
+| `getset key value`                   | 如果不存在值,则返回nil,如果存在值,获取原来的值,并设置新的值 |                                                              |
+
+String类似的使用场景:value除了是字符串还可以是数字,用途举例:
+
+- 计数器
+- 统计多单位的数量:uid:123666:follow 0
+- 粉丝数
+- 对象存储缓存
+
+## List(列表)
+
+> Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
+>
+> 一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
+
+![image-20220517145312078](assets/image-20220517145312078.png)
+
+首先我们列表,可以经过规则定义将其变为队列、栈、双端队列等
+
+正如图Redis中List是可以进行双端操作的,所以命令也就分为了LXXX和RLLL两类,有时候L也表示List例如LLEN
+
+| 命令                                    | 描述                                                         |
+| --------------------------------------- | ------------------------------------------------------------ |
+| `LPUSH/RPUSH key value1[value2..]`      | 从左边/右边向列表中PUSH值(一个或者多个)。                    |
+| `LRANGE key start end`                  | 获取list 起止元素==(索引从左往右 递增)==                   |
+| `LPUSHX/RPUSHX key value`               | 向已存在的列名中push值(一个或者多个)                       |
+| `LINSERT key BEFORE|AFTER pivot value`  | 在指定列表元素的前/后 插入value                              |
+| `LLEN key`                              | 查看列表长度                                                 |
+| `LINDEX key index`                      | 通过索引获取列表元素                                         |
+| `LSET key index value`                  | 通过索引为元素设值                                           |
+| `LPOP/RPOP key`                         | 从最左边/最右边移除值 并返回                                 |
+| `RPOPLPUSH source destination`          | 将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部 |
+| `LTRIM key start end`                   | 通过下标截取指定范围内的列表                                 |
+| `LREM key count value`                  | List中是允许value重复的 `count > 0`:从头部开始搜索 然后删除指定的value 至多删除count个 `count < 0`:从尾部开始搜索… `count = 0`:删除列表中所有的指定value。 |
+| `BLPOP/BRPOP key1[key2] timout`         | 移出并获取列表的第一个/最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
+| `BRPOPLPUSH source destination timeout` | 和`RPOPLPUSH`功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
+
+```shell
+127.0.0.1:6379> LPUSH mylist k1 # LPUSH mylist=>{1}
+(integer) 1
+127.0.0.1:6379> LPUSH mylist k2 # LPUSH mylist=>{2,1}
+(integer) 2
+127.0.0.1:6379> RPUSH mylist k3 # RPUSH mylist=>{2,1,3}
+(integer) 3
+127.0.0.1:6379> get mylist # 普通的get是无法获取list值的
+(error) WRONGTYPE Operation against a key holding the wrong kind of value
+127.0.0.1:6379> LRANGE mylist 0 4 # LRANGE 获取起止位置范围内的元素
+1) "k2"
+2) "k1"
+3) "k3"
+127.0.0.1:6379> LRANGE mylist 0 2
+1) "k2"
+2) "k1"
+3) "k3"
+127.0.0.1:6379> LRANGE mylist 0 1
+1) "k2"
+2) "k1"
+127.0.0.1:6379> LRANGE mylist 0 -1 # 获取全部元素
+1) "k2"
+2) "k1"
+3) "k3"
+
+---------------------------LPUSHX---RPUSHX-----------------------------------
+
+127.0.0.1:6379> LPUSHX list v1 # list不存在 LPUSHX失败
+(integer) 0
+127.0.0.1:6379> LPUSHX list v1 v2  
+(integer) 0
+127.0.0.1:6379> LPUSHX mylist k4 k5 # 向mylist中 左边 PUSH k4 k5
+(integer) 5
+127.0.0.1:6379> LRANGE mylist 0 -1
+1) "k5"
+2) "k4"
+3) "k2"
+4) "k1"
+5) "k3"
+
+---------------------------LINSERT--LLEN--LINDEX--LSET----------------------------
+
+127.0.0.1:6379> LINSERT mylist after k2 ins_key1 # 在k2元素后 插入ins_key1
+(integer) 6
+127.0.0.1:6379> LRANGE mylist 0 -1
+1) "k5"
+2) "k4"
+3) "k2"
+4) "ins_key1"
+5) "k1"
+6) "k3"
+127.0.0.1:6379> LLEN mylist # 查看mylist的长度
+(integer) 6
+127.0.0.1:6379> LINDEX mylist 3 # 获取下标为3的元素
+"ins_key1"
+127.0.0.1:6379> LINDEX mylist 0
+"k5"
+127.0.0.1:6379> LSET mylist 3 k6 # 将下标3的元素 set值为k6
+OK
+127.0.0.1:6379> LRANGE mylist 0 -1
+1) "k5"
+2) "k4"
+3) "k2"
+4) "k6"
+5) "k1"
+6) "k3"
+
+---------------------------LPOP--RPOP--------------------------
+
+127.0.0.1:6379> LPOP mylist # 左侧(头部)弹出
+"k5"
+127.0.0.1:6379> RPOP mylist # 右侧(尾部)弹出
+"k3"
+
+---------------------------RPOPLPUSH--------------------------
+
+127.0.0.1:6379> LRANGE mylist 0 -1
+1) "k4"
+2) "k2"
+3) "k6"
+4) "k1"
+127.0.0.1:6379> RPOPLPUSH mylist newlist # 将mylist的最后一个值(k1)弹出,加入到newlist的头部
+"k1"
+127.0.0.1:6379> LRANGE newlist 0 -1
+1) "k1"
+127.0.0.1:6379> LRANGE mylist 0 -1
+1) "k4"
+2) "k2"
+3) "k6"
+
+---------------------------LTRIM--------------------------
+
+127.0.0.1:6379> LTRIM mylist 0 1 # 截取mylist中的 0~1部分
+OK
+127.0.0.1:6379> LRANGE mylist 0 -1
+1) "k4"
+2) "k2"
+
+# 初始 mylist: k2,k2,k2,k2,k2,k2,k4,k2,k2,k2,k2
+---------------------------LREM--------------------------
+
+127.0.0.1:6379> LREM mylist 3 k2 # 从头部开始搜索 至多删除3个 k2
+(integer) 3
+# 删除后:mylist: k2,k2,k2,k4,k2,k2,k2,k2
+
+127.0.0.1:6379> LREM mylist -2 k2 #从尾部开始搜索 至多删除2个 k2
+(integer) 2
+# 删除后:mylist: k2,k2,k2,k4,k2,k2
+
+
+---------------------------BLPOP--BRPOP--------------------------
+
+mylist: k2,k2,k2,k4,k2,k2
+newlist: k1
+
+127.0.0.1:6379> BLPOP newlist mylist 30 # 从newlist中弹出第一个值,mylist作为候选
+1) "newlist" # 弹出
+2) "k1"
+127.0.0.1:6379> BLPOP newlist mylist 30
+1) "mylist" # 由于newlist空了 从mylist中弹出
+2) "k2"
+127.0.0.1:6379> BLPOP newlist 30
+(30.10s) # 超时了
+
+127.0.0.1:6379> BLPOP newlist 30 # 我们连接另一个客户端向newlist中push了test, 阻塞被解决。
+1) "newlist"
+2) "test"
+(12.54s)
+```
+
+> 小结
+
+- list实际上是一个链表,before Node after , left, right 都可以插入值
+- 如果key不存在,则创建新的链表
+- 如果key存在,新增内容
+- 如果移除了所有值,空链表,也代表不存在
+- 在两边插入或者改动值,效率最高!修改中间元素,效率相对较低
+
+应用:
+
+消息排队!消息队列(Lpush Rpop),栈(Lpush Lpop)
+
+## Set(集合)
+
+> Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
+>
+> Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
+>
+> 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。
+
+| 命令                                      | 描述                                                         |
+| ----------------------------------------- | ------------------------------------------------------------ |
+| `SADD key member1[member2..]`             | 向集合中无序增加一个/多个成员                                |
+| `SCARD key`                               | 获取集合的成员数                                             |
+| `SMEMBERS key`                            | 返回集合中所有的成员                                         |
+| `SISMEMBER key member`                    | 查询member元素是否是集合的成员,结果是无序的                  |
+| `SRANDMEMBER key [count]`                 | 随机返回集合中count个成员,count缺省值为1                    |
+| `SPOP key [count]`                        | 随机移除并返回集合中count个成员,count缺省值为1              |
+| `SMOVE source destination member`         | 将source集合的成员member移动到destination集合                |
+| `SREM key member1[member2..]`             | 移除集合中一个/多个成员                                      |
+| `SDIFF key1[key2..]`                      | 返回所有集合的差集 key1- key2 - …                            |
+| `SDIFFSTORE destination key1[key2..]`     | 在SDIFF的基础上,将结果保存到集合中==(覆盖)==。不能保存到其他类型key噢! |
+| `SINTER key1 [key2..]`                    | 返回所有集合的交集                                           |
+| `SINTERSTORE destination key1[key2..]`    | 在SINTER的基础上,存储结果到集合中。覆盖                     |
+| `SUNION key1 [key2..]`                    | 返回所有集合的并集                                           |
+| `SUNIONSTORE destination key1 [key2..]`   | 在SUNION的基础上,存储结果到及和张。覆盖                     |
+| `SSCAN KEY [MATCH pattern] [COUNT count]` | 在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分     |
+
+```shell
+---------------SADD--SCARD--SMEMBERS--SISMEMBER--------------------
+
+127.0.0.1:6379> SADD myset m1 m2 m3 m4 # 向myset中增加成员 m1~m4
+(integer) 4
+127.0.0.1:6379> SCARD myset # 获取集合的成员数目
+(integer) 4
+127.0.0.1:6379> smembers myset # 获取集合中所有成员
+1) "m4"
+2) "m3"
+3) "m2"
+4) "m1"
+127.0.0.1:6379> SISMEMBER myset m5 # 查询m5是否是myset的成员
+(integer) 0 # 不是,返回0
+127.0.0.1:6379> SISMEMBER myset m2
+(integer) 1 # 是,返回1
+127.0.0.1:6379> SISMEMBER myset m3
+(integer) 1
+
+---------------------SRANDMEMBER--SPOP----------------------------------
+
+127.0.0.1:6379> SRANDMEMBER myset 3 # 随机返回3个成员
+1) "m2"
+2) "m3"
+3) "m4"
+127.0.0.1:6379> SRANDMEMBER myset # 随机返回1个成员
+"m3"
+127.0.0.1:6379> SPOP myset 2 # 随机移除并返回2个成员
+1) "m1"
+2) "m4"
+# 将set还原到{m1,m2,m3,m4}
+
+---------------------SMOVE--SREM----------------------------------------
+
+127.0.0.1:6379> SMOVE myset newset m3 # 将myset中m3成员移动到newset集合
+(integer) 1
+127.0.0.1:6379> SMEMBERS myset
+1) "m4"
+2) "m2"
+3) "m1"
+127.0.0.1:6379> SMEMBERS newset
+1) "m3"
+127.0.0.1:6379> SREM newset m3 # 从newset中移除m3元素
+(integer) 1
+127.0.0.1:6379> SMEMBERS newset
+(empty list or set)
+
+# 下面开始是多集合操作,多集合操作中若只有一个参数默认和自身进行运算
+# setx=>{m1,m2,m4,m6}, sety=>{m2,m5,m6}, setz=>{m1,m3,m6}
+
+-----------------------------SDIFF------------------------------------
+
+127.0.0.1:6379> SDIFF setx sety setz # 等价于setx-sety-setz
+1) "m4"
+127.0.0.1:6379> SDIFF setx sety # setx - sety
+1) "m4"
+2) "m1"
+127.0.0.1:6379> SDIFF sety setx # sety - setx
+1) "m5"
+
+
+-------------------------SINTER---------------------------------------
+# 共同关注(交集)
+
+127.0.0.1:6379> SINTER setx sety setz # 求 setx、sety、setx的交集
+1) "m6"
+127.0.0.1:6379> SINTER setx sety # 求setx sety的交集
+1) "m2"
+2) "m6"
+
+-------------------------SUNION---------------------------------------
+
+127.0.0.1:6379> SUNION setx sety setz # setx sety setz的并集
+1) "m4"
+2) "m6"
+3) "m3"
+4) "m2"
+5) "m1"
+6) "m5"
+127.0.0.1:6379> SUNION setx sety # setx sety 并集
+1) "m4"
+2) "m6"
+3) "m2"
+4) "m1"
+5) "m5"
+```
+
+微博,将A用户所有关注的人放在一个集合中,将他的粉丝也放在一个集合中!
+共同关注,共同爱好
+
+## Hash(哈希)
+
+> Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
+>
+> Set就是一种简化的Hash,只变动key,而value使用默认值填充。可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。
+>
+> 存储 key - map
+
+| 命令                                             | 描述                                                         |
+| ------------------------------------------------ | ------------------------------------------------------------ |
+| `HSET key field value`                           | 将哈希表 key 中的字段 field 的值设为 value 。重复设置同一个field会覆盖,返回0 |
+| `HMSET key field1 value1 [field2 value2..]`      | 同时将多个 field-value (域-值)对设置到哈希表 key 中。        |
+| `HSETNX key field value`                         | 只有在字段 field 不存在时,设置哈希表字段的值。              |
+| `HEXISTS key field`                              | 查看哈希表 key 中,指定的字段是否存在。                      |
+| `HGET key field value`                           | 获取存储在哈希表中指定字段的值                               |
+| `HMGET key field1 [field2..]`                    | 获取所有给定字段的值                                         |
+| `HGETALL key`                                    | 获取在哈希表key 的所有字段和值                               |
+| `HKEYS key`                                      | 获取哈希表key中所有的字段                                    |
+| `HLEN key`                                       | 获取哈希表中字段的数量                                       |
+| `HVALS key`                                      | 获取哈希表中所有值                                           |
+| `HDEL key field1 [field2..]`                     | 删除哈希表key中一个/多个field字段                            |
+| `HINCRBY key field n`                            | 为哈希表 key 中的指定字段的整数值加上增量n,并返回增量后结果 一样只适用于整数型字段 |
+| `HINCRBYFLOAT key field n`                       | 为哈希表 key 中的指定字段的浮点数值加上增量 n。              |
+| `HSCAN key cursor [MATCH pattern] [COUNT count]` | 迭代哈希表中的键值对。                                       |
+
+```shell
+------------------------HSET--HMSET--HSETNX----------------
+127.0.0.1:6379> HSET studentx name sakura # 将studentx哈希表作为一个对象,设置name为sakura
+(integer) 1
+127.0.0.1:6379> HSET studentx name gyc # 重复设置field进行覆盖,并返回0
+(integer) 0
+127.0.0.1:6379> HSET studentx age 20 # 设置studentx的age为20
+(integer) 1
+127.0.0.1:6379> HMSET studentx sex 1 tel 15623667886 # 设置sex为1,tel为15623667886
+OK
+127.0.0.1:6379> HSETNX studentx name gyc # HSETNX 设置已存在的field
+(integer) 0 # 失败
+127.0.0.1:6379> HSETNX studentx email 12345@qq.com
+(integer) 1 # 成功
+
+----------------------HEXISTS--------------------------------
+127.0.0.1:6379> HEXISTS studentx name # name字段在studentx中是否存在
+(integer) 1 # 存在
+127.0.0.1:6379> HEXISTS studentx addr
+(integer) 0 # 不存在
+
+-------------------HGET--HMGET--HGETALL-----------
+127.0.0.1:6379> HGET studentx name # 获取studentx中name字段的value
+"gyc"
+127.0.0.1:6379> HMGET studentx name age tel # 获取studentx中name、age、tel字段的value
+1) "gyc"
+2) "20"
+3) "15623667886"
+127.0.0.1:6379> HGETALL studentx # 获取studentx中所有的field及其value
+ 1) "name"
+ 2) "gyc"
+ 3) "age"
+ 4) "20"
+ 5) "sex"
+ 6) "1"
+ 7) "tel"
+ 8) "15623667886"
+ 9) "email"
+10) "12345@qq.com"
+
+
+--------------------HKEYS--HLEN--HVALS--------------
+127.0.0.1:6379> HKEYS studentx # 查看studentx中所有的field
+1) "name"
+2) "age"
+3) "sex"
+4) "tel"
+5) "email"
+127.0.0.1:6379> HLEN studentx # 查看studentx中的字段数量
+(integer) 5
+127.0.0.1:6379> HVALS studentx # 查看studentx中所有的value
+1) "gyc"
+2) "20"
+3) "1"
+4) "15623667886"
+5) "12345@qq.com"
+
+-------------------------HDEL--------------------------
+127.0.0.1:6379> HDEL studentx sex tel # 删除studentx 中的sex、tel字段
+(integer) 2
+127.0.0.1:6379> HKEYS studentx
+1) "name"
+2) "age"
+3) "email"
+
+-------------HINCRBY--HINCRBYFLOAT------------------------
+127.0.0.1:6379> HINCRBY studentx age 1 # studentx的age字段数值+1
+(integer) 21
+127.0.0.1:6379> HINCRBY studentx name 1 # 非整数字型字段不可用
+(error) ERR hash value is not an integer
+127.0.0.1:6379> HINCRBYFLOAT studentx weight 0.6 # weight字段增加0.6
+"90.8"
+```
+
+Hash变更的数据user name age,尤其是用户信息之类的,经常变动的信息!Hash更适合于对象的存储,Sring更加适合字符串存储!
+
+## Zset(有序集合)
+
+> 不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。
+>
+> score相同:按字典顺序排序
+>
+> 有序集合的成员是唯一的,但分数(score)却可以重复。
+>
+> zset k1 source1 v1
+
+| 命令                                              | 描述                                                         |
+| ------------------------------------------------- | ------------------------------------------------------------ |
+| `ZADD key score member1 [score2 member2]`         | 向有序集合添加一个或多个成员,或者更新已存在成员的分数       |
+| `ZCARD key`                                       | 获取有序集合的成员数                                         |
+| `ZCOUNT key min max`                              | 计算在有序集合中指定区间score的成员数                        |
+| `ZINCRBY key n member`                            | 有序集合中对指定成员的分数加上增量 n                         |
+| `ZSCORE key member`                               | 返回有序集中,成员的分数值                                   |
+| `ZRANK key member`                                | 返回有序集合中指定成员的索引                                 |
+| `ZRANGE key start end`                            | 通过索引区间返回有序集合成指定区间内的成员                   |
+| `ZRANGEBYLEX key min max`                         | 通过字典区间返回有序集合的成员                               |
+| `ZRANGEBYSCORE key min max`                       | 通过分数返回有序集合指定区间内的成员==-inf 和 +inf分别表示最小最大值,只支持开区间()== |
+| `ZLEXCOUNT key min max`                           | 在有序集合中计算指定字典区间内成员数量                       |
+| `ZREM key member1 [member2..]`                    | 移除有序集合中一个/多个成员                                  |
+| `ZREMRANGEBYLEX key min max`                      | 移除有序集合中给定的字典区间的所有成员                       |
+| `ZREMRANGEBYRANK key start stop`                  | 移除有序集合中给定的排名区间的所有成员                       |
+| `ZREMRANGEBYSCORE key min max`                    | 移除有序集合中给定的分数区间的所有成员                       |
+| `ZREVRANGE key start end`                         | 返回有序集中指定区间内的成员,通过索引,分数从高到底         |
+| `ZREVRANGEBYSCORRE key max min`                   | 返回有序集中指定分数区间内的成员,分数从高到低排序           |
+| `ZREVRANGEBYLEX key max min`                      | 返回有序集中指定字典区间内的成员,按字典顺序倒序             |
+| `ZREVRANK key member`                             | 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
+| `ZINTERSTORE destination numkeys key1 [key2 ..]`  | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中,numkeys:表示参与运算的集合数,将score相加作为结果的score |
+| `ZUNIONSTORE destination numkeys key1 [key2..]`   | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
+| `ZSCAN key cursor [MATCH pattern\] [COUNT count]` | 迭代有序集合中的元素(包括元素成员和元素分值)               |
+
+```shell
+-------------------ZADD--ZCARD--ZCOUNT--------------
+127.0.0.1:6379> ZADD myzset 1 m1 2 m2 3 m3 # 向有序集合myzset中添加成员m1 score=1 以及成员m2 score=2..
+(integer) 2
+127.0.0.1:6379> ZCARD myzset # 获取有序集合的成员数
+(integer) 2
+127.0.0.1:6379> ZCOUNT myzset 0 1 # 获取score在 [0,1]区间的成员数量
+(integer) 1
+127.0.0.1:6379> ZCOUNT myzset 0 2
+(integer) 2
+
+----------------ZINCRBY--ZSCORE--------------------------
+127.0.0.1:6379> ZINCRBY myzset 5 m2 # 将成员m2的score +5
+"7"
+127.0.0.1:6379> ZSCORE myzset m1 # 获取成员m1的score
+"1"
+127.0.0.1:6379> ZSCORE myzset m2
+"7"
+
+--------------ZRANK--ZRANGE-----------------------------------
+127.0.0.1:6379> ZRANK myzset m1 # 获取成员m1的索引,索引按照score排序,score相同索引值按字典顺序顺序增加
+(integer) 0
+127.0.0.1:6379> ZRANK myzset m2
+(integer) 2
+127.0.0.1:6379> ZRANGE myzset 0 1 # 获取索引在 0~1的成员
+1) "m1"
+2) "m3"
+127.0.0.1:6379> ZRANGE myzset 0 -1 # 获取全部成员
+1) "m1"
+2) "m3"
+3) "m2"
+
+#testset=>{abc,add,amaze,apple,back,java,redis} score均为0
+------------------ZRANGEBYLEX---------------------------------
+127.0.0.1:6379> ZRANGEBYLEX testset - + # 返回所有成员
+1) "abc"
+2) "add"
+3) "amaze"
+4) "apple"
+5) "back"
+6) "java"
+7) "redis"
+127.0.0.1:6379> ZRANGEBYLEX testset - + LIMIT 0 3 # 分页 按索引显示查询结果的 0,1,2条记录
+1) "abc"
+2) "add"
+3) "amaze"
+127.0.0.1:6379> ZRANGEBYLEX testset - + LIMIT 3 3 # 显示 3,4,5条记录
+1) "apple"
+2) "back"
+3) "java"
+127.0.0.1:6379> ZRANGEBYLEX testset (- [apple # 显示 (-,apple] 区间内的成员
+1) "abc"
+2) "add"
+3) "amaze"
+4) "apple"
+127.0.0.1:6379> ZRANGEBYLEX testset [apple [java # 显示 [apple,java]字典区间的成员
+1) "apple"
+2) "back"
+3) "java"
+
+-----------------------ZRANGEBYSCORE---------------------
+127.0.0.1:6379> ZRANGEBYSCORE myzset 1 10 # 返回score在 [1,10]之间的的成员
+1) "m1"
+2) "m3"
+3) "m2"
+127.0.0.1:6379> ZRANGEBYSCORE myzset 1 5
+1) "m1"
+2) "m3"
+
+--------------------ZLEXCOUNT-----------------------------
+127.0.0.1:6379> ZLEXCOUNT testset - +
+(integer) 7
+127.0.0.1:6379> ZLEXCOUNT testset [apple [java
+(integer) 3
+
+------------------ZREM--ZREMRANGEBYLEX--ZREMRANGBYRANK--ZREMRANGEBYSCORE--------------------------------
+127.0.0.1:6379> ZREM testset abc # 移除成员abc
+(integer) 1
+127.0.0.1:6379> ZREMRANGEBYLEX testset [apple [java # 移除字典区间[apple,java]中的所有成员
+(integer) 3
+127.0.0.1:6379> ZREMRANGEBYRANK testset 0 1 # 移除排名0~1的所有成员
+(integer) 2
+127.0.0.1:6379> ZREMRANGEBYSCORE myzset 0 3 # 移除score在 [0,3]的成员
+(integer) 2
+
+
+# testset=> {abc,add,apple,amaze,back,java,redis} score均为0
+# myzset=> {(m1,1),(m2,2),(m3,3),(m4,4),(m7,7),(m9,9)}
+----------------ZREVRANGE--ZREVRANGEBYSCORE--ZREVRANGEBYLEX-----------
+127.0.0.1:6379> ZREVRANGE myzset 0 3 # 按score递减排序,然后按索引,返回结果的 0~3
+1) "m9"
+2) "m7"
+3) "m4"
+4) "m3"
+127.0.0.1:6379> ZREVRANGE myzset 2 4 # 返回排序结果的 索引的2~4
+1) "m4"
+2) "m3"
+3) "m2"
+127.0.0.1:6379> ZREVRANGEBYSCORE myzset 6 2 # 按score递减顺序 返回集合中分数在[2,6]之间的成员
+1) "m4"
+2) "m3"
+3) "m2"
+127.0.0.1:6379> ZREVRANGEBYLEX testset [java (add # 按字典倒序 返回集合中(add,java]字典区间的成员
+1) "java"
+2) "back"
+3) "apple"
+4) "amaze"
+
+-------------------------ZREVRANK------------------------------
+127.0.0.1:6379> ZREVRANK myzset m7 # 按score递减顺序,返回成员m7索引
+(integer) 1
+127.0.0.1:6379> ZREVRANK myzset m2
+(integer) 4
+
+
+# mathscore=>{(xm,90),(xh,95),(xg,87)} 小明、小红、小刚的数学成绩
+# enscore=>{(xm,70),(xh,93),(xg,90)} 小明、小红、小刚的英语成绩
+-------------------ZINTERSTORE--ZUNIONSTORE-----------------------------------
+127.0.0.1:6379> ZINTERSTORE sumscore 2 mathscore enscore # 将mathscore enscore进行合并 结果存放到sumscore
+(integer) 3
+127.0.0.1:6379> ZRANGE sumscore 0 -1 withscores # 合并后的score是之前集合中所有score的和
+1) "xm"
+2) "160"
+3) "xg"
+4) "177"
+5) "xh"
+6) "188"
+
+127.0.0.1:6379> ZUNIONSTORE lowestscore 2 mathscore enscore AGGREGATE MIN # 取两个集合的成员score最小值作为结果的
+(integer) 3
+127.0.0.1:6379> ZRANGE lowestscore 0 -1 withscores
+1) "xm"
+2) "70"
+3) "xg"
+4) "87"
+5) "xh"
+6) "93"
+```
+
+应用案例:
+
+- set排序 存储班级成绩表 工资表排序!
+- 普通消息,1.重要消息 2.带权重进行判断
+- 排行榜应用实现,取Top N测试
+
+# 三种特殊数据类型
+
+***
+
+
+
+## Geospatial(地理位置)
+
+> 使用经纬度定位地理坐标并用一个有序集合zset保存,所以zset命令也可以使用
+
+| 命令                                                         | 描述                                                         |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| `geoadd key longitud(经度) latitude(纬度) member [..]`       | 将具体经纬度的坐标存入一个有序集合                           |
+| `geopos key member [member..]`                               | 获取集合中的一个/多个成员坐标                                |
+| `geodist key member1 member2 [unit]`                         | 返回两个给定位置之间的距离。默认以米作为单位。               |
+| `georadius key longitude latitude radius m|km|mi|ft [WITHCOORD][WITHDIST] [WITHHASH] [COUNT count]` | 以给定的经纬度为中心, 返回集合包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。 |
+| `GEORADIUSBYMEMBER key member radius...`                     | 功能与GEORADIUS相同,只是中心位置不是具体的经纬度,而是使用结合中已有的成员作为中心点。 |
+| `geohash key member1 [member2..]`                            | 返回一个或多个位置元素的Geohash表示。使用Geohash位置52点整数编码。 |
+
+有效经纬度
+
+> - 有效的经度从-180度到180度。
+> - 有效的纬度从-85.05112878度到85.05112878度。
+
+指定单位的参数 unit 必须是以下单位的其中一个:
+
+- m 表示单位为米。
+- km 表示单位为千米。
+- mi 表示单位为英里。
+- ft 表示单位为英尺。
+
+关于GEORADIUS的参数
+
+> 通过`georadius`就可以完成 附近的人功能
+>
+> withcoord:带上坐标
+>
+> withdist:带上距离,单位与半径单位相同
+>
+> COUNT n : 只显示前n个(按距离递增排序)
+
+朋友定位,附近的人,打车距离技术,这个功能可以推算地理位置信息,两地之间的距离,方圆几里的人!
+可以查询一些测试数据
+只有六个命令!
+
+> **geoadd**
+
+添加数据
+规则:两级无法添加,我们一般会下载城市数据,直接通过java程序一次性导入!
+参数(纬度 经度 名称)
+超过有效的经度纬度就会报错
+
+```shell
+127.0.0.1:6379> geoadd china:city 116.40 39.90 beijin
+(integer) 1
+127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
+(integer) 1
+127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqi
+(integer) 1
+127.0.0.1:6379> geoadd china:city 114.05 22.52 shengzhen
+(integer) 1
+127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 118.96 34.26 xian
+(integer) 2
+```
+
+> ### geopos
+
+获取当前定位:一定是一个坐标值
+
+```shell
+127.0.0.1:6379> geopos china:city beijin
+1) 1) "116.39999896287918091"
+   2) "39.90000009167092543"
+127.0.0.1:6379> geopos china:city beijin chongqi
+1) 1) "116.39999896287918091"
+   2) "39.90000009167092543"
+2) 1) "106.49999767541885376"
+   2) "29.52999957900659211"
+127.0.0.1:6379> 
+```
+
+> ### geodist
+
+两人之间的距离
+
+```shell
+127.0.0.1:6379> geodist china:city beijin shanghai
+"1067378.7564"   #北京上海的==直线距离,默认单位为米==
+127.0.0.1:6379> geodist china:city beijin shanghai km
+"1067.3788"  
+```
+
+> ### georadius已给定的经纬度为中心,找某一半径内的元素
+
+我附近的人?(获得所有附近的人的地址,定位!)通过半径来查询
+
+```shell
+127.0.0.1:6379> georadius china:city 110 30 1000 km #以100经度30纬度为中心1000km为半径的圆内城市
+1) "chongqi"
+2) "shengzhen"
+3) "hangzhou"
+4) "xian"
+127.0.0.1:6379> georadius china:city 110 30 500 km
+1) "chongqi"
+127.0.0.1:6379> georadius china:city 110 30 500 km withdist #显示到中心位置的距离
+1) 1) "chongqi"
+   2) "341.9374"
+127.0.0.1:6379> georadius china:city 110 30 500 km withcoord #显示他人的定位信息
+1) 1) "chongqi"
+   2) 1) "106.49999767541885376"
+      2) "29.52999957900659211"
+127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 1 #筛选出指定数量的结果
+1) 1) "chongqi"
+   2) "341.9374"
+   3) 1) "106.49999767541885376"
+      2) "29.52999957900659211"
+127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 2
+1) 1) "chongqi"
+   2) "341.9374"
+   3) 1) "106.49999767541885376"
+      2) "29.52999957900659211"
+127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 3
+1) 1) "chongqi"
+   2) "341.9374"
+   3) 1) "106.49999767541885376"
+      2) "29.52999957900659211"
+127.0.0.1:6379> 
+```
+
+> ### georadiusbymember
+
+找出指定城市周围的位置
+
+```shell
+127.0.0.1:6379> georadiusbymember china:city beijin 1000 km
+1) "xian"
+2) "beijin"
+127.0.0.1:6379> 
+```
+
+> ### geohash
+
+该命令返回长度为11的字符串
+
+```shell
+127.0.0.1:6379> geohash china:city beijin chongqi
+1) "wx4fbxxfke0"    #将二维的经纬度转换为一维的字符串,如果字符串月接近,那么距离则越近
+2) "wm5xzrybty0"
+```
+
+> ### geo底层实现原理
+
+**原理其实就是zset,我们可以使用zset命令操作geo!**
+
+```shell
+127.0.0.1:6379> zrange china:city 0 -1 ##查看元素
+1) "chongqi"
+2) "shengzhen"
+3) "hangzhou"
+4) "shanghai"
+5) "xian"
+6) "beijin"
+127.0.0.1:6379> zrem china:city beijin
+(integer) 1
+127.0.0.1:6379> zrange china:city 0 -1
+1) "chongqi"
+2) "shengzhen"
+3) "hangzhou"
+4) "shanghai"
+5) "xian"
+127.0.0.1:6379> 
+```
+
+## Hyperloglog(基数统计)
+
+> Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
+>
+> 花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。
+>
+> 因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
+>
+> 其底层使用string数据类型
+
+什么是基数?
+
+> 数据集中不重复的元素的个数。
+
+应用场景:
+
+网页的访问量(UV):一个用户多次访问,也只能算作一个人。
+
+> 传统实现,存储用户的id,然后每次进行比较。当用户变多之后这种方式及其浪费空间,而我们的目的只是计数,Hyperloglog就能帮助我们利用最小的空间完成。
+
+```shell
+----------PFADD--PFCOUNT---------------------
+127.0.0.1:6379> PFADD myelemx a b c d e f g h i j k # 添加元素
+(integer) 1
+127.0.0.1:6379> type myelemx # hyperloglog底层使用String
+string
+127.0.0.1:6379> PFCOUNT myelemx # 估算myelemx的基数
+(integer) 11
+127.0.0.1:6379> PFADD myelemy i j k z m c b v p q s
+(integer) 1
+127.0.0.1:6379> PFCOUNT myelemy
+(integer) 11
+
+----------------PFMERGE-----------------------
+127.0.0.1:6379> PFMERGE myelemz myelemx myelemy # 合并myelemx和myelemy 成为myelemz
+OK
+127.0.0.1:6379> PFCOUNT myelemz # 估算基数
+(integer) 17
+```
+
+如果允许容错,那么一定可以使用Hyperloglog !
+
+如果不允许容错,就使用set或者自己的数据类型即可 !
+
+## BitMaps(位图)
+
+> 使用位存储,信息状态只有 0 和 1
+>
+> Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR,NOT以及其它位操作。
+
+应用场景
+
+签到统计、状态统计
+
+| 命令                                  | 描述                                                         |
+| ------------------------------------- | ------------------------------------------------------------ |
+| `setbit key offset value`             | 为指定key的offset位设置值                                    |
+| `getbit key offset`                   | 获取offset位的值                                             |
+| `bitcount key [start end]`            | 统计字符串被设置为1的bit数,也可以指定统计范围按字节         |
+| `bitop operration destkey key[key..]` | 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。 |
+| `BITPOS key bit [start] [end]`        | 返回字符串里面第一个被设置为1或者0的bit位。start和end只能按字节,不能按位 |
+
+```shell
+------------setbit--getbit--------------
+127.0.0.1:6379> setbit sign 0 1 # 设置sign的第0位为 1 
+(integer) 0
+127.0.0.1:6379> setbit sign 2 1 # 设置sign的第2位为 1  不设置默认 是0
+(integer) 0
+127.0.0.1:6379> setbit sign 3 1
+(integer) 0
+127.0.0.1:6379> setbit sign 5 1
+(integer) 0
+127.0.0.1:6379> type sign
+string
+
+127.0.0.1:6379> getbit sign 2 # 获取第2位的数值
+(integer) 1
+127.0.0.1:6379> getbit sign 3
+(integer) 1
+127.0.0.1:6379> getbit sign 4 # 未设置默认是0
+(integer) 0
+
+-----------bitcount----------------------------
+127.0.0.1:6379> BITCOUNT sign # 统计sign中为1的位数
+(integer) 4
+```
+
+# 事务
+
+Redis的单条命令是保证原子性的,但是redis事务不能保证原子性
+
+> Redis事务本质:一组命令的集合。
+>
+> ----------------- 队列 set set set 执行 -------------------
+>
+> 事务中每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令进行干扰。
+>
+> - 一次性
+> - 顺序性
+> - 排他性
+>
+> ------
+>
+> 1. Redis事务没有隔离级别的概念
+> 2. Redis单条命令是保证原子性的,但是事务不保证原子性!
+
+## Redis事务操作过程
+
+- 开启事务(`multi`)
+- 命令入队
+- 执行事务(`exec`)
+
+所以事务中的命令在加入时都没有被执行,直到提交时才会开始执行(Exec)一次性完成
+
+```shell
+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 k1
+QUEUED
+127.0.0.1:6379> set k3 v3
+QUEUED
+127.0.0.1:6379> keys *
+QUEUED
+127.0.0.1:6379> exec # 事务执行
+1) OK
+2) OK
+3) "v1"
+4) OK
+5) 1) "k3"
+   2) "k2"
+   3) "k1"
+```
+
+取消事务(`discurd`)
+
+```shell
+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> EXEC 
+(error) ERR EXEC without MULTI # 当前未开启事务
+127.0.0.1:6379> get k1 # 被放弃事务中命令并未执行
+(nil)
+```
+
+## 事务错误
+
+代码语法错误(编译时异常)所有的命令都不执行
+
+```shell
+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> error k1 # 这是一条语法错误命令
+(error) ERR unknown command `error`, with args beginning with: `k1`, # 会报错但是不影响后续命令入队 
+127.0.0.1:6379> get k2
+QUEUED
+127.0.0.1:6379> EXEC
+(error) EXECABORT Transaction discarded because of previous errors. # 执行报错
+127.0.0.1:6379> get k1 
+(nil) # 其他命令并没有被执行
+```
+
+代码逻辑错误 (运行时异常) **其他命令可以正常执行 ** >>> 所以不保证事务原子性
+
+```shell
+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> INCR k1 # 这条命令逻辑错误(对字符串进行增量)
+QUEUED
+127.0.0.1:6379> get k2
+QUEUED
+127.0.0.1:6379> exec
+1) OK
+2) OK
+3) (error) ERR value is not an integer or out of range # 运行时报错
+4) "v2" # 其他命令正常执行
+
+# 虽然中间有一条命令报错了,但是后面的指令依旧正常执行成功了,没有进行回滚。
+# 所以说Redis单条指令保证原子性,但是Redis事务不能保证原子性。
+```
+
+## 监控
+
+悲观锁:
+
+- 很悲观,认为什么时候都会出现问题,无论做什么都会加锁
+
+乐观锁:
+
+- 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据
+- 获取version
+- 更新的时候比较version
+
+使用`watch key`监控指定数据,相当于乐观锁加锁。
+
+> 正常执行
+
+```shell
+127.0.0.1:6379> set money 100 # 设置余额:100
+OK
+127.0.0.1:6379> set use 0 # 支出使用: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 use 20
+QUEUED
+127.0.0.1:6379> exec # 监视值没有被中途修改,事务正常执行
+1) (integer) 80
+2) (integer) 20
+```
+
+> 测试多线程修改值,使用watch可以当做redis的乐观锁操作(相当于getversion)
+
+我们启动另外一个客户端模拟插队线程。
+
+线程1:
+
+```shell
+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 use 20
+QUEUED
+127.0.0.1:6379>      # 此时事务并没有执行
+```
+
+模拟线程插队,线程2:
+
+```shell
+127.0.0.1:6379> INCRBY money 500 # 修改了线程一中监视的money
+(integer) 600
+```
+
+回到线程1,执行事务:
+
+```shell
+127.0.0.1:6379> EXEC # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败
+(nil) # 没有结果,说明事务执行失败
+
+127.0.0.1:6379> get money # 线程2 修改生效
+"600"
+127.0.0.1:6379> get use # 线程1事务执行失败,数值没有被修改
+"0"
+```
+
+> 解锁获取最新值,然后再加锁进行事务。
+>
+> `unwatch`进行解锁。
+
+注意:每次提交执行exec后都会自动释放锁,不管是否成功
+
+# Redis.conf
+
+```shell
+# 单位, 对大小写不敏感
+# 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.
+
+################################## INCLUDES ###################################
+# 可以包含其他配置文件
+# include /path/to/local.conf
+# include /path/to/other.conf
+
+################################## NETWORK #####################################
+# 绑定的本地IP
+bind 127.0.0.1
+
+# 是否是受保护模式
+protected-mode yes
+
+# 端口号
+port 6379
+
+################################# GENERAL #####################################
+
+# 是否开启守护进程,即为后台运行
+daemonize no
+
+# 管理守护进程
+supervised no
+
+# 如果以后台方式允许,就需要执行Pid进程文件
+pidfile /var/run/redis_6379.pid
+
+# 日志级别
+# Specify the server verbosity level.
+# This can be one of:
+# debug (a lot of information, useful for development/testing)
+# verbose (many rarely useful info, but not a mess like the debug level)
+# notice (moderately verbose, what you want in production probably)  生成环境使用
+# warning (only very important / critical messages are logged)
+loglevel notice
+
+# 日志的文件名
+logfile ""
+
+# 默认的数据库数量,默认16个
+databases 16
+
+# 是否显示redis的logo
+always-show-logo yes
+
+################################ 快照  ################################
+# 持久化,在规定时间执行多少次操作,则会持久化到文件.rdb .aof
+# 没有持久化,数据断电即失去
+# 900秒内,至少有一个key进行了修改,进行持久化操作
+save 900 1
+save 300 10
+save 60 10000
+# 之后会执行自己的
+
+# 持久化出错,是否需要继续工作
+stop-writes-on-bgsave-error yes
+
+# 是否压缩rdb文件,需要消耗CPU资源
+rdbcompression yes
+
+# 是否校验rdb文件,如果出错,会自动继续修复
+rdbchecksum yes
+
+# The filename where to dump the DB
+dbfilename dump.rdb
+
+# 持久化文件生成的目录
+dir ./
+
+################################# 主从复制 #################################
+
+# 配置主机的ip+端口
+# replicaof <masterip> <masterport>
+
+# 配置主机的密码
+# masterauth <master-password>
+
+# 当 replica 与 master 失去连接或者主从复制在进行时,replica 可以有两种不同的设置:
+# yes(默认值),则 replica 仍将响应客户端请求,可能会有过期数据,或者如果这是第一次同步,则数据集可能为空。
+# no , replica 将对所有请求命令(但不包含 INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST, HOST: and LATENCY)返回 SYNC with master in progress 的错误。
+# 是否是只读模式
+replica-read-only yes
+################################## 安全 ###################################
+
+# 设置redis密码,默认是空值
+# requirepass foobared
+
+################################### 客户端限制 ####################################
+
+# 最大的客户端数量
+# maxclients 10000
+
+############################## 内存设置 ################################
+
+# 最大内存容量
+# maxmemory <bytes>
+
+# 内存达到上限的处理策略
+1、volatile-lru:只对设置了过期时间的key进行LRU(默认值) 
+
+2、allkeys-lru : 删除lru算法的key   
+
+3、volatile-random:随机删除即将过期key   
+
+4、allkeys-random:随机删除   
+
+5、volatile-ttl : 删除即将过期的   
+
+6、noeviction : 永不过期,返回错误
+# maxmemory-policy noeviction
+
+############################## AOF的配置 ###############################
+
+# 默认不开启AOF,默认使用rdb持久化,大部分情况下rdb完全够用!
+appendonly no
+
+# 持久化的文件名字
+appendfilename "appendonly.aof"
+
+# 同步策略
+# 每次修改就会同步
+# appendfsync always
+# 每秒会同步,有可能丢失1s数据
+appendfsync everysec
+# 不执行sync,操作系统自己同步
+# appendfsync no
+```
+
+# redis持久化
+
+***
+
+面试和工作,持久化都是重点! Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中 的数据库状态也会消失。所以 Redis 提供了持久化功能!
+
+## 持久化—RDB
+
+在主从复制中,rdb就是备用了!从机上面
+
+![image-20220518101146275](assets/image-20220518101146275.png)
+
+在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快 照文件直接读到内存里。
+
+Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程 都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。 这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的就是 RDB,一般情况下不需要修改这个配置! 
+
+有时候在生产环境我们会将这个文件进行备份! 
+
+rdb保存的文件是dump.rdb都是在我们的配置文件中快照中进行配置的!
+
+### 触发机制
+
+1. save的规则满足的情况下,会自动触发rdb原则
+2. 执行flushall命令,也会触发我们的rdb原则
+3. 退出redis,也会自动产生rdb文件
+
+### 如何恢复rdb文件
+
+1.只需要将rdb文件放在redis启动目录就可以,redis启动时就会自动检测dum.rdb文件,恢复其中数据
+2.rdb文件存放的位置
+
+### 优缺点
+
+**优点:**
+
+1. 适合大规模的数据恢复
+2. 对数据的完整性要求不高
+
+**缺点:**
+
+1. 需要一定的时间间隔进行操作,如果redis意外宕机了,这个最后一次修改的数据就没有了。
+2. fork进程的时候,会占用一定的内容空间
+
+## 持久化—AOF
+
+将我们的所有命令都记录下来,history,恢复的时候就把这个文件全部在执行一遍!
+
+![image-20220518102710826](assets/image-20220518102710826.png)
+
+以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件 但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件 的内容将写指令从前到后执行一次以完成数据的恢复工作
+
+**Aof保存的是 appendonly.aof 文件**
+
+`appendonly no yes`则表示启用AOF
+
+默认是不开启的,我们需要手动配置,然后重启redis,就可以生效了!
+
+如果这个aof文件有错位,这时候redis是启动不起来的,我需要修改这个aof文件
+
+redis给我们提供了一个工具`redis-check-aof --fix`
+
+> 优点和缺点
+
+```bash
+appendonly yes  # 默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分的情况下,rdb完全够用
+appendfilename "appendonly.aof"
+
+# appendfsync always # 每次修改都会sync 消耗性能
+appendfsync everysec # 每秒执行一次 sync 可能会丢失这一秒的数据
+# appendfsync no # 不执行 sync ,这时候操作系统自己同步数据,速度最快
+```
+
+优点
+
+1. 每一次修改都会同步,文件的完整性会更加好
+2. 每秒同步一次,可能会丢失一秒的数据
+3. 从不同步,效率最高
+
+缺点
+
+1. 相对于数据文件来说,aof远远大于rdb,修复速度比rdb慢!
+2. Aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化
+
+## RDB和AOP选择
+
+### RDB 和 AOF 对比
+
+|            | RDB    | AOF          |
+| ---------- | ------ | ------------ |
+| 启动优先级 | 低     | 高           |
+| 体积       | 小     | 大           |
+| 恢复速度   | 快     | 慢           |
+| 数据安全性 | 丢数据 | 根据策略决定 |
+
+### 如何选择使用哪种持久化方式?
+
+一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
+
+如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
+
+有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。
+
+![img](assets/1363376-20210622212732123-1297981656.png)
+
+# redis发布订阅
+
+Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
+
+Redis 客户端可以订阅任意数量的频道。 
+
+订阅/发布消息图: 第一个:消息发送者, 第二个:频道 第三个:消息订阅者!
+
+![image-20220518103759533](assets/image-20220518103759533.png)
+
+下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
+
+![在这里插入图片描述](assets/20200513215523258.png)
+
+当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
+
+![image-20220518103925266](assets/image-20220518103925266.png)
+
+## 命令
+
+| 命令                                     | 描述                               |
+| ---------------------------------------- | ---------------------------------- |
+| `PSUBSCRIBE pattern [pattern..]`         | 订阅一个或多个符合给定模式的频道。 |
+| `PUNSUBSCRIBE pattern [pattern..]`       | 退订一个或多个符合给定模式的频道。 |
+| `PUBSUB subcommand [argument[argument]]` | 查看订阅与发布系统状态。           |
+| `PUBLISH channel message`                | 向指定频道发布消息                 |
+| `SUBSCRIBE channel [channel..]`          | 订阅给定的一个或多个频道。         |
+| `SUBSCRIBE channel [channel..]`          | 退订一个或多个频道                 |
+
+## 示例
+
+```shell
+------------订阅端----------------------
+127.0.0.1:6379> SUBSCRIBE sakura # 订阅sakura频道
+Reading messages... (press Ctrl-C to quit) # 等待接收消息
+1) "subscribe" # 订阅成功的消息
+2) "sakura"
+3) (integer) 1
+1) "message" # 接收到来自sakura频道的消息 "hello world"
+2) "sakura"
+3) "hello world"
+1) "message" # 接收到来自sakura频道的消息 "hello i am sakura"
+2) "sakura"
+3) "hello i am sakura"
+
+--------------消息发布端-------------------
+127.0.0.1:6379> PUBLISH sakura "hello world" # 发布消息到sakura频道
+(integer) 1
+127.0.0.1:6379> PUBLISH sakura "hello i am sakura" # 发布消息
+(integer) 1
+
+-----------------查看活跃的频道------------
+127.0.0.1:6379> PUBSUB channels
+1) "sakura"
+```
+
+## 原理
+
+每个 Redis 服务器进程都维持着一个表示服务器状态的 redis.h/redisServer 结构, 结构的 pubsub_channels 属性是一个字典, 这个字典就用于保存订阅频道的信息,其中,字典的键为正在被订阅的频道, 而字典的值则是一个链表, 链表中保存了所有订阅这个频道的客户端。
+
+![image-20220518104417240](assets/image-20220518104417240.png)
+
+客户端订阅,就被链接到对应频道的链表的尾部,退订则就是将客户端节点从链表中移除。
+
+## 缺点
+
+1. 如果一个客户端订阅了频道,但自己读取消息的速度却不够快的话,那么不断积压的消息会使redis输出缓冲区的体积变得越来越大,这可能使得redis本身的速度变慢,甚至直接崩溃。
+2. 这和数据传输可靠性有关,如果在订阅方断线,那么他将会丢失所有在短线期间发布者发布的消息。
+
+## 应用
+
+1. 消息订阅:公众号订阅,微博关注等等(起始更多是使用消息队列来进行实现)
+2. 多人在线聊天室。
+
+稍微复杂的场景,我们就会使用消息中间件MQ处理。
+
+# Redis主从复制
+
+ ## 概念
+
+主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), 数据的复制是单向的!只能由主节点复制到从节点(主节点以写为主、从节点以读为主)。
+
+默认情况下,每台Redis服务器都是主节点,一个主节点可以有0个或者多个从节点,但每个从节点只能由一个主节点。
+
+## 作用
+
+1. **数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余的方式。**
+2. **故障恢复:当主节点故障时,从节点可以暂时替代主节点提供服务,是一种服务冗余的方式**
+3. **负载均衡:在主从复制的基础上,配合读写分离,由主节点进行写操作,从节点进行读操作,分担服务器的负载;尤其是在多读少写的场景下,通过多个从节点分担负载,提高并发量。**
+4. **高可用基石:主从复制还是哨兵和集群能够实施的基础。**
+
+## **为什么使用集群**
+
+1. **单台服务器难以负载大量的请求**
+2. **单台服务器故障率高,系统崩坏概率大**
+3. **单台服务器内存容量有限。**
+
+## 环境配置
+
+我们在讲解配置文件的时候,注意到有一个`replication`模块 (见Redis.conf中第8条)
+
+查看当前库的信息:`info replication`
+
+```shell
+127.0.0.1:6379> info replication
+# Replication
+role:master # 角色
+connected_slaves:0 # 从机数量
+master_replid:3b54deef5b7b7b7f7dd8acefa23be48879b4fcff
+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
+```
+
+既然需要启动多个服务,就需要多个配置文件。每个配置文件对应修改以下信息:
+
+- 端口号
+- pid文件名
+- 日志文件名
+- rdb文件名
+
+启动单机多服务集群:
+
+## 一主二从配置
+
+默认情况下,每台Redis服务器都是主节点;我们一般情况下只用配置从机就好了!
+
+认老大!一主(79)二从(80,81)
+
+使用`SLAVEOF host port`就可以为从机配置主机了。
+
+有树状模型和普通的1对多模型
+
+![image-20220520153051951](assets/image-20220520153051951.png)
+
+![image-20220520153115865](assets/image-20220520153115865.png)
+
+## 使用规则
+
+1. 从机只能读,不能写,主机可读可写但是多用于写。
+
+   ```shell
+    127.0.0.1:6381> set name sakura # 从机6381写入失败
+   (error) READONLY You can't write against a read only replica.
+   
+   127.0.0.1:6380> set name sakura # 从机6380写入失败
+   (error) READONLY You can't write against a read only replica.
+   
+   127.0.0.1:6379> set name sakura
+   OK
+   127.0.0.1:6379> get name
+   "sakura"
+   ```
+
+2. 当主机断电宕机后,默认情况下从机的角色不会发生变化(此时从机不能进行读操作) ,集群中只是失去了写操作,当主机恢复以后,又会连接上从机恢复原状。
+
+3. 当从机断电宕机后,若不是使用配置文件配置的从机,再次启动后作为主机是无法获取之前主机的数据的,若此时重新配置称为从机,又可以获取到主机的所有数据。这里就要提到一个同步原理。
+
+4. 第二条中提到,默认情况下,主机故障后,不会出现新的主机,有两种方式可以产生新的主机:
+
+   - 从机手动执行命令`slaveof no one`,这样执行以后从机会独立出来成为一个主机
+   - 使用哨兵模式(自动选举)
+
+> 如果没有老大了,这个时候能不能选择出来一个老大呢?手动!
+
+如果主机断开了连接,我们可以使用`SLAVEOF no one`让自己变成主机!其他的节点就可以手动连接到最新的主节点(手动)!如果这个时候老大修复了,那么久重新连接!
+
+## 复制原理
+
+Slave 启动成功连接到 master 后会发送一个sync同步命令 
+
+Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行 完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。 
+
+全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。 
+
+增量复制:Master 继续将新的所有收集到的修改命令依次传给slave,完成同步 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行! 我们的数据一定可以在从机中 看到!
+
+# 哨兵模式
+
+(自动选举老大的模式)
+
+***
+
+## 概述
+
+主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵) 架构来解决这个问题。
+
+谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
+
+哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
+
+![image-20220520152007648](assets/image-20220520152007648.png)
+
+这里的哨兵有两个作用
+
+- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
+- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
+
+然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。![image-20220520152042159](assets/image-20220520152042159.png)
+
+假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象称为**主观下线**。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover故障转移操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为**客观下线**
+
+## 优缺点
+
+优点:
+
+1、哨兵集群,基于主从复制模式,所有的主从配置优点,它全有
+
+2、 主从可以切换,故障可以转移,系统的可用性就会更好
+
+3、哨兵模式就是主从模式的升级,手动到自动,更加健壮!
+
+缺点:
+
+1、Redis 不好在线扩容的,集群容量一旦到达上限,在线扩容就十分麻烦!
+
+2、实现哨兵模式的配置其实是很麻烦的,里面有很多选择!
+
+## 配置
+
+```shell
+# Example sentinel.conf
+# 哨兵sentinel实例运行的端口 默认26379
+port 26379
+# 哨兵sentinel的工作目录
+dir /tmp
+# 哨兵sentinel监控的redis主节点的 ip port
+# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
+# quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
+# sentinel monitor <master-name> <ip> <redis-port> <quorum>
+sentinel monitor mymaster 127.0.0.1 6379 2
+# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
+# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
+# sentinel auth-pass <master-name> <password>
+sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
+# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
+# sentinel down-after-milliseconds <master-name> <milliseconds>
+sentinel down-after-milliseconds mymaster 30000
+# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行同步,
+# 这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
+# 可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
+# sentinel parallel-syncs <master-name> <numslaves>
+sentinel parallel-syncs mymaster 1
+# 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
+#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
+#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
+#3. 当想要取消一个正在进行的failover所需要的时间。
+#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
+# 默认三分钟
+# sentinel failover-timeout <master-name> <milliseconds>
+sentinel failover-timeout mymaster 180000
+# SCRIPTS EXECUTION
+#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
+#对于脚本的运行结果有以下规则:
+#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
+#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
+#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
+#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
+#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
+#通知脚本
+# shell编程
+# sentinel notification-script <master-name> <script-path>
+sentinel notification-script mymaster /var/redis/notify.sh
+# 客户端重新配置主节点参数脚本
+# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
+# 以下参数将会在调用脚本时传给脚本:
+# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
+# 目前<state>总是“failover”,
+# <role>是“leader”或者“observer”中的一个。
+# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
+# 这个脚本应该是通用的,能被多次调用,不是针对性的。
+# sentinel client-reconfig-script <master-name> <script-path>
+sentinel client-reconfig-script mymaster /var/redis/reconfig.sh # 一般都是由运维来配置!
+```
+
+# Redis缓存穿透和雪崩
+
+***
+
+服务的高可用问题!
+
+在这里我们不会详细的区分析解决方案的底层!
+
+Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也都有比较流行的解决方案。
+
+![image-20220520164337367](assets/image-20220520164337367.png)
+
+## 缓存穿透(查不到)
+
+### 概念
+
+缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中(秒杀!),于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。
+
+### 解决方案
+
+布隆过滤器
+
+布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力;
+
+![image-20220520164434639](assets/image-20220520164434639.png)
+
+## 缓存击穿(量太大,缓存过期)
+
+### 概述
+
+这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
+
+当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。
+
+### 解决方案
+
+**设置热点数据永不过期**
+
+从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题。
+
+**加互斥锁**
+
+分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
+
+![image-20220520164601716](assets/image-20220520164601716.png)
+
+## 缓存雪崩
+
+### 概念
+
+缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis 宕机!
+
+产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
+
+![image-20220520164627927](assets/image-20220520164627927.png)
+
+其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。
+
+### 解决方案
+
+**redis高可用**
+
+这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。(异地多活!)
+
+**限流降级**(在SpringCloud讲解过!)
+
+这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
+
+**数据预热**
+
+数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

+ 2 - 2
大数据/kafka/1.kafka笔记.md

@@ -114,7 +114,7 @@ ls /brokers/ids/
 
 * 查看当前zk中所有的主题
 ```
-./kafka-topics.sh --list --zookeeper 192.168.245.21:2181
+./kafka-topics.sh --list --zookeeper 192.168.0.:2181
 ```
 
 ## 4.发送消息
@@ -159,7 +159,7 @@ kafka⾃带了⼀个producer命令客户端,可以从本地⽂件中读取内
 
 如果多个消费者在同⼀个消费组,那么只有⼀个消费者可以收到订阅的topic中的消息。换⾔之,同⼀个消费组中只能有⼀个消费者收到⼀个topic中的消息。
 ```
-./kafka-console-consumer.sh --bootstrap-server 192.168.0.4:9092 --consumer-property group.id=testGroup --topic test
+./kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --consumer-property group.id=testGroup --topic test
 ```
 
 ## 8.多播消息

BIN
大数据/zookeeper/assets/image-20220424134137986.png


BIN
大数据/zookeeper/assets/image-20220424134506741.png


BIN
大数据/zookeeper/assets/image-20220509154618465.png


BIN
大数据/zookeeper/assets/image-20220510102509795.png


BIN
大数据/zookeeper/assets/image-20220510102714102.png


+ 264 - 0
大数据/zookeeper/zookeeper.md

@@ -0,0 +1,264 @@
+## 1、zookeeper的基本介绍
+
+### 1.1、什么是Zookeeper
+
+官方文档上这么解释zookeeper,它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
+
+### 1.2、Zookeeper应用场景
+
+1. 注册中心(Dubbo+Zookeeper)
+
+2. 分布式配置中心(统一存放配置文件)
+
+3. 分布式锁
+
+4. 分布式队列
+
+5. 分布式文件系统
+
+### 1.3、linux安装zookeeper
+
+```shell
+# 1. 解压zk压缩包
+tar -zxvf zookeeper-3.4.14.tar.gz
+
+# 2. 进入到zk目录
+cd zookeeper-3.4.14
+
+# 3.在zk目录中创建data和logs文件夹
+mkdir data
+mkdir logs
+
+# 4.进入到conf目录,修改文件名称
+mv zoo_sample.cfg zoo.cfg
+vi zoo.cfg
+dataDir =/usr/local/zookeeper-3.4.14/data
+dataLogDir=/usr/local/zookeeper-3.4.14/logs
+
+# 5.启动zk
+./zkServer.sh start
+./zkServer.sh status
+```
+
+   ### 14.、zookeeper的基本特征
+
+文件系统:定义的节点不允许重复
+
+基本特征
+
+* 定义的节点包含节点名称和节点内容
+* 定义的节点名称不允许重复
+* 每个节点可以设定值
+* 根节点允许有多个
+* 节点路径必须保证是唯一的,不允许重复
+* 每个节点都会有事件通知,每当节点发生变化都会获取信息,例如删除修改
+
+### 1.5、zookeeper节点的四种类型
+
+1. 临时节点:会话关闭,自动消失
+2. 临时顺序节点
+3. 持久节点:会话关闭,持久化存储在硬盘
+4. 持久顺序节点:会自动序列自增,保证唯一
+
+![image-20220424134137986](assets/image-20220424134137986.png)
+
+> 注意:要先创建父节点才能创建子节点
+
+![image-20220424134506741](assets/image-20220424134506741.png)
+
+### 1.6、ZooKeeper -状态信息 Stat 的属性说明
+
+| 名称           | 含义                                                         |
+| -------------- | ------------------------------------------------------------ |
+| cZxid          | 数据节点创建时的事务ID                                       |
+| ctime          | 数据节点创建时的时间                                         |
+| mZxid          | 数据节点最后一次更新时的事务ID                               |
+| mtime          | 数据节点最后一次更新时的时间                                 |
+| pZxid          | 数据节点的子节点列表最后一次被修改(是子节点列表变更,而不是子节点内容变更)时的事务ID |
+| cversion       | 子节点的版本号                                               |
+| dataVersion    | 数据节点的版本号                                             |
+| aclVersion     | 数据节点的ACL版本号                                          |
+| ephemeralOwner | 如果节点是临时节点,则表示创建该节点的会话的SessionID;如果节点是持久节点,则该属性值为0 |
+| dataLength     | 数据内容的长度                                               |
+| numChildren    | 数据节点当前的子节点个数                                     |
+
+
+
+## 2、ACL权限控制
+
+ACL权限模型,实际上就是对树每个节点实现控制
+
+身份的认证有4种方式:
+* world:默认方式,相当于全世界都能访问
+* auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)
+* digest:即用户名:密码这种方式认证,这也是业务系统中最常用的
+* ip:使用Ip地址认证 
+
+## 3、分布式锁的实现方式
+
+1. 基于数据库实现分布式锁
+2. 基于redis实现分布式锁
+3. 基于zk实现分布式锁
+4. 基于redission实现分布式锁
+
+zk实现原理:临时节点 + 事件通知
+1. 多请求同时创建相同的节点(lockPath),只要谁能够创建成功 谁就能够获取到锁;
+2. 如果创建节点的时候,突然该节点已经被其他请求创建的话则直接等待;
+3. 只要能够创建节点成功,则开始进入到正常业务逻辑操作,其他没有获取锁进行等待;
+4. 正常业务逻辑流程执行完后,调用zk关闭连接方式释放锁,从而是其他的请求开始进入到获取锁的资源。
+
+> 疑问:如果使用zk实现分布式锁,获取锁之后业务逻辑方法一直没有执行完毕,导致其他所有的请求等待的话如何解决?
+>
+> 设置Session连接超时时间,在规定的时间内获取锁后超时啦~自动回滚当前数据库业务逻辑。
+
+## 4、一致性基本思想
+
+> 注意:zk集群节点最好是奇数。
+>
+> 基本规则:剩余节点的总数大于集群节点总数/2,zk才可以正常运行
+
+![image-20220509154618465](assets/image-20220509154618465.png)
+
+### 4.1、一致性基本思想
+
+1. 强一致性
+
+步骤1在修改数据库的userName为mayikt的时候,步骤2读取的userName内容;如果要一定读取到userName为mayikt而不是meite的话,那么数据库之间的通讯要非常迅速或者步骤2等待步骤1`(这里可以使用锁)`更新同步完成之后在查询,这种流程我们称为强一致性。
+
+> **注意:在分布式领域不可能保证强一致性**
+
+2. 弱一致性
+
+允许数据库之间同步存在短暂的延迟,步骤2可以读取userName内容为meite而不是必须为mayikt;这种我们可以称作为弱一致性;
+
+3. 最终一致性
+
+允许步骤2读取userName为meite,中间允许数据库存在短暂延迟、但是最终读取数据结构必须为mayikt,所以这种访问我们称作为最终一致性;
+
+### 4.2、zk如何保证一致性
+
+原理:投票过半机制,2PC两阶段提交协议
+
+预提交,接受回复,执行请求
+
+如果已经满足了过半机制成为领导角色,后面的角色不需要进行选择步骤
+
+### 4.3、Zookeeper集群使用Observer实现扩展
+
+首先在Zookeeper中分为三种角色:
+
+1. Leader(领导) Zookeeper集群中的主节点、负责写的请求操作,和各个节点同步;
+
+2. Follower(跟随者) 是领导(Leader)角色根随着,出读取操作还可以实现对Leader提议与选举
+
+3. Observer 如果后期当我们在扩展ZK集群节点时如果角色为Follower越来越多会导致选举的时间越来越长,所以Observer角色和Follower角色很相似,只是obServer不能够参与Leader角色选举;
+
+**增加obServer的作用主要不影响原来本身选举的时间效率、目的是提高客户端读的请求效率;**
+
+> ObServer相关配置
+>
+> 在zoo.conf 文件中新增
+>
+> server.4:192.168.212.212:2181:3181:observer
+
+### 4.4、Zk集群节点总数为什么要是为奇数?
+
+1. 加入Zookeeper的集群节点为5的话,宕机几个Zookeeper节点之后剩余的节点个数大于宕机个数;也就是必须大于剩下服务器个数n(集群节点总数)/2,Zookeeper才可以继续使用
+
+2. 无论是奇数还是偶数个数可以做选举leader的,如果是5台集群节点也就是最多只可以允许两台zk宕机;如果是六台zk集群节点那么也是最多只能宕机2台,如果宕机3台的话zk无法可能。
+
+3. 所以占用服务器空间利用成本角度考虑,建议zk集群节点一定是为奇数。
+
+ ## 5、Zookeeper集群选举原理
+
+### 5.1、Zookeeper读写机制
+
+Zookeeper是有多个Server组成的集群,只有一个leader和多个follower
+
+1. 每个follower节点保存父节点的副本;
+
+2. 全局数据一定要保持一致;
+
+3. 分布式读写模式,写的请求统一交个leader完成、follower节点主要实现读操作;
+
+**注意:如果连接的节点不是leader,他会转发给leader进行写操作**
+
+### 5.2、Zookeeper数据如何实现同步
+
+1. 所有follower节点写的请求统一交个leader实现,并且创建一个全局zxid(事务id)
+
+2. Leader节点在第一阶段通知阶段,会带上zxid向每位follower节点发出确认同步通知
+
+3. 只要有过半数的follower节点确认同步ack,这时候leader就会向所有的follower发出commit事务数据提交;
+
+这个和两阶段提交非常类似
+
+> 为什么我们写的请求必须统一交给leader而不是follower节点实现?
+>
+> 因为可以采用借鉴在分布式事务中2pc(两阶段提交协议)解决分布式数据同步问题。
+
+![image-20220510102714102](assets/image-20220510102714102.png)
+
+### 5.3、Zab原子广播两种协议
+
+​		Zookeeper核心是原子广播协议(ZAB原子广播协议),这种机制保证了各个节点的数据同步的问题,ZAB协议有有两种模式 分别为恢复模式(选主)和广播模式(同步)。
+
+​		恢复模式:也就是当我们leader的节点宕机之后,会从新在剩余节点选出新的leader,新的leader选出之后采用广播模式实现各个节点与新的leader同步
+​		广播模式:解决每个节点数据同步问题
+
+### 5.4、zk数据同步原理
+
+​		当我们zk中发出一个事务请求的时候,这时候我们Leader节点就会创建一个全局的zxid事务id。zxid会上锁保障数据的一致性,每次只能处理一个写操作。当我们写操作完成的时候,就会进行2PC同步。
+​		第一阶段同步,带上zxid告诉每个Follower节点是否允许同步数据,Follower会返回给ACK。
+​		第二阶段,如果收到半数ACK,leader节点就会发送commit(同步zxid和数据),发送数据完成数据同步。
+
+> 为什么我们写的请求必须统一交给leader而不是follower节点实现?
+>
+> 因为可以采用借鉴在分布式事务中2pc(两阶段提交协议)解决分布式数据同步问题。
+
+### 5.5、zxid有什么用?(选举实现原理)
+
+发起投票的时候每个Server段都会产生(myid,zxid)选举,zxid(默认情况下为0)取决于每个节点最后一次做事务写的请求保存的ID
+接受自己投票实现PK
+
+1. 先检查zxid,谁最大,谁就是leader
+2. 如果zxid都是一样大的情况下,myid最大的就是leader
+3. leader的选择也与节点的启动顺序有关
+4. 实现中Zxid是一个64位的数字,它高32位是epoch用来标识Leader关系是否改变,低32位来表示事务的顺序,用来保障事物的一致性。
+
+> 为什么要先比较zxid呢?
+>
+> 因为zxid表示该节点有最新的数据。
+
+### 5.6、网络分区(脑裂)
+
+在集群的情况下,一般只会选举一个master节点、其他都是为从节点,那么如果发生了网络抖动或者部分节点相互无法通讯那么就会导致部分节点从新选举,这样就会存在多个master节点;
+
+## 6、CAP理论
+
+1. C:Consistency 在分布式系统中的所有数据备份,在同一时刻是否同样的值。等同于所有节点访问同一份最新的数据副本
+2. A:Availability,在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求
+3. P:Partition tolerance 在分布式系统中网络分区存在脑裂问题以后,部分server与集群其他节点失去联系 无法组成一个群体; 该问题一定是存在的~~~
+
+目前我们当前技术环境下,不能同时满足CA,但是可以满足CP或者AP
+
+>  为什么不能保证CA呢?
+>
+> 因为我们服务节点宕机,很难保证同一时刻同步问题。
+
+## 7、Eureka与Zookeeper区别
+
+### 相同点:
+
+* Eureka和Zookeeper都可以实现微服务注册中心;
+
+* 首先在这时候要明白一点:
+  服务注册中心,可以短暂读取以前服务注册列表信息,但是不可以接受节点宕机不可用;
+
+### 不同点:
+
+* Zookeeper保证CP(一致性和分区容错) 当ZK在某种情况下出现宕机,会重新实现ZK选举新的领导者,如果zk选举的新的领导者时间过长,或者投票没有过半数,那么会导致整个zk集群节点不可用的,这也以为者服务注册中心不可用 ,所以Zk必须要保证数据一致性问题;
+
+* Eureka保证AP,设计时优先考虑可用性,完全去中心化的设计思想,每个节点都均等;没有主从区分,几个节点挂掉了也不会影响正常的Eureka使用,Eureka客户端在连接时发现连接不可用会自动切换其他连接,只要Eureka有一个节点存在也就可以保证Eureka整个服务注册中心的使用;
+
+* Zk保证数据一致性问题,Eureka保证可用性;

+ 13 - 0
煤矿项目问题汇总/华为云.md

@@ -0,0 +1,13 @@
+IP地址:https://10.168.60.10:8443/OmsPortal/index.jsp
+
+账号:DongVM
+
+密码:dong123456
+
+虚拟机:dong’1,dong2,dong3
+
+注意事项:请使用火狐浏览器!!!请使用火狐浏览器!!!请使用火狐浏览器!!!请使用火狐浏览器!!!
+
+ 
+
+别动其他人的虚拟机

+ 7 - 4
部署文档/大数据平台/大数据平台环境搭建.md

@@ -351,15 +351,18 @@ hadoop集群规划:
    
    # 修改如下配置
    KAFKA_JMX_OPTS="
+   # -Dsun.rmi.transport.tcp.responseTimeout=60000   #超时时间
+   # -Dcom.sun.management.jmxremote.local.only=false"  #k
+   
    -Dcom.sun.management.jmxremote=true 
    -Dcom.sun.management.jmxremote.authenticate=false  
    -Dcom.sun.management.jmxremote.ssl=false 
    -Djava.rmi.server.hostname=服务器的IP地址或者域名
-   -Dsun.rmi.transport.tcp.responseTimeout=60000   #超时时间
-   -Dcom.sun.management.jmxremote.rmi.port=9999    #端口
-   -Dcom.sun.management.jmxremote.port=9999        #端口
-   -Dcom.sun.management.jmxremote.local.only=false"
    
+   # JMX port to use
+   if [  $JMX_PORT ]; then
+      KAFKA_JMX_OPTS="$KAFKA_JMX_OPTS -Dcom.sun.management.jmxremote.port=$JMX_PORT -Dcom.sun.management.jmxremote.rmi.port=$JMX_PORT"
+   fi
    ```
 
    ```shell

+ 158 - 0
部署文档/大数据平台/脚本.md

@@ -0,0 +1,158 @@
+# 修改ssh配置
+
+```shell
+cd /etc
+chmod 777 sudoers
+vim sudoers
+# 注释掉 Default requiretty 一行    ------  第56行
+chmod 600 sudoers
+```
+
+# 环境变量
+
+```shell
+cd /etc/profile.d
+vim myenv.sh
+# JAVA
+export JAVA_HOME=/opt/modules/java
+export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/jre/lib/rt.jar
+export PATH=$PATH:$JAVA_HOME/bin
+
+# spark
+export SPARK_HOME=/opt/modules/spark-3.1.2
+export PATH=$PATH:$SPARK_HOME/bin
+
+# scala
+export SCALA_HOME=/opt/modules/scala-2.12.15
+export PATH=$PATH:$SCALA_HOME/bin
+
+# minio
+export MINIO_ROOT_USER=minio
+export MINIO_ROOT_PASSWORD=minio123
+
+# HADOOP_HOME
+export HADOOP_HOME=/opt/modules/hadoop-3.1.3
+export PATH=$PATH:$HADOOP_HOME/bin
+export PATH=$PATH:$HADOOP_HOME/sbin
+
+# HIVE_HOME
+export HIVE_HOME=/opt/modules/hive
+export PATH=$PATH:$HIVE_HOME/bin
+
+# ZOOKEEPER_HOME
+export ZOOKEEPER_HOME=/opt/modules/zookeeper-3.4.14
+export PATH=$PATH:$ZOOKEEPER_HOME/bin
+
+# KAFKA_HOME
+export KAFKA_HOME=/opt/modules/kafka_2.11-2.1.1
+export PATH=$PATH:$KAFKA_HOME/bin
+
+# MYSQL_HOME
+export MYSQL_HOME=/opt/modules/mysql
+export PATH=$PATH:$MYSQL_HOME/bin
+
+# HBASE_HOME
+export HBASE_HOME=/opt/modules/hbase-2.2.6
+export PATH=$PATH:$HBASE_HOME/bin
+export HBASE_LIBRARY_PATH=/opt/modules/hbase-2.2.6/lib/native/Linux-amd64-64
+```
+
+> **不要忘了source一下!不要忘了source一下!不要忘了source一下!**
+>
+> **不要忘了source一下!不要忘了source一下!不要忘了source一下!**
+>
+> **不要忘了source一下!不要忘了source一下!不要忘了source一下!**
+>
+> `source /etc/profile`
+
+# 启动脚本---(脚本名字随便取,务必以sh结尾,eg start.sh)
+
+```shell
+#! /bin/bash
+
+## 三台机器都有环境变量是前提,否则修改脚本中启动命令为全路径
+## start zookeeper cluster 
+ssh lab1 zkServer.sh start
+ssh lab2 zkServer.sh start
+ssh lab3 zkServer.sh start
+
+## start hdfs
+start-dfs.sh
+
+## start yarn
+ssh lab2 start-yarn.sh
+
+## start hbase
+start-hbase.sh
+
+## start kafka
+ssh lab1 kafka-server-start.sh -daemon /opt/modules/kafka_2.11-2.1.1/config/server.properties
+ssh lab2 kafka-server-start.sh -daemon /opt/modules/kafka_2.11-2.1.1/config/server.properties
+ssh lab3 kafka-server-start.sh -daemon /opt/modules/kafka_2.11-2.1.1/config/server.properties
+
+## start solr---------需要修改ssh配置
+ssh lab1 sudo -i -u solr /opt/modules/solr/bin/solr start
+ssh lab2 sudo -i -u solr /opt/modules/solr/bin/solr start
+ssh lab3 sudo -i -u solr /opt/modules/solr/bin/solr start
+
+# start atlas
+ssh lab2 /opt/modules/atlas-2.2.0/bin/atlas_start.py
+```
+
+# 关闭脚本---(脚本名字随便取,务必以sh结尾,eg stop.sh)
+
+```shell
+#! /bin/bash
+## 三台机器都有环境变量是前提,否则修改脚本中启动命令为全路径
+
+# stop atlas
+ssh lab2 /opt/modules/atlas-2.2.0/bin/atlas_start.py
+
+ssh lab1 sudo -i -u solr /opt/modules/solr/bin/solr stop
+ssh lab2 sudo -i -u solr /opt/modules/solr/bin/solr stop
+ssh lab3 sudo -i -u solr /opt/modules/solr/bin/solr stop
+
+
+## stop kafka
+ssh lab1 kafka-server-stop.sh
+ssh lab2 kafka-server-stop.sh
+ssh lab3 kafka-server-stop.sh
+
+## stop hbase
+stop-hbase.sh
+
+## stop yarn
+ssh lab2 stop-yarn.sh
+
+## stop hdfs
+stop-dfs.sh
+
+## stop zookeeper cluster 
+ssh lab1 zkServer.sh stop
+ssh lab2 zkServer.sh stop
+ssh lab3 zkServer.sh stop
+```
+
+# 监控脚本
+
+```shell
+cd /usr/local/bin
+touch jpsall
+chmod +x jpsall
+vim jpsall
+
+#!/bin/bash
+# 执行jps命令查询每台服务器上的节点状态
+echo ======================集群节点状态====================
+
+for i in lab1 lab2 lab3
+do
+        echo ====================== $i ====================
+        ssh root@$i '/opt/modules/java/bin/jps'
+done
+echo ======================执行完毕====================
+
+```
+
+
+