京东

innodb优点

  1. 事务支持

    1. 支持acid 原子性,一致性,隔离性,持久性事务
  2. 行锁

    1. 允许多个事务并发修改和读取不同行,比MyISAM引擎(表级锁)相比有优势
  3. 外键约束

    1. 支持外键联系,可以在数据库层面进行完整性约束,保证关联表之间数据一致性
  4. 崩溃恢复

    1. 提供自动崩溃恢复机制,可以在数据库异常关闭之后,回复到一致的状态
    2. innodb采用事务日志来记录变更,从而恢复数据,和redis中的aof类似
  5. 数据缓存

    1. 使用缓冲池来管理数据和索引的内存缓存,提高访问速度
  6. 支持热备份

    1. 可以在数据库运行时,同时进行备份操作


innodb底层结构

    1. 将数据和索引组织成固定大小的页,通常为16kb,是管理存储空间和数据访问的基本单元,innodb会自动管理这些页的分配和释放(LRU)
  1. 聚簇索引
    1. 主键的索引为聚簇索引,聚簇索引决定了数据在磁盘上面的物理储存次序,相邻的行在物理储存上面也是相邻的,对于范围查询比较友好
  2. 辅助索引
    1. 除了主键索引外,非主键索引包含了索引列的值以及指向对应行的索引键,所以需要两次寻找,先找到索引列的值,再根据聚簇索引找到对应行
  3. 双链表
    1. 使用双链表来管理数据页,链表包括了空闲链表和已使用链表
  4. 日志
    1. 使用事务日志来记录对数据库的变更,日志记录在事务提交前写入,并在崩溃后使用
  5. 缓冲池
    1. 用来缓存数据页的内存区域,加速数据的读取和写入,将变更一并写入到磁盘,减少对磁盘的访问
  6. MVCC
    1. 处理并发事务,通过为每一个事务创建一个可见性的版本,允许不同的事务在互不干扰的情况下,访问同一行数据
  7. 锁管理
    1. 支持行级锁,可以在行级别上进行并发控制

事务

事务可以视为一组被视为单个单元的操作,这些操作要么全部执行成功,要么失败回滚,以保证数据的一致性和完整性

事务的属性 acid

原子性

原子性保证事务要么全部执行成功,要么全部执行失败回滚,如果在执行过程中任何一步失败,会回滚到事务的初始状态,保证数据的完整性

一致性

事务在执行前和执行后,都不能违反数据库定义的规则和约束

隔离性

保证事务在并发执行的时候,不会互相干扰,多个事务在并发执行的时候,不能看到彼此未提交的中间状态,避免脏读

隔离级别
  1. 读未提交:一个事务可以读取另一个事务未提交的数据,可能会读到未提交的临时数据(脏读)
  2. 读已提交:默认级别,事务只能读到已经提交的数据,避免脏读,但是可能会导致不同事务在同一个查询中读到的数据不一致(不可重复读)—-幻读和不可重复读的区别是幻读是行数不一样(插入) 不可重复读是数据不一样(修改)
  3. 可重复读:一个事务在同一个查询中,多次读取相同的数据时,应该看到一样的结果,使用读锁,防止修改数据
  4. 串行化:最高级别,确保每个事务单独执行,避免了脏读,不可重复读,幻读,但是会导致性能下降

持久性

事务一旦提交,将永久保存在数据库中,即使数据库崩溃也不会消失

MVCC

多版本并发控制

保证了事务的隔离性和一致性,提高数据库的并发性能

  • 版本号
    • 每一个数据行都有多个版本号,用于表示不同状态
  • 快照
    • 在事务开始的时候,会对这个事务创建一个快照,对应事务开始的时候数据库的状态
  • 可见性判定
    • 事务读取的时候,只能看见事务开始前已经存在的数据库版本,无法看见正在进行或者未提交的事务的修改
  • 回滚段
    • 对于已经提交的事务,数据库会保存历史版本,用来支持隔离性和崩溃恢复

优点

  • 高并发
  • 无读锁
  • 避免不可重复读和幻读
  • 历史版本

redis持久化

RDB

储存快照到硬盘的二进制文件,包含了数据库在这个时间点的数据状态(覆盖)(膨胀)

用于定期备份和快速数据恢复,但是如果发生故障可能会丢失最后一次持久化的数据

手动执行save或者bgsave

或者设置save频次

AOF

记录redis的写操作,以追加的形式写道文件中(日志),服务器重启的时候,会优先使用aof,通过重新执行这些写操作,重建数据集,

更加安全,但是占用更多的磁盘空间,并且回复数据比较慢


高并发下为什么不用全局HashMap存数据

竞争条件

多个线程同时操作全局,可能会引发竞争条件,导致数据不一致,丢失,覆盖

并发控制

需要自己实现并发控制机制,(读写锁),导致性能下降

内存占用

将数据储存到hashmap中,占用过高的内存

性能

全部的线程都要竞争这一个数据对象,导致性能瓶颈

缓存一致性

需要处理缓存更新失效

适合的方案

redis,数据库,消息队列,

线程池参数

1
2
3
4
5
6
7
8
9
ExecutorService executorService = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, // 线程空闲时间
TimeUnit.MILLISECONDS, // 空闲时间单位
new LinkedBlockingQueue<>(), // 工作队列
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

核心线程数

保持活动状态的线程数量,这些线程就算没有任务需要执行,也会保持存活

最大线程数

定义线程池中最大的线程数,当任务数量增加的时候,线程池会增加新的线程,但是数量不会超过最大的线程数

线程存活时间

定义非核心线程的最大存活时间,如果线程池中的线程数量超过核心线程数,且在一段时间内,一直处于空闲状态,则会被销毁,释放资源

工作队列

用于存储执行任务的队列,当所有的核心线程都在执行任务的时候,新的任务会放到工作队列中等待执行

拒绝策略

当线程池无法接受新的任务的时候,定义了如何处理这些任务,常见的拒绝策略有 直接丢弃,丢弃最旧任务,在调用者线程执行等

核心线程数和最大线程数什么时候使用

  • 核心线程数

    应该设置成和cpu核数相近,核心线程应该设置成能够处理系统的平均负载的数量,这些线程会一直存活,以减少线程的创建和销毁的开销

  • 最大线程数

    根据系统的最大负载和资源限制确定,当任务数量剧增的时候,最大的线程数允许线程扩容用来处理额外负载。但是过多的线程可能会导致过度消耗系统资源