目录
事务(Transaction)
事务(Transaction)是指一组原子性操作,这组操作要么全部成功完成,要么全部失败回滚。在数据库中,事务是用来保证数据一致性和完整性的重要机制。
事务通常具有以下四个特征(称为 ACID 特性):
原子性(Atomicity):将一组操作看作一个整体,要么全部执行成功,要么全部失败回滚。
一致性(Consistency):事务执行前后,数据库的状态应该保持一致。如果事务执行失败,数据库的状态应该回滚到事务开始前的状态。
隔离性(Isolation):多个事务同时执行时,每个事务都应该感觉不到其他事务的存在。也就是说,每个事务的操作应该与其他事务的操作相互隔离,互不干扰。
持久性(Durability):事务提交后,其所做的修改应该被永久保存到数据库中,即使系统故障也不应该受到影响。
在实际应用中,事务往往是由数据库管理系统自动管理的。当用户执行一系列操作时,数据库系统会将这些操作组成一个事务,然后自动处理事务的提交和回滚操作,以保证数据的一致性和完整性。
事务的应用非常广泛,不仅仅局限于数据库系统中。在分布式系统中,事务也是保证数据一致性和可靠性的重要机制。
事务隔离级别
-
读未提交(Read Uncommitted):
- 这是最低的隔离级别,事务可以读取其他未提交事务修改但未提交的数据。
- 会出现脏读的问题,即读取到未提交的数据。
-
读已提交(Read Committed):
- 这是大多数数据库的默认隔离级别。
- 事务只能读取已提交的数据,可以避免脏读问题。
- 但可能出现不可重复读的问题,即在同一事务中,多次读取同一数据会得到不同的结果。
-
可重复读(Repeatable Read):
- 这一级别保证了在同一事务中多次读取同一数据会得到相同的结果。
- 可避免不可重复读的问题,但可能出现幻读的问题,即在事务执行过程中,数据集发生变化。
-
串行化(Serializable):
- 这是最高的隔离级别,事务会按顺序执行,相当于顺序执行。
- 可以避免所有并发问题,如脏读、不可重复读、幻读等。
- 但由于事务需要完全串行化执行,性能会大大降低。
MySQL默认为repeatable read
在MySQL配置文件中my.ini中进行设置,根据不同的需求场景,可以设置不同的档位
原子性
事务最核心的就是原子性
以前人们认为原子不可再分,用原子性来表示一个事务不可分割
update account set balance=balance-500 where name ="张三"
如果在此过程中数据库崩溃,那么张三扣钱而李四不加钱
update account set balance=balance+500 where name ="李四"
要么全部都不执行,要么全部都执行
事务就能保证,当执行过程中出现问题的时候,自动把前面SQL执行的效果进行还原,恢复如初,回滚rollback
在事务执行过程中,mysql才会记录每一步执行了什么,一旦有问题就回滚
事务有空间代价和时间代价,事务只有在开启状态才会消耗这些代价
如果是ctrl+z的话意味着每一步回退和记录都得消耗这个空间和时间
实际上人对SQL操作在实际运用中并不是很多,大部分都是机械在对服务器进行操作,出错概率很小,撤回机制没有必要。
开启事务:start transaction;
执行多条SQL语句
回滚或提交:rollback/commit;
除了原子性外事务还有
一致性
事务执行前后,数据处在一致状态,数据CURD对的上
持久性
事务进行的改动,都是写到硬盘,不会随着程序重启/主机重启后丢失
隔离性
多个事务,并发执行的时候,事务之间能够保持隔离,互不干扰。
同一时刻,多个客户端都给服务器提交事务,执行事务
并发执行可能存在问题
脏读问题
一个事务A在修改数据同时,另一个事务B读取了数据,此时A很可能在提交的时候修改
了B读取的数据,导致事务B读取的数据为无效数据,这种错误操作就是脏读操作
脏读指的是一个事务读取了另一个事务尚未提交的数据。假设有两个事务 A 和 B,A 读取了 B 修改但未提交的数据,如果 B 最终回滚了,那么 A 读取的数据实际上是无效的,即脏数据。脏读可能导致不一致的结果。
解决方法:在事务提交之前,不能进行读操作,提交之后才能读
也就是对 写操作 加锁
在加锁之前,我的写操作,和同学的读操作就是完全高并发的
但是并发性虽然高,隔离性却是最低的
在加锁之后,写的时候不能进行读操作,并非性降低了,隔离性提高了
不可重复读
在一个事务A中多次读取同一个数据,发现不一样
不可重复读指的是一个事务在同一个数据项上的两次读取之间,另一个事务修改或删除了数据,导致第一个事务两次读取的结果不一致。这可以发生在 READ COMMITTED 隔离级别下,其中事务 A 在读取某个数据后,事务 B 修改或删除了该数据,然后事务 A 再次读取该数据,结果不同。
解决方法: 读数据的时候,不能修改,读加锁
引入读加锁后,并发程度进一步降低了,效率也下降了
但隔离性提高了,数据准确性也提高了
幻读
特殊的不可重复读,对A文件进行读的时候,增加了新文件B
导致读之前的结果集与读之后的结果集不同。
幻读是在一个事务内部,同一查询在两次执行之间,由于其他事务的插入操作导致了不一致的结果。幻读通常发生在 REPEATABLE READ 隔离级别下。例如,事务 A 查询了一个范围内的数据,然后事务 B 插入了一条符合该范围的数据,导致事务 A 的第二次查询返回的结果不同。
解决方法:
串行执行,效率最低,并发程度最低,此时,隔离性最高,数据的准确性最高。
MySQL的优化措施: REPEATABLE_READ(可重复读)+间隙锁就能解决幻读问题了,不一定要串行化 这样就构造了一个查锁吗,查的数据被锁了,不查的空数据也被锁了
间隙锁:加锁范围是被查询范围内的空隙(未填写数据的空位置),防止查询期间其他事务插入到间隙中,导致读之前的结果集与读之后的结果集不同。
总结:
- 脏读是一个事务读取到了另一个未提交事务的数据。
- 不可重复读是一个事务内,两次读取同一数据,但其间有另一个事务修改了数据。
- 幻读是一个事务内,两次执行同一个查询,但其间有另一个事务插入了符合查询条件的新数据
上述
三种情况,不一定是bug,要根据实际需求,需要考虑对特定目标的准确性要求
衡量是不是BUG的唯一标准,就是看是否符合需求
难点解析
1.REPEATABLE_READ(可重复读)给查的数据加上锁,在事务结束之前都不能修改,以此来解决不可重复读问题2.REPEATABLE_READ(可重复读)+间隙锁就能解决幻读问题了,不一定要串行化
这样就构造了一个查锁吗,查的数据被锁了,不查的空数据也被锁了3.串行化就是等一个结束了再上另一个事务(类似葫芦娃救爷爷)
4.在串行化隔离级别下,读操作也会被加锁(一般是共享锁),不过在读取数据后就会放开数据,多个事务可以交替读.但是只能由一个事务改,且在写完之前都不能读
主键和外键
主键和外键是关系型数据库中的两个重要概念,它们之间存在一定的关系。
主键是用来唯一标识一条记录的一列或者一组列。在一个表中,每个记录都必须有一个唯一的主键,且主键列不能有重复值。主键可以用于保证数据的完整性和一致性,同时也可以用于加速数据的检索和查询。
外键是用来建立表与表之间联系的一种约束。在关系型数据库中,不同的表之间可能存在一定的关联关系,例如父子关系、从属关系等。这时可以通过外键来建立表与表之间的联系。外键定义了一个字段或者一组字段,它的值必须来自另一个表的主键,即外键引用了另一个表的主键。
因此,主键和外键之间的关系是,一个表的主键可以作为另一个表的外键,建立表与表之间的联系。
这里的外键就是引用主表的主键字段
例如,如果有两个表 A 和 B,其中 B 表的某个字段需要引用 A 表中的主键,则在 B 表中定义一个外键,指向 A 表的主键即可。
通过这种方式,可以实现表与表之间的关联和数据的完整性检查。
主键(Primary Key)
- 主键是一个或多个字段组成的列,用于唯一标识数据表中的每一行记录。
- 主键具有以下特点:
- 唯一性: 每个记录的主键值都是唯一的,不能重复。
- 非空性: 主键列的值不能为 NULL。
- 不可变性: 主键值一旦确定,就不应该被修改。
- 主键可以是单一字段,也可以是多个字段组成的复合主键。
- 主键可以帮助我们快速定位和访问表中的特定记录。
外键(Foreign Key)
- 外键是一个或多个字段,它们引用了另一个表的主键,用于建立两个表之间的关系。
- 外键具有以下特点:
- 引用另一个表的主键: 外键列的值必须在被引用表的主键列中存在,或者为 NULL。
- 维护数据完整性: 外键约束可以防止破坏表之间关系的操作,如删除或更新。
- 可以是单一字段,也可以是多个字段组成的复合外键。
- 外键可以建立一对一、一对多或多对多的关系。
- 外键可以帮助我们在表之间建立关联,实现数据的完整性和一致性。
主键示例:
假设我们有一个"学生"表,其中包含学生的基本信息,如学号、姓名、年龄等。在这个表中,我们可以将"学号"字段设置为主键。
主键"学号"可以帮助我们快速地找到某个具体的学生记录,比如根据学号查询某个学生的信息。
- 主键"学号"具有唯一性,每个学生的学号都是独一无二的。
- 主键"学号"不能为空,必须有值。
- 一旦设置了学号,就不应该轻易地去修改它。
外键示例:
假设我们还有一个"成绩"表,其中包含学生的成绩信息,如课程名称、分数等。在这个表中,我们可以将"学号"字段设置为外键,它引用"学生"表的主键"学号"。
- 外键"学号"的值必须在"学生"表的主键"学号"列中存在,或者为 NULL。
- 通过外键"学号",我们可以建立"学生"表和"成绩"表之间的关联关系。
- 如果要删除某个学生记录,由于"成绩"表的外键约束,我们必须先删除该学生在"成绩"表中的记录,否则无法删除。
外键"学号"可以帮助我们在表与表之间建立关联,确保数据的完整性和一致性。
前面说了这么多表与表之间建立关联,那么表之间是怎么关联的呢?
表的连接方式
参考题目1068. 产品销售分析 I - 力扣(LeetCode)
左连接
LEFT JOIN:
SELECT customers.customer_name, orders.order_id
FROM customers
LEFT JOIN orders
ON customers.customer_id = orders.customer_id;
在上面的例子中,LEFT JOIN 返回了所有客户的信息,以及每个客户的订单(如果有的话),如果客户没有订单,则订单信息为NULL。
- LEFT JOIN 返回左边表格(第一个表格)的所有行,以及与右边表格(第二个表格)中匹配的行。如果没有匹配的行,则在结果中仍然包括左边表格的行,但右边表格的数据将为空(NULL)。
select Product.product_name ,Sales.year ,Sales.price Product left join Sales on Product.product_id =Sales.product_id;
右连接
RIGHT JOIN:
SELECT customers.customer_name, orders.order_id
FROM customers
RIGHT JOIN orders
ON customers.customer_id = orders.customer_id;
在上面的例子中,RIGHT JOIN 返回了所有订单的信息,以及每个订单的客户(如果有的话),如果订单没有客户,则客户信息为NULL。
- RIGHT JOIN 类似于 LEFT JOIN,但它返回右边表格(第二个表格)的所有行,以及与左边表格(第一个表格)中匹配的行。如果没有匹配的行,则在结果中仍然包括右边表格的行,但左边表格的数据将为空(NULL)。
select Product.product_name ,Sales.year ,Sales.price from Sales
right join Product on Product.product_id =Sales.product_id;
内连接
INNER JOIN:
SELECT orders.order_id, customers.customer_name
FROM orders
INNER JOIN customers
ON orders.customer_id = customers.customer_id;
在上面的例子中,INNER JOIN 将订单表格(orders)和客户表格(customers)连接,仅返回匹配的订单和客户数据。
- INNER JOIN 也称为等值连接,它返回两个表格中具有匹配值的行。只有在连接条件成立的情况下,才会返回数据。如果没有匹配的行,则不会包括在结果集中。
select Product.product_name ,Sales.year ,Sales.price from Sales
inner join Product on Product.product_id =Sales.product_id;
全连接(笛卡尔积)
- 对于有考试记录的学生,会展示学生的基本信息以及对应的考试成绩。
- 对于没有考试记录的学生(如 id 为 NULL 的行),会显示学生的基本信息,但考试相关的字段会填充 NULL。
- 对于有考试记录但没有对应学生信息的记录(如 id 为 NULL 的行),也会显示在结果集中
WHERE和HAVING有什么区别
- WHERE 子句用于在数据返回之前对记录进行过滤。它可以应用于表中的任何字段。
- HAVING 子句用于在聚合函数之后对结果集进行过滤。它只能应用于使用了聚合函数的字段。
假设我们有一个名为 sales_table
的表,它包含以下字段:
name
:销售员名字age
:销售员年龄sales
:销售额
SELECT name, age, SUM(sales) AS total_sales
FROM sales_table
WHERE age > 30
GROUP BY name, age
HAVING total_sales > 1000;
哈,谢谢各位同志的阅读,然后呢如果觉得本文对您有所帮助的话,还给个免费的赞捏
Thanks♪(・ω・)ノ