京东
innodb优点
-
事务支持
- 支持acid 原子性,一致性,隔离性,持久性事务
-
行锁
- 允许多个事务并发修改和读取不同行,比MyISAM引擎(表级锁)相比有优势
-
外键约束
- 支持外键联系,可以在数据库层面进行完整性约束,保证关联表之间数据一致性
-
崩溃恢复
- 提供自动崩溃恢复机制,可以在数据库异常关闭之后,回复到一致的状态
- innodb采用事务日志来记录变更,从而恢复数据,和redis中的aof类似
-
数据缓存
- 使用缓冲池来管理数据和索引的内存缓存,提高访问速度
-
支持热备份
- 可以在数据库运行时,同时进行备份操作
innodb底层结构
- 页
- 将数据和索引组织成固定大小的页,通常为16kb,是管理存储空间和数据访问的基本单元,innodb会自动管理这些页的分配和释放(LRU)
- 聚簇索引
- 主键的索引为聚簇索引,聚簇索引决定了数据在磁盘上面的物理储存次序,相邻的行在物理储存上面也是相邻的,对于范围查询比较友好
- 辅助索引
- 除了主键索引外,非主键索引包含了索引列的值以及指向对应行的索引键,所以需要两次寻找,先找到索引列的值,再根据聚簇索引找到对应行
- 双链表
- 使用双链表来管理数据页,链表包括了空闲链表和已使用链表
- 日志
- 使用事务日志来记录对数据库的变更,日志记录在事务提交前写入,并在崩溃后使用
- 缓冲池
- 用来缓存数据页的内存区域,加速数据的读取和写入,将变更一并写入到磁盘,减少对磁盘的访问
- MVCC
- 处理并发事务,通过为每一个事务创建一个可见性的版本,允许不同的事务在互不干扰的情况下,访问同一行数据
- 锁管理
- 支持行级锁,可以在行级别上进行并发控制
事务
事务可以视为一组被视为单个单元的操作,这些操作要么全部执行成功,要么失败回滚,以保证数据的一致性和完整性
事务的属性 acid
原子性
原子性保证事务要么全部执行成功,要么全部执行失败回滚,如果在执行过程中任何一步失败,会回滚到事务的初始状态,保证数据的完整性
一致性
事务在执行前和执行后,都不能违反数据库定义的规则和约束
隔离性
保证事务在并发执行的时候,不会互相干扰,多个事务在并发执行的时候,不能看到彼此未提交的中间状态,避免脏读
隔离级别
- 读未提交:一个事务可以读取另一个事务未提交的数据,可能会读到未提交的临时数据(脏读)
- 读已提交:默认级别,事务只能读到已经提交的数据,避免脏读,但是可能会导致不同事务在同一个查询中读到的数据不一致(不可重复读)----幻读和不可重复读的区别是幻读是行数不一样(插入) 不可重复读是数据不一样(修改)
- 可重复读:一个事务在同一个查询中,多次读取相同的数据时,应该看到一样的结果,使用读锁,防止修改数据
- 串行化:最高级别,确保每个事务单独执行,避免了脏读,不可重复读,幻读,但是会导致性能下降
持久性
事务一旦提交,将永久保存在数据库中,即使数据库崩溃也不会消失
MVCC
多版本并发控制
保证了事务的隔离性和一致性,提高数据库的并发性能
- 版本号
- 每一个数据行都有多个版本号,用于表示不同状态
- 快照
- 在事务开始的时候,会对这个事务创建一个快照,对应事务开始的时候数据库的状态
- 可见性判定
- 事务读取的时候,只能看见事务开始前已经存在的数据库版本,无法看见正在进行或者未提交的事务的修改
- 回滚段
- 对于已经提交的事务,数据库会保存历史版本,用来支持隔离性和崩溃恢复
优点
- 高并发
- 无读锁
- 避免不可重复读和幻读
- 历史版本
redis持久化
RDB
储存快照到硬盘的二进制文件,包含了数据库在这个时间点的数据状态(覆盖)(膨胀)
用于定期备份和快速数据恢复,但是如果发生故障可能会丢失最后一次持久化的数据
手动执行save或者bgsave
或者设置save频次
AOF
记录redis的写操作,以追加的形式写道文件中(日志),服务器重启的时候,会优先使用aof,通过重新执行这些写操作,重建数据集,
更加安全,但是占用更多的磁盘空间,并且回复数据比较慢
高并发下为什么不用全局HashMap存数据
竞争条件
多个线程同时操作全局,可能会引发竞争条件,导致数据不一致,丢失,覆盖
并发控制
需要自己实现并发控制机制,(读写锁),导致性能下降
内存占用
将数据储存到hashmap中,占用过高的内存
性能
全部的线程都要竞争这一个数据对象,导致性能瓶颈
缓存一致性
需要处理缓存更新失效
适合的方案
redis,数据库,消息队列,
线程池参数
ExecutorService executorService = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, // 线程空闲时间
TimeUnit.MILLISECONDS, // 空闲时间单位
new LinkedBlockingQueue<>(), // 工作队列
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
核心线程数
保持活动状态的线程数量,这些线程就算没有任务需要执行,也会保持存活
最大线程数
定义线程池中最大的线程数,当任务数量增加的时候,线程池会增加新的线程,但是数量不会超过最大的线程数
线程存活时间
定义非核心线程的最大存活时间,如果线程池中的线程数量超过核心线程数,且在一段时间内,一直处于空闲状态,则会被销毁,释放资源
工作队列
用于存储执行任务的队列,当所有的核心线程都在执行任务的时候,新的任务会放到工作队列中等待执行
拒绝策略
当线程池无法接受新的任务的时候,定义了如何处理这些任务,常见的拒绝策略有 直接丢弃,丢弃最旧任务,在调用者线程执行等
核心线程数和最大线程数什么时候使用
- 核心线程数
应该设置成和cpu核数相近,核心线程应该设置成能够处理系统的平均负载的数量,这些线程会一直存活,以减少线程的创建和销毁的开销
- 最大线程数
根据系统的最大负载和资源限制确定,当任务数量剧增的时候,最大的线程数允许线程扩容用来处理额外负载。但是过多的线程可能会导致过度消耗系统资源
反问
表现。