MySQL整合篇(SQL语句执行流程-->索引篇-->事务篇-->锁篇)

news2024/11/27 2:27:25

MySQL 基础篇 1.1 执行一条SQL语句会发生什么

 1. MySQL架构一共分为两层 server 和 存储引擎层(一般为Innodb引擎)

     主要执行流程都在server层:连接器,查询缓存,解析SQL(解析器),执行SQL(预处理器,优化器,执行器)

    存储引擎层:索引数据结构由引擎层实现,Innodb引擎支持的是B+树索引。数据表中创建的主键索引和二级索引都由B+树实现。

     执行一条SQL语句会发生什么

1. 连接器:  与客户端进行TCP三次连接(长连接)

                    校验用户名和密码

                    用户名验证通过后,会基于此时读取到的用户权限来进行逻辑判断

       TCP长连接不会轻易断开,所以使用长连接的好处就是减少建立连接和断开连接的过程。

      但是时间长了它也会占用内存过多,因此会采用以下两种方式来断开连接:

               1) 定期断开长连接

               2)客户端主动重置连接

 2.查询缓存:查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL 8.0 已删除该模块;(因为当有表更新之后,对应的语句就会被清除,所以会比较鸡肋)

3. 解析SQL:两个过程:

              1. 词法分析:根据输入的字符识别出来关键字,构建SQL语法树,方便后面的模块获取SQL类型,表名,字段名。

              2.语法分析:根据词法分析的语法树,判断该语句是否符合MySQL的语法。

4. 执行SQL 

    1. 预处理器:

                      检查SQL语句中的表和字段是否存在

                      将SQL语句中的 * 扩展为表中的所有列

    2. 优化器:

                      基于查询成本考虑,选择成本最小的执行计划。(主要选择主键索引还是二级索引)

   3. 执行器:

                       根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端;

MySQL一行记录是怎么存储的

MySQL数据文件存放在哪个文件?

一共有三个文件: db.opt  用来存放当前数据库的默认字符集和字符校验规则

                              t_order.frm 用来存放当前表结构

                              t_order.ibd 用来存放当前表数据(MySQL数据存放在这里)

表空间的文件结构:

 行:数据库表中的数据都是按行的形式存储,每行记录根据不同的行格式采用不同的存储结构。

页:每次只读取一行效率会很低,所以数据库读取数据都是以页为单位来读取数据。默认每个页的大小都是16KB。

区:Innodb存储引擎是以B+树的形式存储,B+树的每一层都是以双向链表的形式连接起来。但是链表中相邻的两个页之间并不是连续的,所以使用区的形式,让页与与页之间相邻。所以,当表中的数据量较大时,按照区为单位分配内存空间,每个区的大小为1MB。

段:表空间是由多个段组成,段由多个区组成。段有数据段,索引段,回滚段等

索引段:存放B+树中非叶子节点的区的集合

数据段:存放B+树中叶子节点的集合

回滚段:存放的是回滚数据的区的集合。

Innodb的行格式:COMPACT

Innodb的行格式一共分为两个部分:记录的额外信息,记录的真实数据

记录的额外信息一共分为三个部分:变长字段长度列表,NULL值列表,记录头信息。

记录的真实数据:

前面包含三个字段:

row_id : 当建表的时候设置了主键或者唯一约束,那么就不会有row_id字段。

trx_id : 事务id,表明了当前数据是由哪个事务生成的。

roll_pointer : 这条记录上一个版本的指针。 

总结:

MySQL的NULL值是怎么存放的:

是由Compact行格式中的NULL值列表来标记NULL的列,NULL不会存储在行格式中的真实数据部分。

MySQL如何知道varchar(n) 实际占用的数据大小:

利用行格式中的变长字段长度列表。

行溢出后,多余的数据怎么处理:

当一行存储不了所有的数据后,会将多余的数据存储到溢出页。并且在真实数据部分流出20KB的空间用来指向溢出页的地址。

_____________________________________________________________________________

MySQL 索引篇

 什么是索引,索引就相当于一本书的目录。通过索引可以快速查找到对应的数据。

索引常见面试题:

1. 索引的分类: 

按数据结构分类:

       

按物理存储分类:一般分为聚簇索引(主键索引),二级索引(辅助索引)

   主键索引的B+树的叶子节点上一般存储的是数据,所有完整的用户数据都存放在主键索引的叶子节点上

   二级索引的B+树的叶子节点上一般存储的是主键值,而不是实际数据。然后再通过主键值再查找到相应的数据,也就是说二级索引要经过两次查询。

   回表:

如果在二级索引的第一次查询中获得相应的数据,就不需要再进行一次查询。这就叫做覆盖索引。如果第一次查询中没有获得相应数据,只获得主键值,那么就需要再进行一次主键检索。这个过程就叫做回表。             

按字段特性分类:

      主键索引:主键索引就是建立在主键上的索引,一般在建表的时候创建,一张表只能有一个主键索引,并且主键索引的列的值不允许为空值。

      唯一索引:唯一索引建立在UNIQUE字段上的索引,一张表可以有多个唯一索引。索引列的值必须唯一,允许有空值。

      普通索引:建立在普通字段上的索引,既不要求字段为主键索引,也不要求字段为唯一索引。

      前缀索引:前缀索引是建立在字符型字段的前几个字符建立的索引。使用前缀索引的目的是减少索引占用的内存空间。提升查询效率。

按字段个数分类:

      单列索引:建立在单列字段上的索引

      联合索引:建立在多列字段上的索引(采用最左匹配原则)。

2. 什么时候需要创建索引 / 什么时候不需要创建索引

 索引的好处和缺点:

     索引最大的好处就是提高查询速度。

     索引的缺点:需要占用物理空间,数量越大,占用空间越大。

                          创建索引和维护索引需要耗费时间,这种时间随着数据量的增大而增大。索引就是典型的以空间换时间

                          会降低表的增删改查的效率,每次增删改查,B+数为了维护索引有序性,都要进行动态维护。

什么时候需要创建索引:

     字段有唯一性限制,就像商品编码

     经常用于where 语句查询语句的字段,这样能够提高整个表的查询效率。如果查询字段不是一个,可以进行联合查询。

      语句中含有ORDER BY和GROUP BY的字段,这样在进行查询时就不用去做一次排序。因为建立好索引之后,B+树上的数据都是有序的。

什么时候不需要查询数据:

      WHERE 和 ORDER BY 和 GROUP BY 用不到的语句。

      字段中出现大量的数据,比如性别字段,每次查询都只会出现一般的数据。

     表数据太少的时候不需要创建索引

     经常更新的字段,B+树需要维护数据的有序性,频繁的增删改查会影响数据库的性能。

3. 索引优化的方法

    前缀索引优化方法:前缀索引就是使用某个字段的前几个字符作为索引,在一些大的字段作为索引时,使用前缀索引可以减少索引项的大小。

    覆盖索引优化:在二级索引的查询中,如果在第一次查询就获得相应的数据,就可以避免回表操作。方法:我们可以建立一个联合查询。即「商品ID、名称、价格」作为一个联合索引。如果索引中存在这些数据,查询将不会再次检索主键索引,从而避免回表。

   主键最好是自增的:如果我们将主键设置为自增的,那么每增加一次数据就会按顺序添加到对应的叶子节点上。不需要移动数据。每插入一次新数据,都是追加操作。

  索引最好设置成NOT NULL:索引中出现NULL会让优化器在做索引选择时会更加麻烦。

NULL值是一个没意义的数据,但是会在行格式中的NULL值列表增加1KB的空间。

  防止索引失效:

  • 当我们使用左或者左右模糊匹配的时候,也就是 like %xx 或者 like %xx%这两种方式都会造成索引失效;
  • 当我们在查询条件中对索引列做了计算、函数、类型转换操作,这些情况下都会造成索引失效;
  • 联合索引要能正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引的匹配,否则就会导致索引失效。
  • 在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。

从数据页的角度看B+树

 Innodb是如何存储数据的

Innodb是按照数据页的形式读取数据的(以行的形式效率会很低),每个页默认大小为16KB,意味着数据库每次读取数据都是以16KB为单位,一次最少读取16KB的数据到内存中。一次最少把内存中16KB的数据刷新到磁盘中。

数据页的主要作用就是存储数据,也就是数据库里的数据。数据页中的记录按照主键顺序组成单向链表。单向链表的优点主要是方便插入和删除。但是检索效率不高。

数据页中有一个页目录,起到记录的检索作用。

每个槽相当于指针指向了不同组的最后一个记录。、

页目录就是由多个槽准备的,槽相当于分组记录的索引。我们查找数据可以通过二分法快速定位到对应的槽,在遍历槽内的所有记录,从而找到对应的记录。

B+树是如何进行查询的

 磁盘的I/O次数对索引的使用效率至关重要。因此,采用B+树是最合适的。

B+树中的每个节点都是一个数据页。

B+树中非叶子节点仅用来存放目录项来进行索引,B+树的叶子节点用来存放数据。

所有的节点按照索引键的大小排序,构成一个双向链表,便于范围查询。

为什么MySQL采用B+树的形式来存储数据

设计一个适合MySQL索引的数据结构需要满足以下几点

能在尽可能少的磁盘中进行I/O操作

能高效的查询一个记录,也要能高效的执行范围查找

 什么是二分查找

 二分查找每次都能将范围减半,时间复杂度会降低。

二分查找树

每一个节点的左子树的所有节点都小于这个节点。右子树的所有节点都大于这个节点。

自平衡二叉树

每个节点的左子树和右子树的高度差不能超过1 . 但是不管是平衡二叉树,随着插入数据的增多,树的高度也会增加。 也就意味着磁盘I/O的次数会增加。会影响整体查询效率。它每个节点都会有两个子节点。

B树

B树的每个子节点都会由多个子结点。降低了树的高度问题。

 B+树

B+ 树与 B 树差异的点,主要是以下这几点:

  • 叶子节点(最底部的节点)才会存放实际数据(索引+记录),非叶子节点只会存放索引;
  • 所有索引都会在叶子节点出现,叶子节点之间构成一个有序链表;
  • 非叶子节点的索引也会同时存在在子节点中,并且是在子节点中所有索引的最大(或最小)。
  • 非叶子节点中有多少个子节点,就有多少个索引;

_____________________________________________________________________________

MySQL 事务篇 

事务有哪些特性?

原子性:

一个事务中的所有操作,必须全部执行。要么全部完成要么就不完成。中间如果出现错误,就要回滚到初始状态。

持久性:

事务处理结束后,对数据的修改就是永久的,就是系统故障也不会改变

一致性:

指事务操作前后,数据保持完整约束性。

隔离性:

数据库具有多个并发事务同时对数据进行读写和修改的能力。隔离性可以防止多个并发事务在执行时由于交叉执行而导致的数据不一致。

为了实现以上四个特性。数据库实现了一下机制:

持久性通过redo log  (重做日志)来实现

原子性通过undo log (回滚日志)实现

隔离性通过MVCC多并发控制或者锁来实现

一致性通过 持久性+原子性+隔离性 实现

 首先讲隔离性:

并发事务会引发的问题:

脏读:一个事务读到了另个事务未提交的数据

不可重复度:在一个事务内多次读取同一个数据,发现两次读取的数据不一致

幻读:在一个事务中多次查询某个符合查询条件的记录数量,发现两次查询的记录数量不一致。

SQL提供四种隔离级别来规避以上四种现象,隔离级别越高,性能效率越低

读未提交:可以读取到另一个事务未提交的数据 (可能会发生脏读,不可重复读,幻读)

读提交:可以读取到另一个事务已提交的数据(可能会发生不可重复读,幻读)

可重复读:从一个事务开启,他读取到的数据从始至终就是一致的 (可能会发生幻读)

Innodb 引擎的默认隔离级别是可重复读。但是他很大程度上避免了幻读。采用了以下两种方式

1. 针对快照读(普通select语句)

它采用了MVCC多并发控制,因为事务在执行过程中看到的数据,一直和事务启动时看到的数据是一致的。所以即使其他事务中途插入一条数据,也不会被该事务看到。

2. 针对当前读(select ...for update)

间隙锁+记录锁来实现。当执行语句时,会加上记录锁和间隙锁。如果有其他事务在在间隙锁和记录锁内插入一条数据,就会被阻塞。无法插入数据,就很好的避免了幻读现象。

 下面讲讲ReadView在MVCC里如何工作的

先了解ReadView的两个重要知识

1. ReadView 的四个字段

2. 聚簇索引记录中两个跟事务有关的两个隐藏列

 1. m_ids 当前数据库中活跃事务id列表,活跃事务指的是启动了但是还未提交的数据

 2. min_trx_id 创建ReadView时,当前数据库活跃且未提交的事务中最小事务的事务id

 3. max_trx_id 创建ReadView当前数据库中应该给下一个事务的id值,当前数据库最大id值+1

  4. creator_trx_id 指创建该事务时的id值

聚簇索引记录中的两个隐藏列

 trx_id 

当一个事务对某条聚簇索引记录进行改动时,就会把该事物的 id 隐藏在trx_id 中

 roll_pointer

每次对聚簇索引记录进行改动时,就会把旧版本的记录写入到 undo 日志中,然后roll_pointer 是一个指针,指向每一个旧版本记录,可以通过它找到修改前的记录。

创建ReadView之后,可以将记录中的trx_id 分为三种情况:

 一个事务访问记录时,除了自己更新的记录总是可见之外,还有以下几种情况:

1. 如果记录的 trx_id 值小于min_trx_id ,说明这个版本的记录是在创建ReadView前已经提交的十五生成的。所以该版本记录对该事务可见。

 2. 如果记录的trx_id 值大于max_trx_id ,说明这个版本的记录是在创建ReadView后的事务提交生成的,所以该版本记录对该事务不可见。

 3. 如果该记录的trx_id在min_trx_id和max_trx_id之间,需要判断trx_id是否在m_ids 列表中

         1)在m_ids 列表中,表示生成该记录的事务仍然活跃着,所以该记录不可见

         2)不在m_ids 列表中,表示该事务已经提交,该记录可见。

这种通过版本链(通过undo日志来实现,使用roll_pointer 来实现指向旧版本的记录)的形式来控制多并发事务访问同一个记录的行为叫做MVCC(多版本并发控制)

可重复读和读提交都是通过ReadView来实现,可重复读是从始至终每个事务就有一个ReadView

而读提交在每次读取数据时都会创建一个新的ReadView。

_____________________________________________________________________________

MySQL 锁篇

1. MySQL 有哪些锁?

       1)全局锁  执行后整个数据库就处于只读状态,一般用于全库逻辑备份

       2)表级锁:

               表锁:

                        表级别的共享锁:读锁     

                        表级别的独占锁:写锁

                元数据锁(MDL):当我们对数据库进行操作的时候,就会给表加上元数据锁

                           当我们对一张表进行CRUD时,加的是MDL读锁

                           对一张表进行表结构变更时,加的是MDL写锁。

               意向锁:当执行增删改查,需要先对表加上意向独占锁,然后再对该纪录加独占锁

                           加意向锁的目的是快速判断表中是否有记录被加锁

               AUTO-INC 锁:表里的主键通常设为自增的,这是通过对字段声明AUTO-INCREMENT                                             实现的

    3)行级锁

            Record lock 记录锁(对表中某一行的数据进行上锁) :

                S锁:当一个事务对记录加了S锁后,另一个事务也可以加S锁,但是不能加X锁

                X锁:当一个事务对记录加了X锁后,其他事务不能再加X锁,也不能再加S锁

            间隙锁:就是在用于可重复读隔离级别,目的是就解决可重复读隔离级别下幻读的情况

                       间隙锁是可以兼容的,即两个事务可以拥有共同的间隙范围的间隙锁。并不存在互斥关系。

             临建锁:是记录锁+间隙锁  锁住一个范围,并锁住记录本身。

2.  MySQL是怎么加锁的

      唯一索引等值查询:

              当查询的记录是存在的,在索引树上定位到这一条记录之后,将记录的索引的临建锁退化成记录锁,仅靠记录所就可以避免幻读现象。X型的意向锁和独占锁。

             当查询的记录不存在时,在索引书上找到第一条大于该查询记录的记录后,将该记录的索引的临建锁退化成间隙锁。

     非唯一索引等值查询:

  • 当查询的记录「存在」时,由于不是唯一索引,所以肯定存在索引值相同的记录,于是非唯一索引等值查询的过程是一个扫描的过程,直到扫描到第一个不符合条件的二级索引记录就停止扫描,然后在扫描的过程中,对扫描到的二级索引记录加的是 next-key 锁,而对于第一个不符合条件的二级索引记录,该二级索引的 next-key 锁会退化成间隙锁。同时,在符合查询条件的记录的主键索引上加记录锁
  • 当查询的记录「不存在」时,扫描到第一条不符合条件的二级索引记录,该二级索引的 next-key 锁会退化成间隙锁。因为不存在满足查询条件的记录,所以不会对主键索引加锁

     

非唯一索引和主键索引的范围查询的加锁规则不同之处在于:

  • 唯一索引在满足一些条件的时候,索引的 next-key lock 退化为间隙锁或者记录锁。
  • 非唯一索引范围查询,索引的 next-key lock 不会退化为间隙锁和记录锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742496.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL存储函数和存储过程习题

创建表并插入数据 字段名 数据类型 主键 外键 非空 唯一 自增id INT 是 否 是 是 否name VARCHAR(50) 否 否 是 否 否glass VARCHAR(50) 否 否 是 否 否sch 表内容id name glass1 xiaommg glass 12 xiaojun glass 21、创建一个可以统计…

零代码编程:用ChatGPT批量识别图片PDF中的文字

有些PDF页面是图片格式,要怎么批量把图片中的文字识别出来?借助ChatGPT可以轻松完成这个任务。 首先要安装一些相关的软件和Python库。 安装tesseract-ocr(OCR)软件,最新版的是tesseract-ocr-w64-setup-v5.3.0.20221…

BP神经网络数据分类——语音特征信号分类(Matlab代码实现)

目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨‍💻4 Matlab代码 💥1 概述 BP神经网络是一种常见的人工神经网络,用于数据分类和回归等任务。在语音特征信号分类中,BP神经网络可…

ANSI转义码sehll演示脚本

ANSI转义码是我在修改文字颜色的时候了解的,以下是我翻译的一个shell小程序的画面 原文在此 他使用c写的,我调整了一下,用shell改写了一个 这是上传的脚本文件,设置的是免费的,可以下载看看玩玩 (3条消息) 【免费】A…

Android Java代码与JNI交互基础数据类型转换(三)

🔥 Android Studio 版本 🔥 🔥 基础类型数据的转换 🔥 定义传递基础数据类型到JNI的函数 package com.cmake.ndk1.jni;public class JNIBasicType{static{System.loadLibrary("native-lib");}public native int callNativeInt(int num);public native byte …

javascript-正则表达式匹配出URL地址,批量添加a标签

一开始接到这个需求觉得简单,结果越搞越复杂,反复了很多次,没有特别好的解决方案 最近接到一个需求,客服输入框需要将发送出去的消息中含有url地址匹配出来添加上a标签,但是由于输入框是富文本,所以输入框内…

优秀的 RocketMQ 可视化管理工具 GUI 客户端

优秀的 RocketMQ 可视化管理工具 GUI 客户端 官网地址:http://www.redisant.cn/rocketmq 快速查看所有 RocketMQ 集群,包括Brokers、Topics和Consumers查看消费者订阅了哪些主题,以及消息队列被分配给了哪些消费者;当出现消息积…

插入排序和希尔排序:用C语言打造高效的排序算法

插入排序 插入排序的思路就像是你在整理一堆扑克牌。你先拿起第一张牌,然后拿起第二张牌,把它插入到合适的位置,使得你手上的两张牌是有序的。接着,你再拿起第三张牌,也把它插入到合适的位置,使得你手上的…

安装pycharm安装激活教程,不想只使用一个月,也不想花钱?

前言 本文方法可以安装使用截止当前2023.1.3最新版本Pycharm,过程非常简单,按照下面的步骤来一分钟即可搞定。 1.下载安装 已经安装过的可以跳过该步骤! 下载 到官网地址下载正版安装包JetBrains Pycharm官网下载地址 安装 开始安装 选择安…

vertical-align详细介绍

1.定义: vertical-align是一个CSS属性,用于指定行内元素或表格单元格中内容的垂直对齐方式。它可以应用于行内元素、表格单元格或表格单元格中的内容。 该属性可以接受多个值,包括关键字(如top、middle、bottom),长度…

Autosar诊断实战系列11-UDS 0x27加解密服务配置开发介绍

本文框架 前言1. DcmDsd 0x27服务添加1.1 DcmDsdSubServices中添加子服务2. DcmDsp中DcmDspSecurity实战配置2.1 DcmDspSecurity实战配置2.2 尝试次数及DelayTime进一步理解前言 在本系列笔者将结合工作中对诊断实战部分的应用经验进一步介绍常用UDS服务的进一步探讨及开发中注…

Python3+requests搭建接口自动化测试框架

目录 一、接口自动化的意义(为什么做这个框架) 二、准备工作 三、框架流程及逻辑 四、各模块介绍 五、具体使用 一、接口自动化的意义(为什么做这个框架) 新版本上线时之前版本的功能需要进行回归测试,导致大量的…

对文件中的数据进行排序

#include<stdio.h> #include<stdlib.h> #include<time.h> int main()//生成1000个随机数据 {srand((size_t)time(NULL));FILE* fpfopen("d:/data.txt","w");if(!fp) return -1;for(int i0;i<1000;i)fprintf(fp,"%d\n",ran…

【Git】保姆级教程:如何在 GitHub 上传大文件(≥100M)?(含自己的操作流程)

文章目录 一、问题导读二、自己的实际操作流程2.1 准备工作2.2 初始化仓库2.3 安装git lfs&#xff08;一个仓库里面执行一次就好了&#xff09;2.4 跟踪一下你要上传&#xff08;push&#xff09;的文件或指定文件类型2.5 添加.gitattributes2.6 添加要上传&#xff08;push&a…

Jmeter上传文件接口测试

Jmeter上传文件接口测试 接口测试&#xff0c;想必大家都做过&#xff0c;但是上传文件的接口&#xff0c;可能就不知所措。其实呢&#xff0c;还是那么回事~ 一、接口的业务 在接口文档缺失的前提下&#xff0c;那就从抓包玩起~Fiddler或者F12都可以。 本次我们接口实现的…

springboot+mysql实现特产销售平台

本次设计任务是要设计一个藏区特产销售平台&#xff0c;通过这个系统能够满足藏区特产销售管理的管理功能。系统的主要包括首页、个人中心、用户管理、特产信息管理、特产分类管理、特产分类管理、特产评分管理、系统管理、订单管理等功能。 管理员可以根据系统给定的账号进行登…

Graphics笔记

1.简介 Graphics是System.Drawing里面的绘图类。 2.开始绘制 2.1.创建绘图对象&#xff0c;三种方法 在窗体或控件的Paint事件中&#xff0c;用PaintEventArgs创创建绘图对象(控件或窗体重绘时&#xff09; /// <summary>/// 1.在窗体或控件的Paint事件中&#xff0c…

以数字孪生技术服务社会,推动企业高质量发展

随着“十四五”规划和2035年远景目标纲要中提出&#xff0c;要加快数字化发展&#xff0c;建设数字中国。为建设数字孪生智慧城市提供了国家战略指引。 新兴技术是数字孪生发展的助推剂&#xff0c;臻图信息通过数字孪生技术及研发的实景三维渲染引擎&#xff0c;搭载自主研发的…

IP地址【图解TCP/IP(笔记十)】

文章目录 IP地址的基础知识IP地址的定义IP地址由网络和主机两部分标识组成IP地址的分类广播地址IP多播子网掩码 IPv4首部 IP地址的基础知识 在用TCP/IP通信时&#xff0c;用IP地址识别主机和路由器。为了保证正常通信&#xff0c;有必要为每个设备配置正确的IP地址。在互联网通…

使用Gradio库进行交互式数据可视化:Timeseries模块介绍

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…