MySQL
数据库基础知识
数据库 : 数据库(DataBase 简称 DB)就是信息的集合或者说数据库是由数据库管理系统管理的数据的集合。
数据库管理系统 : 数据库管理系统(Database Management System 简称 DBMS)是一种操纵和管理数据库的大型软件,通常用于建立、使用和维护数据库。
数据库系统 : 数据库系统(Data Base System,简称 DBS)通常由软件、数据库和数据管理员(DBA)组成。
数据库管理员 : 数据库管理员(Database Administrator, 简称 DBA)负责全面管理和控制数据库系统。
数据库基本概念
元组:元组是关系数据库中的基本概念,关系是一张表,表中的每行(即数据库中的每条记录)就是一个元组,每一列就是一个属性。在二维表里,元组也称为行。
主码:也称为主键。主码是从候选码中选出来的。一个实体集中只能有一个主码,但是可以有多个候选码。不能重复,不允许有空。一个表只能有一个主键。
候选码 : 若关系中的某一属性或属性组的值能唯一的标识一个元组,而其任何、子集都不能再标识,则称该属性组为候选码。例如:在学生实体中,“学号”是能唯一的区分学生实体的,同时又假设“姓名”、“班级”的属性组合足以区分学生实体,那么{学号}和{姓名,班级}都是候选码。
外码:也称为外键。如果一个关系中的一个属性是另外一个关系中的主码则这个属性为外码。用来和其它表建立联系用,外键是另一表的主键,外键是可以有重复的,可以有空值。一个表可以有多个外键。
ER图
E-R 图 也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。 它是描述现实世界关系概念模型的有效方法。 是表示概念关系模型的一种方式。
数据库范式
第一范式 1NF
属性(对应于表中的字段)不能再被分割,也就是这个字段只能是一个值,不能再分为多个其他的字段了。1NF 是所有关系型数据库的最基本要求 ,也就是说关系型数据库中创建的表一定满足第一范式。
2 NF
2NF 在 1NF 的基础之上,消除了非主属性对于码的部分函数依赖。如下图所示,展示了第一范式到第二范式的过渡。第二范式在第一范式的基础上增加了一个列,这个列称为主键,非主属性都依赖于主键。
- 函数依赖(functional dependency) :若在一张表中,在属性(或属性组)X 的值确定的情况下,必定能确定属性 Y 的值,那么就可以说 Y 函数依赖于 X,写作 X → Y。
- 部分函数依赖(partial functional dependency) :如果 X→Y,并且存在 X 的一个真子集 X0,使得 X0→Y,则称 Y 对 X 部分函数依赖。比如学生基本信息表 R 中(学号,身份证号,姓名)当然学号属性取值是唯一的,在 R 关系中,(学号,身份证号)->(姓名),(学号)->(姓名),(身份证号)->(姓名);所以姓名部分函数依赖与(学号,身份证号); 也就是说学生姓名通过学号和身份证号都可以查到唯一的姓名,不唯一了,所以姓名部分函数依赖于学号和身份证号。
- 完全函数依赖(Full functional dependency) :在一个关系中,若某个非主属性数据项依赖于全部关键字称之为完全函数依赖。比如学生基本信息表 R(学号,班级,姓名)假设不同的班级学号有相同的,班级内学号不能相同,在 R 关系中,(学号,班级)->(姓名),但是(学号)->(姓名)不成立,(班级)->(姓名)不成立,所以姓名完全函数依赖与(学号,班级); 部分函数依赖是姓名通过学号,身份证号都能单独查出,而完全依赖就是姓名的查询要依赖于学号和身份证号一起才能够查出。
- 传递函数依赖 : 在关系模式 R(U)中,设 X,Y,Z 是 U 的不同的属性子集,如果 X 确定 Y、Y 确定 Z,且有 X 不包含 Y,Y 不确定 X,(X∪Y)∩Z=空集合,则称 Z 传递函数依赖(transitive functional dependency) 于 X。传递函数依赖会导致数据冗余和异常。传递函数依赖的 Y 和 Z 子集往往同属于某一个事物,因此可将其合并放到一个表中。比如在关系 R(学号 , 姓名, 系名,系主任)中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖。
3 NF
3NF 在 2NF 的基础之上,消除了非主属性对于码的传递函数依赖 。符合 3NF 要求的数据库设计,基本上解决了数据冗余过大,插入异常,修改异常,删除异常的问题。比如在关系 R(学号 , 姓名, 系名,系主任)中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖,所以该表的设计,不符合 3NF 的要求。
MySQL代码
数据库操作
/* 数据库操作 */ ------------------ -- 查看当前数据库 SELECT DATABASE(); -- 显示当前时间、用户名、数据库版本 SELECT now(), user(), version(); -- 创建库 CREATE DATABASE[ IF NOT EXISTS] 数据库名 数据库选项 数据库选项: CHARACTER SET charset_name COLLATE collation_name -- 查看已有库 SHOW DATABASES[ LIKE 'PATTERN'] -- 查看当前库信息 SHOW CREATE DATABASE 数据库名 -- 修改库的选项信息 ALTER DATABASE 库名 选项信息 -- 删除库 DROP DATABASE[ IF EXISTS] 数据库名 同时删除该数据库相关的目录及其目录内容
表的操作
-- 创建表 CREATE [TEMPORARY] TABLE[ IF NOT EXISTS] [库名.]表名 ( 表的结构定义 )[ 表选项] 每个字段必须有数据类型 最后一个字段后不能有逗号 TEMPORARY 临时表,会话结束时表自动消失 对于字段的定义: 字段名 数据类型 [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY] [COMMENT 'string']-- 查看所有表 SHOW TABLES[ LIKE 'pattern'] SHOW TABLES FROM 库名 -- 查看表结构 SHOW CREATE TABLE 表名 (信息更详细) DESC 表名 / DESCRIBE 表名 / EXPLAIN 表名 / SHOW COLUMNS FROM 表名 [LIKE 'PATTERN'] SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern'] -- 修改表 -- 修改表本身的选项 ALTER TABLE 表名 表的选项 eg: ALTER TABLE 表名 ENGINE=MYISAM; -- 对表进行重命名 RENAME TABLE 原表名 TO 新表名 RENAME TABLE 原表名 TO 库名.表名 (可将表移动到另一个数据库) -- RENAME可以交换两个表名 -- 修改表的字段机构(13.1.2. ALTER TABLE语法) ALTER TABLE 表名 操作名 -- 操作名 ADD[ COLUMN] 字段定义 -- 增加字段 AFTER 字段名 -- 表示增加在该字段名后面 FIRST -- 表示增加在第一个 ADD PRIMARY KEY(字段名) -- 创建主键 ADD UNIQUE [索引名] (字段名)-- 创建唯一索引 ADD INDEX [索引名] (字段名) -- 创建普通索引 DROP[ COLUMN] 字段名 -- 删除字段 MODIFY[ COLUMN] 字段名 字段属性 -- 支持对字段属性进行修改,不能修改字段名(所有原有属性也需写上) CHANGE[ COLUMN] 原字段名 新字段名 字段属性 -- 支持对字段名修改 DROP PRIMARY KEY -- 删除主键(删除主键前需删除其AUTO_INCREMENT属性) DROP INDEX 索引名 -- 删除索引 DROP FOREIGN KEY 外键 -- 删除外键 -- 删除表 DROP TABLE[ IF EXISTS] 表名 ... -- 清空表数据 TRUNCATE [TABLE] 表名 -- 复制表结构 CREATE TABLE 表名 LIKE 要复制的表名 -- 复制表结构和数据 CREATE TABLE 表名 [AS] SELECT * FROM 要复制的表名 -- 检查表是否有错误 CHECK TABLE tbl_name [, tbl_name] ... [option] ... -- 优化表 OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... -- 修复表 REPAIR [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... [QUICK] [EXTENDED] [USE_FRM] -- 分析表 ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
数据操作
/* 数据操作 */ ------------------ -- 增 INSERT [INTO] 表名 [(字段列表)] VALUES (值列表)[, (值列表), ...] -- 如果要插入的值列表包含所有字段并且顺序一致,则可以省略字段列表。 -- 可同时插入多条数据记录! REPLACE与INSERT类似,唯一的区别是对于匹配的行,现有行(与主键/唯一键比较)的数据会被替换,如果没有现有行,则插入新行。 INSERT [INTO] 表名 SET 字段名=值[, 字段名=值, ...] -- 查 SELECT 字段列表 FROM 表名[ 其他子句] -- 可来自多个表的多个字段 -- 其他子句可以不使用 -- 字段列表可以用*代替,表示所有字段 -- 删 DELETE FROM 表名[ 删除条件子句] 没有条件子句,则会删除全部 -- 改 UPDATE 表名 SET 字段名=新值[, 字段名=新值] [更新条件]
列属性
/* 列属性(列约束) */ ------------------ 1. PRIMARY 主键 - 能唯一标识记录的字段,可以作为主键。 - 一个表只能有一个主键。 - 主键具有唯一性。 - 声明字段时,用 primary key 标识。 也可以在字段列表之后声明 例:create table tab ( id int, stu varchar(10), primary key (id)); - 主键字段的值不能为null。 - 主键可以由多个字段共同组成。此时需要在字段列表后声明的方法。 例:create table tab ( id int, stu varchar(10), age int, primary key (stu, age)); 2. UNIQUE 唯一索引(唯一约束) 使得某字段的值也不能重复。 3. NULL 约束 null不是数据类型,是列的一个属性。 表示当前列是否可以为null,表示什么都没有。 null, 允许为空。默认。 not null, 不允许为空。 insert into tab values (null, 'val'); -- 此时表示将第一个字段的值设为null, 取决于该字段是否允许为null 4. DEFAULT 默认值属性 当前字段的默认值。 insert into tab values (default, 'val'); -- 此时表示强制使用默认值。 create table tab ( add_time timestamp default current_timestamp ); -- 表示将当前时间的时间戳设为默认值。 current_date, current_time 5. AUTO_INCREMENT 自动增长约束 自动增长必须为索引(主键或unique) 只能存在一个字段为自动增长。 默认为1开始自动增长。可以通过表属性 auto_increment = x进行设置,或 alter table tbl auto_increment = x; 6. COMMENT 注释 例:create table tab ( id int ) comment '注释内容'; 7. FOREIGN KEY 外键约束 用于限制主表与从表数据完整性。 alter table t1 add constraint `t1_t2_fk` foreign key (t1_id) references t2(id); -- 将表t1的t1_id外键关联到表t2的id字段。 -- 每个外键都有一个名字,可以通过 constraint 指定 存在外键的表,称之为从表(子表),外键指向的表,称之为主表(父表)。 作用:保持数据一致性,完整性,主要目的是控制存储在外键表(从表)中的数据。 MySQL中,可以对InnoDB引擎使用外键约束: 语法: foreign key (外键字段) references 主表名 (关联字段) [主表记录删除时的动作] [主表记录更新时的动作] 此时需要检测一个从表的外键需要约束为主表的已存在的值。外键在没有关联的情况下,可以设置为null.前提是该外键列,没有not null。 可以不指定主表记录更改或更新时的动作,那么此时主表的操作被拒绝。 如果指定了 on update 或 on delete:在删除或更新时,有如下几个操作可以选择: 1. cascade,级联操作。主表数据被更新(主键值更新),从表也被更新(外键值更新)。主表记录被删除,从表相关记录也被删除。 2. set null,设置为null。主表数据被更新(主键值更新),从表的外键被设置为null。主表记录被删除,从表相关记录外键被设置成null。但注意,要求该外键列,没有not null属性约束。 3. restrict,拒绝父表删除和更新。 注意,外键只被InnoDB存储引擎所支持。其他引擎是不支持的。
MySQL面试题
关系型数据库介绍
关系型数据库就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多)。
关系型数据库中,我们的数据都被存放在了各种表中(比如用户表),表中的每一行就存放着一条数据(比如一个用户的信息)。
MySQL是一种关系型数据库,主要用于持久化存储我们系统中的一些数据。
MySQL存储引擎
MySQL 当前默认的存储引擎是 InnoDB。并且,所有的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。
MySQL存储引擎采用的是插件式架构,支持多种存储引擎。存储引擎是基于表的,而不是数据库。
而且,还可以根据MySQL定义的存储引擎实现标准接口来编写一个属于自己的存储引擎。
MySQL事务
事务是逻辑上的一组操作,要么都执行,要么都不执行。
- 原子性(
Atomicity
) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用; - 一致性(
Consistency
): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的; - 隔离性(
Isolation
): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的; - 持久性(
Durabilily
): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响
并发事务带来的问题
- 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。 读到了修改中还未提交的数据。
- 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 修改 A=A-1,事务 2 也修改 A=A-1,最终结果 A=19,事务 1 的修改被丢失。 同一个数据被修改了两次,第一次修改的内容丢失。
- 不可重复读(Unrepeatable read): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。 第一个事务还未完成,第二个事务修改数据,第一个事务前后读取到的内容不一致(同一个数据进行了修改)。
- 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。 第一个事务还未完成,第二个事务修改数据,第一个事务前后读取到的数据量不一致。(同一个表进行了增删)
SQL标准定义的事务隔离级别
- READ-UNCOMMITTED(读取未提交) : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
- READ-COMMITTED(读取已提交) : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- REPEATABLE-READ(可重复读) : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- SERIALIZABLE(可串行化) : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
MySQL 的隔离级别基于锁和 MVCC 机制共同实现的。
SERIALIZABLE 隔离级别,是通过锁来实现的。除了 SERIALIZABLE 隔离级别,其他的隔离级别都是基于 MVCC 实现。
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。
MySQL锁
- 表级锁: MySQL 中锁定粒度最大的一种锁,是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
- 行级锁: MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁,只针对当前操作的记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字段加的锁。
表级锁和行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类:
- 共享锁(S 锁) :又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
- 排他锁(X 锁) :又称写锁/独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁(锁不兼容)。
如果需要用到表锁的话,如何判断表中的记录没有行锁呢,一行一行遍历肯定是不行,性能太差。我们需要用到一个叫做意向锁的东东来快速判断是否可以对某个表使用表锁。
意向锁用来快速判断是否可以对某个表使用表锁。
- 意向共享锁(Intention Shared Lock,IS 锁):事务有意向对表中的某些加共享锁(S 锁),加共享锁前必须先取得该表的 IS 锁。
- 意向排他锁(Intention Exclusive Lock,IX 锁):事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的 IX 锁。
意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。
char、varchar的区别是什么
平时使用char类型定义字段时,往往会指定其长度M,即char(M)。其实M指的是字符数,即这个字段最多存储多少个字符,M可不指定,默认为1,范围是[0,255],单个字母、数字、中文等是占用一个字符。utf8字符集下一个中文字符占3个字节。
VARCHAR用于存储可变字符串,是最常见的字符串数据类型。它比char更节省空间,因为它仅使用必要的空间。varchar需要1或2个额外字节记录字符串长度,如果列的最大长度不大于255字节则需要1个字节。
char是定长的,根据定义的字符串长度分配足够的空间。CHAR会删除末尾空格。假如申请了char(10)的空间,无论实际存储多少内容。该字段都占用了10个字符,而varchar是变长的,也就是说申请的只是最大长度,占用的空间为实际字符长度+1。
char适合存储很短的字符串,或所有值都接近同一个长度。对于经常变更的数据,char比varchar更好,因为定长的char来存储只有一个字节,但是varchar需要两个字节,因为还有一个记录长度的额外字节。
在检索效率上来讲,char>varchar,因此在使用中,如果确定某个字段的值的长度,可以使用char,否则尽量使用varchar。
DECIMAL 和 FLOAT/DOUBLE 的区别是什么?
DECIMAL 和 FLOAT 的区别是:DECIMAL 是定点数,FLOAT/DOUBLE 是浮点数。DECIMAL 可以存储精确的小数值,FLOAT/DOUBLE 只能存储近似的小数值。
DECIMAL 用于存储具有精度要求的小数,例如与货币相关的数据,可以避免浮点数带来的精度损失。
在 Java 中,MySQL 的 DECIMAL 类型对应的是 Java 类 java.math.BigDecimal
。
DATETIME 和 TIMESTAMP 的区别是什么?
DATETIME 类型没有时区信息,TIMESTAMP 和时区有关。
TIMESTAMP 只需要使用 4 个字节的存储空间,但是 DATETIME 需要耗费 8 个字节的存储空间。但是,这样同样造成了一个问题,Timestamp 表示的时间范围更小。
- DATETIME:1000-01-01 00:00:00 ~ 9999-12-31 23:59:59
- Timestamp:1970-01-01 00:00:01 ~ 2037-12-31 23:59:59NULL 和 '' 的区别是什么?
NULL 和 '' 的区别是什么?
NULL
跟 ''
(空字符串)是两个完全不一样的值,区别如下:
NULL
代表一个不确定的值,就算是两个NULL
,它俩也不一定相等。例如,SELECT NULL=NULL
的结果为 false,但是在我们使用DISTINCT
,GROUP BY
,ORDER BY
时,NULL
又被认为是相等的。''
的长度是 0,是不占用空间的,而NULL
是需要占用空间的。NULL
会影响聚合函数的结果。例如,SUM
、AVG
、MIN
、MAX
等聚合函数会忽略NULL
值。COUNT
的处理方式取决于参数的类型。如果参数是*
(COUNT(*)
),则会统计所有的记录数,包括NULL
值;如果参数是某个字段名(COUNT(列名)
),则会忽略NULL
值,只统计非空值的个数。- 查询
NULL
值时,必须使用IS NULL
或IS NOT NULLl
来判断,而不能使用 =、!=、 <、> 之类的比较运算符。而''
是可以使用这些比较运算符的。
什么是关系型数据库?
关系型数据库,是指采用了关系模型来组织数据的数据库,其以行和列的形式存储数据,以便于用户理解,关系型数据库这一系列的行和表被称为表,一组表组成了数据库。用户通过查询来检索数据库中的数据,而查询是一个用于限定数据库中某些区域的执行代码。
什么是SQL:
结构化查询语言(Structured Query Language)简称SQL。是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。
什么是MySQL:
MySQL是一个关系型数据库管理系统,MySQL是最流行的关系型数据管理系统之一,常见的关系型数据库还有Oracle、SQL server、Access等等。
超键、候选键、主键、外键分别是什么?
超键:在关系能唯一标识元组属性集称为关系模式的超键。一个属性可以作为一个超键,多个属性组合在一起也可以作为超键。超键包含候选键和主键。
候选键:是最小超键,即没有冗余元素的超键。
主键:数据库表中对存储数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。
外键:在一个表中存在的另一个表的主键称次表的外键。
超键:
在关系中能唯一标识元组的属性集称为关系模式的超键。于是我们从例子中可以发现 学号是标识学生实体的唯一标识。那么该元组的超键就为学号。
除此之外我们还可以把它跟其他属性组合起来,比如:(学号,性别)(学号,年龄),这样也是超键.
候选键:
不含多余属性的超键为候选键。根据例子可知,学号是一个可以唯一标识元组的唯一标识,因此学号是一个候选键,实际上,候选键是超键的子集,比如 (学号,年龄)是超键,但是它不是候选键。因为它还有了额外的属性。
主键:
用户选择的候选键作为该元组的唯一标识,那么它就为主键。简单的说,例子中的元组的候选键为学号,但是我们选定他作为该元组的唯一标识,那么学号就为主键。
外键:
外键是相对于主键的,比如在学生记录里,主键为学号,在成绩单表中也有学号字段,因此学号为成绩单表的外键,为学生表的主键。所以,主键为候选键的子集,候选键为超键的子集,而外键的确定是相对于主键的。
SQL约束有哪几种
NOT NULL:用于控制字段内容一定不能为空。
UNIQUE:控件字段内容不能重复,一个表允许有多个unique约束。
PRIMARY KEY:也是用于控件字段内容不能重复,但它一个表只允许出现一个。
FOREIGN KEY:用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表的值之一。
MySQL的四种隔离级别
未提交读(READ UNCOMMITTED):
这个隔离级别下,其他事务可以看到本事务没有提交的部分修改,因此会造成脏读的问题(读取到了其他事务未提交的部分,而之后该事务进行了回滚)。
这个级别的性能没有足够大的优势,而且又有很多的问题,因此很少使用。
已提交读(READ COMMITTED):
其他事务只能读取到本事务已经提交的部分。这个隔离级别有不可重复读问题,在同一个事务内的两次读取,拿到的结果不一样,因为另外一个事务对数据进行了修改。
可重复读(REPEATABLE READ):
可重复读隔离级别解决了上面不可重复读的问题(看名字也知道),但是仍然有一个新问题,就是 幻读,当你读取 id> 10 的数据行时,对涉及到的所有行加上了读锁,此时例外一个事务新插入了一条 id=11 的数据,因为是新插入的,所以不会触发上面的锁的排斥,那么进行本事务进行下一次的查询时会发现有一条id=11的数据,而上次的查询操作并没有获取到,再进行插入就会有主键冲突的问题。
可串行化(SERIALIZABLE):
这是最高的隔离级别,可以解决上面提到的所有问题,因为它强制所有的操作串行执行,这会导致并发性能极速下降,因此也不是很常用。
WHERE和HAVING有什么区别?
WHERE是一个约束声明,使用WHERE约束来自数据库的数据,WHERE是在结果返回之前起作用的,WHERE中不能使用聚合函数。
HAVING是一个过滤声明,是在查询返回结果集以后对查询结果进行的过滤操作,在HAVING中可以使用聚合函数。另一方面,HAVING子句中不能使用除了分组字段和聚合函数之外的其他字段。
从性能上来讲,HAVING子句中如果使用分组字段作为过滤条件,应该替换成WHERE子句。因为WHERE可以执行分组操作和计算函数聚合数之前过滤掉不需要的数据,性能会更好。
count(*)和count(1)和count(列名)的区别
执行效果上:
(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为null;
(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为null;
(1)和(*)基本没有差别;
(列名)只包括列名那一列,在统计结果的时候会忽略空值(这里的空值不是指空字符串,0,是指null)的计算,即某个字段为NULL时,不统计。
IN和EXIST的区别
exist适合 子查询中表数据大于外查询表中数据的业务场景
in:适合外部表数据大于子查询的表数据的业务场景
in 和 exists的区别: 如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in, 反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists。其实我们区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询,所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了 ,另外IN时不对NULL进行处理。
in 是把外表和内表作hash 连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询。一直以来认为exists比in效率高的说法是不准确的
drop、delete与truncate区别
用法不同
- drop(丢弃数据):
drop table 表名
,直接将表都删除掉,在删除表的时候使用。 - truncate (清空数据) :
truncate table 表名
,只删除表中的数据,再插入数据的时候自增长 id 又从 1 开始,在清空表中数据的时候使用。 - delete(删除数据) :
delete from 表名 where 列名=值
,删除某一行的数据,如果不加 where 子句和truncate table 表名
作用类似。
truncate 和不带 where 子句的 delete、以及 drop 都会删除表内的数据,但是 truncate 和 delete 只删除数据不删除表的结构(定义),执行 drop 语句,此表的结构也会删除,也就是执行 drop 之后对应的表不复存在。
相同点:truncate和不带where子句的delete,以及drop都删除表内的数据。
不同点:
-
truncate会清除表数据并重置id从1开始,delete就只删除记录,drop可以用来删除表或数据库并且将表所占用的空间全部释放
-
truncate和delete只删除数据不删除表的结构。drop语句将删除表的结构被依赖的约(constrain),触发器(trigger),依赖于该表的存储过程/函数将保留,但是变为 invalid 状态。
-
速度上一般来说: drop> truncate > delete
-
使用上,想删除部分数据行用 delete,想删除表用 drop,想保留表而将所有数据删除,如果和事务无关,用truncate即可。如果和事务有关,或者想触发trigger,还是用delete。
-
delete是DML语句,不会自动提交。drop/truncate都是DDL语句,执行后会自动提交。
属于不同的数据库语言
truncate 和 drop 属于 DDL(数据定义语言)语句,操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发 trigger。而 delete 语句是 DML (数据库操作语言)语句,这个操作会放到 rollback segement 中,事务提交之后才生效。
DML 语句和 DDL 语句区别:
- DML 是数据库操作语言(Data Manipulation Language)的缩写,是指对数据库中表记录的操作,主要包括表记录的插入(insert)、更新(update)、删除(delete)和查询(select),是开发人员日常使用最频繁的操作。
- DDL (Data Definition Language)是数据定义语言的缩写,简单来说,就是对数据库内部的对象进行创建、删除、修改的操作语言。它和 DML 语言的最大区别是 DML 只是对表内部数据的操作,而不涉及到表的定义、结构的修改,更不会涉及到其他对象。DDL 语句更多的被数据库管理员(DBA)所使用,一般的开发人员很少使用。
什么是子查询
条件:一条SQL语句的查询结果作为另一条查询语句的条件或查询结果。
嵌套:多条SQL语句嵌套使用,内部的SQL查询语句称为子查询。
连接分为内连接、左连接、右连接
连接可以分为等值连接和非等值连接;内连接和外连接,外连接又可分为左外连接,右外连接,完全外连接。
内连接是根据某个条件连接两个表共有的数据;
左连接是根据某个条件已经左边的表连接数据,右边的表没有数据的话则填null;
右连接是根据某个条件以及右边的表连接数据,左边的表没有数据的话则填null;
内连接:
内连接使用关键字Inner join on,关联条件a.a_id=b.b_id
select * from a_table a inner join b_table b on a.a_id = b.b_id;
这就说明了,内连接是组合两个表中的记录,返回关联字段相符的记录,也就是返回两个表的交集(阴影)部分。
左连接:
左连接使用的是 left join on,关联条件和上方一样 a.a_Id=b.b_id。
select * from a_table a left join b_table b on a.a_id = b.b_id;
left join是left outer join的简写,它的全称是左外连接,是外连接中的一种。
左(外)连接,左表的记录会全部表示出来,而右表只会显示符合搜索条件的记录。右表记录不足的地方均为NULL。
右连接:
select * from a_table a right outer join b_table b on a.a_id = b.b_id;
关系型数据库具有哪些特点?
1.数据集中控制。在文件管理方法中,文件是分散的,每个用户或每种处理都有各自的文件,这些文件之间一般是没有联系的,因此,不能按照统一的方法来控制、维护和管理。而数据库则很好地克服了这一缺点,可以集中控制、维护和管理有关数据。
2.数据独立性高。数据库中的数据独立于应用程序,包括数据的物理独立性和逻辑独立性,给数据库的使用、调整、优化和进一步扩充提供了方便,提高了数据库应用系统的稳定性。
3.数据共享性好。数据库中的数据可以供多个用户使用,每个用户只与库中的一部分数据发生联系,用户数据可重叠,用户可以同时存取数据而互不影响,大大提高了数据库的使用效率。
4.数据冗余度小。数据库中的数据不是面向应用,而是面向系统。数据统一定义、组织和存储,集中管理,避免了不必要的数据冗余,也提高了数据的一致性。
5.数据结构化,整个数据库按一定的结构形式构成,数据在记录内部和记录类型之间相互关联,用户可通过不同的路径存取数据。
6.统一的数据保护功能,在多用户共享数据资源的情况下,对用户使用数据有严格的检查,对数据库规定密码或存取权限,拒接非法用户进入数据库,以确保数据的安全性、一致性和并发控制。
什么是存储过程以及如何调用
存储过程和视图一样,也是对SQL代码进行了封装,可以反复利用。优点同样是清晰且安全,同时还可以减少网络传输量。
和视图不一样的点在于,视图是虚拟表,不会对底层数据表直接操作,而存储过程时程序化的SQL,可以直接操作底层数据表,能够实现一些更加复杂的处理。
简单的说,存储过程是SQL语句和控制语句构成的语句结合,也可以将其理解为函数,可以接收参数,也可以返回输出参数给调用者。
CREATE PROCEDURE `add_num`(IN N INT) BEGIN DECLARE i INT;--- 使用DECLARE 声明变量 DECLARE sum INT; SET i = 1;--- 使用 SET 赋值语句 SET sum = 0; while i<=n do SET sum = sum + i; SET i = i+1; END WHILE; SELECT sum; END
在工作都会怎么样使用存储过程,优点是什么?
一个非常明显的优点即存储过程可以一次编译多次使用,存储过程只是在创造的时候进行编译,之后的使用就不再需要编译,这直接提升了SQL的执行效率。
另外,将代码封装成模块,这种模块化的思想可以减少很多重复的工作,减少开发工作量的同时,还能保障代码的结构清晰。
最后,存储过程的安全性较高,在设定存储过程的时候可以设置用户的使用权限。
缺点:
它的可移植性差,存储过程不能跨数据库移植,比如MySQL,Oracle和SQL Server里编写的存储过程,在换成其他数据库时都需要重新编写。其次调试困难,只有少数DBMS支持存储过程的调试。对于复杂的存储过程来说,开发和维护都不容易。此外,存储过程的版本管理也很困难,比如数据表索引发生变化了,可能导致存储过程失效。最后它不适合高并发的场景,高并发的场景需要减少数据库的压力,有时候数据库会采用分库分表的方式,而且对可扩展性要求很高,在这种情况下,存储过程会变得难以维护,增加数据库的压力,显然就不适用了。
存储过程和函数的区别
存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储和函数可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。
相同点:
(1)存储过程和函数都是为了可重复的执行操作数据库的SQL语句的集合。
(2)存储过程和函数都是一次编译后缓存起来,下次使用就直接命中已经编译好的sql语句,减少网络交互提高了效率。
不同点:
(1)标识符不同,函数的标识符是function,存储过程时procedure。
(2)函数返回单个值或者表对象,而存储过程没有返回值,但是可以通过OUT参数返回多个值
(3)函数限制比较多,比如不能使用临时表,只能使用表变量,一些函数都不可用等,而存储过程的限制相对就比较少;
(4)一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强;
(5)函数的参数只能是IN类型,存储过程的参数可以是IN OUT INOUT三种类型;
(6)存储函数使用select调用,存储过程需要使用call调用。
视图的作用
视图是虚拟的表,与包含数据的表不一样,视图只包含使用时动态检索数据的查询;不包含任何列或数据。使用视图可以简化复杂的sql操作,隐藏具体的细节,保护数据;视图创建后,可以使用与表相同的方式利用它们。
优点:
1.安全性。虚拟表是基于底层数据表的,我们在使用视图时,一般不会轻易通过视图对底层数据进行修改,即使是使用单表的视图,也会受到限制,比如计算字段,类型转换等是无法通过来对底层数据进行修改的,这也在一定程度上保障了数据表的安全性。同时,我们还可以针对不同用户开放不同的数据查询权限,比如人员薪酬是个敏感的字段,那么只给某个级别以上的人员开放,其他人的查询视图中则是不提供这个字段的。
2.简单清晰。视图是对 SQL 查询的封装,它可以将原本复杂的 SQL 查询简化,在编写好查询之后,我们就可以直接重用它而不必要知道基本的查询细节。同时我们还可以在视图之上再嵌套视图。这样就好比我们在进行模块化编程一样,不仅结构清晰,还提升了代码的复用率。
非关系型数据库和关系型数据库区别,优势比较?
(1)关系型数据库
优点:二维表格,容易理解。容易操作。易于维护。支持SQL。
缺点:读写性能比较差。固定的表结构,不够灵活。应对高并发场景,磁盘I/O存在瓶颈。海量数据读写性能差。
(2)非关系型数据库
优点:不需要SQL解析,读写性能高。可以使用内存或硬盘作为载体,速度快。基于键值对,数据没有耦合性,方便扩展。部署简单。
缺点:不支持SQL,增加了学习成本。没有事务。
触发器
什么是触发器:
触发器是MySQL提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表时间相关的特殊存储过程,它的执行不是有程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作(insert,delete,update)时就会激活它执行。简单理解为:你执行一条sql语句,这条sql语句的执行会自动去触发执行其他的sql语句。
作用:
1.可以写入数据表前,强制检验或转换数据;
2.触发器发生错误时,异动的结果会被撤销;
3.部分数据库管理系统可以针对数据定义语言(DDL)使用触发器,称为DDL触发器
4.可依照特定的情况,替换异动的指令(INSTEAD OF)
触发器四要素:
1.监视地点 table
2.监视事件 insert、update、delete
3.触发时间 after、before
4.触发事件 insert、update、delete
基本语法:
before/after: 触发器是在增删改之前执行,还是之后执行
delete/insert/update: 触发器由哪些行为触发(增、删、改)
on 表名: 触发器监视哪张表的(增、删、改)操作
触发SQL代码块: 执行触发器包含的SQL语句
注意: 触发器也是存储过程程序的一种,而触发器内部的执行SQL语句是可以多行操作的,所以在MySQL的存储过程程序中,要定义结束符
索引
优点:
1.索引可以减少服务器需要扫描的数据量,从而大大提高查询效率
2.通过使用索引,可以在查询过程中,使用隐藏优化器,提高系统的性能。
3.唯一索引能保证表中数据的唯一性。
缺点:
时间方面:创建索引和维护索引要耗费时间,具体地,当表中的数据进行增加、修改和删除时,索引也要动态的维护,会降低增/改/删/的执行效率
空间方面:索引需要占用物理空间。
不恰当的使用索引会造成服务器重复扫描数据,造成查询浪费。
哪些情况最好加索引
(1)字段的数值有唯一性的限制
索引本身可以起到约束的作用,比如唯一索引、主键索引、都是可以起到唯一性约束的,因此在我们的数据表中,如果某个字段是唯一性的,就可以直接创建唯一性索引,或主键索引。
(2)频繁作为WHERE查询条件的字段,尤其在数据表大的情况下
在数据量大的情况下,某个字段在SQL查询的WHERE经常被使用到;那么就需要给这个字段创建索引了。创建普通索引就可以大幅提升查询的效率。
(3)需要经常GROUP BY和ORDER BY的列
索引就是让数据按照某种顺序进行存储或检索,因此当我们使用GROUP BY对数据进行分组查询,或者使用ORDER BY对数据进行排序的时候,就需要对分组或排序的字段进行索引。
(4)UPDATE、DELETE和WHERE条件列,一般也需要创建索引
索引分类
- 主键索引:加速查询 + 列值唯一(不可以有 NULL)+ 表中只有一个。
- 普通索引:仅加速查询。
- 唯一索引:加速查询 + 列值唯一(可以有 NULL)。
- 覆盖索引:一个索引包含(或者说覆盖)所有需要查询的字段的值。
- 联合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并。
- 全文索引:对文本的内容进行分词,进行搜索。目前只有
CHAR
、VARCHAR
,TEXT
列上可以创建全文索引。一般不会使用,效率较低,通常使用搜索引擎如 ElasticSearch 代替。
聚集索引和非聚集索引
只要创建了索引,就一定会走索引吗?
不一定。
比如,在使用组合索引时,如果没有遵循从”最左前缀“的原则进行搜索,则索引是不起作用的。
说一说索引实现的原理?
在MySQL中,索引是在存储引擎层实现的,不同存储引擎对索引的实现方式是不同的,下面我们探索MyISAM和InnoDB两个存储引擎的实现方式。
MyISAM实现:
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址,MyISAM索引的原理图如下。这里假设我们以Coll为主键,则上图是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引在结构上没有任何区别,只是要求主索引key是唯一的,而辅助索引key可以重复。
InnoDB索引实现:
虽然InnoDB也使用B+Tree作为索引结构,但具体方式与MyISAM截然不同。
第一个重大的区别是InnoDB的数据文件本身就是索引文件。从上文知道MyISAM索引文件和数据文件是分离的,索引文件仅保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
下图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶结点包含了完整的数据记录。这中索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM没有要求),如果没有显示指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整型。
第二个与MyISAM索引不同的是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。
- InnoDB 支持行级别的锁粒度,MyISAM 不支持,只支持表级别的锁粒度。
- MyISAM 不提供事务支持。InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别。
- MyISAM 不支持外键,而 InnoDB 支持。
- MyISAM 不支持 MVCC,而 InnoDB 支持。
- 虽然 MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,但是两者的实现方式不太一样。
InnoDB 引擎中,其数据文件本身就是索引文件。相比 MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按 B+Tree 组织的一个索引结构,树的叶节点 data 域保存了完整的数据记录。
- MyISAM 不支持数据库异常崩溃后的安全恢复,而 InnoDB 支持。
- InnoDB 的性能比 MyISAM 更强大。
MySQL的索引为什么用B+树
B+树由B树和索引顺序访问方法演化而来的,它是为磁盘或其他直接存取辅助设备设计的一种平衡树,在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点,各叶子节点通过指针进行链接。如下图。
B+树索引在数据库的一个特点就是高扇出性,例如InnoDB存储引擎中,每页的大小为16KB。在数据库中,B+树的高度一般都在2-4层,这意味着某一键值最多只需要2到4次IO操作。因为现在一般的磁盘每秒至少可以做100次IO操作,2-4次的IO操作意味着查询时间只需要0.02-0.04秒。
MySQL的Hsah索引和B树索引有什么区别?
SQL注入
SQL注入(SQL Injection)是一种常见的Web安全漏洞,主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”(请求参数中包含SQL语句)拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数据库受损(被脱库、被删除、甚至整个服务器权限陷)。
即:注入产生的原因是后台服务器接收相关参数未经过滤直接带入数据库查询
解决方法:
MySQL事物回滚
读写分离
读写分离主要是为了将对数据库的读写操作分散到不同的数据库节点上。 这样的话,就能够小幅提升写性能,大幅提升读性能。
一般情况下,我们都会选择一主多从,也就是一台主数据库负责写,其他的从数据库负责读。主库和从库之间会进行数据同步,以保证从库中数据的准确性。这样的架构实现起来比较简单,并且也符合系统的写少读多的特点。
读写分离带来的问题:
主库和从库的数据存在延迟,比如你写完主库之后,主库的数据同步到从库是需要时间的,这个时间差就导致了主库和从库的数据不一致性问题。这也就是我们经常说的 主从同步延迟 。
如何实现读写分离:
- 部署多台数据库,选择其中的一台作为主数据库,其他的一台或者多台作为从数据库。
- 保证主数据库和从数据库之间的数据是实时同步的,这个过程也就是我们常说的主从复制。
- 系统将写请求交给主数据库处理,读请求交给从数据库处理。
分库分表
分库
分库 就是将数据库中的数据分散到不同的数据库上,可以垂直分库,也可以水平分库。
垂直分库 就是把单一数据库按照业务进行划分,不同的业务使用不同的数据库,进而将一个数据库的压力分担到多个数据库。
水平分库 是把同一个表按一定规则拆分到不同的数据库中,每个库可以位于不同的服务器上,这样就实现了水平扩展,解决了单表的存储和性能瓶颈的问题。
分表
分表 就是对单表的数据进行拆分,可以是垂直拆分,也可以是水平拆分。
垂直分表 是对数据表列的拆分,把一张列比较多的表拆分为多张表。
水平分表 是对数据表行的拆分,把一张行比较多的表拆分为多张表,可以解决单一表数据量过大的问题。
遇到下面几种场景可以考虑分库分表:
- 单表的数据达到千万级别以上,数据库读写速度比较缓慢。
- 数据库中的数据占用的空间越来越大,备份时间越来越长。
- 应用的并发量太大。
分库分表带来的问题
join 操作:同一个数据库中的表分布在了不同的数据库中,导致无法使用 join 操作。
事务问题:同一个数据库中的表分布在了不同的数据库中,如果单个操作涉及到多个数据库,那么数据库自带的事务就无法满足我们的要求了。
分布式 ID:分库之后, 数据遍布在不同服务器上的数据库,数据库的自增主键已经没办法满足生成的主键唯一了。
跨库聚合查询问题:分库分表会导致常规聚合查询操作,如 group by,order by 等变得异常复杂。
SQL优化
说一说你对数据库优化的理解?
MySQL数据库优化是多方面的,原则是减少系统的瓶颈,减少资源的占用,增加系统的反应速度。例如,通过优化文件系统,提高磁盘IO的读写速度;通过优化操作系统调度策略,提高MySQL在高负荷情况下的负载能力;优化表结构、索引查询语句等使用查询响应更快。
基础SQL优化
1.查询SQL尽量不要使用select *,而是具体字段
理由:
- 字段多时,大表能达到100多个字段甚至达200多个字段
- 只取需要的字段,节省资源、减少网络开销
- select * 进行查询时,很可能不会用到索引,就会造成全表扫描
2.避免在where子句中使用or来连接条件
or可能会导致索引失效,扫描全表
使用 UNION
SELECT * FROM student WHERE id=1
UNION ALL
SELECT * FROM student WHERE salary=30000
3.使用varchar代替char
4.查询尽量避免返回大量数据
5.使用explain分析你SQL执行计划
SQL很灵活,一个需求可以很多实现,那哪个最优呢?SQL提供了explain关键字,它可以分析你的SQL执行计划,看它是否最佳。Explain主要看SQL是否使用了索引。
6.优化like语句
7.索引不宜太多,一般5个以内
索引并不是越多越好,虽其提高了查询的效率,但却会降低插入和更新的效率
索引可以理解为一个就是一张表,其可以存储数据,其数据就要占空间
再者,索引表的一个特点,其数据是排序的,那排序要不要花时间呢?肯定要
insert或update时有可能会重建索引,如果数据量巨大,重建将进行记录的重新排序,所以建索引需要慎重考虑,视具体情况来定
一个表的索引数最好不要超过5个,若太多需要考虑一些索引是否有存在的必要
8.索引不适合建在有大量重复数据的字段上
如性别字段。因为SQL优化器是根据表中数据量来进行查询优化的,如果索引
列有大量重复数据,Mysql查询优化器推算发现不走索引的成本更低,很可能就放弃索引了。
具体可查看该链接
https://blog.csdn.net/weixin_53601359/article/details/115553449?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169425908716800186544355%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169425908716800186544355&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-115553449-null-null.142^v93^chatsearchT3_2&utm_term=SQL%E4%BC%98%E5%8C%96&spm=1018.2226.3001.4187
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本