MySQL
SQL 语言类别。
- DDL(数据定义语言):DDL 用于定义和管理数据库对象(如表、索引、视图等);
CREATE TABLE
:创建表。ALTER TABLE
:修改表结构。DROP TABLE
:删除表。CREATE INDEX
:创建索引。 - DML(数据操作语言):DML 用于对数据库中的数据进行操作;
SELECT
:查询数据。INSERT
:插入数据。UPDATE
:更新数据。 -DELETE
:删除数据。 - DCL(数据控制语言):管理数据库对象的访问权限,
GRANT
:授予用户访问权限、REVOKE
:回收用户访问权限 等。 - TCL(事务控制语言):用于管理数据库的事务,常用的命令包括
COMMIT
:提交事务、ROLLBACK
:回滚事务、SAVEPOINT
:设置保存点,用于部分回滚。
- DDL(数据定义语言):DDL 用于定义和管理数据库对象(如表、索引、视图等);
MySQL 数据类型
- 数值类型:
- 整数类型:
INT
(整数,4字节)TINYINT
(小整数,1字节)SMALLINT
(小整数,2字节)MEDIUMINT
(中等整数,3字节)BIGINT
(大整数,8字节) - 浮点数类型:
FLOAT
(单精度浮点数)DOUBLE
(双精度浮点数) - 定点数类型:
DECIMAL
(定点数)
- 整数类型:
- 日期和时间类型:
DATE
(日期);TIME
(时间)DATETIME
(日期和时间,包括秒)TIMESTAMP
(日期和时间,包括秒,通常用于记录数据的修改时间) - 字符串类型:
CHAR
(定长字符串);VARCHAR
(变长字符串);TEXT
(较小的文本)MEDIUMTEXT
(中等大小的文本)LONGTEXT
(较大的文本)- CHAR 和 VAVARCHAR 的区别?
1、CHAR 和 VAVARCHAR 类型在存储和检索方面有所不同
2、CHAR 列长度固定为创建表时声明的长度,长度值范围是 1 到 255 当 CHAR值被存储时,它们被用空格填充到特定长度,检索 CHAR 值时需删除尾随空格。
- CHAR 和 VAVARCHAR 的区别?
- 二进制类型:
BINARY
(定长二进制字符串)VARBINARY
(变长二进制字符串)BLOB
(较小的二进制数据)MEDIUMBLOB
(中等大小的二进制数据)LONGBLOB
(较大的二进制数据)- BLOB 和 TEXT 的区别。
- 存储内容:
BLOB
存储二进制数据,不进行字符集的转换。TEXT
存储字符数据,会根据字符集进行相应转换 - 大小限制:
BLOB
可以存储更大的二进制数据。TEXT
可以存储更大的字符数据。 - 排序和比较:
BLOB
进行二进制排序和比较。TEXT
进行字符集排序和比较。 - 用途:
BLOB
适用存储图像、音频、视频等二进制文件。TEXT
适用存储文本文档、HTML、XML 等字符数据。
- 存储内容:
- BLOB 和 TEXT 的区别。
- 其他类型:
ENUM
(枚举类型)SET
(集合类型)
- 数值类型:
MySQL 里记录货币用什么字段类型好?
- 在记录货币金额时,一般建议使用 DECIMAL 类型。DECIMAL 类型是一种精确的定点数类型,提供了更可靠的精确度,确保不会发生舍入误差,适合用于存储货币等需要精确计算的数值。
- 避免使用浮点数类型(如 FLOAT 或 DOUBLE)来表示货币金额,因为浮点数在计算中可能存在精度问题。
1
2
3-- 9(precision)代表将被用于存储值的总的小数位数,而 2(scale)代表将被用于存储小数点后的位数。
-- salary 列中的值的范围是从-9999999.99 到9999999.99。
salary DECIMAL(9,2) - 为表中得字段选择合适得数据类型:
字段类型优先级: 整形>date,time>enum,char>varchar>blob,text
优先考虑数字类型,其次是日期或者二进制类型,最后是字符串类型,同级别的数据类型,应该优先选择占用空间小的
MySQL 中
IN
、EXISTS
和LIKE
的作用和用法,如何优化DISTINCT
?- IN:用于在
WHERE
子句中筛选出符合指定值列表中任一值的数据行。1
2-- 查询出 `id` 字段值为 1、2 或 3 的用户数据行
SELECT * FROM users WHERE id IN (1, 2, 3); - EXISTS:用于检查子查询是否返回任何行,如果子查询返回至少一行数据,则
EXISTS
返回 true;否则返回 false。1
2-- 上述示例会查询出至少有一笔订单关联的产品数据行
SELECT * FROM products p WHERE EXISTS (SELECT 1 FROM orders o WHERE o.product_id = p.id); - LIKE:用于在
WHERE
子句中进行模糊查询,通常配合通配符使用包括%
(匹配任意长度的字符串)和_
(匹配单个字符)1
2-- 上述示例会查询出名称以 "apple" 开头的产品数据行
SELECT * FROM products WHERE name LIKE 'apple%'; - DISTINCT 在所有列上转换为 GROUP BY,并与 ORDER BY 子句结合使用。
1
2
3SELECT DISTINCT t1.a FROM t1,t2 where t1.a=t2.a;
-- 转换为等效的 GROUP BY 查询:
SELECT t1.a FROM t1, t2 WHERE t1.a = t2.a GROUP BY t1.a;
- IN:用于在
什么是通用 SQL 函数?
1、CONCATAT(A, B) – 连接两个字符串值以创建单个字符串输出。通常用于将两个或多个字段合并为一个字段。
2、FORMATAT(X, D)- 格式化数字 X 到 D 有效数字。
3、CURRDATATE(), CURRTIME()- 返回当前日期或时间。
4、NOW() – 将当前日期和时间作为一个值返回。
5、MONTH(),DAYAY(),YEAR(),WEEK(),WEEKDAYAY() – 从日期值中提取给定数据。
6、HOUR(),MINUTE(),SECOND() – 从时间值中提取给定数据。
7、DATATEDIFF(A,B) – 确定两个日期之间的差异,通常用于计算年龄
8、SUBTIMES(A,B) – 确定两次之间的差异。
9、FROMDAYAYS(INT) – 将整数天数转换为日期值一条 sql 的执行过程。
- 连接层:客户端是否与mysql连接。
- server层:mysql8.0前会在缓存中查询是否执行过此sql,命中则直接返回;8.0后取消了这个机制。
- 语法解析器:对接收到的 SQL 语句进行解析,以检查其语法和语义是否正确。如果 SQL 语句有语法错误或违反数据库模式的约束,系统会返回相应的错误信息。
- 编译、优化SQL 语句:解析后,DBMS 将 SQL 语句编译成一个可执行的查询计划 explain。这个计划是一个数据结构,描述了如何从数据库中获取或操作数据。在编译阶段,DBMS 可能会对查询计划进行优化,以提高执行效率。优化过程包括选择合适的索引、调整连接顺序等。
- 执行器:DBMS 根据优化后的查询计划执行 SQL 语句,去存储引擎层读取数据库中的数据。这包括从磁盘读取数据,使用索引加速查询,应用过滤条件等。
执行器执行前,会检查mysql的innodb的buffer pool的缓存,,未命中要先查db。select直接查询buffer pool,dml(增删改)还要设计日志,,, - 存储引擎:如 innodb、myisam、memory都会向上层提供查询接口。
- 返回结果: 执行完成后,DBMS 将结果返回给用户。结果可能是查询的结果集、执行成功的消息,或者在出现错误时的错误信息。
Mysql引擎 如何把硬盘上的数据查到?
- 解析 SQL 语句: MySQL 首先解析查询语句,检查语法和语义,确保查询是合法的。
- 查询优化: MySQL 会对查询进行优化,生成一个查询执行计划。
- 执行查询计划: MySQL 数据库引擎按照优化后的执行计划执行查询。这涉及从硬盘读取数据。
- 使用索引: 如果查询中使用了索引,并且优化器认为使用索引更有效,MySQL 将使用索引来快速定位和检索数据。索引通常存储在磁盘上,但在需要时会被加载到内存中,以提高查询速度。
索引的使用发生在磁盘 I/O 操作之前。 - 读取数据块: 如果数据没有在内存中,MySQL 数据库引擎将从磁盘读取数据块(通常是页)到内存中。
MySQL将数据以页(Page)为单位组织在磁盘上。一页通常包含多条记录,每页的大小是固定的。MySQL从磁盘上读取整个页,而不仅仅是所需的单个记录。读磁盘是一次 I/0 操作,MySQL 使用一种称为预读的技术,一次性读取多个相邻的数据块,以提高性能。
首先,MySQL 发送读取请求到存储设备(硬盘),包括要读取的数据块的位置信息(例如磁盘上的扇区或页)以及读取的数量。磁盘根据请求移动磁头到指定的位置。这个过程称为磁盘寻道,寻道时间是磁盘 I/O 中的主要时间消耗部分。一旦磁头到达目标轨道,磁盘开始旋转,以便将所需的数据块转到磁头下方,磁盘开始传输数据到内存中。 - 缓存: MySQL 使用缓存来存储经常访问的数据块,这样在后续查询中可以更快地访问这些数据。这包括查询结果的缓存、索引缓存和数据缓存等。使用一个称为InnoDB Buffer Pool的缓存池来存储数据页。
- 返回结果: 当查询完成时,MySQL 将结果返回给用户。
数据库存储引擎
- 数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。
存储引擎主要有: 1. MyIsam, 2. InnoDB, 3. Memory, 4. Archive, 5. Federated - InnoDB:底层存储结构为B+树, B树的每个节点对应innodb的一个page, page大小是固定的,一般设为 16k。其中非叶子节点只有键值,叶子节点包含完成数据、、适用场景:
1)经常更新的表,适合处理多重并发的更新请求。
2)支持事务。
3)可以从灾难中恢复(通过 bin-log 日志等)。
4)外键约束。只有他支持外键。
5)支持自动增加列属性 auto_increment。 - MyIASM:是 MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 NSERT(插入)或 UPDATATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。
ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。在设计之初就预想数据组织成有固定长度的记录,按顺序存储的。 —ISAM 是一种静态索引结构。缺点是它不 支持事务处理。 - Memery 就是将数据放在内存中,数据处理速度很快,但是安全性不⾼。
- 数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。
MyISAM 和 InnoDB 有什么区别?
(1)MyISAM 只支持表级别的锁粒度,InnoDB 支持行级别的锁粒度。
(2)MyISAM 不提供事务支持。InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别。
(3)MyISAM 不支持外键,而 InnoDB 支持。
(4)MyISAM 不支持 MVCC,而 InnoDB 支持。
(5)虽然 MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,但是两者的实现方式不太一样。
(6)MyISAM 不支持数据库异常崩溃后的安全恢复,而 InnoDB 支持。
(7)InnoDB 的性能比 MyISAM 更强大。索引、、
在 MySQL 中,索引是一种特殊的数据结构,用于加快数据库表中数据的检索速度。索引在数据库表中的一个或多个列上创建,可以将这些列的值快速映射到实际数据的物理位置。
索引(Index)是帮助 MySQL 高效获取数据的数据结构。 常见的查询算法,顺序查找,二分查找,二叉排序树查找,哈希散列法,分块查找,平衡多路搜索树 B 树(B-tree) ,索引是对数据库表中一个或多个列的值进行排序的结构,建立索引有助于快速获取信息。
你也可以这样理解:索引就是加快检索表中数据的方法。数据库的索引类似于书籍的索引。在书籍中,索引允许用户不必翻阅完整个书就能迅速地找到所需要的信息。在数据库中,索引也允许数据库程序迅速地找到表中的数据,而不必扫描整个数据库 mysql。
索引并非是越多越好,创建索引也需要耗费资源,一是增加了数据库的存储空间,二是在插入和删除时要花费较多的时间维护索引
索引加快数据库的检索速度
索引降低了插入、删除、修改等维护任务的速度
唯一索引可以确保每一行数据的唯一性
通过使用索引,可以在查询的过程中使用优化隐藏器,提高系统的性能
索引需要占物理和数据空间索引有哪些种类。
- 从数据结构维度进⾏分类:
B+树索引:所有数据存储在叶⼦节点,复杂度为O(logn),适合范围查询。
哈希索引:适合等值查询,检索效率⾼,⼀次到位
全⽂索引: MyISAM 和 InnoDB 中都⽀持使⽤全⽂索引,⼀般在⽂本类型char,text,varchar 类型上创建
R-Tree 索引:⽤来对 GIS 数据类型创建 SPATIAL 索引 - 从物理存储维度进⾏分类:
聚集索引:数据存储与索引⼀起存放,叶⼦节点会存储⼀整⾏记录,找到索引也就找到了数据。
⾮聚集索引:数据存储与索引分开存放,叶⼦节点不存储数据,存储的是数据⾏地址。 - 从逻辑维度进⾏分类:
主键索引:⼀种特殊的唯⼀索引,不允许有空值。
普通索引:MySQL中基本索引类型,允许空值和重复值
联合索引:多个字段创建的索引,使⽤时遵循最左前缀原则
唯⼀索引:索引列中的值必须是唯⼀的,但是允许为空值
空间索引:MySQL5.7之后⽀持空间索引,在空间索引这⽅⾯遵循OpenGIS⼏何数据模
- 从数据结构维度进⾏分类:
主键?聚集索引,非聚集索引?
- 主键:主键是一种用于唯一标识表中每一行数据的列或列组合。主键列的值必须唯一且不为空(NULL)。在InnoDB中,主键索引是表的物理排序顺序,它是表的聚集索引。如果表没有显式定义主键,InnoDB会选择一个合适的唯一非空索引来充当主键索引。
- 聚集索引:在MySQL中,聚集索引决定了数据在磁盘上的物理存储顺序,即数据的存储顺序与索引顺序一致。在InnoDB存储引擎中,主键索引就是一个聚集索引。如果表没有显式定义主键,则InnoDB会选择一个唯一非空的索引来充当聚集索引。
- 非聚集索引:非聚集索引在磁盘上维护索引键和对应数据行的引用,索引键的顺序与实际数据行的物理存储顺序无关。在MySQL中,除了InnoDB存储引擎的聚集索引(主键索引),其他索引都是非聚集索引,例如普通索引或唯一索引。
InnoDB 建议为大部分表使用默认的自增主键的主要原因
- 聚簇索引: InnoDB 表的主键是聚簇索引(Clustered Index),这意味着数据行的物理顺序与聚簇索引的顺序一致。使用自增主键作为聚簇索引可以确保新插入的数据按顺序添加到表的末尾,减少数据页的分裂和碎片,提高数据的顺序性。
- 插入性能: 自增主键的顺序性有助于提高插入性能。因为数据行按主键的顺序插入,新的数据行往往直接添加到表末尾,而不会导致页面的分裂和数据的重新排序。
- 查询性能: 使用自增主键作为聚簇索引可以提高范围查询和排序查询的性能,因为相关数据在物理上是相邻存储的。
- 减少索引大小: 自增主键通常是整数,占用的空间相对较小。相比于使用其他类型的主键,这可以减少非聚簇索引的大小,提高缓存的效率,减少磁盘 I/O。
- 减少碎片: 自增主键的插入顺序有助于减少数据页的分裂和碎片,减小了表的维护成本。
什么时候需要创建索引?
表的主关键字:⾃动建⽴唯⼀索引
直接条件查询的字段:经常⽤于WHERE查询条件的字段,这样能够提⾼整个表的查询速度
查询中与其它表关联的字段:例如字段建⽴了外键关系
查询中排序的字段:排序的字段如果通过索引去访问将⼤⼤提⾼排序速度
唯⼀性约束列: 如果某列具有唯⼀性约束,那么为了确保数据的唯⼀性,可以在这些列上创建唯⼀索引。
⼤表中的关键列: 在⼤表中,如果查询的效率变得很低,可以考虑在关键列上创建索引。什么时候不需要创建索引?
⼩表: 对⼩表创建索引可能会带来额外的开销,因为在⼩数据集中扫描整个表可能⽐使⽤索引更快。
频繁的插⼊、更新和删除操作: 索引的维护成本会随着数据的插⼊、更新和删除操作⽽增加。如果表经常被修改,过多的索引可能会影响性能。
数据重复且分布平均的表字段:假如⼀个表有10万⾏记录,性别只有男和⼥两种值,且每个值的分布概率⼤约为50%,那么对这种字段建索引⼀般不会提⾼数据库的查询速度。
很少被查询的列: 如果某列很少被⽤于查询条件,那么为它创建索引可能没有明显的性能提升。
查询结果总⾏数较少的表: 如果查询的结果集总⾏数很少,使⽤索引可能不会有太⼤的性能提升。常见索引原则
- 选择唯一性索引,唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
- 为经常需要排序、分组和联合操作的字段建立索引。
- 为常用作为查询条件的字段建立索引。
- 限制索引的数目:越多的索引,会使更新表变得很浪费时间。尽量使用数据量少的索引
- 如果索引的值很长,那么查询的速度会受到影响。尽量使用前缀来索引
- 如果索引字段的值很长,最好使用值的前缀来索引。
- 删除不再使用或者很少使用的索引
- 最左前缀匹配原则,非常重要的原则。
- 尽量选择区分度高的列作为索引区分度的公式是表示字段不重复的比例
- 索引列不能参与计算,保持列“干净”:带函数的查询不参与索引。
- 尽量的扩展索引,不要新建索引
最左前缀匹配原则
- 在使用复合索引(composite index)时,如果查询条件中使用了索引的第一个列作为条件,那么数据库可以利用这个索引来加速查询;但如果查询条件中只使用了索引的前几个列,而没有使用索引的第一个列作为条件,那么数据库无法利用这个索引来加速查询。
- 具体来说,假设有一个复合索引
(col1, col2, col3)
,这个索引按照col1
、col2
、col3
的顺序排列。根据最左前缀匹配原则,以下情况适用:
1.当查询条件中包含了索引的第一个列col1
时,数据库可以使用索引来加速查询。例如:2.当查询条件中包含了索引的前几个列1
SELECT * FROM table_name WHERE col1 = 'value';
col1
、col2
时,数据库也可以使用索引来加速查询。例如:3.如果查询条件中包含了索引的所有列1
SELECT * FROM table_name WHERE col1 = 'value1' AND col2 = 'value2';
col1
、col2
、col3
并且按照索引的顺序出现,那么可以触发索引。1
SELECT * FROM table_name WHERE col1 = 'value1' AND col2 = 'value2' AND col3 = 'value3';
- 但是,如果查询条件中只使用了索引的后几个列,而没有使用索引的第一个列作为条件,则无法利用这个索引加速查询。如:
1
SELECT * FROM table_name WHERE col2 = 'value2' AND col3 = 'value3';
覆盖索引?回表查询?
- 覆盖索引是指一个查询语句所需的数据可以从索引中直接获取,而无需访问表格中的实际数据行。这种情况下,索引“覆盖”了查询的所有需要的列。覆盖索引的优势在于可以减少磁盘 I/O 和内存的消耗,因为不需要额外的表格访问。
- 回表查询指的是在使用索引的情况下,通过索引定位到主键,然后再根据主键的值去表中检索数据的过程。这通常发生在覆盖索引(Covering Index)无法满足查询需求时。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(50),
price DECIMAL(10, 2),
quantity INT
);
-- 为 product_name 列创建索引 idx_product_name
CREATE INDEX idx_product_name ON products (product_name);
-- 假设我们有一条数据
INSERT INTO products (product_id, product_name, price, quantity) VALUES (1, 'Laptop', 999.99, 50);
-- 现在,如果我们执行以下查询:
SELECT product_name, price FROM products WHERE product_name = 'Laptop';
-- 从索引中获取的列 product_name,但 price 列不在查询的列中,MySQL 将执行回表查询
-- MySQL 首先使用索引 idx_product_name 找到匹配 Laptop 的行,获取到对应的 product_id(主键),然后根据 product_id 到表格中检索完整的行数据以获取 price 值
-- 这种情况下,如果我们希望避免回表查询,可以考虑创建一个覆盖索引,将查询语句中需要的所有列都包含在索引中,便无需执行额外的回表查询
CREATE INDEX idx_product_name_covering ON products (product_name, price);
B+ Tree(InnoDB)索引
- 数据分块存储,每一块称为一页。所有的值都是按顺序存储的,并且每一个叶子到根的距离相同。
- 非叶节点存储数据的边界,叶子节点存储指向数据行的指针。通过边界缩小数据的范围,从而避免全表扫描,加快了查找的速度。
- B+ 树索引是一种索引结构,通常用于数据库管理系统中作为数据的索引方式。它可以用作聚集索引或非聚集索引,并不是严格意义上的主键索引。在数据库中,B+树索引在不同存储引擎下,例如在InnoDB中作为主键索引(聚集索引)使用,或作为其他索引(非聚集索引)的实现。
B树 与 B+树 的区别
- B树:节点从小到大排序,一个节点可存多个元素
- B+树:拥有B树的特点,叶子结点间有指针,叶子结点存储了所有的元素
- B树和B+树,一般都是应用在文件系统和数据库系统中,用来减少磁盘IO带来的性能损耗。
以Mysql中的InnoDB为例,当我们通过select语句去查询一条数据时,InnoDB需要从磁盘上去读取数据,这个过程会涉及到磁盘IO以及磁盘的随机IO(如图所示)我们知道磁盘IO的性能是特别低的,特别是随机磁盘IO。因为,磁盘IO的工作原理是,首先系统会把数据逻辑地址传给磁盘,磁盘控制电路按照寻址逻辑把逻辑地址翻译成物理地址,也就是确定要读取的数据在哪个磁道,哪个扇区。为了读取这个扇区的数据,需要把磁头放在这个扇区的上面,为了实现这一个点,磁盘会不断旋转,把目标扇区旋转到磁头下面,使得磁头找到对应的磁道,这里涉及到寻道事件以及旋转时间
很明显,磁盘IO这个过程的性能开销是非常大的,特别是查询的数据量比较多的情况下。所以在InnoDB中,干脆对存储在磁盘块上的数据建立一个索引,然后把索引数据以及索引列对应的磁盘地址,以B+树的方式来存储。如图所示,当我们需要查询目标数据的时候,根据索引从B+树中查找目标数据即可,由于B+树分路较多,所以只需要较少次数的磁盘IO就能查找到。
为什么 MySQL 的索引要使用 B+ 树而不是其它树形结构?
- B 树是一种多路平衡树,用这种存储结构来存储大量数据,它的整个高度会相比二叉树来说,会矮很多。
而对于数据库来说,所有的数据必然都是存储在磁盘上的,而磁盘 IO 的效率实际上是很低的,特别是在随机磁盘 IO 的情况下效率更低。所以树的高度能够决定磁盘 IO 的次数,磁盘 IO 次数越少,对于性能的提升就越大,这也是为什么采用 B 树作为索引存储结构的原因。 - 对于 B 树,不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),指针少的情况下要保存大量数据,只能增加树的高度,导致 IO 操作变多,查询性能变低。在 Mysql 的 InnoDB 存储引擎里面用了一种增强的 B 树结构,也就是 B+树来作为索引和数据的存储结构。
- 使用 B+树来实现索引的原因,我认为有几个方面。
- B+树非叶子节点不存储数据,所以每一层能够存储的索引数量会增加,意味着 B+树在层高相同的情况下存储的数据量要比 B 树要多,使得磁盘 IO 次数更少。
- 在 Mysql 里面,范围查询是一个比较常用的操作,而 B+树的所有存储在叶子节点的数据使用了双向链表来关联,所以在查询的时候只需查两个节点进行遍历就行,而 B 树需要获取所有节点,所以 B+树在范围查询上效率更高。
- 在数据检索方面,由于所有的数据都存储在叶子节点,所以 B+树的 IO 次数会更加稳定一些。
- 叶子节点存储所有数据,所以 B+树的全局扫描能力更强一些,它只需要扫描叶子节点。但是 B 树需要遍历整个树。
另外,基于 B+树这样一种结构,如果采用自增的整型数据作为主键,还能更好的避免增加数据的时候,带来叶子节点分裂导致的大量运算的问题。
- B 树是一种多路平衡树,用这种存储结构来存储大量数据,它的整个高度会相比二叉树来说,会矮很多。
为什么选择 B+Tree 而不是红黑树?
- 红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统,普遍采用 B+ Tree作为索引结构这是因为其访问磁盘数据有更高的性能。我主要从两个点来回答
- 第一点:对于一个数据库来说 存储的数据量会比较多,导致索引也很大 因此需要将索引存储在磁盘,但是磁盘的 IO 操作又非常耗,所以提高索引效率的关键在于减少磁盘 IO 的次数。相同节点个数 的 B+Tree 的高度更小,树的高度基本决定了磁盘的 IO 次数 ,所以使用 B+Tree 性能要高很多
- 第二点:B+Tree 有个特点是相邻的数据在物理上也是相邻的,因为 B+Tree 的 node 的大小设为一个页,而一个节点上存有多个相邻的关键字和分支信息,每个节点只需要一次 IO就能完全载入,相当于一次 IO 载入了多个相邻的关键字和分支,而红黑树不具有这个特性,红黑树中大小相邻的数据,在物理结构上可能距离相差很大。由于程序的局部性原理,如果我们在索引中采用了预加载的技术,每次磁盘访问的时候除了将访问到的页加载到磁盘,我们还可以基于局部性原理加载,几页相邻的数据到内存中,而这个加载是不需要消耗多余磁盘 IO 时间的。
因此 基于局部性原理,以及 B+Tree 存储结构物理上的特性,所以 B+Tree 的索引性能比红黑树要好很多。
在数据库的表上建立了
(time DESC, name DESC)
的复合索引,什么情况下失效/生效?- 索引生效的情况:
- 查询条件涉及到两个字段: 当查询条件涉及到索引的两个字段时,复合索引可以生效。例如:查询条件包括了
time
和name
,而且与索引的顺序一致,因此该复合索引可以被有效利用。1
SELECT * FROM your_table WHERE time > '2022-01-01' AND name = 'John' ORDER BY time DESC, name DESC;
- 查询条件只涉及到部分字段: 当查询条件只涉及到索引的一部分字段时,也可能会使用到该复合索引。例如:查询条件只包含了
time
字段,但由于time
是索引的第一个字段,所以该索引可能仍然生效。1
SELECT * FROM your_table WHERE time > '2022-01-01' ORDER BY time DESC, name DESC;
- 查询条件涉及到两个字段: 当查询条件涉及到索引的两个字段时,复合索引可以生效。例如:查询条件包括了
- 索引不生效的情况:
- 查询条件没有使用到索引的前缀: 如果查询条件中没有使用到索引的前缀字段,那么复合索引可能不会被使用。例如:查询条件只包含了
name
字段,没用到time
字段,因此(time DESC, name DESC)
这个复合索引可能不被使用1
SELECT * FROM your_table WHERE name = 'John' ORDER BY time DESC, name DESC;
- 查询条件的顺序不符合索引的顺序: 复合索引的顺序很重要,如果查询条件的顺序与索引的顺序不一致,索引可能无法被有效利用。例如:虽然包含了索引的两个字段,但查询条件的顺序与索引的顺序不一致,因此索引可能不会被使用。
1
SELECT * FROM your_table WHERE name = 'John' AND time > '2022-01-01' ORDER BY time DESC, name DESC;
- 查询条件没有使用到索引的前缀: 如果查询条件中没有使用到索引的前缀字段,那么复合索引可能不会被使用。例如:查询条件只包含了
- 索引生效的情况:
MySQL 索引失效的几种情况
- OR 语句前后没有同时使用索引。要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引。
1
2
3
4<!-- 生效的情况 -->
SELECT * FROM your_table WHERE time > '2022-01-01' OR name = 'John' ORDER BY time DESC, name DESC;
<!-- 不生效的情况 -->
SELECT * FROM your_table WHERE time > '2022-01-01' OR other_column = 'some_value' ORDER BY time DESC, name DESC; - 复合索引未用左列字段。对于复合索引,如果不使用前列,后续列也将无法使用。
1
SELECT * FROM your_table WHERE name = 'John' ORDER BY time DESC, name DESC;
- like以%开头;模糊匹配
1
SELECT * FROM your_table WHERE name LIKE '%John' ORDER BY time DESC, name DESC;
- 需要类型转换。存在索引列的数据类型隐形转换,则用不上索引。
1
SELECT * FROM your_table WHERE CAST(time AS VARCHAR) = '2022-01-01' ORDER BY time DESC, name DESC;
- where中索引列有数学运算。
1
SELECT * FROM your_table WHERE time * 2 = '2022-01-01' ORDER BY time DESC, name DESC;
- where中索引列使用了函数。
1
SELECT * FROM your_table WHERE YEAR(time) = 2022 ORDER BY time DESC, name DESC;
- 如果mysql觉得全表扫描更快时(数据少)。
- OR 语句前后没有同时使用索引。要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引。
联合索引(Composite Index)
- 也称为复合索引,是指同时包含多个列的索引,它可以更加精确地定位数据,提高查询的效率。
通常情况下,一个表中可能存在多个需要经常用于查询的列,使用联合索引可以将这些列组合起来,建立一个复合索引。在查询时,如果查询条件同时包含联合索引中的多个列,数据库可以直接使用索引定位到符合条件的行,避免了全表扫描,提高了查询效率。
需要注意的是,在使用联合索引时,需要考虑索引的顺序。通常情况下,应该将最常用于查询的字段放在索引的前面,这样可以更加有效地利用索引。另外,联合索引也存在一些限制。由于索引是按照索引列的顺序建立的,因此只有在查询条件中包含索引的最左侧的列时,MySQL 才能利用这个索引。如果查询条件中包含的列不是索引的最左侧列,那么 MySQL 就无法使用这个索引。
此外,由于联合索引包含多个列,因此其维护成本也相对较高。如果经常更新其中一个列的值,可能会导致索引的重建,影响数据库的性能。因此,在建立联合索引时,应该根据具体的应用场景,权衡利弊,避免滥用。 - 如何使用联合索引?
1、联合索引的最左前缀匹配指的是where条件一定要有联合索引的第一个字段
2、是否走联合索引与where条件的顺序无关,只与字段有关 - 联合索引的最左前缀匹配原则
最左前缀匹配原则指的是,在使用联合索引时,MySQL 会根据联合索引中的字段顺序,从左到右依次到查询条件中去匹配,如果查询条件中存在与联合索引中最左侧字段相匹配的字段,则就会使用该字段过滤一批数据,直至联合索引中全部字段匹配完成,或者在执行过程中遇到范围查询(如 >、<)才会停止匹配。对于 >=、<=、BETWEEN、like 前缀匹配的范围查询,并不会停止匹配。所以,我们在使用联合索引时,可以将区分度高的字段放在最左边,这也可以过滤更多数据。 - 联合索引的作用?
1、减少io操作的开销和磁盘空间的开销;
2、提升性能。索引列越多,通过索引筛选出的数据越少。
3、覆盖索引。直接通过遍历索引取得数据,无需回表。
提高查询效率:联合索引可以加速对多列数据的查询,对于联合索引中包含的列,可以同时使用它们进行筛选,减少了查询的数据量,提高了查询效率。
减少磁盘IO:联合索引可以将多个列的数据存储在一起,减少了需要读取的磁盘块数,从而降低了IO的开销。
优化排序操作:如果查询需要按照联合索引中的多个列进行排序,联合索引可以避免对多个独立索引的排序操作,从而提高排序操作的效率
- 也称为复合索引,是指同时包含多个列的索引,它可以更加精确地定位数据,提高查询的效率。
MySQL 最左前缀索引的底层原理、、
涉及到 B-Tree 索引结构以及 MySQL 的查询优化器。
B-Tree 索引是一种常见的索引结构,用于快速检索数据库中的数据。在 B-Tree 索引中,数据按照键值有序存储,每个节点包含多个键值和对应的指针。MySQL 使用 B-Tree 索引来实现各种索引类型,包括最左前缀索引。
当你创建一个多列索引时,MySQL 实际上会创建一个按照指定列顺序建立的 B-Tree 索引。这个索引会按照指定的列值顺序存储数据,因此在查询时,如果查询条件只涉及到索引的最左前缀列,MySQL 就可以利用这个索引来加速查询。
???简单描述 MySQL 中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响?(从读写两方面)
索引是一种特殊的文件(InnoDB 数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
普通索引(由关键字 KEY 或 INDEX 定义的索引)的唯一任务是加快对数据的访问速度。索引可以极大的提高数据的查询速度,但是会降低插入、删除、更新表的速度,因为在执行这些写操作时还要操作索引文件。普通索引允许被索引的数据列包含重复的值。
如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字 UNIQUE 把它定义为一个唯一索引。唯一索引可以保证数据记录的唯一性。
主键,是一种特殊的唯一索引,在一张表中只能定义一个主键索引,用于唯一标识一条记录,使用关键字 PRIMARY KEY 来创建。
索引可以覆盖多个数据列,如像 INDEX(columnA, columnB)索引,这就是联合索引。EXPLAIN
执行计划- 通过分析 EXPLAIN 返回的执行计划,可以了解查询的优化情况,如是否使用了索引、是否进行了全表扫描等。对于需要优化的查询,可以根据执行计划进行优化,例如添加适当的索引、调整查询语句等。
1
EXPLAIN SELECT * FROM customers WHERE city = 'New York';
- 执行计划中的信息通常包括以下内容:
- id:查询步骤的标识符。
- select_type:查询类型,如 SIMPLE、PRIMARY、SUBQUERY 等。
- table:查询涉及的表。
- type:访问类型,如 ALL、INDEX、RANGE 等,表示数据库如何访问表中的数据。
- possible_keys:可能用到的索引。
- key:实际使用的索引。
- rows:估计的返回行数。
- Extra:其他信息,如使用了临时表、使用了文件排序等。
- 通过分析 EXPLAIN 返回的执行计划,可以了解查询的优化情况,如是否使用了索引、是否进行了全表扫描等。对于需要优化的查询,可以根据执行计划进行优化,例如添加适当的索引、调整查询语句等。
SQL 优化?
- 优化查询语句、、
- 选择需要的列而不是使用
SELECT *
: - 减少子查询,使用关联查询: 尽量使用关联查询(LEFT JOIN、RIGHT JOIN、INNER JOIN)替代子查询,以提高查询效率。使用 JOIN 操作来代替手动创建的临时表,提高查询效率。
- 避免使用 IN 或 NOT IN,使用 EXISTS 或关联查询: 以提高查询性能。
- 使用 UNION 或 UNION ALL 代替 OR 查询: 特别是当确定没有重复数据时,使用 UNION ALL 更为效率。
- 选择合适的字段属性: 尽可能减少定义字段宽度,设置合适的数据类型。尽量把字段设置 NOT NULL,例如’省份’、’性别’最好适用 ENUM
- 选择需要的列而不是使用
- 基于索引查询、、
- 为常用的查询条件和连接条件创建索引,以加速查询速度。确保数据库引擎能够充分利用索引。
- 避免对索引列进行函数或运算: 尽量避免在索引列上使用函数或运算,以确保索引的有效使用。
- WHERE 子句中的 LIKE 使用 % 放在右边: 在使用 LIKE 操作时,尽量将 % 放在右边,以便索引的有效使用。
- 尽量使用 SQL 语句用到的索引完成排序: 避免使用文件排序的方式。
- 避免在 WHERE 子句中使用 != 或 <> 操作符: 以免引擎放弃使用索引而进行全表扫描。
- 避免在 WHERE 子句中对字段进行 NULL 值判断:可以设置默认值或使用其他方式,以免引擎放弃使用索引而进行全表扫描。如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=0
- 数据库事务、、
- 事务处理和锁定表优化: 合理使用事务处理,避免锁定表的情况,以提高数据库并发性能。
- 优化查询语句、、
MySQL 优化?
- 硬件和操作系统层面的优化
硬件层面来说,影响 Mysql 性能的因素有,CPU、可用内存大小、磁盘读写速度、网络带宽从操作系层面来说,应用文件句柄数、操作系统网络的配置都会影响到 Mysql 性能。这部分的优化一般由 DBA 或者运维工程师去完成。在硬件基础资源的优化中,我们重点应该关注服务本身承载的体量,然后提出合理的指标要求,避免出现资源浪费! - 架构设计层面的优化
MySQL 是一个磁盘 IO 访问量非常频繁的关系型数据库,在高并发和高性能的场景中承受巨大的并发压力,
1、搭建 Mysql 主从集群,单个 Mysql 服务容易单点故障,一旦服务器宕机,将会导致依赖 Mysql 数据库的应用全部无法响应。 主从集群或者主主集群可以保证服务的高可用性。
2、读写分离设计,在读多写少的场景中,通过读写分离的方案,可以避免读写冲突导致的性能影响??
3、引入分库分表机制,通过分库可以降低单个服务器节点的 IO 压力,通过分表的方式可以降低单表数据量,从而提升 sql 查询的效率。
4、针对热点数据,可以引入更为高效的分布式数据库,比如 Redis、MongoDB 等,他们可以很好的缓解 Mysql 的访问压力,同时还能提升数据检索性能。 - MySQL 程序配置优化
对于 Mysql 数据库本身的优化,一般是通过 Mysql 中的配置文件 my.cnf 来完成的,比如。
Mysql5.7 版本默认的最大连接数是 151 个,这个值可以在 my.cnf 中修改。binlog 日志,默认是不开启。缓存池 bufferpoll 的默认大小配置等。
由于这些配置一般都和用户安装的硬件环境以及使用场景有关系,因此这些配置官方只会提供一个默认值,具体情况还得由使用者来修改。
关于配置项的修改,需要关注两个方面。1. 配置的作用域,分为会话级别和全局;2. 是否支持 热加载
因此,针对这两个点,我们需要注意的是:1. 全局参数的设定对于已经存在的会话无法生效; 2. 会话参数的设定随着会话的销毁而失效; 3. 全局类的统一配置建议配置在默认配置文件中,否则重启服务会导致配置失效 - 表结构和索引的优化
主要可以下面这些方面去优化分库分表、读写分离、为字段选择合适的数据类型、适当的反范式设计,适当冗余设计、为查询操作创建必要的索引但是要避免索引滥用、尽可能使用 Not Null。 - ??? SQL 优化
第一、慢 SQL 的定位和排查我们可以通过慢查询日志和慢查询日志分析工具得到有问题的 SQL 列表。
第二、执行计划分析针对慢 SQL,我们可以使用关键字 explain 来查看当前 sql 的执行计划.可以重点关注type key rows filterd 等字段 ,从而定位该 SQL 执行慢的根本原因。再有的放矢的进行优化
第三、使用 show profile 工具Show Profile 是 MySQL 提供的可以用来分析当前会话中,SQL 语句资源消耗情况的工具,可用于 SQL 调优的测量。在当前会话中.默认情况下处于 show profile 是关闭状态,打开之后保存最近 15 次的运行结果。针对运行慢的 SQL,通过 profile 工具进行详细分析.可以得到 SQL 执行过程中所有的资源开销情况. 如 IO 开销,CPU 开销,内存开销等.
- 硬件和操作系统层面的优化
什么是内联接、左外联接、右外联接、全连接?
内联接(Inner Join):匹配2张表中相关联的记录。
左外联接(Left Outer Join):除了匹配2张表中相关联的记录,还会匹配左表中剩余的记录,右表中未匹配到的字段用NULL表示。
右外联接(Right Outer Join):除了匹配2张表中相关联的记录,还会匹配右表中剩余的记录,左表未匹配到的字段用NULL表示。
全连接(Full Join 或 Full Outer Join):在使用全连接时,无论左表和右表是否存在匹配,都会返回两个表中所有的行,并在没有匹配的行处填充 NULL 值。drop、delete与truncate的区别
SQL中的drop、delete、truncate都表示删除,但是三者有一些差别
delete 和 truncate 只删除表的数据不删除表的结构速度,一般来说: drop> truncate >delete
delete 语句是 dml,这个操作会放到rollback segement中,事务提交之后才生效;如果有相应的trigger,执行的时候将被触发.
truncate,drop是 ddl, 操作立即生效,原数据不放到rollbacksegment中,不能回滚. 操作不触发trigger部分依赖和传递依赖。
- 部分依赖:发生在一个表的主键(Primary Key)中包含了多个字段,但是其中的某些字段并不完全依赖于主键的所有部分。简言之,某个非主键字段只依赖于主键的一部分。
举例:考虑一个订单表,主键是订单号和商品编号(OrderID, ProductID),如果订单表中还有商品名称(ProductName),而商品名称仅依赖于商品编号而不依赖于订单号,那么商品名称对于订单表来说就是一个部分依赖。 - 传递依赖:发生在一个非主键字段依赖于其他非主键字段,而这些其他非主键字段又依赖于主键。简言之,某个非主键字段依赖于其他非主键字段,而这些其他字段依赖于主键。
举例:考虑员工表,主键是员工编号(EmployeeID),表中包含了部门编号(DepartmentID))和部门名称(DepartmentName)。如果部门名称依赖于部门编号,而部门编号又依赖于员工编号,那么部门名称对于员工表来说就是一个传递依赖。
- 部分依赖:发生在一个表的主键(Primary Key)中包含了多个字段,但是其中的某些字段并不完全依赖于主键的所有部分。简言之,某个非主键字段只依赖于主键的一部分。
范式:解决部分依赖和传递依赖的一种方法是通过数据库范式化,通常是将表拆分成更小的表。
- 范式理论是为了解决四种异常。不符合范式的关系(表的属性的组合),会产生很多异常:1、冗余数据。2、修改异常: 修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改。3、删除异常: 删除一个信息,那么也会丢失其它信息。4、插入异常: 例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。
- 高级别范式的依赖于低级别的范式,1NF 是最低级别的范式。
- 第一范式 (1NF):属性不可分。
- 第二范式 (2NF):每个非主属性完全函数依赖于键码。可以通过分解来满足。(一张表分解成多张表)
- 第三范式 (3NF):非主属性不传递函数依赖于键码。可以进行分解。
- 如何通俗地理解三个范式?
第一范式:1NF 是对属性的原子性约束,要求属性具有原子性,不可再分解;
第二范式:2NF 是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
第三范式:3NF 是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。
什么是基本表?什么是视图?游标是什么?
基本表是本身独立存在的表,在 SQL 中一个关系就对应一个表。
视图本身不独立存储在数据库中,是一种虚拟的表,具有和物理表相同的功能。可以对视图进行增,改,查,操作,视图是从一个或几个基本表导出的表,视图通常是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表。它使得我们获取数据更容易,相比多表查询。
游标:是对查询出来的结果集作为一个单元来有效的处理。游标可以定在该单元中的特定行,从结果集的当前行检索一行或多行。可以对结果集当前行做修改。一般不使用游标,但是需要逐条处理数据的时候,游标显得十分重要。事务、、
- 事务(Transaction)是一组数据库操作,它们形成一个逻辑工作单元,要么全部成功执行,要么全部失败回滚。事务的目的是保证数据库的一致性和完整性,确保在多个操作中要么全部成功,要么全部失败,不会留下中间状态。
- 事务的特性:通常被称为 ACID 特性
- 原子性(Atomicity) 事务是一个完整的操作。事务的各步操作是不可分的(原子的);要么都执行,要么都不执行。
- 一致性(Consistency)当事务完成时,数据必须处于一致状态。
- 隔离性(Isolation) 对数据进行修改的所有并发事务是彼此隔离的, 这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。
- 永久性(Durability) 事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性
- 事务的隔离性
- 并发异常:第一类丢失更新、第二类丢失更新脏读、不可重复读、幻读
- 隔离级别:Read Uncommitted、Read Conmitted、Repeatable Read、Serializable
并发事务带来哪些问题?
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。- 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
- 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
- 不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
- 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。不可重复读和幻读区别:不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了
SQL 标准定义的四个事务隔离级别有哪些?
- READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能导致脏读、幻读或不可重复读。
- READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍可能发生。
- REPEATATATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍可能发生(特点情况下)、、InnoDB 存储引擎的默认支持的隔离级别是 【可重复读】。
- SERIALIZABLE(可串行化):最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读
MySQL的事务隔离级别与锁的关系、、
- MySQL 中的事务隔离级别通过不同类型的锁(共享锁、排它锁等)以及锁的粒度(行级锁、表级锁等)来实现对事务的隔离,保证了事务在并发环境下的正确执行。选择合适的事务隔离级别需要根据具体业务需求和性能要求进行权衡。
- 读未提交(Read Uncommitted):不涉及锁的机制,允许事务读取其他事务未提交的数据,可能会出现脏读。
- 读提交(Read Committed):使用共享锁(Shared Locks)和瞬时的排他锁(Exclusive Locks)来实现;
- 当一个事务读取数据时,会对数据进行共享锁定,这样其他事务可以读取相同的数据,但不能修改。
- 当一个事务修改数据时,会对数据进行排他锁定,这样其他事务不能读取也不能修改相同的数据,直到该事务提交或回滚。
- 可重复读(Repeatable Read):使用共享锁(Shared Locks)和持久的排他锁(Exclusive Locks)来实现;
- 当一个事务读取数据时,会对数据进行共享锁定,这样其他事务可以读取相同的数据,但不能修改。
- 当一个事务修改数据时,会对数据进行持久的排他锁定,直到该事务提交或回滚,其他事务都不能读取或修改相同的数据。
- 串行化(Serializable):
- MySQL 会对读取的数据行和写入的数据行都进行(行级)排它锁,确保事务串行执行,避免了幻读的问题。
1
2
3
4
5START TRANSACTION;
-- 对某个数据进行读取时,其他事务无法对该数据进行写入或者读取的操作,直到该事务完成读取操作
SELECT * FROM table_name WHERE condition FOR UPDATE;
-- 在这里进行读取和处理操作
COMMIT;
- MySQL 会对读取的数据行和写入的数据行都进行(行级)排它锁,确保事务串行执行,避免了幻读的问题。
- 隔离级别越低,锁机制越不严格,事务请求的锁越少,并发性越高,性能开销也越小
- InnoDB 默认支持的隔离级别是REPEATATATABLE-READ(可重读),在 分布式事务 的情况下一般会用到 SERIALIZABLE(可串行化) 隔离级别。
- MySQL 中的事务隔离级别通过不同类型的锁(共享锁、排它锁等)以及锁的粒度(行级锁、表级锁等)来实现对事务的隔离,保证了事务在并发环境下的正确执行。选择合适的事务隔离级别需要根据具体业务需求和性能要求进行权衡。
MySQL 支持事务吗?
在缺省模式下,MySQL 是 autocommit 模式的,所有的数据库更新操作都会即时提交,所以 MySQL 默认不支持事务。
但是如果你的 MySQL 表类型是使用 InnoDB TaTables 或 BDB tables 的话,你的MySQL 就可以使用事务处理,在非autocommit 模式下,你必须使用 COMMIT 来提交你的更改,或者用 ROLLBACK来回滚你的更改。???Innodb是如何实现事务的
Innodb通过BufferPool,LogBuffer,Redo Log,undoLog来实现事务,以一个update语句为例:
1.Innodb在收到一个update语句后,会先根据条件找到数据所在的页,并将该页缓存在BufferPool中
2.执行update语句,修改BufferPool中的数据,也就是内存中的数据
3.针对update语句生成一个Redo Log对象,并存入LogBuffer中
4.针对update语句生成undolog日志,用于事务回滚
5.如果事务提交,那么则把Redo Log对象进行持久化,后续还有其他机制将BufferPool中所修改的数据页持久化到磁盘中
6.如果事务回滚,则利用undolog日志进行回滚innoDB 如何解决幻读?
1、Mysql 有四种事务隔离级别,其中 InnoDB 在 RR(可重复读)的隔离级别下,解决了幻读的问题(在特定的情况下会出现幻读的问题。具体什么情况下会出现幻读呢???
2、幻读是指在同一个事务中,前后两次查询相同的范围时,得到的结果不一致
3、InnoDB 引入了间隙锁和 next-key Lock 机制来解决幻读问题??MySQL 单表为什么不要超过 2千万条 时最优?
- 查询性能:随着数据量的增加,查询可能变得更慢,特别是在没有合适索引支持的情况下。大表需要更多的计算资源和时间来处理查询,可能导致性能下降。
- 索引和缓存:维护大表的索引和缓存可能会变得更加困难。索引的大小随着数据量的增加而增加,这可能导致索引扫描变慢,同时也会增加对内存的需求。
1、一个高度为 3 的 B+ 树可以存放: 1170X1170X16=21902400 条这样的记录,即2千万多些,通过主键查询一条数据,只需要3次磁盘IO访问,当超出2千万条时,索引树高度为4。
2、mysql都有缓存,树高度为3时,第一层和第二层的数据都在缓存,高度为3查询效率很快,但是超过高度4时,查询效率就急速下降了。 - 锁和并发:在执行更新或删除操作时,数据库可能需要对表进行锁定,以确保数据的一致性。大表的锁定可能会阻塞其他操作,影响并发性能。
分表、、
- 分表是一种应对大表数据量的常见方法。通过将大表拆分为多个小表(分区),可以减轻数据库管理系统的负担,提高查询性能和管理效率。分表可以根据业务逻辑或特定的列值进行拆分,例如按时间范围、地理区域等方式进行分区。
- 在考虑分表之前,建议进行以下操作:
优化查询和索引:确保数据库表有适当的索引来支持常见的查询,并优化查询语句以提高性能。
限定数据的范围:务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内;
???读/写分离:经典的数据库拆分方案,主库负责写,从库负责读;
垂直和水平分割:考虑将大表进行垂直切分(按列拆分)或水平切分(按行拆分)以减少单个表的数据量。
使用分区:针对数据库支持的分区功能,可以考虑根据特定的标准将表分成多个逻辑分区。
数据库优化:定期清理无用数据、重新构建索引、优化数据库配置等,以提高数据库的整体性能。
分库分表之后,id 主键如何处理???
因为要是分成多个表之后,每个表都是从 1 开始累加,这样是不对的,我们需要一个全局唯一的 id 来支持。生成全局 id 有下面这几种方式:
UUID:不适合作为主键,因为太长了并且无序不可读,查询效率低。比较适合用于生成唯一的名字的标示比如文件的名字。
数据库自增 id : 两台数据库分别设置不同步长,生成不重复ID的策略来实现高可用。这种方式生成的 id 有序,但是需要独立部署数据库实例,成本高,还会有性能瓶颈。
利用 redis 生成 id : 性能比较好,灵活方便,不依赖于数据库。但是,引入了新的组件造成系统更加复杂,可用性降低,编码更加复杂,增加了系统成本。
Mysql锁、、
什么是锁?
答:数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。锁的类型(按类型)
- 悲观锁:悲观锁就是在读取数据的时候,为了不让别人修改自己读取的数据,就会先对自己读取的数据加锁,只有自己把数据读完了,才允许别人修改那部分数据,或者反过来说,就是自己修改某条数据的时候,不允许别人读取该数据,只有的整个事务提交了,才释放自己加上的锁,允许其他用户访问那部分数据。
- 悲观锁所说的加“锁”,其实分为几种锁,分别是: 排它锁(写锁)和共享锁(读锁) 。
- 乐观锁:乐观锁认为一个用户读数据的时候,别人不会去写自己所读的数据;悲观锁就刚好相反,觉得自己读数据库的时候,别人可能刚好在写自己刚读的数据,其实就是持一种比较保守的态度;
- 时间戳:时间戳就是不加锁,通过时间戳来控制并发出现的问题。在数据库表中单独加一列时间戳,比如“TimeStamp”, 每次读出来的时候,把该字段也读出来,当写回去的时候,把该字段加1,提交之前 ,跟数据库的该字段比较一次,如果比数据库的值大的话,就允许保存,否则不允许保存,这种处理方法虽然不使用数据库系统提供的锁机制,但是这种方法可以大大提高数据库处理的并发量
- 悲观锁:悲观锁就是在读取数据的时候,为了不让别人修改自己读取的数据,就会先对自己读取的数据加锁,只有自己把数据读完了,才允许别人修改那部分数据,或者反过来说,就是自己修改某条数据的时候,不允许别人读取该数据,只有的整个事务提交了,才释放自己加上的锁,允许其他用户访问那部分数据。
锁的类型(按粒度)
- 全局锁:锁定数据库中所有的表(数据备份)
- 表级锁:对当前操作的整张表加锁,它实现简单,开销小、加锁快,发生锁冲突的概率高、并发度低,不会出现死锁;被大部分 MySQL 引擎支持。最常使用的 MYISAM 与 INNODB 都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
- 表锁:锁住整张表,粒度较大
- 元数据锁:防止 DML 和 DDL 冲突
- 意向锁:避免加锁是一行一行查看行锁加锁情况,解决上述低效的问题
- 意向共享锁 (IS): 表级,准备加共享锁
- 意向排他锁 (Ix) : 表级,准备加排他锁
- 行级锁:开销大、加锁慢,发生锁冲突的概率低、并发度高,会出现死锁
- 行级锁是一种排他锁,防止其他事务修改此行;在使用以下语句时, Oracle 会自动应用行级锁:
- INSERT、 UPDATATE、 DELETE、 SELECT … FOR UPDATATE [OF columns] [WAWAIT n | NOWAWAIT];
- SELECT … FOR UPDATATE 语句允许用户一次锁定多条记录进行更新
- 使用 COMMIT 或 ROLLBACK 语句释放锁。
- 共享锁 (s) : 行级,读取一行
- 排他锁 (x) : 行级,更新一行
- 间隙锁 (NK) : 行级,使用范围条件时
- 行级锁是一种排他锁,防止其他事务修改此行;在使用以下语句时, Oracle 会自动应用行级锁:
加锁、、
- 对范围内不存在的记录加锁。一是为了防止幻读,二是为了满足恢复和复制的需要
- 增加行级锁之前,InnoDB会自动给表加意向锁
- 执行DML语句(update、delete和insert)时,InnoDB会自动给数据加排他锁
- 执行DQL语句时:
- 通常情况下,单纯的select不会加锁,因为InnoDB默认的隔离级别是可重复读,使用MVCC来避免加锁
- 共享锁 (s) :SELECT … FROM … WHERE … LOCK IN SHARE MODE
- 排他锁 (x) :SELECT … FROM … WHERE … FOR UPDATE;
- 间隙锁 (NK) : 上述sQ采用范围条件时,InnoDB对不存在的记录自动增加间隙锁
数据库并发策略
并发控制一般采用三种方法,分别是乐观锁和悲观锁以及时间戳。???锁的优化策略
1、读写分离
2、分段加锁
3、减少锁持有的时间
4.多个线程尽量以相同的顺序去获取资源不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁。存储过程。什么是存储过程?用什么来调用?
一组为了完成特定功能的 SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。如果某次操作需要执行多次 SQL,使用存储过程比单纯 SQL 语句执行要快。存储过程是数据库中的一个重要对象。存储过程优化思路
- 尽量利用一些 sql 语句来替代一些小循环,例如聚合函数,求平均函数等。
- 中间结果存放于临时表,加索引。
- 少用游标。sql 是个集合语言,对于集合运算具有较高性能。而 cursors 是过程运算。比如对一个 100 万行的数据进行查询。游标需要读表 100 万次,而不使用游标则只需要少量几次读取。
- 事务越短越好。 sqlserver 支持并发操作。如果事务过多过长,或者隔离级别过高,都会造成并发操作的阻塞,死锁。导致查询极慢,cpu 占用率极地。
- 使用 try-catch 处理错误异常。6. 查找语句尽量不要放在循环内
SQL 注入、、
- 产生的原因:程序开发过程中不注意规范书写 sql 语句和对特殊字符进行过滤,导致客户端可以通过全局变量 POST 和 GET 提交一些 sql 语句正常执行。
- 类型:1、恶意拼接查询 2、利用注释执行非法命令 3、传入非法参数 4、添加额外条件
- 如何避免 SQL 注入?
1、过滤输入内容,校验字符串:在数据提交到数据库之前,就把用户输入中的不合法字符剔除掉。
2、参数化查询:参数化查询目前被视作是预防 SQL 注入攻击最有效的方法。指在设计与数据库连接并访问数据时,在需要填入数值或数据的地方,使用参数(Parameter)来给值。
MVCC 机制、、???
- MVCC(Multi-Version Concurrency Control)多版本并发控制,⽤于管理多个事务同时访问和修改数据库的数据,⽽不会导致数据不⼀致或冲突。MVCC的核⼼思想是每个事务在数据库中看到的数据版本是事务开始时的⼀个快照,⽽不是实际的最新版本。这使得多个事务可以并发执⾏,⽽不会互相⼲扰。
MySQL的隔离性可以通过锁和MVCC来实现,MVCC适合在⼀些锁性能较为差的情况下使⽤,提⾼效率。 - 如何实现:每⼀个 UndoLog ⽇志中都有⼀个 roll_pointer (回滚指针)⽤于指向上⼀个版本的 Undo Log 。这样对于每⼀条记录就会构成⼀个版本链,⽤于记录所有的修改,每⼀次进⾏新的修改后,新的 Undo Log 会放在版本链的头部。
- 在我们进⾏查询的时候应该查询哪个版本呢?这时候就可以通过 ReadView 来实现。在事务SELECT查询数据时,就会构造⼀个 ReadView ,它包含了版本链的统计信息:
m_ids 当前活跃的所有事务id(所有未提交的事务)
min_trx_id 版本链尾的id
max_trx_id 下⼀个将要分配的事务id(版本链头事务id+1)
creator_trx_id 创建这个ReadView的事务的id 查询规则:
该版本是否为当前事务创建(读取⾃⼰修改的数据),如果是就返回,否则进⼊下⼀个判断
该版本的事务id是否⼩于min_trx_id(在ReadView创建之前,数据已经提交),可以直接访问
该版本的事务id是否⼤于max_trx_id(在ReadView创建后,该版本才开启),不能被访问
该版本事务id在[min_trx_id, max_trx_id]之间,则判断当前版本事务id是否在m_ids中,如果不在,说明事务已经提交可以访问,否则不能访问。 - 对 MVCC 的理解。
- 对于 MVCC 的理解,我觉得可以先从数据库的三种并发场景说起:
第一种:读读就是线程 A 与线程 B 同时在进行读操作,这种情况下不会出现任何并发问题。
第二种:读写就是线程 A 与线程 B 在同一时刻分别进行读和写操作。这种情况下,可能会对数据库中的数据造成以下问题:事物隔离性问题,出现脏读,幻读,不可重复读的问题
第三种:写写就是线程 A 与线程 B 同时进行写操作。这种情况下可能会存在数据更新丢失的问题。而 MVCC 就是为了解决事务操作中并发安全性问题的无锁并发控制技术全称为Multi-Version Concurrency Control ,也就是多版本并发控制。它是通过数据库记录中的隐式字段,undo 日志 ,Read View 来实现的。 - MVCC 主要解决了三个问题
第一个:通过 MVCC 可以解决读写并发阻塞问题从而提升数据并发处理能力
第二个:MVCC 采用了乐观锁的方式实现,降低了死锁的概率
第三个:解决了一致性读的问题。也就是事务启动时根据某个条件读取到的数据,直到事务结束时,再次执行相同条件,还是读到同一份数据,不会发生变化。而我们在使用 MVCC 时一般会根据业务场景来选择组合搭配乐观锁或悲观锁。这两个组合中,MVCC 用来解决读写冲突,乐观锁或者悲观锁解决写写冲突从而最大程度的提高数据库并发性能。以上就是我的对 MVCC 的理解。
- 对于 MVCC 的理解,我觉得可以先从数据库的三种并发场景说起:
- MVCC(Multi-Version Concurrency Control)多版本并发控制,⽤于管理多个事务同时访问和修改数据库的数据,⽽不会导致数据不⼀致或冲突。MVCC的核⼼思想是每个事务在数据库中看到的数据版本是事务开始时的⼀个快照,⽽不是实际的最新版本。这使得多个事务可以并发执⾏,⽽不会互相⼲扰。
Mysql日志
- binlog(归档⽇志) 是 Server 层⽣成的⽇志,主要⽤于数据备份(宕机后的恢复工作)和主从复制, 解决数据库和缓存之间一致性可以用canal??组件去监听binlog
- redolog 是 Innodb 物理⽇志,记录了某个数据⻚做了什么修改,每当执⾏⼀个事务就会产⽣⼀条或者多条物理⽇志。
如果发送宕机,对读取到内存中的bufferpool中的数据没有同步到硬盘mysql中,使用redolog来同步,保证事务的一致性。 - undolog 是 Innodb 逻辑⽇志,用于记录数据修改前的信息(记录对数据库操作的逆操作,事务回滚时逆操作恢复原数据)比如我们删除一条数据的时候,就会在undolog日志文件中新增一条delete语句; 实现了事务中的原⼦性,主要⽤于事务回滚和MVCC。
- relaylog 中继⽇志,⽤于主从复制场景下, slave 通过io线程拷⻉master的 binlog 后本地⽣成的⽇志
当MySQL的日志空间不足时,可能会导致以下影响:
- 写入操作: 日志(例如二进制日志和事务日志)是用来记录数据库的写入操作的,如果日志空间不足,数据库可能无法正常写入事务日志,导致事务无法提交。这可能会导致写入操作被阻塞或失败。
- 查询操作: 查询操作通常不直接受到日志空间不足的影响。然而,如果写入操作因为日志空间不足而受阻,可能会导致查询操作因为等待事务完成而变慢。
- 备份操作: 备份通常会涉及到数据库的日志文件。如果日志空间不足,可能会影响备份的正常执行。备份通常需要确保数据库的一致性,而事务日志在这个过程中是至关重要的。
MySQL 数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?
1、设计良好的数据库结构,允许部分数据冗余,尽量避免 join 查询,提高效率。
2、选择合适的表字段数据类型和存储引擎,适当的添加索引。
3、MySQL 库主从读写分离。
4、找规律分表,减少单表中的数据量提高查询速度。
5、添加缓存机制,比如 memcached,apc 等。
6、不经常改动的页面,生成静态页面。
7、书写高效率的 SQL。比如 SELECT * FROM TATABEL 改为 SELECT field_1,field_2, field_3 FROM TATABLE.数据库如何处理大数据量?
- 分区:一份数据文件拆分多个磁盘文件存储,隔离数据访问。相当于做了负载均衡。
- 水平分库/表,各个库和表的结构一模一样。
垂直分库/表,各个库和表的结构不一样。 - 读写分离:主机负责写,从机负责读。
MySQL主从、、
- 主是主库的意思,从是从库的意思。数据库主库对外提供读写的操作,从库对外提供读的操作。
- 数据库为什么需要主从架构呢?
- 高可用,实时灾备,用于故障切换。比如主库挂了,可以切从库。
- 读写分离,提供查询服务,减少主库压力,提升性能
- 备份数据,避免影响业务。
- 主从复制原理,简言之,分三步曲进行:
- 主数据库有个
bin 1og
二进制文件,纪录了所有增删改 sQL语句。(binlog线程) - 从数据库把主数据库的
bin 1og
文件的 sQL 语句复制到自己的中继日志relay 1og
(io线程) - 从数据库的
relay 1og
重做日志文件,再执行一次这些sql语句。(Sql执行线程)
- 主数据库有个
- 怎么保证主从一致?
我们学习数据库的主从复制原理后,了解到从库拿到并执行主库的binlog日志,就可以保持数据与主库一致了。这是为什么呢?哪些情况会导致不一致呢?- 1 长链接
主库和从库在同步数据的过程中断怎么办呢,数据不就会丢失了嘛。因此主库与从库之间维持了一个长链接,主库内部有一个线程,专门服务于从库的这个长链接的。 - 2 bintog格式
binlog 日志有三种格式,分别是statement,row和mixed。
如果是 statement 格式,binlog记录的是SQL的原文,如果主库和从库选的索引不一致,可能会导致主库不一致。
如何解决这个问题呢?可以把binlog格式修改为 row。row 格式的 binlog 日志,记录的不是SQL原文,而是两个 event:Table_map 和 Delete_rows 。Table_map event说明要操作的表,Delete_rows event用于定义要删除的行为:记录删除的具体行数。row 格式的binlog记录的就是要删除的主键ID信息,因此不会出现主从不一致的问题。
但是如果SQL删除10万行数据,使用row格式就会很占空间的,10万条数据都在binlog里面写binlog的时候也很耗I0。但是 statement 格式的binlog可能会导致数据不一致,因此设计MySQL的大叔想了一个折中的方案, mixed 格式的binlog。所谓的mixed格式其实就是 row 和statement 格式混合使用,当MySQL判断可能数据不一致时,就用row 格式,否则使用就用statement 格式。
- 1 长链接
- 主从延迟、、
- 高可用方案
- 双机主备:两台机器A和B,A为主库,负责读写,B为备库,只备份数据。如果A库发生故障,B库成为主库负责读写。修复故障后,A成为备库,主库B同步数据到备库A
- 一主一从:两台机器A和B,A为主库,负责读写,B为从库,负责读数据。如果A库发生故障,B库成为主库负责读写。修复故障后,A成为从库,主库B同步数据到从库A
- 一主多从:多个从库支持读,分担了主库的压力,明显提升了读的并发度。但只有台主机写,因此写的并发度不高
- MariaDB同步多主机:有代理层实现负载均衡,多个数据库可以同时进行读写操作;各个数据库之间可以通过 Galera Replication 方法进行数据同步,每个库理论上数据是完全一致的。数据库不支持过大。
- 数据库中间件:mycat分片存储,每个分片配置一主多从的集群。优点:解决高并发高数据量的高可用方案;缺点:维护成本比较大。
MyBatis
什么是 Mybatis?
1、Mybatis 是一个开源的Java持久层框架,半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。(JDBC(Java Database Connectivity)是 Java 语言用于与关系型数据库进行交互的一种标准接口。JDBC 提供了一组 Java API,允许开发者通过 Java 代码来执行 SQL 查询、更新数据库、以及处理数据库事务等操作。)
2、MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
3、通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。(从执行 sql 到返回 result 的过程)。ORM,DAO,持久层?
ORM 和 DAO 是持久层的实现方式,ORM 和 DAO 通常一起协同工作,以实现数据的持久化和访问。持久层包括了使用 ORM 框架进行对象和数据库映射的方式,以及使用 DAO 设计模式封装数据访问操作的方式。DAO 提供了数据访问的通用接口,业务逻辑层通过 DAO 来进行数据访问,从而实现了持久层的解耦。一般 JDBC 执行 SQL 查询或更新操作的流程涉及以下步骤:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//1. 加载数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2. 建立数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/xx", "username", "password");
//3. 创建 Statement 或 PreparedStatement 对象
Statement statement = connection.createStatement();
//4. 执行 SQL 语句
ResultSet resultSet = statement.executeQuery("SELECT * FROM yy");
//5. 处理结果集(如果有)
while (resultSet.next()) { // 处理查询结果
}
//6. 关闭资源:方法关闭 ResultSet、Statement 和 Connection 对象,释放数据库连接和资源。
resultSet.close();
statement.close();
connection.close();Mybatis执行流程
- 加载配置文件:MyBatis 首先会加载配置文件(通常是 XML 格式的),该配置文件包含了数据源信息、SQL 映射文件的位置、全局配置等。
- 解析配置文件:MyBatis 会解析加载的配置文件,将配置信息存储在相应的配置对象中,比如数据源对象、全局配置对象、SQL 映射配置对象等。
- 创建 SqlSessionFactory:根据解析得到的配置信息,MyBatis 创建一个 SqlSessionFactory 对象,该对象是一个线程安全的工厂类,用于创建 SqlSession 对象。
- 创建 SqlSession:通过 SqlSessionFactory 创建 SqlSession 对象,SqlSession 提供了执行 SQL 操作的方法,比如查询、插入、更新等。每个线程都应该有自己的 SqlSession 实例,SqlSession 不是线程安全的,因此通常在方法内部创建和关闭。
- 解析 Mapper 文件:在执行 SQL 操作之前,MyBatis 会解析 Mapper 文件,Mapper 文件中定义了 SQL 语句的映射关系,包括 SQL 语句、参数映射、结果映射等。
- 执行 SQL 操作:通过 SqlSession 执行 SQL 操作,MyBatis 提供了多种方法来执行 SQL,比如 selectOne、selectList、insert、update、delete 等。
- 映射结果:执行 SQL 操作后,MyBatis 将结果映射为 Java 对象,根据 Mapper 文件中的配置将查询结果映射为 Java 对象,并返回给调用者。
- 关闭资源:在完成所有操作后,需要关闭 SqlSession,释放数据库连接和其他资源,以避免资源泄露和性能问题。
Mybatis的优缺点,适用场合。
- 优点:
1.基于 SQL 编程,相当灵活,不会对应用程序或者数据库的现有设计成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态 SQL 语句,并可重用。
2.与 JDBC 相比,减少了 50%以上的代码量,消除了JDBC 大量几余的代码,不需要手动开关连接;
3.很好的与各种数据库兼容(因为 Myatis 使用JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持)。
4.能够与Spring 很好的集成;
5.提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射标签,支持对象关系组件维护。 - 缺点:
1.SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL 语句的功底有一定要求.
2.SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。 - MyBatis 框架适用场合
1、MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案。
2、对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis 将是不错的选择。
- 优点:
#{} 与 ${} 的区别?
- 两种占位符,都是实现动态sql的方式,可以把参数传递到xml中,执行操作前,mybatis会对这两个占位符进行动态解析
#{}
(预编译):#{}
是 MyBatis 的预处理语法,会在 SQL 中使用占位符?
的形式,通过预编译的方式来处理参数,可以有效防止 SQL 注入攻击。使用#{}
时,MyBatis 会将传入的参数值转义并进行预编译,同时会根据参数的类型决定使用不同的 JDBC 类型。这种方式可以确保 SQL 的安全性。${}
(拼接字符串):${}
是 MyBatis 的字符串替换语法,会直接将传入的参数值以字符串的形式拼接到 SQL 语句中。使用${}
时,需要注意潜在的 SQL 注入风险,因为参数值会直接替换到 SQL 语句中,不会进行预编译。
- #{} 适用于参数值,防止 SQL 注入,用于预编译。${} 适用于非参数值,直接将参数值拼接到 SQL 语句中。
- 在开发中,推荐使用 #{} 来处理参数,以保障 SQL 的安全性。${} 的使用应谨慎,尽量避免直接将用户输入的数据用于
${}
形式的参数。
- 两种占位符,都是实现动态sql的方式,可以把参数传递到xml中,执行操作前,mybatis会对这两个占位符进行动态解析
当实体类中的属性名和表中的字段名不一样,怎么办 ?
- 使用注解映射:ORM 框架(如 Hibernate)提供了注解(如 @Column)来映射实体类属性和数据库表字段之间的关系。
可以在实体类的属性上使用注解,指定属性与表字段的映射关系优点:灵活,可以通过注解直接在实体类中定义映射关系,不需要修改数据库表结构。1
2
3
4
5
6
7
8
9
public class MyEntity {
private Long id;
// other fields and methods
}
缺点:如果数据库表结构已经存在,需要在实体类中添加注解进行映射,可能会导致代码中出现大量注解。 - 通过在查询的 sql 语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
1
2
3<select id=”selectorder” parametertype=”int” resultetype=”me.gacl.domain.order”>
select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select> - 通过 resultMap 映射字段名和实体类属性名的一一对应的关系。 优点:将映射关系从代码中分离出来,使得配置更加灵活。
1
2
3
4
5
6
7
8
9
10<select id="getOrder" parameterType="int" resultMap="orderresultmap">
select * from orders where order_id=#{id}
</select>
<resultMap type=”me.gacl.domain.order” id=”orderresultmap”>
<!–用 id 属性来映射主键字段–>
<id property=”id” column=”order_id”>
<!–用 result 属性来映射非主键字段,property 为实体类属性名,column为数据表中的属性–>
<result property = “orderno” column =”order_no”/>
<result property=”price” column=”order_price” />
</reslutMap>
缺点:需要额外的配置文件,增加了维护成本。
- 使用注解映射:ORM 框架(如 Hibernate)提供了注解(如 @Column)来映射实体类属性和数据库表字段之间的关系。
???通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,这个 Dao 接口的工作原理是什么?
Dao 接口里的方法,参数不同时,方法能重载吗?Dao 接口即 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值;接口的方法名,就是映射文件中 Mapper 的 Statement的 id 值;接口方法内的参数,就是传递给 sql 的参数。Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MapperStatement。在 Mybatis 中,每一个
,,、、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
- 延迟加载的意思是:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。Mybatis支持一对一关联对象和一对多关联集合对象的延迟加载
在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=truelfalse,默认是关闭的 - 延迟加载的底层原理
1.使用 CGLIB 创建目标对象的代理对象
2.当调用目标方法时,进入拦截器invoke方法,发现目标方法是null值,执行sql查询
3.获取数据以后,调用set方法设置属性值,再继续查询目标方法,就有值了
- 延迟加载的意思是:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。Mybatis支持一对一关联对象和一对多关联集合对象的延迟加载
Mybatis 的一级、二级缓存
- 一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
- 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;
- 对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
Mybatis是怎么分页的?
- 逻辑分页:先查出数据,如何自己编写逻辑实现分页;
- 物理分页:使用mysql提供的分页关键词limit
- Mybatis提供了 3 种分页方式:
- mybatis mapper配置文件写分页sql,直接在select语句里面加limit
- ??使用mybatis提供的RowBounds对象,实现内存级的分页
- ??基于mybatis中的Interceptor拦截器,在select语句执行之前动态拼接分页关键字
千万级数据量,分页查询优化、、
不要 select * from Table limit 90000, 10
而先 select id from Table limit 90000, 10,再根据 select * from Table where id = id
Redis
什么是 Redis?
- Redis 是完全开源免费的,遵守 BSD 协议,是一个高性能的 key-value 数据库。 Redis是一种NoSQL。
NoSQL(Not Only SQL)是一种用于存储和检索非结构化或半结构化数据的数据库系统,主要包括以下几种类型:
1、键值存储数据库(Key-Value Stores)包括Redis、Amazon DynamoDB和Riak。
2、文档型数据库:存储的是类似于JSON或XML格式的文档,可以使用键来检索。示例包括MongoDB和Couchbase。
3、图形数据库:以图形结构存储数据,用于处理复杂的关系和连接。示例包括Neo4j和Amazon Neptune。
4、搜索引擎:搜索引擎允许用户通过关键字搜索文本数据。示例包括Elasticsearch和Apache Solr。 - Redis 与其他 key-value 缓存产品有以下三个特点:
Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis 不仅仅支持简单的 key-value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。
Redis 支持数据的备份,即 master-slave 模式的数据备份。 - Redis 优势
性能极高;Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s 。
丰富的数据类型,Redis 支持二进制案例的 Strings, Lists, Hashes,Sets 及Ordered Sets 数据类型操作。
原子:Redis 的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。通过 MULTI 和 EXEC指令包起来。
丰富的特性 – Redis 还支持 publish/subscribe, 通知, key 过期等等特性。
- Redis 是完全开源免费的,遵守 BSD 协议,是一个高性能的 key-value 数据库。 Redis是一种NoSQL。
为什么要用Redis?
无论Redis、MySQL、HDFS、HBase都是存储数据的地方,因为设计理念的不同,我们会根据不同的应用场景使用不同的存储。像Redis一般我们会把它用作于缓存(当然,日常有的应用场景比较简单,用个HashMap也能解决很多的问题了
1、高性能:首先,它是纯内存操作,内存本身就很快。其次,它是单线程的,Redis服务器核心是基于非阻塞的IO多路复用机制,单线程避免了多线程的频繁上下文切换问题
2、高可靠:主从复制,哨兵机制
3、高拓展:数据发片,负载均衡Redis 为什么这么快?
- Redis 的大部分操作都在内存中完成,并且采用了高效的数据结构(压缩表、跳跃表等方式降低了时间复杂读,同时还提供了不同时间复杂度的数据类型),因此 Redis 瓶颈可能是机器的内存或者网络带宽,而并非 CPU,既然 CPU 不是瓶颈,那么自然就采用单线程的解决方案了;
- Redis 采用单线程模型可以避免了多线程之间的竞争,省去了多线程切换的时间和性能上的开销,而且也不会导致死锁问题;
- Redis 采用了 I/0 多路复用机制处理大量的客户端 Socket 请求,I0 多路复用机制是指一个线程处理多个I0流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听 Socket 和已连接 Socket.内核会一直监听这些 Socket 上的连接请求或数据请求。一旦有请求到达,就会交给 Redis线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。
- Redis支持丰富的数据结构,包括字符串、哈希、列表、集合、有序集合等,每种数据结构都针对不同的应用场景进行了优化,能够更高效地处理各种数据操作。
Redis 是单进程单线程的?
Redis 是单进程单线程的,redis 利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。Redis网络多路I/O复用??
- 多路复用是一种提高 I/O 效率的技术,它可以同时监听多个文件描述符的 I/O 事件,并且在有事件发生时立即通知应用程序,从而避免了轮询的开销。在 Redis 中,网络 I/O 多路复用主要涉及到客户端与服务端之间的通信,使用类似 epoll 的事件驱动机制来处理网络 I/O 多路复用,通过监听套接字上的事件并采用非阻塞 I/O 模式,实现了高效的网络通信和并发处理能力。
- 事件驱动模型:
- Redis 使用事件驱动模型来处理网络 I/O,通过监听套接字上的读写事件,实现异步的网络通信。
- 在客户端连接到 Redis 服务器时,服务器会将该客户端的套接字加入到事件驱动器中进行监听。
- epoll 机制:
- 在 Linux 系统中,Redis 使用 epoll 作为网络 I/O 多路复用的实现机制之一。
- epoll 是一种高效的 I/O 多路复用技术,可以同时监控多个文件描述符的 I/O 事件,并将有事件发生的文件描述符返回给应用程序处理。
- Redis 使用 epoll_wait 函数来等待套接字上的事件,并在事件发生时处理对应的网络 I/O 操作。
- 非阻塞 I/O:
- Redis 使用了非阻塞 I/O 模式来处理套接字的读写操作,这样可以避免在等待数据到达时线程被阻塞,提高了系统的并发处理能力。
- 当客户端发送数据给 Redis 服务器时,服务器会尽可能地读取数据,而不是等待数据全部到达再进行处理。
- 事件循环:
- Redis 的服务器主循环是一个事件驱动的循环,不断地监听客户端套接字上的事件并处理。
- 当有新的连接请求、数据到达、连接断开等事件发生时,服务器会相应地调用相应的处理函数来处理这些事件。
Redis 常见性能问题和解决方案?
(1) Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件
(2) 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-Slave3…Redis的教据结构有?
数据类型 key string hash list set sorted set bitmap hyperloglog 最大存储数据量 512M 512M 2^32 - 1 2^32 - 1 2^32 -1 512M 12K Redis数据结构分别有哪些典型的应用场景?
- 字符串:可以用来做最简单的数据,可以颂存某个简单的字符串,也可以存某个json格式的字符审,Redis分布式的实现就利用了这种数据结构,还包括可以实现计数器、Session共享、分布式ID
- 哈希表:可以用来存储一些key-value对,更适合用来存储对象,统计类数据,购物车
- 列表:Redis的列表通过命令的组合,既可以当做栈,也可以当做队列来使用,可以用来缓存类似微信公众号、微博等消息流教据,文章列表,消息队列
- 集合:和列表类似,也可以存储多个元素,但是不能重复,集合可以进行交集、并集、差集损作,从而可以实现类似,我和某人共同关注的人、朋友画点赞等功能
- 有序集合:集合是无序的,有序集合可以设置顺序,可以用来实现排行榜功能,按时间播放量点击
Redis 中 String 的底层实现、、
- Redis底层是C实现的,Redis中String是一种动态字符串类型,它的底层实现就是 SDS。每个字符串对象(string object)都有对应的 SDS 结构,存储在内存中。
- SDS 是 Redis 自己实现的字符串抽象数据结构,当 Redis 执行字符串操作如 SET、GET、APPEND 等操作,实际上是对 SDS 结构进行操作,包括修改长度、扩展空间、拷贝数据等操作。相较于 C 语言的原生字符串,SDS 具有以下优势:
- O(1) 复杂度的长度计算:SDS 不需要每次计算字符串长度,因为它在结构中记录了字符串的长度,因此获取字符串长度的操作是 O(1) 复杂度的。
- 空间预分配:SDS 在空间分配时会预留额外的空间,以减少字符串增长时频繁地重新分配内存的次数,从而提高性能。
- 修改操作的高效性:SDS 通过记录字符串的长度,可以直接修改字符串内容,而无需像 C 语言原生字符串那样,重新计算长度和分配内存。
- SDS 的结构如下所示:
1
2
3
4
5struct sdshdr {
int len; // 字符串长度
int free; // 未使用空间长度
char buf[]; // 字符串数据
};
Zset底层实现
- 压缩列表:普通数组+列表长度+尾部偏移量+列表元素个数+列表结束标识,对于非首尾节点仍然使用遍历逐个查询
- 跳表:根据有序列表元素值,加多级索引以快速查找。时间复杂度为O(logn)(相当于二分查找)
- 什么时候采用跳表呢?
1.有序集合保存的元素数量小于128个
2.有序集合保存的所有元素的长度小于 64字节 - zset为什么用跳表而不用二叉树或者红黑树呢?
红黑树不支持范围查找,且构造起来比跳表复杂
Redis为什么不用b+树?MySQL为什么不用跳表?
这个问题在于 Redis是直接操作内存的并不需要磁盘io,跳表明显是更快更简单的方式;而MySQL需要去读取io,所以mysql要使用b+树的方式减少磁盘ioRedis 应用场景。
- 缓存层。防止所有的请求打到DB,做过多的IO
- 分布式锁。正常的 Threadlocal 是单进程JVM内的一个锁,不能对所有的服务起到同步的效果,所有用redis抽象出来做一个集群,这种分布式锁是全局可见的
- 作消息队列。redis发布/订阅模式,类似与消息队列。
如何使用 Redis 做异步队列么???
一般使用 list 结构作为队列,rpush生产消息,lpop消费消息。当 lpop没有消息的时候,要适当 sleep一会再重试。
可不可以不用 sleep 呢? list 还有个指令叫 blpop,在没有消息的时候,它会阻塞住直到消息到来。
能不能生产一次消费多次呢?使用 pub/sub 主题订阅者模式,可以实现 1:N 的消息队列。
pub/sub 有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如 RabbitMQ等。
redis 如何实现延时队列?使用sortedset,拿时间戳作为score,消息内容作为 key 调用 zadd 来生产消息,消费者用 zrangebyscore 指令获取 N 秒前的数据轮询处理。Redis持久化。
- Redis是基于内存的,假设不做任何操作,只要Redis服务器重启(或者中途故障挂掉了)那内存的数据就会没掉。所以Redis提供了持久化机制给我们用,分别是RDB和AOF。通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。
- RDB:Redis默认的持久化方式。根据我们自己配置的时间或者手动去执行BGSAVE或SAVE命令,Redis就会去(fork一个子进程来)生成RDB文件。
RDB是按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件,Redis可以通过这个文件在启动的时候来还原我们的数据。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。) - AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
https://www.bilibili.com/read/cv28294981/?spm_id_from=333.999.0.0&jump_opus=1
缓存雪崩、缓存击穿、缓存穿透
- 缓存穿透
- 缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
- 解决办法:
- 1、最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
- 2、另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库。
- 5TB的硬盘上放满了数据,请写一个算法将这些数据进行排重。如果这些数据是一些32bit大小的数据该如何解决?如果是64bit的呢?
对于空间的利用到达了一种极致,那就是Bitmap和布隆过滤器(Bloom Filter)。
- Bitmap:典型的就是哈希表;缺点是,Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的空间、时间来完成了
- 布隆过滤器(推荐):就是引入了 k(k>1) 个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。
它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。
- 缓存击穿
- 场景:一份热点数据,它的访问量非常大。在其缓存失效瞬间,大量请求直达存储层,导致服务崩溃
- 解决方案:
1、分布式锁:对数据的访问加互斥锁,当一个线程访问该数据时,其他线程只能等待这个线程访问过后,缓存中的数据将被重建,届时其他线程就可以直接从缓存取值.
2、永不过期:不设置过期时间,所以不会出现上述问题,这是“物理”上的不过期。为每个value设置逻辑过期时间,当发现该值逻辑过期时,使用单独的线程重建缓存.
- 缓存雪崩
- 我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
- 解决办法:
1、加锁:大多数系统设计者考虑用加锁(最多的)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。
2、设置随机值:将缓存失效时间分散开。
- 缓存穿透
???分布式锁、、
- 场景:修改时,经常需要先将数据读取到内存,在内存中修改后再存回去。在分布式应用中,可能多个进程同时执行上述操作,而读取和修改非原子操作,所以会产生冲突。增加分布式锁,可以解决此类问题.
- 基本原理:
- 同步锁:在多个线程都能访问到的地方,做一个标记,标识该数据的访问权限。
- 分布式锁:在多个进程???都能访问到的地方,做一个标记,标识该数据的访问权限
- 实现方式:1、基于数据库实现分布式锁 2、基于Redis实现分布式锁 3、基于zookeeper实现分布式锁
具体做法:??? 先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。 - 分布式锁实现???
实现分布式锁可以通过数据库 redis zookeeper。
redis setnx是最简单的一种。可以满足基本使用但有些特性和场景没法满足 比如不可重入 不可重试 锁过期删除的一些问题。redisson是在redis基础上也实现了分布式锁,解决了这几个问题就是redisson里面的可重入锁,比如解决不可重入用的是hash结构解决。解决不可重试基于redis的发布订阅。解决锁过期删除基于自己实现的一个看门狗机制。但是redisson这把可重入锁还有一些问题,比如主从时宕机的问题,redisson里面又可以通过有联锁啊 红锁啊解决。不过setnx基本上可以应用于分布式锁了,有一些业务上需要满足一些锁的需求的时候可以考虑更复杂的实现
支付宝一面:如何基于Redis实现分布式锁???https://mp.weixin.qq.com/s/y8kAflIenRpa4zIwCP_8iA
缓存预热、缓存更新、缓存降级
- 缓存预热
系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决思路:1、直接写个缓存刷新页面,上线时手工操作下;2、数据量不大,可以在项目启动的时候自动进行加载;3、定时刷新缓存 - 缓存更新
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
(1)定时去清理过期的缓存;
(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡 - ??缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。以参考日志级别设置预案:
(1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
(3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
(4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户
- 缓存预热
热点数据和冷数据?
数据更新前至少读取两次,缓存才有意义。对于热点数据,比如生日祝福模块中当天的寿星列表,缓存以后可能读取数十万次,同时信息修改频率不高。这个是最基本的策略,若缓存还没有起作用就失效就没有太大价值了。
那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力过期策略。
- Redis会把设置了过期时间的key放入一个独立的字典里,过期时并不会立刻删除,通过如下两种策略来删除过期的key:
1、惰性删除:客户端访问某个key时,Redis会检查该key是否过期,若过期则删除。(问题: 有些键值对可能已过期,但是由于没有再被访问,导致未被删除,因而占用内存)。
2、定期扫描:Redis默认每秒执行10次过期扫描 (配置hz选项) ,扫描策略如下 (1). 从过期字典中随机选择20个key; (2)删除这20个key中已过期的key; (3)如果过期的key的比例超过25%,则重复步骤(1) - 为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略. - 定期删除+惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。 - 采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。在redis.conf中有一行配置
- Redis会把设置了过期时间的key放入一个独立的字典里,过期时并不会立刻删除,通过如下两种策略来删除过期的key:
淘汰策略。
当Redis占用内存超出最大限制(maxmemory)时,可采用如下策略(maxmemory-policy)淘汰一些数据以腾出空间继续提供读写服务:
noeviction:对可能导致增大内存的命令返回错误 (大多数写命令,DEL除外)
volatile-ttl:在设置了过期时间的key中,选择剩余寿命(TTI) 最短的key,将其淘汰
volatile-lru:在设置了过期时间的kev中,选择最少使用的kev (LRu) ,将其淘汰:
volatile-random:在设置了过期时间的key中,随机选择一些key,将其淘汰;
allkeys-lru:在所有的ke中,选择最少使用的key (LRu) ,将其淘汰
allkeys-random:在所有的key中,随机选择一些key,将其淘汰
(这里其实还有volatile-lfu、allkeys-lfu,所谓==LFU算法==,就是先考虑键值对访问的次数,优先淘汰访问次数少的键值对,对于访问次数相同的键值对,再选择最近久未被访问的键值对进行淘汰(也就是LRU算法))
LRU算法:维护一个链表,用于顺序存储被访问过的key。在访问数据时,最新访问过的kev将被移动到表头, 即最近访问的key在表头,最少访问的key在表尾。??Redis事务。
Redis事务功能是通过 MULTI、EXEC、DISCARD 和 WAWATATCH 四个原语实现的
Redis会将一个事务中的所有命令序列化,然后按顺序执行。
1.redis 不支持回滚。在事务失败时不进行回滚,而是继续执行余下的命令,所以 Redis 的内部可以保持简单且快速。
2.如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
3.如果在一个事务中出现运行错误,那么正确的命令会被执行。
1)MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
2)EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil 。
3)通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。
4)WAWATATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令为什么Redis的操作是原子性的,怎么保证原子性的?
对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。
Redis的操作之所以是原子性的,是因为Redis是单线程的。Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?不一定,将get和set改成单命令操作,???incr 。使用Redis的事务,或者使用Redis+Lua==的方式实现。Redis 的同步机制了解么?
Redis 可以使用主从同步,从从同步。第一次同步时,主节点做一次 bgsave,并同时将后续修改操作记录到内存 buffer,待完成后将 rdb文件全量同步到复制节点,复制节点接受完成后将 rdb 镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。Redis 多节点部署主要有以下几种方式。
- 主从复制(Master-Slave Replication):主从复制是 Redis 的基本高可用性架构。一个 Redis 主节点可以拥有多个从节点,主节点负责写操作和同步数据到从节点,从节点负责复制主节点的数据。当主节点不可用时,可以选择一个从节点提升为主节点,实现故障切换。
- 哨兵模式(Redis Sentinel):Redis Sentinel 是用于监控 Redis 实例并支持自动故障转移的组件。它可以监控多个 Redis 主从复制集群,当主节点不可用时,自动将一个从节点晋升为新的主节点,保证服务的可用性。哨兵模式提供了更强大的故障检测和自动切换功能。
- 集群模式(Redis Cluster):Redis Cluster 是 Redis 提供的分布式解决方案,用于在多个节点之间分片存储数据。Redis Cluster 将数据分成多个槽(slot),每个槽可以分配给集群中的不同节点。它支持横向扩展、高可用性和自动数据分片。当集群中的某个节点不可用时,可以通过复制和重新分片来保证服务的可用性。
- 第三方解决方案:除了 Redis 官方提供的方案外,还有一些第三方解决方案可以用于构建 Redis 的多节点部署,比如一些代理软件或者中间件,它们提供了更多高级功能,比如自动负载均衡、故障转移等。
Redis 集群
- 1、主从复制:Redis 集群中的每个节点通常都有主节点和若干个从节点。主节点负责处理写操作和部分读操作,而从节点负责复制主节点的数据,用于读操作和故障恢复。主从复制可以提高系统的可用性和容错能力。Redis Sentinal 着眼于高可用,在 master 宕机时会自动将 slave 提升为master,继续提供服务。
- 2、数据分片:Redis 集群通过分片机制将数据分布到多个节点上。每个节点负责一部分数据的存储和处理。这样可以提高系统的并发处理能力和数据存储容量。Redis Cluster 着眼于扩展性,在单个 redis 内存不足时,使用 Cluster 进行分片存储。
- 3、数据同步:在 Redis 集群中,数据同步是保证数据一致性的关键。主节点将写操作同步到从节点,确保数据在各个节点之间的同步。在读操作时,可以从主节点或者从节点读取数据,但要注意可能存在数据的延迟和不一致性。
- Redis 集群的主从复制模型是怎样的?
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有 N-1 个复制品. - 场景:查询一个字符串类型数据为什么花了5秒?
Redis 集群中的某个节点网络连接不稳定或者网络延迟较高,或者某个节点数据负载较高。
如果查询的数据正好位于 Redis 集群中的某个分片,而该分片的主节点或者从节点负载较高或者网络延迟较高,会导致查询时间变长。 - Redis 集群会有写操作丢失吗: Redis 并不能保证数据的强一致性,实际中集群在特定的条件下可能会丢失写操作。
- 集群之间是如何复制的:异步复制
- 集群最大节点个数是多少: 6384 个
- 集群如何选择数据库:Redis 集群目前无法做数据库选择,默认在 0 数据库。
Redis 和 Mysql 如何保证数据一致?
1、先更新 Mysql,再更新 Redis,如果更新 Redis失败,可能仍然不一致
2、先删除 Redis缓存数据,再更新 MySql,再次查询的时候在将数据添加到缓存中,这种方案能解决方案1的问题,但是在高并发下性能较低,而且仍然会出现数据不一致的问题:比如线程1删除了Redis缓存数据,正在更新 MySql,此时另外一个查询再查询,那么就会把 MySql中老数据又查到 Redis中
3、延时双删,步聚是:先删除 Redis存数,再更新 MySql,延迟几百毫秒除 Redis存数据,这样就算在更新 MySql时,有其他线程读了Mysql,把老数据读到了 Redis中,那么也会被制除掉,从而把数据保持一致
4、如果需要在极端情况下仍然保证 Redis 和 Mysql 的数据一致性,就只能采用最终一致性方案。比如基于 RocketMQ 的可靠性消息通信,来实现最终一致性:把(更新redis)失败的请求写入MQ事务消息,然后异步重试,确保成功。还可以直接通过 Canal 组件,监控 Mysql 中 binlog 的日志,把更新后的数据同步到 Redis 里面。??
消息队列
消息队列 mq
- 消息队列 Message Queue,简称 MQ。是一种应用间的通信方式,主要由三个部分组成。
- 生产者:Producer,消息的产生者与调用端,主要负责消息所承载的业务信息的实例化,是一个队列的发起方
- 代理:Broker,主要的处理单元,负责消息的存储、投递、及各种队列附加功能的实现,是消息队列最核心的组成部分
- 消费者:Consumer,一个消息队列的终端,也是消息的调用端,具体是根据消息承载的信息,处理各种业务逻辑。
- mq理解
- 消息路由
- 路由器的类型和绑定规则
- 消息不丢失
- 消息确认机制
- 配置合适的消息TTL
- 备份队列
- 消息确认机制
- 消息消费
- 顺序消费
- 消息优先级
- 单线程消费
- 重复消费
- durable参数:true
- deliveryMode参数:1为非持久,2为持久化
- 顺序消费
- 消息持久化
- 消息路由
- mq应用
- 异步处理
- 应用解耦
- 流量削峰
- 限速
- 死性队列
- 消息优先级
- 公平分发
- 限流
- 延迟队列
- 消息队列 Message Queue,简称 MQ。是一种应用间的通信方式,主要由三个部分组成。
消息队列的应用场景较多,常用的可以分为三种:
- 异步处理:主要应用于对实时性要求不严格的场景,比如:用户注册发送验证码、下单通知、发送优惠券等等。服务方只需要把协商好的消息发送到消息队列,剩下的由消费消息的服务去处理,不用等待消费服务返回结果。
- 应用解耦:可以看作是把相关但耦合度不高的系统联系起来。比如订单系统与 WMS、EHR 系统,有关联但不哪么紧密,每个系统之间只需要把约定的消息发送到 MQ,另外的系统去消费即可。解决了各个系统可以采用不同的架构、语言来实现,从而大大增加了系统的灵活性。
- 流量削峰:一般应用在大流量入口且短时间内业务需求处理不完的服务中心,为了权衡高可用,把大量的并行任务发送到 MQ 中,依据 MQ 的存储及分发功能,平稳的处理后续的业务,起到一个大流量缓冲的作用。
如何进行消息队列选型?
- Kafka:日志分析、大数据采集
- 一种高吞吐量、分布式、基于发布/订阅的消息系统,最初由 LinkedIn 公司开发,使用Scala 语言编写,目前是 Apache 的开源项目。
- 优点: 各吐量非常大,性能非常好,集群高可用。
- 缺点:会丢数据,功能比较单一
- RabbitMQ:小规模场景
- 是什么:采用 AMQP 高级消息队列协议的一种消息队列技术,最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦
- 优点: 消息可靠性高,功能全面。
- 缺点:吞吐量比较低,消息积累会严重影响性能。erlang语言不好定制.
- RocketMQ:几乎是全场景
- 优点:高吞吐、高性能、高可用,功能非常全面。
- 缺点:开源版功能不如云上商业版。官方文档和周边生态还不够成熟。客户端只支持java。
- Kafka:日志分析、大数据采集
Kafka
kafka https://www.yuque.com/itwanger/gykdzg/ngd5g3glaxii4h6g udd1
Kafka 优缺点?
- 优点
高性能、高吞吐量、低延迟:Kafka 生产和消费消息的速度都达到每秒10万级高可用:所有消息持久化存储到磁盘,并支持数据备份防止数据丢失
高并发:支持数千个客户端同时读写
容错性:允许集群中节点失败(若副本数量为n,则允许 n-1 个节点失败)
高扩展性:Kafka 集群支持热伸缩,无须停机 - 缺点
没有完整的监控工具集
不支持通配符主题选择
- 优点
kafka 架构?
- Kafka 将消息以 topic 为单位进行归纳,发布消息的程序称为 Producer,消费消息的程序称为 Consumer。它是以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个 Broker,Producer 通过网络将消息发送到 kafka 集群,集群向消费者提供消息,broker 在中间起到一个代理保存消息的中转站。
- kafka 组成:
- Producer:消息生产者,发布消息到Kafka集群的终端或服务
- Broker:一个 Kafka 节点就是一个 Broker,多个Broker可组成一个Kafka 集群
如果某个 Topic 下有n个Partition 且集群有n个Broker,那么每个 Broker会存储该 Topic 下的一个 Partition
如果某个 Topic 下有n个Partition 且集群中有 m+n个Broker,那么只有n个Broker会存储该Topic下的一个Partition
如果某个 Topic 下有n个Partition 且集群中的Broker数量小于 n,那么一个 Broker 会存储该 Topic 下的一或多个 Partition,这种情况尽量避免,会导致集群数据不均衡 - Topic:逻辑概念,消息主题,每条发布到Kafka集群的消息都会归集于此,Kafka是面向Topic 的
- Partition:是Topic在物理上的分区,从Partition消费信息。一个Topic可以分为多个Partition,每个Partition是一个有序的不可变的记录序列。单一主题中的分区有序,但无法保证主题中所有分区的消息有序。
- Consumer:从Kafka集群中消费消息的终端或服务
- Consumer Group:每个Consumer都属于一个Consumer Group,每条消息只能被Consumer Group中的一个Consumer消费,但可以被多个Consumer Group消费。
- Zookeeper:Kafka 通过Zookeeper来存储集群中的 meta 消息(0.9版本后,将消息偏移量offset存储本地)
如何判断一个 Broker 是否还有效
1.Broker必须可以维护和ZooKeeper的连接,Zookeeper通过心跳机制检查每个结点的连接
2.如果Broker是个Follower,它必须能及时同步Leader的写操作,延时不能太久。kafka分区,partition
- 分区的概念
主题是一个逻辑上的概念,还可以细分为多个分区,一个分区只属于单个主题,很多时候也会把分区称为主题分区(Topic-Partition)。同一主题下的不同分区包含的消息是不同的,分区在存储层面可以看做一个可追加的 日志文件 ,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset)。 - offset:是消息在分区中的唯一标识,表示在分区中消费到消息的位置;
kafka 通过它来保证消息在分区内的顺序性,不过 offset 并不跨越分区,也就是说,kafka保证的是分区有序而不是主题有序。 - 在分区中又引入了多副本(replica)的概念,通过增加副本数量可以提高容灾能力。同一分区的不同副本中保存的是相同的消息。副本之间是一主多从的关系,其中主副本负责读写,从副本只负责消息同步。副本处于不同的broker中,当主副本出现异常,便会在从副本中提升一个为主副本。
- Kafka 中分区的原则???
1.指明Partition的情况下,直接将指明的值作为Partition值
2.没有指明Partition值但有 key 的情况下,将 key 的 Hash 值与 topic的Partition值进行取余得到Partition值
3.既没有Partition值又没有 key 值的情况下,第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),将这个值与Topic可用的Partition总数取余得到Parittion值,也就是常说的round-robin 算法 - Kafka 为什么要把消息分区
1.方便在集群中扩展,每个 Partition 可用通过调整以适应它所在的机器,而一个Topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了
2.可以提高并发,因为可以以Partition为单位进行读写
- 分区的概念
kafka 消费模式
- 发布-订阅模式:
- 在发布-订阅模式中,消息被发送到一个主题(Topic),而每个消费者都可以独立地订阅这个主题并接收消息。
- Push 模式:一种主动推送的方式,即消息生产者(Publisher)将消息直接推送给感兴趣的订阅者(Subscriber)。
这种模式下,订阅者无需主动拉取消息,而是等待消息被推送过来。因此,消息的发送和接收是异步的。 - Pull 模式:一种被动获取的方式,即订阅者需要主动向消息服务器请求消息,然后消息服务器才会将消息发送给订阅者。
在这种模式下,订阅者需要周期性地或者根据需要发起拉取消息的请求,消息的发送和接收是同步的。
- 消费者组模式:
- 在消费者组模式中,多个消费者被组织成一个消费者组(Consumer Group),并且每个分区(Partition)只能被同一个消费者组中的一个消费者处理。
- 当一个新的消费者加入消费者组时,它会负责消费未被分配的分区。
- 这种模式可以实现消息的负载均衡和水平扩展,适合于处理大量消息的情况。
- 发布-订阅模式:
Kafka是如何保证消息不丢失
- 生产者发送消息到Brocker丢失
设置异步发送,发送失败使用回调进行记录或重发;失败重试,参数配置,可以设置重试次数 - 消息在Brocker中存储丢失
发送确认设置acks=all,让所有的副本都参与保存数据后确认(设置acks=1时,只要leader节点收到消息,生产者就会收到一个来自服务器的成功响应) - (发送到Broker之后,走的是操作系统缓存,在异步刷盘(保存到硬盘)这个过程还有可能丢失)
- 消费者从Brocker接收消息丢失
关闭自动提交偏移量,开启手动提交偏移量;提交方式,最好是同步+异步提交
- 生产者发送消息到Brocker丢失
kafka 消费重复数据?
- 原因:
1、服务宕机,5秒钟内没有提交offerset;
2、5分钟内没消费完partition数据,触发reblance - 解决:
1、关闭自动提交偏移量,开启手动提交偏移量提交方式,最好是同步+异步提交,
2、提升消费端性能避免触发balance:异步消费、挑战消费时长、减少一次性从broker拉取的消息条数
3、幂等方案:对消费的消息生成md5存到redis,再存入时比对
- 原因:
Kafa 中如何保证顺序消费
- 问题原因:
一个topic的数据可能存储在不同的分区Partition 中,Kafka 的消费单元是 Partition,同一个 Partition 使用 offset 作为唯一标识保证顺序性。但这只是保证了在Partition 内部的顺序性而不是 Topic 中的顺序,如果消费者关联了多个分区,不能保证顺序性。 - 解决方法:(普通情况下)只需要把相同userld/orderld发送到相同的partition,因为一个partition由(一个消费者组的)一个Consumer消费
- 发送消息时指定分区号
- 发送消息时按照相同的业务设置相同的key
- 问题原因:
kafka高可用?
- 可以从两个层面回答,第一个是集群,第二个是复制机制集群:
- 一个kafka集群由多个broker实例组成,即使某一台宕机,也不耽误其他broker继续对外提供服务复制机制:
- 一个topic有多个分区,每个分区有多个副本,有一个leader,其余的是follower,副本存储在不同的broker中所有的分区副本的内容是都是相同的,如果leader发生故障时,会自动将其中一个follower提升为leader,保证了系统的容错性、高可用性
- ISR(in-syncreplica)机制:需要同步复制保存的folower
分区副本分为了两类,一个是ISR,与leader副本同步保存数据,另外一个普通的副本,是异步同步数据,当leader挂掉之后,会优先从ISR副本列表中选取一个作为leader
- 可以从两个层面回答,第一个是集群,第二个是复制机制集群:
kafka 如何实现高性能?
- 消息分区:不受单台服务器的限制,可以不受限的处理更多的数据;我们往一个Topic发送消息或者读取消息时,实际内部是多个Partition在并行处理
- 顺序读写:磁盘顺序读写,提升读写效率
- 页缓存:把磁盘中的数据缓存到内存中,把对磁盘的访问变为对内存的访问
- 零拷贝:减少上下文切换及数据拷贝??在读写数据中也减少CPU拷贝文件的次数
- 消息压缩:减少磁盘I0和网络I0
- 分批发送:将消息打包批量发送,减少网络开销
kafka消息积压问题
- 消息积压根本原因是:生产者的生产速度,大于消费者的消费速度。consumer还来不及从PageCache中消费消息,producer就已经生产消息把容量占完了,只能把PageCache中的消息保存到硬盘中,consumer要到硬盘中取出未消费的消息、、
- 遇到消息积压问题时,我们需要先排查,是不是有bug产生了。如果不是bug,我们可以优化一下消费的逻辑,比如之前是一条一条消息消费处理的话,我们可以确认是不是可以优为批量处理消息。如果还是慢,我们可以考虑水平扩容,增加Topic的队列数,和消费组机器的数量,提升整体消费能力。
- 如果是bug导致几百万消息持续积压几小时,需要解决bug,临时紧急扩容,大概思路如下:
- 先修复consumer消费者的问题,以确保其恢复消费速度,然后将现有consumer 都停掉。
- 新建一个 topic:partition 是原来的 10 倍,临时建立好原先10倍的queue 数量。
- 然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue。
- 接着临时征用 10倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queke 资源和 consumer 资源扩大 10倍,以正常的 10倍速度来消费数据。
- 等快速消费完积压数据之后,得恢复原先部署的架构,重新用原先的 consumer 机器来消费消息。
网 络
计算机网络:https://leo710aka.github.io/2022/02/28/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/
浏览器发出一个请求到收到响应经历了哪些步骤?
1,浏览器解析用户输入的URL,生成一个HTTP格式的请求
2,先根据URL域名从本地hosts文件查找是否有映射IP,如果没有就将域名发送给电脑所配置的DNS进行域名解析,得到IP地址
3,浏览器通过操作系统将请求通过四层网络协议发送出去
4,途中可能会经过各种路由器、交换机,最终到达服务器
5,服务器收到请求后,根据请求所指定的端口,将请求传递给绑定了该端口的应用程序,比如8080被tomcat占用了
6,tomcat接收到请求数据后,按照http协议的格式进行解析,解析得到所要访问的servlet
7,然后servlet来处理这个请求,如果是pringMVC中的DispatcherServet,那么则会找到对应的Controller中的方法,并执行该方法得到结果
8,Tomcat得到响应结果后封装成HTTP响应的格式,并再次通过网络发送给浏览器所在的服务器
9,浏览器所在的服务器拿到结果后再传递给浏览器,浏览器则负责解析并洁染介绍 TCP/IP 模型。
- 链路层(Link Layer):负责物理介质上的数据传输,处理硬件相关的问题。主要包括网络驱动、接口卡、物理介质等。作用:实现原始比特流的传输,通过物理链路进行直接通信。
- 网络层(Internet Layer):提供数据包在不同网络之间的传输和路由。主要包括IP、ICMP等。作用:实现不同网络之间的通信,处理数据包的寻址和路由问题。
- 传输层(Transport Layer):提供端到端的通信,处理数据流控制、差错校验和恢复等问题。主要包括TCP和UDP。作用:确保数据的可靠传输,处理数据的分段、排序和重组。
- 应用层(Application Layer): 提供网络服务和应用软件之间的接口。包括各种应用层协议,如HTTP、FTP、SMTP等。作用:为用户提供网络服务,支持各种应用软件的运行。
OSI七层模型从底层到顶层分别是:
- 物理层(Physical Layer): 定义物理设备和传输媒体的规范,如电缆、光纤、网卡等。
- 数据链路层(Data Link Layer): 提供可靠的点对点通信,处理相邻节点之间的数据链路错误和流控制。
- 网络层(Network Layer): 负责在整个网络中找到数据的最佳路径,处理不同网络上的路由和转发。
- 传输层(Transport Layer): 提供端到端的通信,负责数据的可靠传输,处理流量控制和差错恢复。
- 会话层(Session Layer): 管理用户会话和数据交换,提供对话控制和管理。
- 表示层(Presentation Layer): 负责数据格式的转换、加密和压缩,确保一个系统的应用层能理解另一个系统的数据。
- 应用层(Application Layer): 提供用户接口和网络服务,是用户直接与网络交互的地方,包括文件传输、电子邮件、远程登录等。
HTTP请求常⻅的状态码和字段
- 常见的HTTP状态码:
1xx(信息性状态码): 100 Continue:继续。客户端应继续其请求。
2xx(成功状态码): 200 OK:请求成功。一般用于GET和POST请求。 201 Created:已创建。成功请求并创建了新的资源。 204 No Content:无内容。服务器成功处理请求,但没有返回任何内容。
3xx(重定向状态码): 301 Moved Permanently:永久重定向。请求的资源已被永久移动到新位置。 302 Found:临时重定向。请求的资源临时移动到新位置。
4xx(客户端错误状态码): 400 Bad Request:请求无效。服务器无法理解请求的语法。401 Unauthorized:未授权。需要身份验证或权限不足。 403 Forbidden:禁止访问。服务器理解请求,但拒绝执行。 404 Not Found:请求的资源不存在。
5xx(服务器错误状态码): 500 Internal Server Error:服务器内部错误。通用错误消息,服务器遇到意外的情况。 502 Bad Gateway:网关错误。服务器作为网关或代理,从上游服务器接收到无效的响应。 503 Service Unavailable:服务不可用。服务器目前无法处理请求。 - 常见的HTTP字段:
- 通用字段: Cache-Control:控制缓存行为。 Connection:控制是否保持连接。Date:表示消息创建时间。
- 请求字段: Host:指定被请求资源的主机名和端口号。User-Agent:表示客户端信息。
- 响应字段: Server:表示服务器信息。 Content-Type:指定响应内容的MIME类型。Content-Length:响应内容长度。
- 实体字段:ETag:用于检测资源是否发生改变。 Last-Modified:表示资源的最后修改时间。
- 常见的HTTP状态码:
HTTP中的主要方法及其作用:
- GET:从服务器获取资源。GET 方法请求指定的资源,并返回响应主体。它只用于检索数据,而不会修改服务器状态。
- POST:向服务器提交数据。POST 方法用于向服务器提交数据,通常用于创建新资源、提交表单数据、上传文件等操作。
- PUT:向服务器存储资源。PUT 方法用于向服务器上传或更新指定的资源,通常用于更新现有资源的内容。
- DELETE:从服务器删除资源。DELETE 方法用于请求服务器删除指定的资源。
- PATCH:对资源进行局部更新。PATCH 方法用于对现有资源进行局部更新,只更新部分内容而不是整个资源。
- HEAD:获取资源的元数据。HEAD 方法类似于 GET 方法,但只返回资源的头部信息而不返回响应主体,通常用于获取资源的元数据,如大小、类型等。
- OPTIONS:获取服务器支持的方法。OPTIONS 方法用于请求服务器列出对指定资源支持的 HTTP 方法列表,以及其他一些元数据信息。
- TRACE:追踪请求的传输路径。TRACE 方法用于测试发送到服务器的请求在其传输路径上的变化,通常用于诊断和调试。
- CONNECT:与目标服务器建立代理连接。CONNECT 方法用于建立客户端与目标服务器之间的代理连接,通常用于建立安全的TLS/SSL连接。
HTTP1.0 和 HTTP1.1 的区别?
- ⻓连接
HTTP1.1 ⽀持⻓连接,每⼀个TCP连接上可以传送多个HTTP请求和响应,默认开启 Connection:Keep-Alive;
HTTP1.0 默认为短连接,每次请求都需要建⽴⼀个TCP连接。 - 缓存
HTTP1.0 主要使⽤ If-Modified-Since/Expires 来做为缓存判断的标准
HTTP1.1 则引⼊了更多的缓存控制策略,如 Entity tag / If-None-Match 等更多可供选择的缓存头来控制缓存策略。 - 管道化:基于 HTTP1.1 的⻓连接,使得请求管线化成为可能。管线化使得请求能够“并⾏”传输,但是响应必须按照请求发出的顺序依次返回,性能在⼀定程度上得到改善
- 增加Host字段: 使得⼀个服务器能够⽤来创建多个 Web 站点。
- 状态码: 新增了24个错误状态响应码
- 带宽优化
HTTP1.0 中,存在⼀些浪费带宽的现象,例如客户端只是需要某个对象的⼀部分,⽽服务器却将整个对象送过来了,并且不⽀持断点续传功能
HTTP1.1 则在请求头引⼊了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content)
- ⻓连接
HTTP2.0 与 HTTP1.1 的区别?
- ⼆进制分帧:在应⽤层 (HTTP/2.0) 和传输层(TCP or UDP) 之间增加⼀个⼆进制分帧层,从⽽突破 HTTP1.1 的性能限制,改进传输性能,实现低延迟和⾼吞吐量。
- 多路复⽤(MultiPlexing):允许同时通过单⼀的 HTTP/2 连接发起多重的请求-响应消息,这个强⼤的功能则是基于“⼆进制分帧”的特性。
- ⾸部压缩: HTTP1.1 不⽀持 header 数据的压缩,HTTP/2.0 使⽤ HPACK 算法对 header 的数据进⾏压缩,这样数据体积⼩了,在⽹络上传输就会更快。⾼效的压缩算法可以很⼤的压缩 heeader ,减少发送包的数量从⽽降低延迟。
- 服务端推送(server push): 在 HTTP/2 中,服务器可以对客户端的⼀个请求发送多个响应,即服务器可以额外的向客户端推送资源,⽽⽆需客户端明确的请求。
??HTTPS的⼯作原理?(https是怎么建⽴连接的)
- ⾸先,客户端向服务器端发送请求报⽂,请求与服务端建⽴连接。
- 服务端产⽣⼀对公私钥,然后将⾃⼰的公钥发送给CA机构,CA机构也有⼀对公私钥,然后CA机构使⽤⾃⼰的私钥将服务端发送过来的公钥进⾏加密,产⽣⼀个CA数字证书。
- 服务端响应客户端的请求,将CA机构⽣成的数字证书发送给客户端。
- 客户端将服务端发送过来的数字证书进⾏解析(因为浏览器产商跟CA机构有合作,所以浏览器中已经保存了⼤部分CA机构的密钥,⽤于对服务端发送过来的数字证书进⾏解密),验证这个数字证书是否合法,如果不合法,会发送⼀个警告。如果合法,取出服务端⽣成的公钥。
- 客户端取出公钥并⽣成⼀个随机码key(其实就是对称加密中的密钥)
- 客户端将加密后的随机码key发送给服务端,作为接下来的对称加密的密钥
- 服务端接收到随机码key后,使⽤⾃⼰的私钥对它进⾏解密,然后获得到随机码key。
- 服务端使⽤随机码key对传输的数据进⾏加密,在传输加密后的内容给客户端
- 客户端使⽤⾃⼰⽣成的随机码key解密服务端发送过来的数据,之后,客户端和服务端通过对称加密传输数据,随机码Key作为传输的密钥。
HTTPS与HTTP的区别
HTTP 是明⽂传输,⽽ HTTPS 通过 SSL\TLS 进⾏了加密
HTTP 的端⼝号是 80,HTTPS 是 443
HTTPS 需要到 CA 申请证书
HTTP 的连接简单,是⽆状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进⾏加密传输、身份认证的安全的⽹络协议。常⻅的请求⽅式?GET 和 POST 请求的区别?
- 作⽤不同: GET⽤于从服务端获取资源,POST⼀般⽤来向服务器端提交数据
- 参数传递⽅式不同: GET请求的参数⼀般写在URL中,且只接受ASCII字符;POST请求参数⼀般放在请求体中,对于数据类型也没有限制
- 安全性不同: 因为参数传递⽅式的不同,所以两者安全性不同,GET请求的参数直接暴露在URL中,所以更不安全,不能⽤来传递敏感信息。
- 参数⻓度限制不同
GET传送的数据量较⼩,不能⼤于2KB。POST传送的数据量较⼤,⼀般被默认为不受限制。
HTTP 协议没有 Body 和 URL 的⻓度限制,对 URL 限制的⼤多是浏览器和服务器的原因。 - 编码⽅式不同
GET 请求只能进⾏ URL 编码(application/x-www-form-urlencoded)
POST ⽀持多种编码⽅式(application/x-www-form-urlencoded 或 multipart/form-data。为⼆进制数据使⽤多种编码。) - 缓存机制不同
GET 请求会被浏览器主动cache,⽽ POST 不会,除⾮⼿动设置。
GET 请求参数会被完整保留在浏览器历史记录⾥,⽽ POST 中的参数不会被保留。
GET 产⽣的 URL 地址可以被 保存为书签,⽽ POST 不可以。
GET 在浏览器回退时是⽆害的,⽽ POST 会再次提交请求。 - 时间消耗不同
GET 产⽣⼀个 TCP 数据包;POST 产⽣两个 TCP 数据包。
对于 GET ⽅式的请求,浏览器会把 header 和 data ⼀并发送出去,服务器响应 200(返回数据);⽽对于 POST,浏览器先发送 Header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据) - 幂等:意思是多次执⾏相同的操作,结果都是「相同」的。
GET ⽅法就是安全且幂等的,因为它是「只读」操作,⽆论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。
POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。
??什么是强缓存和协商缓存
- 缓存可以解决什么问题:
减少不必要的⽹络传输,节约带宽
更快的加载⻚⾯
减少服务器负载,避免服务过载的情况出现 - 强缓存:浏览器判断请求的⽬标资源是否有效命中强缓存,如果命中,则可以直接从内存中读取⽬标资源,⽆需与服务器做任何通讯。
- Expires强缓存 :设置⼀个强缓存时间,此时间范围内,从内存中读取缓存并返回,因为 Expires 判断强缓存过期的机制是获取本地时间戳,与之前拿到的资源⽂件中的Expires字段的时间做⽐较。来判断是否需要对服务器发起请求。这⾥有⼀个巨⼤的漏洞:“如果我本地时间不准咋办?”所以⽬前已经被废弃了。
- Cache-Control强缓存 : http1.1 中增加该字段,只要在资源的响应头上写上需要缓 存多久就好了,单位是秒。 Cache-Control:max-age=N , 有max-age、s-maxage、 no-cache、no-store、private、public这六个属性。
max-age决定客户端资源被缓存多久。 s-maxage决定代理服务器缓存的时⻓。 no-cache表示是强制进⾏协商缓存。 no-store是表示禁⽌任何缓存策略。 public表示资源既可以被浏览器缓存也可以被代理服务器缓存。 private表示资源只能被浏览器缓存,默认为private
- 基于 last-modified 的协商缓存
⾸先需要在服务器端读出⽂件修改时间,将读出来的修改时间赋给响应头的last-modified字段。最后设置Cache-control:no-cache,当客户端读取到last-modified的时候,会在下次的请求标头中携带⼀个字段:If-ModifiedSince,⽽这个请求头中的If-Modified-Since就是服务器第⼀次修改时候给他的时间。之后每次对该资源的请求,都会带上If-Modified-Since这个字段,⽽服务端就需要拿到这个时间并再次读取该资源的修改时间,让他们两个做⼀个⽐对来决定是读取缓存还是返回新的资源。
缺点:
因为是更具⽂件修改时间来判断的,所以,在⽂件内容本身不修改的情况下,依然有可能更新⽂件修改时间(⽐如修改⽂件名再改回来),这样,就有可能⽂件内容明明没有修改,但是缓存依然失效了。当⽂件在极短时间内完成修改的时候(⽐如⼏百毫秒)。因为⽂件修改时间记录的最⼩单位是秒,所以,如果⽂件在⼏百毫秒内完成修改的话,⽂件修改时间不会改变,这样,即使⽂件内容修改了,依然不会返回新的⽂件。 - 基于 ETag 的协商缓存:
将原先协商缓存的⽐较时间戳的形式修改成了⽐较⽂件指纹(根 据⽂件内容计算出的唯⼀哈希值)。第⼀次请求某资源的时候,服务端读取⽂件并计算出⽂件指纹,将⽂件指纹放在响应头的 Etag字段中跟资源⼀起返回给客户端。第⼆次请求某资源的时候,客户端⾃动从缓存中读取出上⼀次服务端返回的ETag也就是⽂件指纹。并赋给请求头的if-None-Match字段,让上⼀次的⽂件指纹跟随请求⼀起回到服务端。
服务端拿到请求头中的if-None-Match字段值(也就是上⼀次的⽂件指纹),并再次读取⽬标资源并⽣成⽂件指纹,两个指纹做对⽐。如果两个⽂件指纹完全吻合,说明⽂件没有被改变,则直接返回304状态码和⼀个空的响应体并return。如果两个⽂件指纹不吻合,则说明⽂件被更改,那么将新的⽂件指纹重新存储到响应头的ETag中并返回给客户端。
缺点:
ETag需要计算⽂件指纹这样意味着,服务端需要更多的计算开销。。如果⽂件尺⼨⼤,数量多,并且计算频繁,那么ETag的计算就会影响服务器的性能。显然,ETag在这样的场景下就不是很适合。ETag有强验证和弱验证,所谓将强验证,ETag⽣成的哈希码深⼊到每个字节。哪怕⽂件中只有⼀个字节改变了,也会⽣成不同的哈希值,它可以保证⽂件内容绝对的不变。但是,强验证⾮常消耗计算量。ETag还有⼀个弱验证,弱验证是提取⽂件的部分属性来⽣成哈希值。因为不必精确到每个字节,所以他的整体速度会⽐强验证快,但是准确率不⾼。会降低协商缓存的有效性。 - 有哈希值的⽂件设置强缓存即可。没有哈希值的⽂件(⽐如index.html)设置协商缓存。
- 缓存可以解决什么问题:
DNS、、
- DNS(Domain Name System)域名管理系统,是当⽤户使⽤浏览器访问⽹址之后,使⽤的第⼀个重要协议。DNS 要解决的是域名和 IP 地址的映射问题。
- 查询过程:
- ⾸先⽤户在浏览器输⼊URL地址后,会先查询浏览器缓存是否有该域名对应的IP地址。
- 如果浏览器缓存中没有,会去计算机本地的Host⽂件中查询是否有对应的缓存。
- 如果Host⽂件中也没有则会向本地的DNS解析器(通常由你的互联⽹服务提供商(ISP)提供)发送⼀个DNS查询请求。
- 如果本地DNS解析器没有缓存该域名的解析记录,它会向根DNS服务器发出查询请求。根DNS服务器并不负责解析域名,但它能告诉本地DNS解析器应该向哪个顶级域(.com/.net/.org)的DNS服务器继续查询。
- 本地DNS解析器接着向指定的顶级域DNS服务器发出查询请求。顶级域DNS服务器也不负责具体的域名解析,但它能告诉本地DNS解析器应该前往哪个权威DNS服务器查询下⼀步的信息。
- 本地DNS解析器最后向权威DNS服务器发送查询请求。 权威DNS服务器是负责存储特定域名和IP地址映射的服务器。当权威DNS服务器收到查询请求时,它会查找”example.com”域名对应的IP地址,并将结果返回给本地DNS解析器。
- 本地DNS解析器将收到的IP地址返回给浏览器,并且还会将域名解析结果缓存在本地,以便下次访问时更快地响应
HTTP多个TCP连接怎么实现???
多个tcp连接是靠某些服务器对 Connection: keep-alive 的 Header 进⾏了⽀持。简⽽⾔之,完成这个 HTTP 请求之后,不要断开 HTTP 请求使⽤的 TCP 连接。这样的好处是连接可以被重新使⽤,之后发送 HTTP 请求的时候不需要重新建⽴ TCP 连接,以及如果维持连接,那么 SSL 的开销也可以避免TCP的三次握手和四次挥手
- 在建立TCP连接时,需要通过三次握手来建立,过程是:
1,客户端向服务端发送一个SYN
2,服务端接收到SYN后,给客户端发送一个SYN_ACK
3,客户端接收到SYN_ACK后,再给服务端发送一个ACK - 在断开TCP连接时,需要通过四次挥手来断开,过程是
1,客户端向服务端发送FIN
2,服务端接收FIN后,向客户燃发送ACK,表示我接收到了断开连接的请求,客户端你可以不发数据了,不过服务端这边可能还有数据正在处理
3,服务端处理完所有数据后,向客户端发送FIN,表示服务端现在可以断开连接
4,客户端收到服务端的FIN,向服务端发送ACK,表示客户端也会断开连接了
- 在建立TCP连接时,需要通过三次握手来建立,过程是:
三次握⼿的过程,以及为什么是三次,⽽不是四次,两次?
- 三次握⼿的过程如下:
- 客户端向服务器发送 SYN 报⽂、初始化序列号 ISN(seq=x),然后客户端进⼊ SYN_SEND 状态,等待服务器确认。
- 服务端发送 ACK 确认服务端的 SYN 报⽂ (ack=x+1) 同时发出⼀个 SYN 报⽂,带上⾃⼰的初始化序列号 (seq=y ),然后服务端进⼊ SYN_RECV 状态。
- 客户端接收到服务端的 SYN、ACK 报⽂,ACK确认服务端的 SYNC 报⽂ (ACK=y+1) ,然后客户端和服务器端都进⼊ ESTABLISHED 状态,完成 TCP 三次握⼿
- 为什么不是四次握⼿? 为什么不能两次握⼿?
因为三次握⼿才能保证双⽅具有接收和发送的能⼒。 两次握⼿可能导致资源的浪费,由于没有第三次握⼿,服务端就⽆法确认客户端是否收到了⾃⼰的回复,所以每收到⼀个 SYN ,服务器都会主动去建⽴⼀个连接, ⽽四次握⼿可以优化为三次。
- 三次握⼿的过程如下:
四次挥⼿的过程,以及为什么是四次?
- 四次挥⼿的过程:
- 客户端发送⼀个 FIN 报⽂给服务端,表示⾃⼰要断开数据传送,报⽂中会指定⼀个序列号 (seq=x) 。然后,客户端进⼊ FIN-WAIT-1 状态。
- 服务端收到 FIN 报⽂后,回复 ACK 报⽂给客户端,且把客户端的序列号值 +1 ,作为ACK + 1 报⽂的序列号 (seq=x+1) 然后,服务端进⼊ CLOSE-WAIT (seq=x+1) 状态,客户端进⼊ FIN-WAIT-2 状态。
- 服务端也要断开连接时,发送 FIN 报⽂给客户端,且指定⼀个序列号 (seq=y+1) ,随后服务端进⼊ LAST-ACK 状态。
- 客户端收到 FIN 报⽂后,发出 ACK 报⽂进⾏应答,并把服务端的序列号值 +1 作为 ACK 报⽂序列号 (seq=y+2) 。此时客户端进⼊ TIME-WAIT 状态。服务端在收到客户端的 ACK 报⽂后进⼊ CLOSE 状态。如果客户端等待 2MSL 没有收到回复,才关闭连接
- 为什么是四次挥⼿?
TCP 是全双⼯通信,可以双向传输数据。任何⼀⽅都可以在数据传送结束后发出连接释放的通知,待对⽅确认后进⼊半关闭状态。 当另⼀⽅也没有数据再发送的时候,则发出连接释放通知,对⽅确认后才会完全关闭了 TCP 连接。 总结:两次握⼿可以释放⼀端到另⼀端的 TCP 连接,完全释放连接⼀共需要四次握⼿ - 为什么会有
TIME_WAIT
状态?发生在客户端还是服务端?- 确保数据可靠传输:在四次挥手中,最后一次挥手由服务端发送
FIN
报文给客户端,表示服务端已经关闭了连接。此时客户端进入TIME_WAIT
状态,等待一段时间(通常是2倍的最大报文段生存时间(MSL)),确保服务端接收到最后一个ACK
报文,以保证数据的可靠传输。 - 处理可能的重传数据包:在网络中可能会出现一些延迟到达的数据包或者服务端未收到客户端的确认报文的情况。如果客户端在收到服务端的
FIN
报文后直接关闭连接而不进入TIME_WAIT
状态,那么如果服务端在关闭连接后的一段时间内重传了FIN
报文,客户端就无法正确处理这个重传的FIN
报文,可能会导致连接异常。
TIME_WAIT
状态一般在客户端设置。这是因为在 TCP 协议中,客户端主动发起连接并主动关闭连接的情况比较常见,因此客户端需要等待一段时间确保连接关闭的可靠性。服务端在关闭连接后会直接进入CLOSED
状态,不需要等待,因为服务端已经没有发送数据的需求,只需等待客户端的最后一个ACK
确认即可。通常,操作系统会对TIME_WAIT
状态的持续时间进行配置,以便合理利用系统资源并确保连接关闭的可靠性。
- 确保数据可靠传输:在四次挥手中,最后一次挥手由服务端发送
- 四次挥⼿的过程:
TCP与UDP的概念,特点,区别和对应的使⽤场景?
- TCP与UDP的概念
TCP(传输控制协议)是⼀种⾯向连接的、可靠的、基于字节流的传输层通信协议。
UDP(⽤户数据报协议)为应⽤程序提供了⼀种⽆需建⽴连接就可以发送封装的IP数据包的⽅法。 - 特点
TCP:⾯向连接,传输可靠,传输形式为字节流,传输效率慢,所需资源多。
UDP:⽆连接、传输不可靠、传输形式为数据报⽂段,传输效率快,所需资源少。 - 区别
- 连接:TCP 是⾯向连接的传输层协议,传输数据前先要建⽴连接。UDP 是不需要连接,即刻传输数据。
- 服务对象:TCP 是⼀对⼀的两点服务,即⼀条连接只有两个端点。UDP ⽀持⼀对⼀、⼀对多、多对多的交互通信
- 是否有状态:TCP 传输是有状态的,它会去记录⾃⼰发送消息的状态⽐如消息是否发送了、是否被接收了等等,⽽ UDP 是⽆状态的。
- 可靠性:TCP可靠交付数据,数据可以⽆差错、不丢失、不重复、按需到达。UDP尽最⼤努⼒,不保证可靠交付数据。
TCP在传递数据之前,会有三次握⼿来建⽴连接;在数据传递时,有确认、窗⼝、重传、拥塞控制机制,保证数据传输的安全性。UDP数据传递不需要给出任何确认,且不保证数据不丢失及到达顺序;没有拥塞控制,即使⽹络⾮常拥堵了,也不会影响 UDP 的发送速率。 - ⾸部开销:TCP ⾸部⻓度较⻓,会有⼀定的开销,⾸部在没有使⽤「选项」字段时是 20 个字节,如果使⽤了「选项」字段则会变⻓的 (20 ~ 60字节)。UDP ⾸部只有 8 个字节,并且是固定不变的,开销较⼩。
- 传输⽅式:TCP 是⾯向字节流的,UDP ⾯向报⽂。TCP 是流式传输,没有边界,但保证顺序和可靠。UDP 是⼀个包⼀个包的发送,是有边界的,但可能丢包和乱序。
- 传输效率:由于TCP 传输的时候多了连接、确认重传等机制,所以TCP 的传输效率要⽐UDP 低。
- 分⽚不同:
- TCP 的数据⼤⼩如果⼤于 MSS ⼤⼩,则会在传输层进⾏分⽚,⽬标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了⼀个分⽚,只需要传输丢失的这个分⽚。
- UDP 的数据⼤⼩如果⼤于 MTU ⼤⼩,则会在 IP 层进⾏分⽚,⽬标主机收到后,在 IP 层组装完数据,接着再传给传输层,但是如果中途丢了⼀个分⽚,在实现可靠传输的 UDP 时则就需要重传所有的数据包,这样传输效率⾮常差,所以通常 UDP 的报⽂应该⼩于 MTU。
- 对应的使⽤场景
TCP常⽤于要求通信数据可靠场景(如⽹⻚浏览、⽂件传输、邮件传输、远程登录、数据库操作等)。
UDP常⽤于要求通信速度⾼场景(如域名转换、视频直播、实时游戏等)。
- TCP与UDP的概念
TCP 的 Keepalive 和 HTTP 的 Keep-Alive 是⼀个东⻄吗?
- HTTP 的 Keep-Alive,是由应⽤层(⽤户态) 实现的,称为 HTTP ⻓连接;
每次请求都要经历这样的过程:建⽴ TCP -> 请求资源 -> 响应资源 -> 释放连接,这就是HTTP短连接,但是这样每次建⽴连接都只能请求⼀次资源,所以HTTP 的 Keep-Alive实现了使⽤同⼀个 TCP 连接来发送和接收多个 HTTP 请求/应答,避免了连接建⽴和释放的开销,就就是 HTTP ⻓连接。 - TCP 的 Keepalive,是由 TCP 层(内核态) 实现的,称为 TCP 保活机制;通俗地说,就是TCP有⼀个定时任务做倒计时,超时后会触发任务,内容是发送⼀个探测报⽂给对端,⽤来判断对端是否存活。
- HTTP 的 Keep-Alive,是由应⽤层(⽤户态) 实现的,称为 HTTP ⻓连接;
TCP连接如何确保可靠性?
- 数据块⼤⼩控制: 应⽤数据被分割成TCP认为最合适发送的数据块,再传输给⽹络层,数据块被称为报⽂段或段。
- 序列号:TCP给每个数据包指定序列号,接收⽅根据序列号对数据包进⾏排序,并根据序列号对数据包去重。
- 校验和:TCP将保持它⾸部和数据的校验和。这是⼀个端到端的检验和,⽬的是检测数据在传输中的任何变化。若收到报⽂的检验和有差错,TCP将丢弃这个报⽂段和不确认收到此报⽂段。
- 流量控制: TCP连接的每⼀⽅都有固定⼤⼩的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收⽅来不及处理发送⽅的数据,能提示发送⽅降低发送的速率,防⽌包丢失。TCP利⽤滑动窗⼝实现流量控制。
- 拥塞控制: 当⽹络拥塞时,减少数据的发送。
- 确认应答: 通过 ARQ 协议实现。基本原理是每发完⼀个分组就停⽌发送,等待对⽅确认。如果没收到确认,会重发数据包,直到确认后再发下⼀个分组。
- 超时重传: 当TCP发出⼀个数据段后,它启动⼀个定时器,等待⽬的端确认收到这个报⽂段。如果不能及时收到⼀个确认,将重发这个报⽂段。
既然提到了拥塞控制,那你能说说说拥塞控制是怎么实现的嘛
拥塞控制算法主要有以下⼏种:- 慢启动: 在连接刚开始时,发送⽅会逐渐增加发送窗⼝⼤⼩,从⽽以指数增⻓的速度增加发送的数据量。
- 拥塞避免: ⼀旦慢启动阶段过去,发送⽅进⼊拥塞避免阶段。在这个阶段,发送⽅逐渐增加发送窗⼝的⼤⼩,但增加速率较慢,避免过快增加导致⽹络拥塞。
- 超时重传: 如果发送⽅在超时时间内未收到确认,它会认为数据包丢失,并重传这些数据包。这是拥塞控制的最后⼿段,⽤于检测和处理⽹络中的丢包或拥塞情况。当⽹络出现拥塞,也就是会发⽣数据包重传
- 快速重传(Fast Retransmit)和快速恢复(Fast Recovery): 当发送⽅发送的数据包丢失或⽹络出现拥塞时,接收⽅会发送重复确认(duplicate ACK)通知 发送⽅有数据包丢失。当发送⽅收到⼀定数量的重复确认时,它会⽴即重传丢失的数据包,⽽不是等待超时。这样可以减少⽹络的拥塞程度。
- 拥塞窗⼝调整: 发送⽅根据⽹络的拥塞程度动态调整发送窗⼝的⼤⼩,通过监测⽹络延迟和丢包情况来确定合适的发送速率,以避免⽹络拥塞。
TCP 传送数据的过程、、
- 建立连接:在 TCP 通信开始前,发送方和接收方需要通过三次握手建立连接。这个过程包括发送方发送 SYN 报文段给接收方,接收方回复 SYN+ACK 报文段给发送方,最后发送方发送 ACK 报文段给接收方,建立起双向的通信连接。
- 数据分割:发送方将应用层的数据流分割成适当大小的数据块,每个数据块称为一个报文段(Segment),每个报文段都带有序列号,用于标识数据的顺序。(TCP 基于字节流传输,这种字节流传输的方式是在 TCP 协议的抽象层面上实现的,实际传输时,TCP 使用报文段作为传输单位来保证数据的可靠传输。)
- 发送数据:发送方根据 TCP 的滑动窗口机制和拥塞控制算法,将报文段发送给接收方。发送方根据接收方的确认情况和网络情况动态调整发送速率和窗口大小,确保数据的可靠传输和网络的稳定性。
- 接收数据:接收方接收到报文段后,会进行确认,即发送 ACK 报文段给发送方,确认接收到的数据。如果有丢失或损坏的数据,接收方会要求发送方重新发送数据。
- 数据重组:接收方根据报文段的序列号和数据部分将数据重组成完整的数据流,然后交给应用层进行处理。
- 连接关闭:当数据传输完成后,发送方和接收方会通过四次挥手的方式关闭连接。发送方发送 FIN 报文段给接收方,表示不再发送数据;接收方回复 ACK 报文段确认,并发送 FIN 报文段给发送方;发送方再次发送 ACK 报文段给接收方,完成连接关闭。
Cookie 和 Session 是什么?有什么区别?
- 都⽤于管理⽤户的状态和身份, Cookie 通过在客户端记录信息确定⽤户身份, Session 在服务器端记录信息确定⽤户身份。
- Cookie:是存储在⽤户浏览器中的⼩型⽂本⽂件,⼀般为⼏ KB,⽤于在⽤户和服务器之间传递数据。通常,服务器会将⼀个或多个 Cookie 发送到⽤户浏览器,然后浏览器将这些 Cookie 存储在本地。
服务器在接收到来⾃客户端浏览器的请求之后,就能够通过分析存放于请求头的Cookie得到客户端特有的信息,从⽽动态⽣成与该客户端相对应的内容。 - Session:客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是 Session,主要⽤于维护⽤户登录状态、存储⽤户的临时数据和上下⽂信息等。存储容量较⼤,通常没有固定限制,取决于服务器的配置和资源。
- 安全性:由于 Cookie 存储在⽤户浏览器中,因此可以被⽤户读取和篡改。相⽐之下,Session 数据存储在服务器上,更难被⽤户访问和修改。
- 传输⽅式:Cookie 在每次 HTTP 请求中都会被⾃动发送到服务器,⽽ Session ID 通常通过 Cookie 或 URL 参数传递。
WEB 负载均衡方案。
是通过合理分配网络流量,将请求均匀分发到多个服务器上,以提高系统的性能、可用性和可伸缩性。
1、硬件负载均衡:通常是专门设计用于处理负载均衡任务的设备。这些设备可以位于网络流量的前端,根据预定义的算法和策略将请求分发给后端的多个服务器。硬件负载均衡的优势在于它可以处理大量的并发连接,同时提供高性能和低延迟。性能优越: 硬件负载均衡器通常具有高性能的硬件处理能力,能够应对大量请求。丰富的算法: 支持多种负载均衡算法,如轮询、加权轮询、最小连接数等。高可用性: 支持冗余配置,以确保负载均衡设备本身的高可用性。
2、软件负载均衡:通常是通过在服务器上运行特定的负载均衡软件来实现的,这些软件可以在普通服务器上运行,也可以部署在云环境中。软件负载均衡器通常提供一系列配置选项,以便根据特定需求进行定制。
3、DNS负载均衡:可以将域名映射到多个IP地址,实现流量的分发。DNS负载均衡的优点在于其简单性和低成本,但在实际应用中可能会受到DNS缓存的影响,导致流量分发不均匀。
4、网络负载均衡:通过网络设备(如交换机、路由器)进行流量分发。这种方式通常涉及更底层的网络配置,可以在不同层次上实现负载均衡。
适用于需要处理大规模流量的场景。配置和管理较为复杂,需要深入理解网络结构。192.168.1.0 分成 4 个子网,子网掩码是什么?
- 子网掩码不能单独存在,它必须结合IP地址一起使用。IP地址我们都知道是计算机在网络内的唯一标识,而子网掩码顾名思义是用于划分子网的
子网掩码只有一个作用,就是将某个IP地址划分成网络地址和主机地址两部分。子网掩码是一个32位地址,用于屏蔽IP地址的一部分以区别网络标识和主机标识,并说明该IP地址是在局域网上,还是在远程网上。 - 默认的子网掩码255.255.255.0:将该子网掩码的二进制由24个1和8个0组成,8个0表示该子网掩码划分出的子网容量为256(2的8次方),也就是说192.168.1.0-255都在同一个子网中,这256个地址中可用地址只有254个,因为规定每个子网的第一个IP地址为网段地址,最后一个IP地址为广播地址,都不可用。
- 某个小型公司有四个部门,每个部门各40台计算机接入公司局域网交换机,如果要在192.168.1.0网段为每个部门划分子网,子网掩码应该怎么设置,每个子网的地址范围分别是什么?
- 192.168.1.0网段共256个地址,划分4个子网,每个子网需要64个地址;64是2的6次方,子网掩码应该以6个0结尾,剩下的用1补齐,由26个1和6个0组成,转换成十进制是255.255.255.192;
- 每个子网共64个IP地址,掐头去尾后可用地址只有62个,第1个子网的可用IP地址范围是:192.168.1.1-62,第2个子网可用IP地址范围是192.168.1.65-126,第3个子网的可用IP地址范围是:192.168.1.129-190,第4个子网可用IP地址范围是192.168.1.193-254;
- 各部门计算机按照上面各子网的IP地址范围进行设置,所有计算机的子网掩码都必须设置为255.255.255.192,设置完毕后各部门内的计算机能正常联网,不同部门间的计算机无法直接联通。
- 子网掩码不能单独存在,它必须结合IP地址一起使用。IP地址我们都知道是计算机在网络内的唯一标识,而子网掩码顾名思义是用于划分子网的
操 作 系 统
进程与线程?
- 进程是程序的执行实例,拥有独立的内存空间和系统资源,是操作系统进行资源分配和调度的基本单位。是系统进⾏资源分配和调度的基本单位。
- 线程 Thread 是进程内的独立执行单元,共享相同的内存空间和资源,是进程的子执行单元。是操作系统能够进⾏运算调度的最⼩单位。
线程⼀个进程⾄少有⼀个线程,⼀个进程可以运⾏多个线程,这些线程共享同⼀块内存。 - 资源开销:
进程:由于每个进程都有独⽴的内存空间,创建和销毁进程的开销较⼤。进程间切换需要保存和恢复整个进程的状态,因此上下⽂切换的开销较⾼。
线程:线程共享相同的内存空间,创建和销毁线程的开销较⼩。线程间切换只需要保存和恢复少量的线程上下⽂,因此上下⽂切换的开销较⼩。 - ??通信与同步:
进程:由于进程间相互隔离,进程之间的通信需要使⽤⼀些特殊机制,如管道、消息队列、共享 内存等。
线程:由于线程共享相同的内存空间,它们之间可以直接访问共享数据,线程间通信更加⽅便。 - 安全性:
进程:由于进程间相互隔离,⼀个进程的崩溃不会直接影响其他进程的稳定性。
线程:由于线程共享相同的内存空间,⼀个线程的错误可能会影响整个进程的稳定性。
为什么进程切换比线程切换开销大?
- 地址空间切换: 进程拥有独立的地址空间,进程切换时需要切换地址空间,包括虚拟内存、页表等,而线程之间共享地址空间,线程切换时不需要这种开销。
- 资源切换: 进程拥有独立的资源,如文件描述符、堆栈、全局变量等,进程切换时需要保存和恢复这些资源,而线程之间共享资源,线程切换时开销较小。
- 上下文切换: 进程切换时需要保存和恢复更多的上下文信息,包括进程控制块(PCB)、寄存器、栈指针等,而线程切换时只需保存和恢复部分上下文信息。
- 系统调用开销: 进程切换时可能需要进行系统调用,如切换到内核态、更新进程状态等,而线程切换时可以在用户态完成。
- 同步和通信开销: 进程之间的通信和同步需要额外的开销,如信号量、消息队列等,而线程之间可以通过共享内存等更高效的方式进行通信和同步。
进程调度算法你了解多少?
进程调度算法是操作系统中⽤来管理和调度进程(也称为任务或作业)执⾏的⽅法。这些算法决定了在多任务环境下,如何为各个进程分配 CPU 时间,以实现公平性、⾼吞吐量、低延迟等不同的调度⽬标。- 先来先服务调度算法: 按照进程到达的先后顺序进⾏调度,即最早到达的进程先执⾏,直到完成或阻塞。
- 最短作业优先调度算法: 优先选择运⾏时间最短的进程来运⾏
- ⾼响应⽐优先调度算法: 综合考虑等待时间和服务时间的⽐率,选择具有最⾼响应⽐的进程来执⾏
- 时间⽚轮转调度算法: 将 CPU 时间划分为时间⽚(时间量),每个进程在⼀个时间⽚内运⾏,然后切换到下⼀个进程。
- 最⾼优先级调度算法: 为每个进程分配⼀个优先级,优先级较⾼的进程先执⾏。这可能导致低优先级进程⻓时间等待, 可能引发饥饿问题。
- 多级反馈队列调度算法: 将进程划分为多个队列,每个队列具有不同的优先级,进程在队列之间移动。具有更⾼优先级的 队列的进程会更早执⾏,⽽⻓时间等待的进程会被提升到更⾼优先级队列。
- 最短剩余时间优先: 每次选择剩余执⾏时间最短的进程来执⾏。
- 最⼤吞吐量调度: 旨在最⼤化单位时间内完成的进程数量
进程间通信
进程间通信(IPC)可以通过多种方式实现,包括管道、消息队列、信号量、共享内存等。这些机制允许不同进程之间交换数据和同步操作。- 管道:是⼀种半双⼯的通信⽅式,数据只能单向流动⽽且只能在具有⽗⼦进程关系的进程间使⽤。
- 命名管道: 也是半双⼯的通信⽅式,但是它允许⽆亲缘关系进程间的通信。
- 信号量:是⼀个计数器,可以⽤来控制多个进程对共享资源的访问,常作为⼀种锁机制,防⽌某进程正在访问共享资源时,其他进程也访问该资源。因此主要作为进程间以及同⼀进程内不同线程之间的同步⼿段。
- 消息队列:消息队列是消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载⽆格式字节流以及缓冲区⼤⼩受限等缺点。
- 信号:⽤于通知接收进程某个事件已经发⽣,从⽽迫使进程执⾏信号处理程序。
- 共享内存:就是映射⼀段能被其他进程所访问的内存,这段共享内存由⼀个进程创建,但多个进程都可以访问。共享内存是最快的进程通信⽅式,它是针对其他进程间通信⽅式运⾏效率低⽽专⻔设计的。它往往与其他通信机制,⽐如信号量配合使⽤,来实现进程间的同步和通信。
- Socket 套接字:是⽀持TCP/IP 的⽹络通信的基本操作单元,主要⽤于在客户端和服务器之间通过⽹络进⾏通信
什么是死锁?如何避免死锁?
死锁是指两个或多个进程在争夺系统资源时,由于互相等待对⽅释放资源⽽⽆法继续执⾏的状态。死锁只有同时满⾜以下四个条件才会发⽣:
1、互斥条件:⼀个进程占⽤了某个资源时,其他进程⽆法同时占⽤该资源。
2、请求保持条件:⼀个线程因为请求资源⽽阻塞的时候,不会释放⾃⼰的资源。
3、不可剥夺条件:资源不能被强制性地从⼀个进程中剥夺,只能由持有者⾃愿释放。
4、环路等待条件:多个进程之间形成⼀个循环等待资源的链,每个进程都在等待下⼀个进程所占有的资源。
只需要破坏上⾯⼀个条件就可以破坏死锁。
破坏请求与保持条件:⼀次性申请所有的资源。
破坏不可剥夺条件:占⽤部分资源的线程进⼀步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
破坏循环等待条件:靠按序申请资源来预防。让所有进程按照相同的顺序请求资源,释放资源则反序释放。多进程与多线程区别:
- 多进程是在不同的地址空间中执行,相互独立,通信相对复杂,但更稳定。
- 多线程共享相同的地址空间,通信更方便,但需要考虑同步和竞态条件,可能导致不稳定性。
解释⼀下⽤户态和核⼼态
⽤户态 User Mode 和核⼼态 Kernel Mode ,是操作系统中两种不同的执⾏模式,⽤于控制进程或程序对计算机硬件资源的访问权限和操作范围。
⽤户态:进程或程序只能访问受限的资源和执⾏受限的指令集,不能直接访问操作系统的核⼼部分,也不能直接访问硬件资源,⽤户态下的 CPU 不允许独占,也就是说 CPU 能够被其他程序获取。
核⼼态:核⼼态是操作系统的特权级别,允许进程或程序执⾏特权指令和访问操作系统的核⼼部分。在核⼼态下,进程可以直接访问硬件资源,执⾏系统调⽤,管理内存、⽂件系统等操作。处于内核态的 CPU 可以从⼀个程序切换到另外⼀个程序,并且占⽤ CPU 不会发⽣抢占情况,⼀般处于特权级 0 的状态我们称之为内核态用户态切换到内核态。
是通过系统调用(System Call)实现的。下面是大致的步骤:
1、触发系统调用: 当用户程序需要执行一些特权操作(例如文件操作、网络访问、内存分配等),需要请求操作系统的帮助。用户程序通过系统调用指令触发这一请求。在 x86 架构中,通常使用int 0x80
或syscall
指令来触发系统调用。
2、切换到内核态: 当发生系统调用时,处理器会由用户态切换到内核态。这个切换的过程中,处理器的特权级别会提升,从而允许操作系统执行一些受保护的指令。
3、系统调用处理: 操作系统的内核接收到系统调用请求后,会根据请求的类型执行相应的系统调用服务例程。这些服务例程负责完成用户程序请求的操作,可能涉及到内核中的数据结构和硬件资源。
4、返回用户态: 完成系统调用服务例程后,操作系统将控制权返回给用户程序,同时降低处理器的特权级别,将其切换回用户态。这个过程中,操作系统可能会将系统调用的结果返回给用户程序。解释⼀下进程同步和互斥,以及解决方法
- 进程同步是指多个并发执⾏的进程之间协调和管理它们的执⾏顺序,以确保它们按照⼀定的顺序或时间间隔执⾏。⽐如说,你想要和你的队友⼀起完成⼀个副本,你们需要相互配合,有时候等待对⽅的信号或者消息,有时候按照对⽅的要求执⾏某些动作,这就是进程同步。
- 互斥指的是在某⼀时刻只允许⼀个进程访问某个共享资源。当⼀个进程正在使⽤共享资源时,其他进程不能同时访问该资源。⽐如说,你想要使⽤⼀个祭坛来祈愿,但是这个祭坛⼀次只能被⼀个⼈使⽤,如果有其他⼈也想要使⽤,他们就必须等待你使⽤完毕后再去使⽤,这就是进程互斥。
- 解决进程同步和互斥的问题有很多种⽅法,其中⼀种常⻅的⽅法是使⽤信号量和 PV 操作。信号量是⼀种特殊的变量,它表示系统中某种资源的数量或者状态。PV 操作是⼀种对信号量进⾏增加或者减少的操作,它们可以⽤来控制进程之间的同步或者互斥。
- 举个例⼦,假设有⼀个信号量 s 表示⼀个祭坛是否可⽤,初始值为 1。如果 s 的值为 1,表示祭坛空闲;如果 s 的值为 0,表示祭坛被占⽤;如果 s 的值为 -1,表示有⼀个⼈在等待使⽤祭坛。那么我们可以⽤ PV 操作来实现对祭坛的互斥访问:
如果你想要使⽤祭坛,你就执⾏ P(s) 操作,将 s 的值减 1。如果结果为 0 或者正数,表示你可以使⽤祭坛;如果结果为负数,表示有⼈在使⽤祭坛,你就必须等待。
如果你使⽤完了祭坛,你就执⾏ V(s) 操作,将 s 的值加 1。如果结果为正数或者 0 ,表示没有⼈在等待使⽤祭坛;结果为负数,表示有⼈在等待使⽤祭坛,你就需要唤醒他们中的⼀个。
这样就可以保证每次只有⼀个⼈能够使⽤祭坛,实现了进程互斥。 - 除此之外,下⾯的⽅法也可以解决进程同步和互斥问题:
临界区(Critical Section): 将可能引发互斥问题的代码段称为临界区。为了实现互斥,每个进程在进⼊临界区前必须获取⼀个锁,退出临界区后释放该锁。这确保同⼀时间只有⼀个进程可以进⼊临界区。
互斥锁(Mutex): 互斥锁是⼀种同步机制,⽤于实现互斥。每个共享资源都关联⼀个互斥锁,进程在访问该资源前需要先获取互斥锁,使⽤完后释放锁。只有获得锁的进程才能访问共享资源。
条件变量(Condition Variable): 条件变量⽤于在进程之间传递信息,以便它们在特定条件下等待或唤醒。通常与互斥锁⼀起使⽤,以确保等待和唤醒的操作在正确的时机执⾏
什么是中段和异常?它们有什么区别?
- 中断和异常是两种不同的事件,它们都会导致CPU暂停当前的程序执⾏,转⽽去执⾏⼀个特定的处理程序。
- 中断和异常的区别主要有以下⼏点:
中断是由外部设备或其他处理器产⽣的,它们通常是异步的,也就是说,它们可以在任何时候发⽣,与当前执⾏的指令⽆关。例如,键盘输⼊、⿏标移动、⽹络数据到达等都会产⽣中断信号,通知CPU去处理这些事件。
异常是由CPU内部产⽣的,它们通常是同步的,也就是说,它们只会在执⾏某些指令时发⽣,与 当前执⾏的指令有关。例如,除法运算时除数为零、访问⾮法内存地址、执⾏⾮法指令等都会产⽣异常信号,通知CPU去处理这些错误或故障。
中断可以被屏蔽或禁⽌,这意味着CPU可以通过设置某些标志位或寄存器来忽略或延迟响应某些中断信号。这样可以避免中断过于频繁或⼲扰重要的任务。
异常不能被屏蔽或禁⽌,这意味着CPU必须⽴即响应异常信号,并进⾏相应的处理。这样可以保证程序的正确性和系统的稳定性。
介绍⼀下⼏种典型的锁
- 两个基础的锁:
互斥锁:互斥锁是⼀种最常⻅的锁类型,⽤于实现互斥访问共享资源。在任何时刻,只有⼀个线程可以持有互斥锁,其他线程必须等待直到锁被释放。这确保了同⼀时间只有⼀个线程能够访问被保护的资源。
⾃旋锁:⾃旋锁是⼀种基于忙等待的锁,即线程在尝试获取锁时会不断轮询,直到锁被释放。 - 其他的锁都是基于这两个锁的
读写锁:允许多个线程同时读共享资源,只允许⼀个线程进⾏写操作。分为读(共享)和写(排他)两种状态。
悲观锁:认为多线程同时修改共享资源的概率⽐较⾼,所以访问共享资源时候要上锁
乐观锁:先不管,修改了共享资源再说,如果出现同时修改的情况,再放弃本次操作。
- 两个基础的锁:
你知道的线程同步的⽅式有哪些?
线程同步机制是指在多线程编程中,为了保证线程之间的互不⼲扰,⽽采⽤的⼀种机制。常⻅的线程同步机制有以下⼏种:- 互斥锁:互斥锁是最常⻅的线程同步机制。它允许只有⼀个线程同时访问被保护的临界区(共享资源)
- 条件变量:条件变量⽤于线程间通信,允许⼀个线程等待某个条件满⾜,⽽其他线程可以发出信号通知等待线程。通常与互斥锁⼀起使⽤。
- 读写锁: 读写锁允许多个线程同时读取共享资源,但只允许⼀个线程写⼊资源。
- 信号量:⽤于控制多个线程对共享资源进⾏访问的⼯具
什么是内存分段和分⻚?作⽤是什么?
- 内存分段是将⼀个程序的内存空间分为不同的逻辑段 segments ,每个段代表程序的⼀个功能模块或数据类型,如代码段、数据段、堆栈段等。每个段都有其⾃⼰的⼤⼩和权限。
- 内存分⻚是把整个虚拟和物理内存空间分成固定⼤⼩的⻚(如4KB)。这样⼀个连续并且尺⼨固定的内存空间,我们叫⻚ Page
- 作⽤:
- 逻辑隔离: 内存分段和分⻚都实现了程序的逻辑隔离,使不同的功能模块或数据类型能够被单独管理和保护,提⾼了程序的可靠性和安全性。
- 内存保护: 通过将不同的段或⻚⾯设置为只读、可读写、不可执⾏等权限,操作系统可以确保程序不会越界访问或修改其他段的内容,从⽽提⾼了系统的稳定性。
- 虚拟内存: 分段和分⻚都有助于实现虚拟内存的概念,允许应⽤程序认为它们在使⽤的是⼀个⽐实际物理内存更⼤的内存空间。
- 内存共享: 通过分⻚,操作系统可以实现内存⻚⾯的共享,从⽽节省内存空间,多个进程可以共享相同的代码或数据⻚⾯。
- 内存管理: 分⻚更加灵活,允许操作系统将不同进程的⻚⾯分散存放在物理内存中,从⽽提⾼内存利⽤率。分段则更适⽤于管理不同的逻辑模块。
- 分段与分⻚的区别
分⻚对⽤户不可⻅,分段对⽤户可⻅
分⻚的地址空间是⼀维的,分段的地址空间是⼆维的
分⻚(单级⻚表)、分段访问⼀个逻辑地址都需要两次访存,分段存储中可以引⼊快表机制
分段更容易实现信息的共享和保护(纯代码或可重⼊代码可以共享) - 分段与分⻚优缺点:
分⻚管理: 内存空间利⽤率⾼,不会产⽣外部碎⽚,只会有少量的⻚内碎⽚。但是不⽅便按照逻辑模块实现信息的共享和保护 。
分段管理: 很⽅便按照逻辑模块实现信息的共享和保护。但是如果段⻓过⼤,为其分配很⼤的连续空间会很不⽅便,段式管理会产⽣外部碎⽚
解释⼀下⻚⾯置换算法,例如LRU(最近最少使⽤)、FIFO(先进先出)等
- 假设你的⼿机内存有限,只能同时运⾏四个原神的⻆⾊。当你想切换到⼀个新的⻆⾊时,你需要从内存中换出⼀个旧的⻆⾊,以便为新的⻆⾊腾出空间。不同的⻚⾯置换算法就相当于不同的切换策略,例如:
LRU(最近最少使⽤)算法:每次选择最⻓时间没有被使⽤的⻆⾊进⾏切换。这种策略基于你对⻆⾊的喜好,认为最近被使⽤过的⻆⾊很可能还会被使⽤,⽽最久未被使⽤的⻆⾊很可能不会再 被使⽤。 LRU算法可以有效地减少切换次数,但是实现起来⽐较复杂,需要记录每个⻆⾊的使⽤时间或者维护⼀个使⽤顺序的列表。
FIFO(先进先出)算法:每次选择最早进⼊内存的⻆⾊进⾏切换。这种策略很简单,只需要维护⼀个⻆⾊队列,每次淘汰队⾸的⻆⾊,然后把新的⻆⾊加⼊队尾。但是FIFO算法可能会淘汰⼀些经常被使⽤的⻆⾊,导致切换次数增加。⽽且FIFO算法有可能出现⻉拉迪异常(Belady anomaly),即当分配给内存的空间增加时,切换次数反⽽增加 - 常⻅⻚⾯置换算法有最佳置换算法(OPT)、先进先出(FIFO)、最近最久未使⽤算法(LRU)、时钟算法(Clock)
- 最佳置换算法: 该算法根据未来的⻚⾯访问情况,选择最⻓时间内不会被访问到的⻚⾯进⾏置换。那么就有⼀个问题了,未来要访问什么⻚⾯,操作系统怎么知道的呢?操作系统当然不会知道,所以这种算法只是⼀种理想情况下的置换算法,通常是⽆法实现的。
- 先进先出算法:也就是最先进⼊内存的⻚⾯最先被置换出去。这个算法⽐较简单明了,就不过多 解释了。但是先进先出算法会存在⼀个问题,就是Belady问题,即随着分配给进程的空闲⻚⾯数 增加,缺⻚的情次反⽽也会增加。 这和我们常识是相悖的,因为我们通常认为如果⼀个进程经常 发⽣缺⻚,那么就应该应该为他多分配⼀点内存。然⽽使⽤FIFO算法时,反⽽可能导致更多缺⻚ 情况出现。这就是Belady问题,Belady问题只会在使⽤FIFO算法时出现。
- 最近最久未使⽤算法:LRU算法基于⻚⾯的使⽤历史,通过选择最⻓时间未被使⽤的⻚⾯进⾏置 换。LRU算法的核⼼思想是,最近被访问的⻚⾯可能在未来被再次访问,⽽最⻓时间未被访问的 ⻚⾯可能是最不常⽤的,因此将其置换出去可以腾出空间给新的⻚⾯。LRU算法通常是使⽤⼀个 数据结构去维护⻚⾯的使⽤历史,维护使⽤历史就是通过访问字段实现的。访问字段的位数和操 作系统分配给该进程的⻚⾯数有关,⽐如分配4个⻚⾯,访问字段就是2位,16个⻚⾯,访问字段 就是4位,依次类推。如此,每⼀个⻚⾯的访问字段都可以不同,通过访问字段的不同,我们就 可以判断⻚⾯的使⽤历史。
- 时钟算法:Clock算法基于⼀个环形链表或者循环队列数据结构来管理⻚⾯的访问情况,⽤于选 择被置换的⻚⾯。Clock算法的核⼼思想是通过使⽤⼀个指针(称为时钟指针)在环形链表上遍历, 检查⻚⾯是否被访问过。这个访问过同样需要我们上⾯说到的访问字段来表示,此时访问字段只 有⼀位。每个⻚⾯都与⼀个访问位相关联,标记该⻚⾯是否被访问过。 当需要进⾏⻚⾯置换时,Clock算法从时钟指针的位置开始遍历环形链表。 如果当前⻚⾯的访问 位为0,表示该⻚⾯最久未被访问,可以选择进⾏置换。将访问位设置为1,继续遍历下⼀个⻚ ⾯。 如果当前⻚⾯的访问位为1,表示该⻚⾯最近被访问过,它仍然处于活跃状态。将访问位设 置为0,并继续遍历下⼀个⻚⾯如果遍历过程中找到⼀个访问位为0的⻚⾯,那么选择该⻚⾯进⾏ 置换。
- 假设你的⼿机内存有限,只能同时运⾏四个原神的⻆⾊。当你想切换到⼀个新的⻆⾊时,你需要从内存中换出⼀个旧的⻆⾊,以便为新的⻆⾊腾出空间。不同的⻚⾯置换算法就相当于不同的切换策略,例如:
CPU 飙高系统反应慢怎么排查?
CPU 是整个电脑的核心计算资源,对于一个应用进程来说,CPU 的最小执行单元是线程。导致 CPU 飙高的原因有几个方面:
1、CPU 上下文切换过多,对于 CPU 来说,同一时刻下每个 CPU 核心只能运行一个线程,如果有多个线程要执行,CPU 只能通过上下文切换的方式来执行不同的线程。较多的上下文切换会占据大量 CPU 资源,从而使得 cpu 无法去执行用户进程中的指令,导致响应速度下降。在 Java 中,文件 IO、网络 IO、锁等待、线程阻塞等操作都会造成线程阻塞从而触发上下文切换
2、CPU 资源过度消耗,也就是在程序中创建了大量的线程,或者有线程一直占用CPU 资源无法被释放,比如死循环!CPU 利用率过高之后,导致应用中的线程无法获得 CPU 的调度,从而影响程序的执行效率
3、既然是这两个问题导致的 CPU 利用率较高,于是我们可以通过 top 命令,找到 CPU利用率较高的进程,再通过 Shift+H 找到进程中 CPU 消耗过高的线程,有两种情况:
CPU 利用率过高的线程一直是同一个,说明程序中存在线程长期占用 CPU 没有释放的情况,这种情况直接通过jstack 获得线程的 Dump 日志,定位到线程日志后就可以找到问题的代码。
CPU 利用率过高的线程 id 不断变化,说明线程创建过多,需要挑选几个线程id通过 jstack 去线程 dump 日志中排查。
4、最后有可能定位的结果是程序正常,只是在 CPU 飙高的那一刻,用户访问量较大,导致系统资源不够。
分布式
分布式、SOA、微服务之间有什么关系和区别?
1,分布式架构是指将单体架构中的各个部分拆分,然后部署不同的机器或进程中去,SOA和微服务都是分布式架构的
2,SOA 是一种面向服务的架构,系统的所有服务都注册在总线上,当调用服务时,从总线上查找服务信息,然后调用
3,微服务是一种更彻底的面向服务的架构,将系统中各个功能个体抽成一个个小的应用程序,基本保持一个应用对应的一个服务的架构微服务架构?
微服务是一种软件开发架构风格,用于构建复杂应用程序。它将大型应用程序拆分成一系列较小、独立的服务,每个服务专注于完成特定的业务功能。这些服务之间通过轻量级的通信机制(通常是基于 HTTP 或 RPC)进行交互,可以独立部署、扩展和管理。微服务的主要特点包括:
- 单一责任:每个微服务专注于执行一个明确定义的业务功能。这使得开发人员可以更容易地理解和维护服务。
- 松耦合:微服务之间是独立的,它们可以使用不同的编程语言、技术堆栈和数据存储。这种松耦合使得开发团队能够独立地开发、测试和部署各个服务。
- 独立部署:每个微服务都可以独立地部署,这意味着当对一个服务进行更改时,不需要重新部署整个应用程序。这提高了开发和发布的速度,并允许快速迭代和灵活性。
- 弹性扩展:由于每个微服务是独立的,可以根据需要对它们进行独立的扩展。这使得应用程序能够更好地处理高负载情况,并具有更好的可伸缩性。
- 有限上下文:每个微服务维护自己的数据存储,这意味着它们可以使用不同类型的数据库或存储技术。这种隔离有助于减少整个系统的复杂性,并提高可靠性。
微服务架构也存在一些挑战和缺点:
- 分布式系统复杂性:微服务架构中的服务是分布式的,需要处理服务间通信、数据一致性、错误处理等问题。这增加了系统的复杂性,需要更多的设计和管理工作。
- 服务间通信开销:由于微服务架构中的服务通过网络通信进行交互,会增加一定的延迟和开销。此外,需要实现适当的通信机制和协议来确保可靠性和数据一致性。
- 运维复杂性:微服务架构中涉及多个独立的服务,每个服务都需要独立进行监控、日志记录和故障排除。这增加了运维的复杂性,需要适当的工具和自动化来管理和监控服务。
- 数据一致性:由于每个微服务维护自己的数据存储,确保数据一致性变得更加困难。在跨多个服务的业务操作中,需要采取适当的策略和技术来保证数据的一致性和完整性。
CAP?
- CAP 理论指出,在分布式系统中,无法同时满足三个核心属性:一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance),只能选择其中的两个。
- 一致性:在分布式系统中的所有节点上,对于某个操作的结果应该是一致的。换句话说,当一个节点执行了更新操作后,其他节点应该能够读取到更新后的值。这意味着所有节点在同一时间看到的数据都是相同的。
- 可用性:系统在面对用户请求时,应该保证能够进行响应,即系统保持可用状态。即使部分节点发生故障,系统仍然能够继续提供服务。
- 分区容忍性:系统能够在面对网络分区或者节点之间通信失败的情况下,仍然能够继续运行。即使系统中的某些节点之间无法进行通信,整个系统仍然可以继续工作。
- CAP 理论指出,在分布式系统中,无法同时满足三个核心属性:一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance),只能选择其中的两个。
Dubbo的架构设计是怎样的?
Dubbo 中的架构设计是非常优秀的,分为了很多层次,并且每层都是可以扩展的,比如:
1.Proxy服务代理层,支持JDK动态代理、javassist等代理机制
2.Registry注册中心层,支持Zookeeper、Redis等作为注册中心
3.Protocol远程调用层,支持Dubbo、Http等调用协议
4.Transport网络传输层,支持netty、mina等网络传输框架
5,Serialize数据序列化层,支持JSON、Hessian等序列化机制Dubbo 与 Spring Cloud 的区别。
Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。而 Spring Cloud 诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托了 Spirng、Spirng Boot 的优势之上,两个框架在开始目标就不一致,Dubbo 定位服务治理、Spirng Cloud 是一个生态。
两者最大的区别是 Dubbo 底层是使用 Netty 这样的 NIO 框架,是基于 TCP 协议传输的,配合以 Hession 序列化完成 RPC 通信。而 SpringCloud 是基于 Http 协议+Rest 接口调用远程过程的通信,相对来说,Http 请求会有更大的报文,占的带宽也会更多。但是 REST 相比 RPC 更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖。关于“你对 Spring Cloud 的理解”
Spring Cloud 是一套分布式微服务的技术解决方案,它提供了快速构建分布式系统的常用的一些组件比如说配置管理、服务的注册与发现、服务调用的负载均衡、资源隔离、熔断降级等等;不过 Spring Cloud 只是 Spring 官方提供的一套微服务标准定义,而真正的实现目前有两套体系用的比较多。
Spring Cloud Alibaba 是基于阿里巴巴开源组件集成的一套微服务解决方案,包括:1. Dubbo————消息通讯 2. Nacos————服务注册与发现 3.Seata————事务隔离 4. Sentinel————熔断降级
有了 Spring Cloud 这样的技术生态,使得我们在落地微服务架构时。不用去考虑第三方技术集成带来额外成本,只要通过配置组件来完成架构下的技术问题,从而可以让我们更加侧重性能方面弄懂 RPC。
常见的远程通信方式,有基于 REST 架构的 HTTP 协议、以及 RPC 框架。- 什么是远程调用
远程调用是指跨进程的功能调用,跨进程可以理解成一个计算机节点的多个进程,或者多个计算机节点的多个进程 - 什么是 RPC
全称为 Remote Procedure Call,翻译过来就是远程过程调用,它是一种通过网络从远程计算机程机程序上请求服务,而不需要了解底层网络技术的协议,凡是符合该协议的框架,我们都可以称它为 RPC 框架。关于 RPC 协议,通俗来讲就是,A 计算机提供一个服务,B 计算机可以像调用本地服务那样调用 A 计算机的服务。要实现 RPC,需要通过网络传输数据,并对调用的过程进行封装。现在比较流行的 RPC 框架,都会采用 TCP 作为底层传输协议。RPC 强调的是过程调用,调用的过程对用户而言是是透明的,用户不需要关心调用的细节,可以像调用本地服务一样调用远程服务。 - RPC 的运用场景和优势
分布式架构的核心,就是利用多个普通计算机节点,组成一个庞大而复杂的计算网络,提供高性能以及高并发的能力支撑。在分布式架构中,原本的单体应用被拆分成多个独立部署的服务分部在计算机网络上,这些服务必然需要通过网络进行数据交互。而 RPC 框架就是解决在分布式架构中的各个业务服务彼此的网络通信问题。
一般来说,RPC 服务主要是针对大型的企业,也就说当我们业务复杂度以及用户量都比较高时,需要解耦服务,扩展性强、部署灵活一般市面上开源的 PRC 框架,除了提供基础的远程通信功能以外,还会在性能消耗、传输效率、服务治理等方面做很多的设计,比如阿里开源的 RPC 框架 Dubbo。
- 什么是远程调用
负载均衡的实现算法
- 负载均衡是指将网络流量或工作负载分配到多个服务器或计算资源上,以提高系统的性能、可靠性和可扩展性。在实现负载均衡时,通常会采用以下算法:
- 轮询(Round Robin):按照轮询的方式依次将请求分发给后端服务器。每个请求按照顺序依次分配给不同的服务器,循环往复。这种算法简单且均衡,适用于服务器性能相似且无状态的情况。
- 最少连接(Least Connection):根据当前连接数选择连接数最少的服务器来处理新的请求。这种算法可以有效地将负载均衡到连接数较少的服务器上,以保持各服务器的负载相对均衡。
- IP哈希(IP Hash):根据客户端的 IP 地址进行哈希计算,将同一个 IP 地址的请求发送到同一个后端服务器。这样可以确保同一个客户端的请求都发送到同一台服务器上,适用于需要保持会话一致性的场景。
加权轮询(Weighted Round Robin):给每个服务器分配一个权重值,根据权重值的比例来分配请求。具有较高权重的服务器会接收到更多的请求,适用于服务器性能不均衡的情况。 - 加权最少连接(Weighted Least Connection):根据服务器的当前连接数和权重值来选择服务器。连接数越少且权重值越高的服务器会被优先选择。
- 随机(Random):随机选择一个后端服务器来处理请求。这种算法简单且均衡,但无法保证每个服务器的负载一致。
- 响应时间加权(Response Time Weighted):根据服务器的平均响应时间或处理时间来分配请求。响应时间较短的服务器会得到更多的请求,以提高系统整体的响应速度。
- 负载均衡是指将网络流量或工作负载分配到多个服务器或计算资源上,以提高系统的性能、可靠性和可扩展性。在实现负载均衡时,通常会采用以下算法:
分布式事务的原理
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点之上。比如大型的电商系统中的下单场景,会涉及到扣库存、优惠促销计算、订单 ID 生成。通常情况下,库存、促销、主键生成策略都位于不同的服务器和数据库表中。下单接口的成功与否,不仅取决于本地节点的数据库操作,而且还依赖第三方系统的结果,这时候分候分布式事务就保证这些操作要么全部成功,要么全部失败。
因此,本质上来说,分布式事务就是为了保证不同数据库的数据一致性。基于 CAP 定理可以知道,对于上述情况产生的分布式事务问题,我们要么采用强一致性方案、要么采用弱一致性方案。
??实现分布式锁的解决方案中,你认为 Zookeeper和 Redis 哪种更好?
两种方案都有各自的优缺点:
对于 redis 的分布式锁而言,它有以下缺点: 它获取锁的方式简单粗暴,如果获取不到锁,会不断尝试获取锁,比较消耗性能。 Redis 是 AP 模型,在集群模式中由于数据的一致性会导致锁出现问题,即便使用Redlock 算法来实现,在某些复杂场景下,也无法保证其实现 100%的可靠性。不过在实际开发中使用 Redis 实现分布式锁还是比较常见,而且大部分场情况下不会遇到”极端复杂的场景“,更重要的是 Redis 性能很高,在高并发场景中比较合适。
对于 zk 分布式锁而言: zookeeper 天生设计定位就是分布式协调,强一致性。锁的模型健壮、简单易用、适合做分布式锁。 如果获取不到锁,只需要添加一个监听器就可以了,不用一直轮询,性能消耗较小。如果要在两者之间做选择,就我个人而言的话,比较推崇 ZK 实现的锁,因为对于分布式锁而言,它应该符合 CP 模型,但是 Redis 是 AP 模型,所以在这个点上,Zookeeper会更加合适。什么是Docker?有什么优势?
?Docker 核心组件。
什么是 Kubernetes?
近些年,随着 Docker 与微服务的普及,K8s 也乘着这两股东风,迅速蹿红起来。作为最火的容器编排工具之一,它的很多思想设计都契合了微服务和云原生应用的设计法则。也正因如此,越来越多的公司开始使用起 k8s。K8s 全称 Kubernetes,8 是中间 8 个字母的简称。作为一种容器自动部署、扩容以及管理的技术,我们可以简单理解其是一种容器编排技术。前身是 Borg 系统,在谷歌内部已经有了十多年的使用经验。
,、、负载均衡的诞生背景
在互联网发展早期,由于用户量较少、业务需求也比较简单。对于软件应用,我们只需要一台高配的服务器即可完成业务的支撑,这样的软件架构称为单体架构
随着用户量的增加,服务器的请流量也随之增加,在这个过程中单体架构会产生两个问题。
1.软件的性能逐步下降,访问延迟越来越高
2.容易出现单点故障
为了解决这个问题,我们引入了集群化部署的架构,也就是把一个软件应用同时部署在多个服务器上。引入了负载均衡的设计,简单来说,负载均衡机制的核心目的是让客户端的请求合理均匀的分发到多台目标服务器,由于请求被多个节点分发,使得服务端的性能得到有效的提升。如何实现负载均衡呢?
1、基于 DNS 实现负载均衡:实现方式比较简单,只需要在 DNS服务器上针对某个域名做多个 IP 映射即可。
2、基于硬件实现负载均衡
3、基于软件实现负载均衡负载均衡的常用算法
所谓负载均衡算法,就是决定当前客户端请求匹配到目标服务器集群中的具体哪个节点。常见的负载均衡算法有:
1、轮训,也就是多个服务器按照顺序轮训返回,这样每个服务器都能获得相同的请求次数
2、随机,根据随机算法获得一个目标服务地址,由于该算法具备随机性,因此每个服务器获得的请求数量不一定均等。
3、一致性 hash,也就是对于具有相同 hash 码的请求,永远发送到同一个节点上。
4、最小连接数,根据目标服务器的请求数量来决定请求分发的权重,也就是目标服务集群中,请求更少的节点将会获得更多的请求。这是负载均衡中比较好的策略,真正能够实现目标服务器的请求均衡。
Linux
绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目录用什么表示? 切换目录用什么命令?
绝对路径: 如/etc/init.d
当前目录和上层目录: ./ ../
主目录: ~/
切换目录: cdLinux 下命令有哪几种可使用的通配符?分别代表什么含义?
- “?” 可替代单个字符。例如,file?.txt 可以匹配 file1.txt、fileA.txt 等。
- “*” 可替代任意多个字符。例如,
*.txt
可以匹配所有以 .txt 结尾的文件。 - 方括号 “[charset]” 可替代 charset 集中 的任何单个字符,例如 file[123].txt 可以匹配 file1.txt、file2.txt、file3.txt。
[a-zA-Z]+:[ ]+[0-9]*
代表:匹配一个或多个字母(大小写不敏感)、冒号、至少一个空格,然后是零个或多个数字。
例子:Example: 123
、abc:
。
正则表达式
\s+ 表示一个或多个空格;
\w:表示匹配任意字母、数字或下划线字符(即单词字符),等价于[a-zA-Z0-9_]
;
/a?/:表示字符a
可以出现 0 次或 1 次,匹配空字符串或者单个字母a
;
i.:匹配一个字符i
后面紧跟着任意字符。常用 linux 命令。
执行退出: exit
列出文件列表:ls[参数] -a 所有文件; -l 详细信息,包括大小字节数,可读可写可执行的权限等
用于显示文件后几行内容:tail,例如: tail -n 1000:显示最后1000行
打包:tar -xvf;打包并压缩:tar -zcvf
查找字符串:grep
显示当前所在目录:pwd
创建空文件:touch
编辑器:vim vi
查看用过的命令列表:history目录命令。
创建目录:mkdir
移除目录:rmdir
创建父目录的示例:mkdir -p
创建带权限的目录:mkdir -m目录、文件命令。
创建文件:典型的如 touch,vi 也可以创建文件,其实只要向一个不存在的文件输出,都会创建文件
复制文件: cp
移动文件,改名:mv
删除文件,空文件夹:rm rmdir
列出当前系统上已打开的文件(包括目录、文件、网络连接等)的相关信息:lsof 的名字是 “List Open Files” 的缩写。搜索文件用什么命令? 格式是怎么样的?
find <指定目录> <指定条件> <指定动作>
whereis 加参数与文件名
locate 只加文件名
find 直接搜索磁盘,较慢。
find / -name “string*”查看文件的命令。
- vi 文件名 #编辑方式查看,可修改
- cat 文件名 #显示全部文件内容
- more 文件名 #分页显示文件内容
- less 文件名 #与 more 相似,更好的是可以往前翻页
1
2
3
4less log2013.log # 查看文件
ps -ef | less # ps查看进程信息并通过less分页显示
history | less # 查看命令历史使用记录并通过less分页显示
less log2013.log log2014.log # 浏览多个文件 - tail 文件名 #仅查看尾部,还可以指定行数
1
2
3
4
5
6
7tail -n 10 test.log # 查询日志尾部最后10行的日志;
tail -n +10 test.log # 查询10行之后的所有日志;
tail -fn 10 test.log # 循环实时查看最后1000行记录(最常用的)
# 一般还会配合着grep搜索用,例如 :
tail -fn 1000 test.log | grep '关键字'
# 如果一次性查询的数据量太大,可以进行翻页查看,例如 :
tail -n 4700 aa.log |more -1000 可以进行多屏显示 (ctrl + f 或者 空格键可以快捷键) - head 文件名 #仅查看头部,还可以指定行数
1
2head -n 10 test.log 查询日志文件中的头10行日志;
head -n -10 test.log 查询日志文件除了最后10行的其他所有日志; - sed 这个命令可以查找日志文件特定的一段 , 根据时间的一个范围查询,可以按照行号和时间范围查询按照行号
1
2sed -n '5,10p' filename # 这样你就可以只查看文件的第5行到第10行。
sed -n '/2014-12-17 16:17:20/,/2014-12-17 16:17:36/p' test.log # 按照时间段
查看日志的方法
- 查看系统日志、登录日志、应用程序日志:使用
cat
或less
命令1
cat /var/log/syslog
- 使用
dmesg
查看内核日志 - 使用
journalctl
查看 Systemd 日志
- 查看系统日志、登录日志、应用程序日志:使用
查看进程线程的方法
- windows
任务管理器可以查看进程和线程数,也可以用来杀死进程
tasklist 查看进程
taskkill 杀死进程 - linux
ps -fe 查看所有进程
ps -fT -p < PID> 查看某个进程(PID)的所有线程
kill 杀死进程
top 按大写 H 切换是否显示线程
top -H -p < PID> 查看某个进程(PID)的所有线程 - Java
jps 命令查看所有 Java 进程
jstack < PID> 查看某个 Java 进程(PID)的所有线程状态
jconsole 来查看某个 Java 进程中线程的运行情况(图形界面)
- windows
Linux 中进程有哪几种状态?在 ps 显示出来的信息中分别用什么符号表示的?
1、不可中断状态:进程处于睡眠状态,但是此刻进程是不可中断的。不可中断,指进程不响应异步信号。
2、暂停状态/跟踪状态:向进程发送一个 SIGSTOP 信号,它就会因响应该信号 而进入 TATASK_STOPPED 状态;当进程正在被跟踪时,它处于 TATASK_TRACED 这个特殊的状态。正被跟踪”指的是进程暂停下来,等待跟踪它的进程对它进行操作。
3、就绪状态:在 run_queue 队列里的状态
4、运行状态:在 run_queue 队列里的状态
5、可中断睡眠状态:处于这个状态的进程因为等待某某事件的发生(比如等待socket 连接、等待信号量),而被挂起
6、zombie 状态(僵尸):父亲没有通过 wait 系列的系统调用会顺便将子进程的尸体(task_struct)也释放掉
7、退出状态
D 不可中断 Uninterruptible(usually IO)、R 正在运行,或在队列中的进程、S 处于休眠状态、T 停止或被追踪、Z 僵尸进程、W 进入内存交换(从内核 2.6 开始无效)、X 死掉的进程使用什么命令查看网络是否连通?使用什么命令查看 ip 地址及接口信息?
natstat,ifconfig文件的权限可以用一串字符表示,其中”-rwxrw-rw-“是一个典型的例子。
每个权限字符串由10个字符组成,分为四个部分:
1、文件类型: 第一个字符表示文件的类型。包括:-
:普通文件。d
:目录。l
:符号链接(软链接)
2、用户权限: 接下来的三个字符表示文件所有者的权限。r
:读权限(Read)w
:写权限(Write)x
:执行权限(eXecute)
3、组权限: 再接下来的三个字符表示文件所属组的权限。
4、其他用户权限: 最后的三个字符表示其他用户的权限。
在给定的例子”-rwxrw-rw-“中:文件类型是普通文件,文件所有者有读、写和执行权限,文件所属组有读和写权限,其他用户有读和写权限。文件唯一标识符(File Identifier)
通常是通过文件的inode
来实现的,而不是文件目录。inode
是文件系统中用于唯一标识文件或目录的数据结构。每个文件或目录都有一个唯一的inode
号码,它是一个整数值。