第八章、项目发布与总结
1、单元测试

- @BeforeClass:类加载之前(初始化数据)
- @AfterClass:类加载之后(销毁数据)
- @Before:调用方法之前(初始化数据)
- @After:调用方法之后(销毁数据)
测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CommunityApplication.class)
public class SpringBootTests {
@Autowired
private DiscussPostService discussPostService;
// 提升作用域
DiscussPost data;
@BeforeClass
public static void beforeClass(){
System.out.println("before Class");
}
@AfterClass
public static void afterClass(){
System.out.println("after Class");
}
/**
* 初始化测试数据
*/
@Before
public void before(){
System.out.println("before");
data=new DiscussPost();
data.setUserId(111);
data.setTitle("Test Title");
data.setContent("Test Data");
data.setCreateTime(new Date());
discussPostService.addDiscussPost(data);
}
/**
* 删除测试数据
*/
@After
public void after(){
System.out.println("after");
// 删除测试数据
discussPostService.updateStatus(data.getId(),2);
}
@Test
public void testFindById(){
DiscussPost post = discussPostService.findDiscussPostById(data.getId());
// 结果是否非空
Assert.assertNotNull(post);
System.out.println(data);
System.out.println(post);
// 查到的数据与初始化的数据是否一致
Assert.assertEquals(data.getTitle(),post.getTitle());
Assert.assertEquals(data.getContent(),post.getContent());
}
@Test
public void testUpdateScore(){
int rows = discussPostService.updateScore(data.getId(), 2000.00);
// 期望的值
Assert.assertEquals(1,rows);
DiscussPost post = discussPostService.findDiscussPostById(data.getId());
// 判断是否相同 delta:精度
Assert.assertEquals(2000.00,post.getScore(),2);
}
}
2、项目监控

1. 导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2. 配置
# actuator
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=info,caches
management:
endpoints:
web:
exposure:
include: *
exclude: info,caches
/actuator/health
/actuator/info
自定义监控/actuator/database
@Component
@Endpoint(id = "database")
public class DataBaseEndpoints {
private static final Logger logger = LoggerFactory.getLogger(DataBaseEndpoints.class);
@Autowired
private DataSource dataSource;
@ReadOperation
public String checkConnection() {
try (
Connection conn = dataSource.getConnection()
) {
return CommunityUtil.getJsonString(0,"获取连接成功!");
} catch (SQLException e) {
logger.error("获取连接失败"+e.getMessage());
return CommunityUtil.getJsonString(1,"获取连接失败!");
}
}
}
权限管理
只有管理员可以访问
antMatchers(
"/discuss/delete",
"/data/**",
"/actuator/**"
).hasAnyAuthority(AUTHORITY_ADMIN)
3、项目部署

4、项目总结


5、常见面试题

1. MySQL
1.1 存储引擎

存储引擎
存储数据、建立索引、更新/查询数据等技术的实现方式。
存储引擎是基于表的,而不是基于库的,所以存储引擎也可以被称为表类型。
基本操作
- 创建表时,指定存储引擎
create table tableName(
// 字段信息
...
) engine = innoDB
- 查看当前数据库支持的存储引擎
show engines;
1. InnoDB
介绍
一种兼顾高可靠性和高性能的通用存储引擎,在MySQL 5.5之后,InnoDB是默认的MySQL存储引擎。
特点
DML操作遵循ACID模型,支持事务行级锁,提高并发访问性能- 支持
外键 FOREIGN KEY 约束,保证数据的完整性和正确性
文件
- xxx.ibd文件:xxx代表表名,innoDB引擎的每张表都会对应这样一个
表空间文件,存储该表的表结构(frm,sdi)、数据和索引。
参数:innodb_fille_per_table

2. MyISAM
介绍
MyISAM是MySQL早期的默认存储引擎
特点
- 不支持
事务,不支持外键 - 支持
表锁,不支持行锁 - 访问速度快
文件
- xxx.sdi: 存储表结构信息
- xxx.MYD:存储数据
- xxx.MYI:存储索引
3. Memory
介绍
Memory引擎的表数据是存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用。
特点
- 内存存放
hash索引(默认)
文件
- xxx.sdi:存储表结构信息
MyISAM 由 MangoDB 替代 ,MEMORY 由 Redis 替代
三者对比

2. 事务
2.1 简介
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么全部成功,要么全部失败。

默认MySQL的事务是自动提交的,也就是说,当执行一条DML语句,MySQL会立即隐式的提交事务。
基本操作
- 查看/设置事务提交方式
select @@autocommit;
-- 设置为手动提交事务
set @@autocommit = 0 ;
- 提交事务
commit;
- 回滚事务
rollback;
如果业务正常提交,执行commit 指令
如果业务出现异常,执行rollback 指令
- 开启事务
start transaction 或 begin ;
2.2 特性
- 原子性、一致性、隔离性、持久性
原子性(Atomicity)
- 事务是不可分割的最小操作单元,要么全部成功,要么全部失败;
一致性(Consistency)
- 事务完成时,必须使所有的数据都保持一致状态;
隔离性(Isolation)
- 数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行;
持久性(Durability)
- 事务一旦提交或回滚,它对数据库的数据的改变是永久的。
2.3 隔离性
并发事务出现的问题
1. 第一类丢失更新
- 某一个事务的回滚,导致另外一个事务已更新的数据丢失了

事务1的回滚,导致了事务2已提交的数据丢失了
2. 第二类丢失更新
- 某一个事务的提交,导致另外一个事务已经更新的数据丢失了。

事务1的提交导致了事务2的已更新的数据丢失了。
3. 脏读
- 一个事务读到另一个事务还没有提交的数据;

事务A执行了update语句后,事务B执行了select语句,此时事务A并未提交,事务B却读取到了更新后的数据。
4. 不可重复读
- 一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。

事务A的第二个select语句执行时,事务B的update已经提交了,所以造成读取的两次数据不一致。
5. 幻读
- 一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了“幻影”。

事务A在执行insert语句时,此时事务B的insert语句已经提交,所以在执行时,会发现数据已被插入,但是事务A的下一个select语句却查不到数据。
事务的隔离级别

基本操作
-- 查看事务隔离级别
select @@transaction_isolation
-- 设置事务隔离级别
set [session|global] transaction isolation level {read uncommitted | read committed | repeatable read | serializble}
Read uncommitted
- 未提交读
- 解决了更新丢失,但还是可能会出现脏读
Read committed
- 读已提交
- 解决了更新丢失和脏读问题
Repeatable Read
- 可重复读
- 解决了更新丢失、脏读、不可重复读、但还是会出现幻读
Serializable
- 可串行化
- 解决了更新丢失、脏读、不可重复读、幻读
小结
生产中要根据业务的情形来使用隔离级别,随着隔离级别增高,数据库的性能会直线下降,所以运用需要权衡。
2.4 Spring 事务
Spring中的默认隔离级别是 Read committed (读已提交)
1. 声明式事务
- 通过XML配置,生命某方法的事务特征。
- 通过注解,声明某方法的事务特征。
@Transactional
- isolation:隔离级别
- propagation:传播机制
- REQUIRED:支持当前事务(外部事务),如果不存在则创建新事务;
- REQUIRES:创建一个新事务,并且暂停当前事务(外部事务);
- NESTED:如果当前存在(外部事务),则嵌套在该事务中执行(独立的提交和回滚),否则就和REQUIRED一样。
2. 编程式事务
-
通过Transaction Template 管理事务,并通过它执行数据库的操作。
TransactionTemplate.setIsolationLevel();
TransactionTemplate.setPropagationBehavior();
3. 锁
3.1 介绍
- 锁是计算机协调多个进程或线程并发访问某一资源的机制。
- 如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。
分类
- 全局锁:锁定数据库中的所有表;
- 表级锁:每次操作锁住整张表;
- 行级锁:每次操作所著对应的数据行;
3.2 全局锁
- 对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句、DDL语句,已经更新操作的事务提交语句都将被阻塞;
- 典型应用场景:全库的逻辑备份。获取一致性视图,保证数据的完整性。
基本操作
-- 加全局锁
flush tables with read lock;
-- 备份数据
mysqldump -uroot -p1234 itcast>itcast.sql; -- 存放路径
-- 释放锁
unlock tables;
全局锁存在的问题
- 如果在主库上备份,那么在备份期间都不能执行更新,业务基本就得停摆。
- 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。


3.3 表级锁
每次操作锁住整张表。锁定粒度大,发生锁冲突的该路最高,并发度最低。分为:
- 表锁
- 元数据锁
- 意向锁
1. 表锁
1. 表共享读锁(read lock)
2. 表独占写锁(write lock)
操作
- 加锁:
lock tables tableName read/wirte - 释放锁:
unlock tables 或 客户端断开连接
加个read锁后,当前客户端和其他客户端可以读。当前客户端不可以写,其他客户端阻塞
加了write锁后,当前客户端可读可写。其他客户端不可读不可泄,处于阻塞状态。
读锁不对阻塞其他客户端的读,但会阻塞写。写锁既会阻塞其他客户端的读,又会阻塞其他客户端的写
2. 元数据锁
meta data lock MDL
1.系统自动控制,无需显示使用,在访问一张表的时候会自动加上。
2. 主要作用是维护表元数据的数据一致性,在表上有活动事务时,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。
在MySQL5.5中引入,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。
操作
-- 查看元数据锁
select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks;
3. 意向锁
意向共享锁(IS)
- 与表锁共享锁(read)兼容,与表锁排他锁(write)互斥。
意向排他锁(IX)
- 与表锁共享锁(read)及排他锁(write)都互斥。意向锁之间不会互斥。
-- 查看意向锁及行锁的加锁情况
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data
from performance_schema.data_locks;
3.4 行级锁
1. 行锁
- 共享锁:允许事务去读一行,阻止其他事务获得相同数据集的排他锁。
- 排他锁:允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。


默认情况下,InnoDB在REPEATABLE READ 事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读。
- 针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会
自动优化为行锁。 InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。
操作
-- 查看意向锁及行锁的加锁情况
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data
from performance_schema.data_locks;
2. 间隙锁/临建锁



悲观锁
当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制,也称为悲观锁。
乐观锁
乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。
4. 索引
InnoDB中采用B+Tree的数据结构存储索引。
索引
帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优点
- 提高数据检索的效率,降低数据库的成本;
- 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗
缺点
- 索引列占用额外的空间
- 索引大大的提高了查询效率,同时也降低了更新表的速度,如对表进行插入、更新、删除时效率降低


为什么InnoDB存储引擎选择使用B+Tree索引结构?
- 相对于二叉树,层级较少,搜索效率高;
- 相对于B-Tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;
- 相对于hash索引,B+Tree支持范围匹配及排序操作;
5. Redis
5.1 数据类型

5.2 过期策略

5.3 淘汰策略


5.4 缓存穿透(查不到)

5.5 缓存击穿(热点数据,缓存过期)

5.5 缓存雪崩(缓存不能正常服务)

5.6 分布式锁




6. Spring IOC

7. Spring AOP

8. Spring MVC

本文总结了项目发布过程中的单元测试、监控配置、项目部署,并深入探讨了MySQL的存储引擎、事务特性和锁机制,还涉及了Spring事务管理、Redis缓存策略和Spring框架组件。

被折叠的 条评论
为什么被折叠?



