优惠券分发系统-学习笔记-第二章

第二章学习旅程记录

Maven

maven 常用命令

  • mvn -v 查看版本
  • mvn compile 编译
  • mvn test 执行测试用例
  • mvn package 打包成 jar 包
  • mvn clean 清楚 target 下的内容
  • mvn install 当前项目安装到本地

可以再命令后加入参数 -Dmaven.test.skip=true -U 来跳过测试用例的测试。

maven 常用概念

传递依赖:直接引入的 jar 包和它依赖的间接引入的 jar 包都会被引入。

依赖排除:可以再 pom.xml 中通过指明要排除依赖的 jar 包的坐标来排除不需要的依赖。

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.apache.hbasee</groupId>
<artifactId>hbase</artifactId>
<version>0.94.17</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

maven 冲突解决

maven 理论上不会发生依赖冲突,因为 maven 具有下列特征

  • 短路优先
  • 声明优先

短路优先即发生依赖冲突时,谁短先引用谁,所以不会存在依赖冲突。

声明优先即如果发生冲突且长度一致,谁先声明,引用谁。

maven 多项目/模块聚合知识点

  • 父模块项目POM的 packing 类型必须是 pom
  • 聚合子模块使用 modules标签
  • 使用父模块来统一管理依赖包,使用 dependencyManagement 标签
    • dependencies 标签声明的子模块都会全部继承。
    • dependencyManagement 只是在父类进行声明,子模块声明后才进行引入。
    • 通过 dependencyManagement 声明的,子模块需要显示声明的依赖,才会进行继承。
    • 子项目如果需要指定版本,显式指定版本号即可。
  • 子模块需要在 POM 中声明父模块,从而进行依赖引用等。

Redis

Redis 是一个基于内存的 key-value 数据库,也可以称作缓存数据库,它不需要提前定义 scheme,所以是非关系型数据库。

Redis 所有操作都是原子的,是数据库事务的特性,即要不全执行,要不全都不执行。这是数据库理论里面的原子性,即操作不可继续划分,为最小操作。原因是因为 Redis 的IO模型是单线程,单进程,顺序操作。

常用数据类型

  • String
    • 最基本的类型,二进制存储,所以可以包含任何数据,单个value的最大可以为1GB
  • List
    • 存储序列集合,有序
    • 双向列表实现
    • 可以当做栈和队列
    • 不要求元素唯一
  • Hash
    • 字典
    • 存储多个 key-value 数据
    • 多个 key-value 是一类的
    • 类似 java hashmap
  • Set
    • 无序的方式存储多个不同的元素。
    • 相同元素只保存一个
    • 最大长度为 2的32次方-1
    • 除了基本操作外,还包含集合的并集,交集,差集等操作
  • SortedSet
    • 与 set 类型类型
    • 不同的是,每个元素都关联一个浮点数的权重值
    • 通过权重值来有序的获取集合中的元素
    • 如果元素权重一致,则按照元素的字节排序来获取
    • 最常见应用,排行榜

Key 过期策略

redi可以对 key 设置过期时间,理论上实现方式有三种

  1. 定时删除
    • 即创建key时启动一个定时器,到时间进行删除,不浪费内存,但消耗了CPU资源
  2. 惰性删除
    • 不管key是否过期,但每次获取key时,要进行检查,如果过期,则删除key
    • 平时不处理,使用时进行检查,过期则删除,未过期则返回该值。
    • 浪费了内存,但节约了CPU资源
    • 假如一个key永远不使用,则会一直保存在内存中,浪费内存
  3. 定期删除
    • 启动一个定时器,按照一定时间标准,对所有过期的key进行删除,耗费了内存,CPU占用优化

redis 的过期策略是使用了 惰性删除和定期删除

Redis 持久化方式

  • RDB (默认方式,快照)
    • 定期全量持久化数据到内存
    • 占用资源多
    • 定期间隔长,恢复时可能丢失数据
    • 一般线上环境不采用
    • 恢复速度快
  • AOF (APPEND OPERATION FILE)
    • 把用户写指令保存在文件中
    • 占用资源少
    • 恢复慢
    • 正式环境一般采用该方式

Redis 速度快的原因

  • 完全基于内存
  • 数据结构简单,避免了一些复杂操作
  • 单线程,没有线程切换和竞争带来的性能影响
  • 多路IO复用模型

Redis 常见问题

  • 缓存穿透
    • cache 不命中,数据库中又没有,查询了两次,性能低下。
    • 解决办法:给没有命中的key设定没有意义的空值
  • 缓存雪崩
    • 同一批cache设置了相同的过期时间,同一时间失效,请求全部转发的DB,操作DB压力过大
    • 解决办法:给key设定不同的(随机的)过期时间

Redis IO 模型

基本概念

文件描述符:内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。简称 FD

常见 IO 模型

  1. Blocking IO 阻塞型
    • 当 read/write 对某一个文件描述符(FD)进行读写时,如果当前FD不可读或不可写,则服务阻塞
  2. IO 多路复用模型
    • 同时监控(select/epoll 根据操作系统不同和性能不能进行选择,优先选择性能高的函数)多个FD的可读可写状态,当其中的某些FD可读或可写时,就返回可读以及可写的文件描述符(个数)

Redis IO 模型

Redis IO 模型

每一个网络连接(访问redis的请求)对应的是一个文件描述符,即FD
通过 redis IO 多路复用模块,去找到一个可操作的FD,然后交给 redis 事件分发器(单线程),去交给 redis 事件处理器来执行操作,从而达到了单线程,但是能处理多个任务。

MySQL

常用的 MySQL 版本

  • MySQL 5.5
    • 2010年发行 达到了企业级使用标准 innodb 是MySQL 默认存储引擎,具有提交,回滚,兼容ACID,行级锁等特性
  • MySQL 5.6
    • 2012发布 explain 语句优化,支持全文索引
  • MySQL 5.7
    • 2015 性能优化,易用性优化
  • MySQL 8.0
    • 性能优化,比 MySQL 5.7 快两倍

使用 MySQL 8.0 开发时,部分配置参数发生了变化

  • driverclass 变成了 com.mysql.cj.jdbc.Driver
  • 数据库要配置时区,set GLOBAL time_zone = ‘+8:00’;

MySQL 索引

B树和B+树

B树是一种多路平衡树,M阶的B树具有以下特征:

  1. 树中的每个节点最多有M个子节点。
  2. 除了根节点和叶子节点外,其他每个节点至少有 M/2 个子节点
  3. 所有的叶子节点都在同一层
  4. 节点中关键字的顺序按照升序排列

btree

B树和B+树通常用于数据库或文件系统中,因为它的结构可以减少磁盘IO的次数,减少深度,因为实际的数据块(节点)是存储在文件系统中的,如果深度过深,就很有很多IO发生。
它们的特点是能保证数据稳定有序。插入和修改都有对数时间复杂度。

B+树是B树的变体,同样是多路平衡查找树,它与B树不同的是

  1. 非叶子节点不存储数据,只存储索引。
  2. 叶子节点包含了全部的关键信息,且叶子节点按照关键字顺序相互连接。

简要概述,叶子节点存储了所有数据,并且按照关键字的顺序来进行连接。

b+tree

MySQL 聚簇索引

每个innodb的表都有一个索引,称之为聚簇索引,此索引中存储着行记录,一般来说,聚簇索引是根据主键生成的。

聚簇索引创建规则

  1. 有定义主键,利用主键生成聚簇索引
  2. 无主键,选择一个非空且唯一的列作为索引
  3. 没有定义主键且没有唯一列,会自动生成一个隐形的自增的列来作为聚簇索引。

聚簇索引包含了表内的数据,所以可以说表中的数据就是叶子节点的一部分。

聚簇索引的整体是一个B+树,叶子节点存放了实际的行数据(数据页,对应的是磁盘的地址 ),数据页之间通过双向链表连接,逻辑上是按照顺序连接的,实际上不一定,因为在内存中找出大块连续空间是很难得。

非叶子节点存放的是键值(所谓的B+树中的关键字,即就是磁盘地址)

除了聚簇索引以外的,都是辅助索引,与聚簇索引的区别是,叶子节点中存放的不是行数据,而是主键值,所以辅助索引需要有两次查找过程。

  1. 第一次在辅助索引中查找到对应的主键值
  2. 去聚簇索引中查找对应的行数据

常见索引类型

  1. 主键索引(聚簇索引)PRIMARY KEY
  2. 唯一所有 UNIQUE 关键字不能重复
  3. 单列索引,INDEX 对关键字没有要求,是单列上设置索引
  4. 多列索引,INDEX 对关键字没有要求,在多列上设计索引,需要考虑索引顺序

Spring Data JPA

java persistence API 用于对象持久化的API 它是 ORM 规范,使得应用程序以统一的方式访问持久层.

jpa 和 hibernate 的关系

  • jpa 是 hibernate 的抽象
  • jpa 是一个 ORM 规范,不是 ORM 框架
  • hibernate 是 JPA 的一种实现
  • jpa 是 hibernate 的一个功能子集,hibernate 还有很多其他功能

数据库连接池

数据库连接池是一套数据库连接使用的管理策略,使得数据库连接可以得到高效,安全的复用,避免了数据库连接频繁建立,关闭的开销。

数据库连接池的优势

  • 资源重用
  • 更快的系统响应
  • 优化的资源分配
  • 统一的连接管理

hikariCP 常用配置

springboot2 中用 hikariCP 替换了 springboot1 中的tomcat 连接池。

配置路径 含义
spring.datasource.hikari.connection-timeout 客户端等待连接池连接的最大毫秒数。即如果在这个时间内客户端获取不到连接,则会抛出连接超时异常
spring.datasource.hikari.minimum-idle 连接池中维护的最小空闲连接数。即使当前没有任何客户端使用数据库连接,连接池中也会维护一些连接供将来使用。这个配置就是指定连接池中保持连接的最小个数
spring.datasource.hikari.maximum-pool-size 最大池大小。因为连接池中的连接会随着客户端连接的增长而增长,因为客户端不断的申请连接,连接池也需要去创建连接。但是,这种增长不能是无限制的,这会导致内存被耗尽。所以,这个配置用于限制连接池维护的最大连接数。一旦与客户端的连接达到了这个数字,即使客户端再来申请,也只能等待其他的客户端释放连接,或者报连接超时异常
spring.datasource.hikari.idle-timeout 允许连接在连接池中空闲的最长时间(毫秒)。这个参数比较好理解,空闲的连接一直保持在连接池中,无疑是资源的浪费。所以,超过一定时间之后,连接池会主动释放掉。但是,需要注意,连接池中总会维护一些连接,这个数字由minimum-idle控制
spring.datasource.hikari.max-lifetime 池中连接关闭后的最长生命周期(毫秒)。在使用中的连接永远不会被关闭,只有当它关闭时才会在最长生命周期后删除掉
spring.datasource.hikari.auto-commit 从池返回的连接的默认自动提交行为(默认为 true)。在InnoDb表中,所有的语句都是需要commit后,才会在真实数据库中生效

Kafka

kafka 是一个分布式发布订阅消息系统,可划分,冗余备份,缓存日志服务 ,可以用作活跃数据和离线系统之间的缓存。

kafka 将消息持久化到硬盘里。对消息创建备份,保证数据安全(单机 kafka 不能备份,备份需要分布式环境)

kafka 的优势

  • 可靠性
    • kafka 具有分区机制,副本机制,容错机制的消息系统
  • 可拓展性
    • 支持集群规模的拓展
  • 高性能

消息系统

一般来说,消息系统的功能是基于可靠的消息队列,在生产者和消费者之间异步的传递消息。

常见的消息系统类型有下列两种:

  • 点对点消息系统
    • 消息持久化到队列中
    • 会有一个或多个消费者消费消息
    • 一条消息只能被消费一次
    • 消息被消费后,会从队列中移除
    • 能保证数据的顺序性
  • 发布订阅消息系统
    • 消息被持久化到一个topic中
    • msg 不仅有消息队列,还有不同的 topic 来标识属于那一个topic
    • 与点对点不同,消费者可以订阅一个或多个 topic ,一个消息可以被多个消费者消费。
    • 数据被消费后,不会被立即删除。
    • 消息被划分到 topic 中

kafka 术语

kafka 术语

Topic

kafka 中使用类别属性来划分消息所属的类,就称作 topic。

topic 中的数据被分割为一个或多个 partition (默认一个,至少有一个,可以通过参数进行配置)。

每个 partition 的数据是有序的,但 partition 间的数据是无序的。

每一个 partition 都有一个偏移量 offset 。每一个消息都会根据当前所处的 partition 有一个 64 字节的标识来唯一标识文件的起始位置。

partition还有副本的概念,副本不会被消费者消费,是为了防止丢失。
单机版不能设置副本,分布式架构下才能保存副本。

Broker

kafka 集群包含多个服务器,每台服务器我们称作 broker,broker 中存储 topic 中的数据(partition)。

broker接受到生产者发布的消息后,将消息追加的当前用于追加数据的 partition 文件中。

Producer

producer 是数据的发布者,该角色将消息发布到 kafka 的 topic 中。

生产者可以指定将数据存储到那个 partition 中,但通常不这么做,一般交给 kafka 自己处理。

Consumer

consumer 消息的消费者,也称作订阅者,消费者从 broker 中读取数据,且可以消费多个 topic 中的数据。
多个 consumer 构成一个 消费者组。

kafka 安装配置

常用配置

  • broker.id kafka 集群标识
  • log.dirs kafka kafka 日志目录位置

kafka 常用命令

组件 启动命令 说明
zookeeper bin/zookeeper-server-start.sh -daemon config/zookeeper.properties Kafka 依赖于 ZK,先启动 ZK
kafka-server bin/kafka-server-start.sh config/server.properties 启动 Kafka 服务器
create topic bin/kafka-topics.sh –create –zookeeper localhost:2181 –replication-factor 1 –partitions 1 –topic test 创建 Topic
topic list bin/kafka-topics.sh –list –zookeeper localhost:2181 查看 Topic 列表
producer bin/kafka-console-producer.sh –broker-list localhost:9092 –topic test 启动 Producer
consumer bin/kafka-console-consumer.sh –bootstrap-server localhost:9092 –topic test –from-beginning 启动 Consumer
topic info bin/kafka-topics.sh –describe –zookeeper localhost:2181 –topic test 查看单个 Topic 信息

kafka 重要特性

kafka-core

消费者组中的消费者数量应该小于等于 parition 的数量。

kafka 保证 topic 中的每一个 partition 只被消费者组内的一个 consumer 消费。