本模块是公共类课程,适合后端开发
、嵌入式开发
、移动端原生开发(SQLite、Realm)
等需要使用数据库进行数据持久化的岗位。
讲讲数据库的三范式
数据库的三范式是数据库设计的一种规范,主要有以下三个范式:
- 第一范式(1NF):属性不可再分。每个属性都是不可再分的基本数据项,不可再分是指属性不能再分解为更小的数据项,如学生表的学号、姓名、性别等。
- 第二范式(2NF):属性完全依赖于主键。每个非主属性完全依赖于主键,而不依赖于主键的任何一部分,如学生表的学号、课程号、成绩等。
- 第三范式(3NF):属性不依赖于其他非主属性。每个非主属性不依赖于其他非主属性,如学生表的学号、姓名、性别等。
数据库的三范式是为了提高数据库的性能和可靠性,减少数据冗余和数据不一致,实际开发中根据需求选择合适的范式,没有必要必须满足三范式。
讲讲数据库的索引
数据库的索引是一种数据结构,用于提高数据库的查询性能,主要有以下几种索引:
- 普通索引:普通索引是最基本的索引,没有任何限制,可以加速查询,但不会唯一。
- 唯一索引:唯一索引是保证索引列的唯一性,可以加速查询,保证数据的唯一性。
- 主键索引:主键索引是一种唯一索引,用于保证表的唯一性,主键索引是一种特殊的唯一索引。
- 复合索引:复合索引是多个列组合在一起的索引,可以加速多列的查询,提高查询性能。
- 全文索引:全文索引是对文本的索引,可以加速文本的查询,提高查询性能。
- 空间索引:空间索引是对空间数据的索引,可以加速空间数据的查询,提高查询性能。
数据库的索引是为了提高数据库的查询性能,根据不同的需求选择合适的索引,索引的实现常常是 B 树(MongoDB、PostgreSQL)、B+ 树(MySQL InnoDB)、哈希表(MySQL Memory)等。
索引的优点是可以加快检索速度,使用优化隐藏器,提高系统性能;缺点是占用磁盘空间,降低写入速度,增加了维护成本。
讲讲数据库的事务和事务等级
数据库的事务是一组操作单元,要么全部成功,要么全部失败,主要有以下四个特性:
- 原子性(Atomicity):事务是一个不可分割的工作单位,要么全部成功,要么全部失败。
- 一致性(Consistency):事务执行前后,数据库的完整性约束没有被破坏。
- 隔离性(Isolation):事务之间是相互隔离的,一个事务的执行不会影响其他事务。
- 持久性(Durability):事务一旦提交,对数据库的修改是永久性的,不会被回滚。
数据库的事务是为了保证数据的一致性和完整性,提高数据库的可靠性和可靠性。
数据库的事务等级主要有以下四个等级:
- 读未提交(Read Uncommitted):一个事务可以读取另一个事务未提交的数据,最低的隔离级别。
- 读提交(Read Committed):一个事务只能读取另一个事务已提交的数据,避免脏读,不可重复读。
- 可重复读(Repeatable Read):一个事务在执行期间,不会看到其他事务的更新,避免脏读和不可重复读,但不能防止幻读。
- 串行化(Serializable):一个事务在执行期间,不会看到其他事务的更新,避免脏读、不可重复读和幻读,最高的隔离级别。
数据库的事务等级是为了保证数据的一致性和完整性,提高数据库的可靠性和可靠性。
MySQL 的默认事务等级是可重复读(Repeatable Read),MongoDB 的默认事务等级是读提交(Read Committed),ClickHouse 的默认事务等级是读未提交(Read Uncommitted)。
讲讲数据库的锁和锁的类型
数据库的锁是为了保证数据的一致性和完整性,主要有以下几种锁:
- 共享锁(S 锁):共享锁是读锁,多个事务可以同时获取共享锁,但是不能获取排他锁,用于读取数据。
- 排他锁(X 锁):排他锁是写锁,只有一个事务可以获取排他锁,其他事务不能获取共享锁和排他锁,用于修改数据。
- 行级锁:行级锁是对数据行加锁,只锁定需要修改的数据行,其他数据行不受影响,提高并发性能。
- 表级锁:表级锁是对整个表加锁,锁定整个表,其他事务不能访问表,降低并发性能。
- 乐观锁:乐观锁是通过版本号或时间戳实现,不加锁,通过版本号或时间戳判断数据是否被修改。
- 悲观锁:悲观锁是通过加锁实现,锁定数据,其他事务不能访问数据,提高数据的一致性和完整性。(select ... for update)
数据库的锁是为了保证数据的一致性和完整性,提高数据库的可靠性和可靠性,根据不同的需求选择合适的锁。
单靠乐观锁可以解决秒杀并发问题吗?
很多八股文说,单靠乐观锁可以解决秒杀并发问题,实际上这种说法纯粹是误人子弟。你若听信他的胡扯,那你这辈子也是真的有了。
乐观锁通过版本号或时间戳实现,不加锁。在修改数据时,先读取数据,然后比较版本号或时间戳,如果一致,则修改数据,否则抛出异常。只从这个层面看,似乎乐观锁解决了悲观锁出现的阻塞、死锁等问题。
但秒杀并发问题并不单纯指代数据层面的并发修改问题,还会包括并发引起的系统压力等问题,需要综合考虑多种因素。
一般情况下,秒杀系统的乐观锁都处于数据库层面,会增加一个冗余字段专门存储版本号或时间戳。
眼看单靠乐观锁可以解决秒杀并发问题,但是此时所有请求都会到达数据库,数据库的压力会非常大,容易导致数据库宕机,因此还需要进行流量削峰、限流等操作,比如使用令牌桶算法、漏桶算法,让请求直接到达应用业务层就被打回,降低数据库的压力。
而且在实际业务中,一般会建立一个评估团队,对秒杀活动进行评估,根据评估结果进行调整,比如集群扩容、数据库优化、代码优化等。
如果单靠乐观锁就可以解决秒杀并发问题,那这软件程序未免也太好写了,这么好写为什么大家天天加班呢?
笔者写了一篇文章专门研究过这个问题,有兴趣的可以看看:为什么仅靠简单的乐观锁和悲观锁不能处理秒杀系统业务
如何定位慢 SQL(SQL 调优)
定位慢 SQL 主要有以下几种方法:
- 慢查询日志:开启慢查询日志,记录执行时间超过阈值的 SQL 语句,通过分析慢查询日志,找出慢 SQL。(上线前关闭)
- explain 命令:使用 explain 命令,查看 SQL 语句的执行计划,分析索引、表连接、排序等情况,找出慢 SQL。
- show profile 命令:使用 show profile 命令,查看 SQL 语句的执行时间、IO 操作、锁等情况,找出慢 SQL。
- 数据库监控工具:使用数据库监控工具,监控数据库的性能指标,如 CPU、内存、磁盘、网络等,找出慢 SQL。
- 慢 SQL 分析工具:使用慢 SQL 分析工具,分析 SQL 语句的执行计划、IO 操作、锁等情况,找出慢 SQL。
定位慢 SQL 主要是通过慢查询日志、explain 命令、show profile 命令、数据库监控工具、慢 SQL 分析工具等方法,找出慢 SQL,然后进行优化。
讲讲数据库的索引失效的情况
数据库的索引失效主要有以下几种情况:
- 索引列不在查询条件中:索引列不在查询条件中,索引失效,无法加速查询。
- 索引列使用函数:索引列使用函数,索引失效,无法加速查询。
- 索引列使用运算符:索引列使用运算符,索引失效,无法加速查询。
- 索引列使用 or 条件:索引列使用 or 条件,索引失效,无法加速查询。
- 索引列使用 not 条件:索引列使用 not 条件,索引失效,无法加速查询。
- 索引列使用 is null 条件:索引列使用 is null 条件,索引失效,无法加速查询。
- 索引列使用!=条件:索引列使用!=条件,索引失效,无法加速查询。
数据库的索引失效主要是因为索引列不在查询条件中、索引列使用函数、索引列使用运算符、索引列使用 or 条件、索引列使用 not 条件、索引列使用 is null 条件、索引列使用!=条件等情况,根据不同的情况选择合适的索引。