使用命令和SQL操作MySQL

显示数据库的版本

创建一个数据库

创建一张表

常见的数据类型

分类数据类型说明
数值类型BIT(M)位类型。M指定位数,默认值1,范围1-64。
 BOOL,BOOLEAN使用0或1表示假或真
 TINYINT [UNSIGNED] [ZEROFILL]带符号的范围是-128到127。无符号0到255。
 SMALLINT [UNSIGNED] [ZEROFILL]2的16次方
 INT [UNSIGNED] [ZEROFILL]2的32次方
 BIGINT [UNSIGNED] [ZEROFILL]2的64次方
 FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]单精度浮点数,M指定显示长度,D指定小数位数
 DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]双精度浮点数
文本、二进制类型CHAR(size)定长字符串,size是字符数
 VARCHAR(size)变长字符串,size是上限
 BLOB二进制数据 比如图片、音乐等
 LONGBLOB比较大的二进制数据
 TEXT(clob)文本数据
 LONGTEXT(longclob)比较大的文本数据
时间和日期DATE日期
 TIME(一天之中的)时间
 DATETIME时间和日期的组合
 TIMESTAMP时间和日期的组合 时间戳(其显示受时区的影响)

增加表内容:插入数据

查询表内容

展示表中的数据

select的参数和where子句可以配合使用:

使用select语句的结果默认没有明确顺序,有些情况下,用户需要对结果进行排序以更快地得到想要的内容,此时,就可以使用order by子句:

增加desc修饰可以按降序排序:

也可以根据多列情况依次排序:

空值NULL相关的操作

使用is null和is not null运算符可以判断某个值是否为NULL:

不能对NULL使用比较运算符,比如<、<>、=之类的:

简单的模式匹配

使用like和not like可以启用通配符匹配:

聚集函数和统计行数

多表连接操作

下面,我们来执行一个多表内连接的例子:

在from语句当中可以对多表进行连接操作以生成一个新表。

inner join表示内连接:pet表和event表的内连接会创建一个新表:新表当中的每一行,都是从由pet的某一行和event中某一行组合而成,并且必须两行都满足ON子句的条件。

需要注意的是,如果两个表当中存在同名字的列,需要使用.运算符来指明该列所归属的表。

通过给表起别名可以实现自己和自己内连接,下面是示例:

使用内连接得到的新表存在一个特点,那就是左表的行和右表的行都必须满足条件才会组合成新行出现在结果当中。在有些情况下,用户除了需要知道左表和右表当中有哪里公共对象以外,还需要知道哪些左表中的对象从来没有在右表当中出现过。这种情况下就需要使用左外连接:

右外连接和左外连接类型,不同之处在于会把右表当中存在,而在左表中未出现的对象也放入新表当中。

全外连接是左外连接和右外连接的并集,但是MySQL不支持。

修改表内容

更新

删除

操作表结构

获取表的创建信息

修改表的结构

修改表的名字

表的约束

为了保证数据的完整性,数据库当中所有存储数据的值都必须满足一些特定的一致性约束。这些一致性约束是为了保证插入到数据库中的数据是正确的,它防止了用户可能的输入错误。主要分为以下三类:

可以在创建表的时候设置好约束,下面是创建表的时候设置约束的语法。

参照约束的语法如下:

使用primary key指明主键约束,即不允许为空也不允许重复,从而可以区分两条记录的唯一性。

也可以在表创建完成之后,使用alter table语句再增加或者删除主键约束:

下面的示例中可以设置自动增长的主键:

MySQL C API

事务和索引

事务和隔离级别

事务的基本概念

MySQL中的事务是一系列SQL操作的集合,这些操作要么全部成功执行,要么全部失败回滚。事务是数据库管理系统提供的一种机制,用于确保数据库的完整性、一致性和持久性。事务具有以下特性:

上面的四个性质一般可以缩写为ACID。值得注意的是,一致性和其他三个性质并不在同一个层次上,具体来说就是:一致性是上层业务追求的目标,而原子性、隔离性和持久性是事务本身的属性。原子性、一致性和隔离性一起合作从而实现一致性。

下面我们通过一个例子来说明上述性质:

当涉及到银行转账时,事务的性质尤为重要,因为涉及到资金的安全和准确性。假设有一个银行系统,其中有两个账户:账户A和账户B。现在,用户要进行一笔转账操作,从账户A向账户B转移一定金额的资金。

首先,业务目标是一致性:在转账之前和之后,系统必须保持一致性。也就是说,无论转账是否成功,银行系统都必须确保账户A和账户B的总余额保持不变。如果转账成功,账户A的余额减少了100元,而账户B的余额增加了100元,使得总资金仍然保持不变。

那么事务是如何保证执行过程中总资金不变的呢?

  1. 原子性:假设用户要转移100元。在一个事务中,银行系统会首先从账户A中扣除100元,然后将这笔资金存入账户B。在这个过程中,要么这两个操作都成功执行,要么都失败。如果任何一个操作失败,例如因为网络故障或系统崩溃,资金将不会丢失,因为整个操作将被回滚,账户A和账户B的余额将保持不变。

  2. 隔离性:当一个用户正在进行转账操作时,其他用户可能也同时进行转账操作或查询账户余额。隔离性要求这些并发操作之间不会相互干扰。在事务中,转账操作应该独立于其他操作,直到事务完成。这意味着在转账过程中,其他用户不应该看到账户余额的不一致或不正确的状态。

  3. 持久性:一旦转账操作成功提交,银行系统必须保证这笔资金转移是永久性的。即使系统发生故障或重新启动,转账后的余额变化也必须得到保留,不能丢失。

通过使用事务,银行系统可以确保转账操作的安全性和准确性,从而保护用户的资金和数据。

在MySQL当中使用事务

下面是在MySQL当中和事务相关的几个命令。

命令名称描述
START TRANSACTION/BEGIN用于启动一个新的事务。在启动事务之后,后续的SQL操作将被视为一个原子操作序列,直到显式地提交或回滚事务为止。
COMMIT用于提交事务。当事务中的所有操作都成功完成并且需要将更改永久保存到数据库时,应使用COMMIT命令。提交事务会使得事务中的所有操作生效。
ROLLBACK用于回滚事务。当在事务中发生错误或需要取消之前的更改时,可以使用ROLLBACK命令。回滚将撤销事务中的所有操作,并将数据库恢复到事务开始之前的状态。
SAVEPOINT用于创建一个保存点(Savepoint),使得可以在事务中的任何位置进行部分回滚。SAVEPOINT命令允许将事务分割成更小的逻辑单元,并在需要时回滚到指定的保存点。
RELEASE SAVEPOINT用于释放一个保存点,表示不再需要回滚到该保存点。RELEASE SAVEPOINT命令允许释放事务中已经创建的保存点,以节省资源。
ROLLBACK TO SAVEPOINT用于回滚到指定的保存点。当需要在事务中的某个位置进行部分回滚时,可以使用ROLLBACK TO SAVEPOINT命令将事务回滚到指定的保存点。

如果系统变量autocommit的数值为1,那么每一句DML默认就是一个事务。如果系统变量autocommit的数值为0,那么执行DML默认在一个事务当中,需要commit才能结束事务。

并发带来的问题

在之前的例子当中,我们了解到事务具有隔离性:在一个连接的事务执行过程中,另一个连接当中是无法立刻感知到表内容的数据变化的。隔离性的实现需要数据库底层能够处理多个用户并发访问带来的问题。那接下来我们就要解决这两个问题:

  1. 脏写(Dirty Write):脏写是指在并发环境下,一个事务修改了另一个事务尚未提交的数据,然后另一个事务又修改了相同的数据并提交,导致数据变得不一致或无效。

  2. 脏读(Dirty Read): 当一个事务读取了另一个事务尚未提交的数据时,就发生了脏读。如果该事务后来回滚,则读取的数据实际上是无效的或不一致的。这可能会导致系统中出现不正确的结果。

  3. 不可重复读(Non-repeatable Read): 不可重复读是指在一个事务内多次读取同一行数据,但由于其他事务的更新操作,每次读取的数据都不一致。这可能会导致在同一事务内对相同数据进行多次读取时出现不一致的结果。

  4. 幻读(Phantom Read): 幻读则是同一事务中,两次查询同一条件返回的行数不同,因为其他事务插入了新数据。

隔离级别

为了在临界区的持续时长和不同程度的并发问题之间取权衡,数据库提出了隔离级别的概念:

  1. 读未提交(Read Uncommitted)

    • 在这个隔离级别下,一个事务可以读取到其他事务尚未提交的数据。这是最低的隔离级别,可能导致脏读、不可重复读和幻读等并发问题。

  2. 读已提交(Read Committed)

    • 在这个隔离级别下,一个事务只能读取到其他事务已经提交的数据。这可以避免脏读,但是仍然可能发生不可重复读和幻读的问题。

  3. 可重复读(Repeatable Read)

    • 在这个隔离级别下,一个事务在同一个事务中多次读取相同的数据,得到的结果是一致的。这可以避免脏读和不可重复读,但是仍然可能发生幻读的问题。

  4. 串行化(Serializable)

    • 在这个隔离级别下,事务是串行执行的,每个事务都像是在独立的系统中执行一样。这可以避免脏读、不可重复读和幻读等所有并发问题,但是会降低系统的并发性能。

从上往下的隔离级别等级越来越高,等级越高,隔离效果就越好,但是消耗的系统资源也越多,用户可以根据实际情况进行设定。这些隔离级别可以不同程度地解决之前并发带来的问题。如下表所示:

 脏写脏读不可重复读幻读
读未提交 READ UNCOMMITTED×
读已提交 READ COMMITTED××
可重复读 REPEATABLE READ×××
串行化 SERIALIZABLE××××

索引

想象一下,你有一本很厚的书,想要快速找到某个特定的章节或内容。如果没有目录(索引),你可能需要从头到尾一页一页地翻阅,这会非常耗时。书的目录就是一种索引,它列出了章节名称和对应的页码,让你能够迅速定位到目标内容。

在 MySQL 中,索引(Index) 也是类似的概念。它是数据库表中一列或多列值的排序结构,用于帮助 MySQL 高效地获取数据。数据库索引允许数据库服务器比在没有索引的情况下更快地查找和检索特定的行。

简单来说,索引是数据库管理系统中一个排序的数据结构,用于提高数据检索操作的速度。

索引的优点

使用索引的主要目的是提高数据库的查询性能。具体来说,有以下几个优点:

  1. 提高查询速度:这是索引最核心的价值。通过索引,MySQL 可以不必扫描整个表(全表扫描),而是直接定位到符合条件的记录,大大减少了查询所需的时间,尤其是在数据量巨大的表中。

  2. 保证数据的唯一性:通过创建唯一索引(Unique Index),可以确保表中的某一列(或几列组合)的值是唯一的,防止插入重复数据。主键索引本身就是一种唯一索引。

  3. 加速表连接(JOIN 操作):如果连接条件中的列建立了索引,MySQL 可以更快地找到匹配的行,从而加速 JOIN 操作。

  4. 加速排序(ORDER BY)和分组(GROUP BY):如果排序或分组的列上有索引,MySQL 可以利用索引的有序性来避免额外的排序操作,或者更快地进行分组。

  5. 优化 WHERE 子句的查询:索引使得数据库引擎能够快速定位到满足 WHERE 子句条件的行。

索引的基本原理

MySQL 中的索引通常使用特定的数据结构来实现,最常见的是 B-Tree(或其变种,如 B+Tree)

这种方式避免了逐行扫描整个表的低效操作。

索引类型

MySQL 支持多种类型的索引,以适应不同的查询需求:

  1. 主键索引 (PRIMARY KEY)

    • 一种特殊的唯一索引,用于唯一标识表中的每一行。

    • 每个表只能有一个主键。

    • 主键列的值不能为空(NOT NULL)且必须唯一。

    • 在 InnoDB 存储引擎中,表数据本身就是按照主键索引(聚簇索引)的顺序存储的。

  2. 唯一索引 (UNIQUE INDEX)

    • 确保索引列中的所有值都是唯一的,但允许有单个 NULL 值(如果列允许 NULL)。

    • 一个表可以有多个唯一索引。

  3. 普通索引/二级索引 (NORMAL INDEX / SECONDARY INDEX)

    • 最基本的索引类型,没有任何唯一性或主键的限制。

    • 其唯一任务就是加快对数据的访问速度。

    • 也称为非唯一索引。

  4. 全文索引 (FULLTEXT INDEX)

    • 主要用于在文本数据(如 CHAR, VARCHAR, TEXT 类型的列)中进行关键词搜索。

    • 它允许你执行更复杂的文本搜索,比如查找包含特定单词或短语的行,而不是简单的精确匹配或范围匹配。

    • 主要在 MyISAM 和 InnoDB (MySQL 5.6+) 存储引擎中支持。

  5. 空间索引 (SPATIAL INDEX)

    • 用于地理空间数据类型(如 GEOMETRY 类型)。

    • 允许对地理空间数据进行高效查询,例如查找某个点附近的地点。

    • 主要在 MyISAM 存储引擎中支持。

  6. 组合索引 (Composite Index / Multi-column Index)

    • 在表的多个列上创建的索引。

    • 当查询条件涉及到这些列的组合时,组合索引会非常有效。

    • 遵循“最左前缀原则”:查询条件必须从索引的最左边的列开始使用,才能有效地利用该索引。例如,如果在 (col1, col2, col3) 上创建了组合索引,那么 WHERE col1 = ?WHERE col1 = ? AND col2 = ?WHERE col1 = ? AND col2 = ? AND col3 = ? 都可以有效利用该索引。但 WHERE col2 = ?WHERE col3 = ? 则不能。

  7. 覆盖索引 (Covering Index)

    • 这不是一种特定的索引类型,而是一种索引的使用方式。

    • 当一个查询需要的所有数据都可以直接从索引中获取,而无需回表(即无需访问表中的实际数据行)时,这个索引就被称为覆盖索引。

    • 覆盖索引可以极大地提高查询性能,因为它减少了磁盘 I/O。

索引的缺点

虽然索引能带来巨大的性能提升,但它们并非没有代价:

  1. 占用存储空间:索引本身也是数据,需要存储在磁盘上,有时甚至可能比数据本身占用的空间还要大。

  2. 降低写操作(INSERT, UPDATE, DELETE)的速度:当对表中的数据进行增加、删除或修改时,不仅需要修改数据本身,还需要更新相关的索引结构,以保持索引的正确性和有序性。这会增加写操作的开销。索引越多,写操作的开销就越大。

  3. 创建和维护索引需要时间:创建索引可能是一个耗时的操作,尤其是在大表上。索引的维护(如重建、优化)也需要时间和资源。

何时创建索引

  1. 经常用于 WHERE 子句的列:这是最常见的创建索引的场景。

  2. 经常用于 JOIN 操作的连接条件的列:例如外键列。

  3. 经常用于 ORDER BY 子句的列:可以利用索引的有序性避免额外的排序操作。

  4. 经常用于 GROUP BY 子句的列:可以帮助更快地进行分组。

  5. 列中值的区分度高(高基数性):如果一个列有很多不同的值(例如用户ID),那么索引的效果会比较好。如果一个列的值大部分都相同(例如性别,只有男/女/未知),那么索引的效果可能不佳,甚至会降低性能。

  6. 查询中需要覆盖索引的场景

何时不宜创建索引?

  1. 表记录太少:如果表中的数据量非常小(比如几百行),那么全表扫描可能比使用索引更快,因为索引查找本身也有开销。

  2. 数据重复度高、选择性低的列:例如“性别”列,索引带来的性能提升可能微乎其微,甚至可能因为维护索引而降低整体性能。

  3. 经常进行写操作(INSERT, UPDATE, DELETE)且读操作不频繁的列:索引会增加写操作的负担。需要权衡读写性能。

  4. 不再使用或者很少使用的索引:它们只会占用空间并拖慢写操作,应该及时删除。

创建索引的基本语法示例

索引优化的一些提示