- 基础(√)
- 集合(√)
- 多线程(√)
- JVM(√)
- Redis(√)
- Mysql(√)
- MongoDB
- 计网+操作系统
- Spring
1. Redis
- 什么是Redis:非关系型的键值对数据库,数据存在内存中,读写速度快,一般用于缓存数据
- 项目为什么使用mongoDB:(https://blog.csdn.net/dog_flying/article/details/100052831)(https://zhuanlan.zhihu.com/p/670763653)
-
为什么不使用Redis缓存+Mysql存数据的方式
- 要维护Redis缓存跟Mysql数据的一致性问题
- 业务开发时要维护Mysql的表结构,比如有新业务要新增表字段,开发起来很麻烦
-
为什么选择mongoDB
- MongoDB自身有一个缓存层,这样就可以用一个MongoDB就能实现缓存+存储数据的功能
- MongoDB的是文档型,玩家的某个系统的数据是以文档形式保存在mongoDB中,读取数据时,从库中读取,放到内存中,更新数据时,先更新内存的数据,然后定义一定的时间间隔,再把内存的数据写回数据库(项目一般是5分钟执行一次,也可以手动调用update()方法更新,一般在玩家下线或者停服的时候update()玩家数据和全服玩家的数据
- MongoDB最大的特点就是表结构灵活,像游戏这种经常有新需求需要新增字段的,使用mongoDB直接在表里加字段就行了,不需要考虑表的结构
- 对数据的操作一般都是在业务代码中进行,不需要在SQL层面进行复杂的查询操作和事务操作
-
- Redis优缺点
-
优点:
- 基于内存,读写速度快
- 单线程,避免了线程切换的开销和多线程的竞争。执行命令是由单线程执行
- 支持持久化,RDB和AOF两种持久化方式,可以将数据保存到磁盘,避免数据丢失
- 支持事务
- 支持主从复制,主节点自动将数据同步到从节点
-
缺点:
- 因为是将数据放在内存,因此会受到服务器内存大小的限制,不支持大量数据的存放
-
- 为什么快
- 基于内存,数据读写在内存进行,速度快
- 单线程执行命令,避免了线程切换的开销和多线程竞争
- io多路复用
- 数据结构简单,kv键值对形式
- Redis是单线程还是多线程
- redis6.0之前是单线程的,redis6.0之后引入多线程技术,只是用来解决网路IO读写的问题,执行命令还是单线程的
- Redis数据类型
-
String:键值对,key和value都是字符串
- 指令
- 设置kv,可设置存活时间:set key value [ex second][millseconds]
- 获取key:get key
- 自增:incr key
- 自减:decr key
- 应用
- 缓存
- 计数器:粉丝数
- 指令
Hash:key-field,常用于存储对象
Set:存储无序不重复单独字符串,最多MAX_VALUE个,支持并集、交集。可用于存储共同关注
-
List:存储有序可重复的字符串,最多MAX_VALUE个,可以两端弹出插入
- 指令:lpop、lpush、rpop、rpush
ZSet:存储有序不重复的字符串,zrange指令可做排行榜
-
- Redis事务
- multi开启事务、exec执行事务、discard取消事务
- redis事务无法保证原子性,单条命令是原子性的,但是整个事务不是原子性,如果事务中某条命令出错,不影响其他命令的执行,事务也不会回滚
- redis事务没有隔离级别,multi开启事务后,exec执行事务前,输入的命令会先进入队列,先不执行。也就不存在事务内的某条查询命令会看到其他事务的更新,其他事务的查询也无法看到本事务内的更新
- 持久化机制
-
RDB:redis默认的持久化机制,将数据库快照保存到dump.rdb文件中
- 触发
- save命令:是同步命令,阻塞客户端请求,直到保存成功
- bgsave命令:异步命令,fork一个子进程来进行数据保存,不会阻塞客户端请求
- 通过配置文件,自动保存:save 60 1000:指的是当60秒内有1000个key被改动时,触发自动保存
- 优点:数据恢复快
- 缺点:如果是用主动输入命令进行保存,耗时、耗性能。
如果是自动保存,又不一定能触发保存机制
- 触发
-
AOF:
- 需要在配置文件中开启:appendonly yes
- 原理:开启AOF后,当redis执行一个命令后,这个命令就会被追加到AOF文件的末尾
- 策略
- always:每次有新命令追加到AOF文件时就执行一次fsync,非常慢,但非常安全。
- everySec:每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。
- no:由操作系统执行,不安全
-
- Redis集群
-
为什么要部署集群:
- 单机情况下,一旦服务器宕机,数据不可用
- 单机的内存容量有限,可缓存的数据有限
-
主从复制:主节点负责读写,从节点只负责读,主节点修改后的数据会同步到从节点
- 配置主从模式
- 从服务器节点的配置文件,将slaveOf的port端口改成主服务器的port,然后进入主服务器,通过info replication命令查看是否配置成功
- 在从服务器节点使用slaveOf 主服务器port
- 优点:
- 高可用,当主节点宕机后,从节点接替主节点的工作
- 读写分离,主节点负责读写,从节点只负责读
- 缺点:主节点宕机后无法恢复,只能由从节点全权接替工作
- 配置主从模式
-
哨兵模式:为了解决主从复制模式下主节点宕机后只剩下从节点的问题,引入了哨兵模式,在哨兵模式下,当主节点宕机后,从节点会通过竞争,选取一个从节点升级为主节点
- 配置哨兵模式:
- 配置sentinel.conf配置文件,sentinel monitor 主机名 主机ip 主机port 触发切换的哨兵数量
- 使用redis-sentinel命令启动哨兵服务
- 哨兵的作用
- 监视和提醒,当服务器节点出现问题时,报告
- 自动故障迁移,当主节点宕机后,哨兵之间会选取一个从节点升级为主节点,原来的主节点重连后,只能作为从节点
- 优点:解决了主从复制模式的主节点缺失问题
- 配置哨兵模式:
集群:配置多个主从复制+哨兵模式
-
- 过期策略:惰性删除、定期删除、定时删除
expire设置key存活时间、set key value []- 定期删除:每隔一段时间对一些key进行检查,删除已经过期的key
- 定时删除:为每个设置过期时间的key开一个定时器进行清理
- 惰性删除:访问这个key的时候才检查是否已过期,过期就清除
- 缓存击穿
- 原因:一个被大量访问的key突然过期了,导致大量的查询请求要去到数据库查询
- 解决:热点key不设置过期时间
- 缓存穿透
- 原因:大量的恶意请求查询一个不存在的key,因为缓存里找不到,要去到数据库中查询,导致数据库压力增大
- 解决:使用布隆过滤器,其原理相当于在缓存跟数据库之间再加了一层缓存,在初始化时,从数据库中把已有的key拿出来,放到布隆过滤器中,如果key在布隆过滤器中找不到,则说明在数据库中也没有,可以拦截恶意请求打到数据库
- 缓存雪崩
- 原因:同一时间有大量的key同时失效,导致大量请求打到数据库上
- 解决:设置key过期时间时,用随机数,避免大量的key同时过期
2. Mysql
- mysql是关系型数据库,redis和mongoDB是非关系型数据库,什么时候用
- 数据量小的时候用关系型数据库,因为是将数据存放在磁盘,读写数据要进行磁盘IO,速度慢
- 数据量大的时候用非关系型数据库,将数据读取到内存,内存读写速度快
- 用非关系型数据库做缓存,用关系型数据库存数据
- 事务四大特性:ACID
- 原子性:事务包括的操作要么全部成功,要么失败回滚
- 一致性:事务执行前后保持一致。比如a和b账户共有1000块,两人之间的转账无论是成功还是失败,总数都是1000
- 隔离性:事务进行的修改在最终提交之前,其他事务不能见其修改结果
- 持久性:事务对数据的修改一旦提交,就会保存到数据库中
- 脏读、不可重复读、幻读
- 脏读:一个事务处理过程中读取了另一个未提交事务的数据
- 不可重复读:同一个事务内,多次查询同一个记录,返回了不同的数据,这是因为在两个查询间隔,另一个事务修改了数据并提交了结果
- 幻读:事务在读取某个范围内的记录时(记录的范围区间),有另一个事务在该范围区间插入或删除了一条记录,导致读取到的记录条数不一致
- 事务隔离级别
- 串行化:加锁处理,强制事务按照顺序执行。可解决上面三个问题
- 可重复读:同一个事务中多次读取的数据是一致的,是mysql的默认隔离级别。可解决不可重复读和脏读问题。
- 读已提交:事务只能看到已经提交事务的修改结果,可解决脏读问题。
- 读未提交:所有事务都可以看到其他未提交事务的执行结果
- 隔离级别如何实现
- 读已提交和可重复读通过MVCC实现,串行化通过锁实现
- MVCC机制
- 当前读:读取的是数据库的最新版本的数据,并且在读取时要求其他事务不会修改当前的记录,所以是对读取的记录进行了加锁处理
- 快照读:不加锁,通过MVCC版本号读取UNDO LOG快照日志读取中的数据
- 什么是索引:索引是加快数据表的访问速度的一种数据结构
- 为什么索引可以加快检索速度
索引的作用就像一本书的目录,可以快速定位数据在表中的位置。如果没有索引就要遍历全部数据逐个查找
索引的底层是B+树,在B+树中,节点的key按值的大小从左到右升序排序,数据存在叶子节点。在进行查找时,首先在根节点进行二分查找,找到key所在的指针,然后在指针指向的节点区域进行查找,找到叶子节点,得到key对应的数据
3. B+树的索引又可以分为聚簇索引和非聚簇索引
1. 主索引为聚簇索引,辅助索引为非聚簇索引。
2. 聚簇索引是以主键作为键值,聚簇索引的叶子节点存放着完整的数据。非聚簇索引是以非主键作为键值,叶子节点存放着主键值,所以非聚簇索引在查找时,要先找到主键,然后根据聚簇索引找到主键对应的数据(回表操作)
3. 聚簇索引使用场景:
select id from test where id = 10; //搜索id索引树
4. 非聚簇索引使用场景
select * from test where name = "123"; //非主键查询,先搜索name字段,得到name="123"对应的主键id值,再搜索聚簇索引,找到对应id值的数据
select name from test where name = "123"; //如果非聚簇索引上有name的值等于123,那么它就不用回表
4. 为什么B+树比B树更适合
1. B树每个节点都要存key和data,而B+树的data都存在叶子节点,非叶子节点存key。这样就使得B+树的一个节点可以存放更多的索引,树的高度更低,磁盘IO次数更少,数据检索速度更快
2. B+树的叶子节点是按照key的值升序相连的,范围查找更快
3. B+树的数据都在叶子节点,查询更稳定,每次查询都是从根节点到叶子节点
- 什么时候要建索引,什么时候不用建索引
- 用索引:经常查询的字段、主键必须加索引、排序字段使用索引可以加快检索速度、联表查询
- 不用索引:经常增删数据的字段、有大量重复数据的字段、表数据太少时没必要用索引
- 索引有哪些类型
- 主键索引:数据不能重复,不能为null,一张表只能有一个主键索引
- 唯一索引:数据不能重复,可以为null
- 普通索引
- 组合索引:由多个数据列组成的索引
- 全文索引:对文本内容进行搜索
- 创建索引和删除索引
- CREATE INDEX在已创建的指定的表和列上创建一个普通索引:CREATE INDEX (INDEX_NAME) ON (TABLE_NAME)(COLUNM_LIST)
- 建表时指定数据列
- ALTER TABLE创建索引
- ALTER TABLE DROP删除索引
- 组合索引
-
为什么使用组合索引
- 一个索引能够覆盖多个索引字段可以减少空间的开销。
- 减少回表次数,比如有1000W的数据,搜索3个字段,不使用联合索引执行select操作,每次都要进行回表操作。而使用联合索引时,如果3个字段能够全部覆盖,则不进行回表(覆盖索引情况)
如:select col1,col2,col3 from table where col1 = 1 and col2 = 2 and col3 = 3; //每个条件可以筛选出10%的数据 不使用联合索引:先查询col1的10%,即1000W*10%=100W,然后回表从100W条数据中再查询符合col2数据,100W*10%,再回表查询符号col3的数据,10W*10%。 使用联合索引,直接搜索:1000W*10%*10%*10%=1W
-
最左匹配原则:从SQL语句的左边开始,当遇到范围查询(>、<、between、like>)等就会停止匹配,后面的字段不会使用索引
比如:对abc三个列建立联合索引- 当查询a/ab/abc时会走索引,查询bc时不会走索引
- 当查询a=1 and b>2 and c=3时,c就不会走索引
-
什么情况下索引会失效
- 组合索引,在不符合最左匹配原则时,索引失效
- 以%或者like开头的查询
- 查询条件以or连接
- 在索引上进行计算
-
- mysql的数据结构:数值、日期、字符串
- 数值:整数int、浮点型float、double
- 日期:date
- 字符串:varchar、char
- mysql的锁
并发事务时,保证数据访问顺序的机制叫锁
-
锁跟隔离级别的关系
- 串行化:锁住整个事务范围,直到事务结束
- 可重复读:读取数据时加共享锁,事务结束后才释放共享锁
- 读已提交:读取数据时加共享锁,读取完之后释放锁
- 读未提交:不加锁
锁的类型:表锁、页锁、行锁。innodb默认采用表锁
共享锁:读锁,一个事务对一个数据加了共享锁,只能进行读取操作,不能更新该数据,其他事务也不能对该数据加排他锁
排他锁:写锁,一个事务对一个数据加了排他锁,可以对该数据进行读取和更新,期间其他事务不能对该数据加共享锁和排他锁