<p>本篇文章给大家带来了关于<a href="https://www.php.cn/course/list/51.html" target="_blank" textvalue="mysql视频教程">视频教程</a>的相关知识,其中主要介绍了关于数据库设计概念的相关问题,包括了设计简介、多表查询、事务操作等等内容,下面一起来看一下吧,希望对大家有帮助。</p>
<p><img src="https://img.php.cn/upload/article/000/000/067/628b6328be705537.jpg" alt="mysql设计概念及多表查询和事务操作" ></p>
<p>推荐学习:<a href="https://www.php.cn/course/list/51.html" target="_blank" textvalue="mysql视频教程">mysql视频教程</a></p>
<h2>数据库设计简介</h2>
<p><strong>1.数据库设计概念</strong></p>
<ul>
<li>数据库设计就是根据业务系统具体需求,结合我们所选用的DBMS,为这个业务系统构造出最优的数据存储模型。</li>
<li>建立数据库中的<code>表结构</code>以及<code>表与表之间的关联关系</code>的过程。</li>
<li>有哪些表?表里有哪些字段?表和表之间有什么关系?</li>
</ul>
<p><strong>2.数据库设计步骤</strong></p>
<ul>
<li><p>需求分析:数据库是什么?数据具体有哪些属性?数据与属性的特点是什么?</p></li>
<li><p>逻辑分析:通过<code>ER图</code>对数据库进行逻辑建模,不需要考虑我们所选用的数据库管理系统。</p></li>
<li><p>物理设计:根据数据库自身的特点把逻辑设计转换为物理设计。</p></li>
<li><p>维护设计:对新的需求进行建表和对表的优化。</p></li>
</ul>
<p><strong>3.表关系简介</strong></p>
<ul>
<li><p>在真实的开发中,一个项目中的数据,一般都会保存在同一个数据库中,但是不同的数据需要保存在不同的数据表中。这时不能把所有的数据都保存在同一张表中。</p></li>
<li><p>那么在设计保存数据的数据表时,我们就要根据具体的数据进行分析,然后把同一类数据保存在同一张表中,不同的数据进行分表处理。</p></li>
<li><p>数据之间必然会有一定的联系,我们把不同的数据保存在不同的数据表中之后,同时还要在数据表中维护这些数据之间的关系。这时就会导致表和表之间必然会有一定的联系。这时要求设计表的人员,就需要考虑不同表之间的具体关系。</p></li>
</ul>
<p><mark>在数据库中,表总共存在三种关系,真实的数据表之间的关系:</mark><strong>多对多关系、一对多(多对一)</strong>、一对一(极少),(一对一关系就是我们之前学习的Map集合的key-value关系)</p>
<h2>表关系(多对多)</h2>
<p><strong>1.多对多</strong></p>
<ul>
<li>如:订单 和 商品</li>
<li>一个商品对应多个订单,一个订单对应多个商品</li>
<li>实现方式:建立第三张<code>中间表</code>,中间表至少包含<code>两个外键</code>,分别<code>关联两方主键</code><br><img src="https://img.php.cn/upload/article/000/000/067/ad269369a7e12b5cbdb2c92327e55cb3-0.png" alt="在这里插入图片描述"><br><strong>说明:如果两张表是多对多的关系,需要创建第三张表,并在第三张表中增加两列,引入其他两张表的主键作为自己的外键。</strong>
</li>
</ul>
<p><strong>2.外键约束</strong></p>
<ul>
<li>外键用来让两个表的数据之间建立链接,保证数据的一致性和完整性(例如上述多对多中的订单商品表来维护订单表和商品表之间的关系)</li>
<li>使用之间表的目的是维护两表之间多对多的关系:<strong>中间表插入的数据,必须在多对多的主表中存在</strong>,<strong>如果主表的记录在中间表中维护了关系,就不能随意的删除。如果要删除,必须先要删除中间表关联的数据</strong>
</li>
</ul>
<p><strong>3.外键约束语法</strong></p>
<pre class="brush:php;toolbar:false">-- 关键字解释:constraint: 添加约束,可以不写foreign key(当前表中的列名): 将某个字段作为外键references 被引用表名(被引用表的列名) : 外键引用主表的主键-- 创建表时添加外键约束CREATE TABLE 表名(
列名 数据类型,
… [CONSTRAINT] [外键名称] FOREIGN KEY(外键列名) REFERENCES 主表(主表列名) ); -- 建完表后添加外键约束ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称);-- 删除约束ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;</pre>
<p><strong>4.创建外键约束</strong></p>
<pre class="brush:php;toolbar:false">-- 订单表CREATE TABLE tb_orders(
id int primary key auto_increment,
payment double(10, 2),
payment_type TINYINT, -- 0 微信支付 1 支付宝支付
status TINYINT -- 0 未付款 1 已经支付);-- 商品表CREATE TABLE tb_goods(
id int primary key auto_increment,
title varchar(100),
price double(10, 2));-- 订单商品中间表CREATE TABLE tb_order_goods(
id int primary key auto_increment,
order_id int, -- 外键,来自于订单表的主键
goods_id int, -- 外键,来自于商品表的主键
count int, -- 购买商品数量
foreign key(order_id) references tb_orders(id),
foreign key(goods_id) references tb_goods(id));</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/ad269369a7e12b5cbdb2c92327e55cb3-1.png" alt="在这里插入图片描述"><br><strong>5.外键级联</strong></p>
<p><mark>在修改和删除主表的主键时,同时更新或删除从表的外键值,称为级联操作</mark></p>
<ul>
<li>
<code>ON UPDATE CASCADE</code> – 级联更新,主键发生更新时,外键也会更新</li>
<li>
<code>ON DELETE CASCADE</code> – 级联删除,主键发生删除时,外键也会删除</li>
</ul>
<p><strong>6.总结</strong></p>
<blockquote>
<p>1.为何要引用外键约束?</p>
<ul><li>让表的数据有效性,正确性。提高查询效率。</li></ul>
<p>2.添加外键约束语法?</p>
<ul><li>constraint 外键约束名 foreign key(当前表的字段名) references 主表(主键)</li></ul>
<p>3.有了外键约束操作数据注意事项?</p>
<ul>
<li>要求添加数据需要先添加主表,然后添加从表。</li>
<li>要求删除数据需要先删除从表,然后再删除主表。</li>
</ul>
</blockquote>
<h2>表关系(一对多)</h2>
<p><strong>一对多(多对一)</strong></p>
<ul>
<li>如:部门表 和 员工表</li>
<li>一个部门对应多个员工,一个员工对应一个部门</li>
<li>实现方式:<mark>在多的一方建立外键,指向一的一方的主键</mark>
</li>
</ul>
<p><img src="https://img.php.cn/upload/article/000/000/067/ad269369a7e12b5cbdb2c92327e55cb3-2.png" alt="在这里插入图片描述"></p>
<h2>表关系之一对一</h2>
<p><strong>一对一</strong></p>
<ul>
<li>如:用户和 用户信息</li>
<li>一对一关系多用于表拆分,将一个实体中经常使用的字段放一张表,不经常使用的字段放另一张表,用于提升查询性能</li>
<li>实现方式:在任意一方加入外键,关联另一方主键,并且设置外键为<strong>唯一</strong><code>(UNIQUE)</code>
</li>
</ul>
<p><img src="https://img.php.cn/upload/article/000/000/067/efe2de8ea05d57aa111c2c60576fb57c-3.png" alt="在这里插入图片描述"></p>
<h1>多表查询</h1>
<p><img src="https://img.php.cn/upload/article/000/000/067/efe2de8ea05d57aa111c2c60576fb57c-4.png" alt="在这里插入图片描述"><br><strong>准备数据</strong></p>
<pre class="brush:php;toolbar:false">-- 价格create table price(
id int primary key auto_increment,
price double);-- 水果 create table fruit(
id int primary key auto_increment,
name varchar(20) not null,
price_id int,
foreign key (price_id) references price (id));-- 数据insert into pricevalues (1, 2.30);insert into pricevalues (2, 3.50);insert into pricevalues (4, null);insert into fruitvalues (1, '苹果', 1);insert into fruitvalues (2, '橘子', 2);insert into fruitvalues (3, '香蕉', null);</pre>
<h2>笛卡尔积现象</h2>
<p><strong>1.什么是笛卡尔积现象</strong></p>
<ul>
<li>笛卡尔积问题:把多张表放在一起,同时去查询,会得到一个结果,而这结果并不是我们想要的数据,这个结果称为笛卡尔积。</li>
<li>笛卡尔积缺点:查询到的结果冗余了,里面有很多错误的数据,需要过滤。</li>
<li>多表查询语法:<code>select * from 表名1,表名2;</code>
</li>
</ul>
<p><em>需求:查询两张表中关于水果的信息,要显示水果名称和水果价格</em></p>
<p><strong>表设计原则:将价格的主键作为水果的外键</strong></p>
<pre class="brush:php;toolbar:false">-- 多表查询语法(同时查询多个表获取到需要的数据)select * from 表名1,表名2;-- 查询价格(我们向查询水果对应的价格,需要将水果表和价格表同时进行查询;)select * from fruit,price;</pre>
<p>查询结果:<br><img src="https://img.php.cn/upload/article/000/000/067/efe2de8ea05d57aa111c2c60576fb57c-5.png" alt="在这里插入图片描述"><br><strong>2.笛卡尔积产生原因</strong><br><img src="https://img.php.cn/upload/article/000/000/067/efe2de8ea05d57aa111c2c60576fb57c-6.png" alt="在这里插入图片描述"><br><code>fruit</code>表中的每一条记录,都和<code>price</code>表中的每一条进行匹配连接。所得到的最终结果是:fruit表中的条目数乘以<code>price</code>表中的数据的条目数。</p>
<p><mark>将<code>fruit</code>表的每行记录和<code>price</code>表的每行记录组合的结果就是笛卡尔积</mark></p>
<p><strong>3.如何避免笛卡尔积</strong></p>
<p><mark>解决上述查询的方案:在查询两张表的同时添加条件进行过滤,比如fruit表的id和必须和price表的id相同</mark></p>
<pre class="brush:php;toolbar:false">-- 条件过滤笛卡尔积select * from fruit,price where fruit.price_id=price.id;</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/3b857041b4c528113c17679e4015a6b9-7.png" alt="在这里插入图片描述"></p>
<h2>内连接查询</h2>
<p><strong>1.什么是内连接</strong></p>
<blockquote><p>内连接查询又称为交集查询,也就是查询只显示满足条件的数据</p></blockquote>
<p><strong>2.显示内连接</strong></p>
<p>显示内连接:使用<code>INNER JOIN...ON</code>语句,可以省略<code>INNER</code>关键字</p>
<pre class="brush:php;toolbar:false">-- 语法核心select * from 表名1 inner join 表名2 on 条件;-- 或者select * from 表名1 join 表名2 on 条件;</pre>
<p><strong>3.隐式内连接</strong></p>
<p>看不到<code>JOIN</code>关键字,条件使用<code>WHERE</code>指定</p>
<pre class="brush:php;toolbar:false">select 列名,列名,... from 表名1,表名2 where 表名1.列名=表名2.列名;</pre>
<p><strong>4.示例</strong></p>
<p><em>查询水果的价格</em></p>
<pre class="brush:php;toolbar:false">-- 隐式内连接select * from fruit,price where fruit.price_id=price.id;-- 显式内连接select * from fruit inner join price on fruit.price_id=price.id;</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/3b857041b4c528113c17679e4015a6b9-8.png" alt="在这里插入图片描述"><br><em>查询苹果的信息,显示苹果的id,名字,价格</em></p>
<pre class="brush:php;toolbar:false">-- 方式1select fruit.id, fruit.name, price.pricefrom fruit,
pricewhere fruit.price_id = price.id and fruit.name = '苹果';-- 方式2select fruit.id, fruit.name, price.pricefrom fruit inner join
price on fruit.price_id = price.id and fruit.name = '苹果';</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/3b857041b4c528113c17679e4015a6b9-9.png" alt="在这里插入图片描述"><br><strong>5.总结</strong></p>
<p>1.内连接作用?</p>
<ul>
<li><p>过滤笛卡尔积</p></li>
<li><p>获取两表的交集部分(都满足条件的部分)</p></li>
</ul>
<p>2.什么是隐式内连接和显示内连接?</p>
<ul>
<li>隐式内连接:看不到JOIN:<code>select 列名,列名....from 表名1,表名2 where 表名1.列名=表名2.列名;</code>
</li>
<li>显示内连接:看得到JOIN:<code>select * from 表名1 inner join 表名2 on 条件;</code>
</li>
</ul>
<p>3.内连接查询步骤?</p>
<ul>
<li>1)确定查询几张表</li>
<li>2)确定表连接条件</li>
<li>3)根据需要在操作</li>
</ul>
<h2>外连接查询</h2>
<p><strong>1.左外连接</strong></p>
<ul>
<li>左表的记录全部显示出来</li>
<li>外表只会显示符合搜索条件的记录</li>
</ul>
<p>语法格式:</p>
<pre class="brush:php;toolbar:false">select * from 表1 left [outer] join 表2 on 条件;</pre>
<p>说明:</p>
<ul>
<li>
<code>left</code>关键字左边的表定义为<mark>左表</mark>,<code>left</code>关键字右边的表定义为<mark>右表</mark>,查询的内容以<mark>左表为主</mark>
</li>
<li>如果左表有数据,而右表没有数据对应的数据,仍然会把左表数据进行显示</li>
<li>
<code>outer</code>关键字可以省略</li>
</ul>
<p>练习:</p>
<p><em>不管能否查到水果对应价格,都要把水果显示出来</em></p>
<pre class="brush:php;toolbar:false">-- 左外连接查询select * from fruit left outer join price on fruit.price_id=price.id;</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/3b857041b4c528113c17679e4015a6b9-10.png" alt="在这里插入图片描述"><br><strong>2.右外连接</strong></p>
<ul>
<li>右表的记录全部表示出来</li>
<li>左表只会显示符合搜索条件的记录</li>
</ul>
<p>语法格式:</p>
<pre class="brush:php;toolbar:false">select * from 表名1 right [outer] join 表名2 on 条件;</pre>
<p>说明:</p>
<ul>
<li>
<code>right</code>关键字左边的表定义为左表,<code>right</code>关键字右边的表定义为右表,查询的内容以<mark>右表为主</mark>
</li>
<li>如果右表没有数据,而左表没有对应的数据,仍然会把右表数据进行显示</li>
<li>
<code>outer</code>关键字可以省略</li>
</ul>
<p>练习:</p>
<p><em>不管能否查到价格对应的水果,都要把价格显示出来</em></p>
<pre class="brush:php;toolbar:false">select * from fruit right outer join price on fruit.price_id=price.id;</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/bf39a634faa46618bcd456e099b97614-11.png" alt="在这里插入图片描述"><br> 总结:</p>
<p>1.掌握左外连接查询格式?</p>
<ul>
<li><code>select * from 表1 left outer join 表2 on 条件;</code></li>
<li>表1看作为左表,表2看做为右表</li>
</ul>
<p>2.左外连接查询特点?</p>
<ul><li>在满足要求的基础上保证左表的数据全部显示</li></ul>
<p>3.掌握右外连接查询格式?</p>
<ul><li><code>select * from 表1 right outer join 表2 on 条件;</code></li></ul>
<p>4.右外连接查询特点?</p>
<ul><li>在满足要求的基础上,保证右表的数据全部显示</li></ul>
<h2>嵌套查询(子查询)</h2>
<p><strong>1.什么是子查询</strong></p>
<p><mark><strong>一条查询语句结果作为另一条查询语法一部分。</strong></mark></p>
<pre class="brush:php;toolbar:false">SELECT 查询字段 FROM 表 WHERE 条件;举例:SELECT * FROM employee WHERE salary=(SELECT MAX(salary) FROM employee);</pre>
<p>说明:子查询需要放在()中</p>
<p>三种子查询情况:<code>单行单列</code>、<code>多行单列</code>、<code>多行多列</code>。</p>
<p><strong>2.单行单列</strong></p>
<p>子查询结果是<code>单列</code>,在<code>WHERE</code>后面作为<code>条件</code></p>
<pre class="brush:php;toolbar:false">SELECT 查询字段 FROM 表 WHERE 字段=(子查询);</pre>
<p>通常使用比较运算符: <code>></code> 、<code>>=</code> 、<code><</code>、<code><=</code>、<code>=</code>等</p><p><strong>3.多行单列</strong></p><p>子查询结果是多行单列,结果集类似于一个数组,在<code>WHERE</code>后面作为<code>条件</code>,父查询使用<code>IN</code>运算符</p><pre class="brush:php;toolbar:false">-- IN表示在数值中SELECT 查询字段 FROM 表 WHERE 字段 IN (子查询);</pre><p><strong>4.多行多列</strong></p><p>子查询结果是<code>多列</code>,在<code>FROM</code>后面作为<code>表</code></p><pre class="brush:php;toolbar:false">SELECT 查询字段 FROM (子查询) 表别名 WHERE 条件;</pre><p><mark>注意:子查询作为表需要取别名,使用as,可以省略,否则这张表没用名称无法访问表中的字段</mark></p><h2>事务操作</h2><h2>事务的概念</h2><p><strong>什么是事务</strong></p><p>在实际的业务开发中,有些业务操作要多次访问数据库。一个业务要发送多条SQL语句给数据库执行。需要将多次访问数据库的操作视为一个整体来执行,要么所有的SQL语句全部执行成功。如果其中有一条SQL语句失败,就进行事务的回滚,所有的SQL语句全部执行失败。</p><p>简而言之,事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败。</p><p>事务作用:<mark>保证在一个事务中多次操作数据库表中数据时,要么全都成功,要么全都失败。</mark></p><p><strong>事务的应用场景声明</strong></p><p>关于事务在实际中的应用场景:</p><p>假设我在淘宝买了一部手机,然后当我付完款,钱已经从我的账户中扣除。正当此时,淘宝转账系统宕机了,那么此时淘宝还没有收到钱,而我的账户的钱已经减少了,这样就会导致我作为买家钱已经付过,而卖家还没有收到钱,他们不会发货物给我。这样做显然是不合理。实际生活中是如果淘宝出问题,作为用户的账户中钱是不应该减少的。这样用户就不会损失钱。</p><p>还有种情况,就是当我付完款之后,卖家看到我付款成功,然后直接发货了,我如果有权限操作,我可以撤销,这样就会导致我的钱没有减少,但是卖家已经发货,同样这种问题在实际生活中也是不允许出现的。</p><p><strong>MySQL中可以有两种方式进行事务的操作:</strong></p><ul><li>手动提交事物:先开启,在提交</li><li>自动提交事物(默认的):即执行一条<code>sql</code>语句提交一次事物</li></ul><p><strong>数据准备</strong></p><pre class="brush:php;toolbar:false"># 创建账号表create table account(
id int primary key auto_increment,
name varchar(20),
money double);# 初始化数据insert into account values (null,'a',1000);insert into account values (null,'b',1000);</pre><h2>手动提交事务</h2><p><strong>手动提交事务有关的sql语句</strong></p><table><thead><tr class="firstRow"><th align="center">SQL语句</th><th>描述</th></tr></thead><tbody><tr><td align="center"><code>start transaction</code></td><td>开启手动控制事物</td></tr><tr><td align="center"><code>commit</code></td><td>提交事物</td></tr><tr><td align="center"><code>rollback</code></td><td>回滚事物</td></tr></tbody></table><p><strong>手动提交事务使用步骤</strong></p><ul><li>开启事务–>执行SQL语句–>成功–>提交事务<li>开启事务–>执行SQL语句–>失败–>回滚事务</li>
<p><img src="https://img.php.cn/upload/article/000/000/067/bf39a634faa46618bcd456e099b97614-12.png" alt="在这里插入图片描述"><br><strong>演示案例</strong>:演示提交事务,a给b转账100元</p>
<pre class="brush:php;toolbar:false">-- 1.开启事务start transaction;-- 2.执行sql语句update account set money=money-100 where name='a';update account set money=money+100 where name='b';-- 3.提交事务commit;</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/bf39a634faa46618bcd456e099b97614-13.png" alt="在这里插入图片描述"><br><strong>案例演示</strong>:演示回滚事务,a给b转账100元</p>
<pre class="brush:php;toolbar:false">-- 1.开启事务start transaction;-- 2.执行sql语句update account set money=money-100 where name='a';update account set money=money+100 where name='b';-- 3.回滚事务rollback;</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/bf39a634faa46618bcd456e099b97614-14.png" alt="在这里插入图片描述"><br><strong>注意:</strong></p>
<ul>
<li>提交事务(<code>commit</code>) :事务提交之后,<code>sql</code>语句对数据库产生的操作才会被永久的保存</li>
<li>事务的回滚(<code>rollback</code>):撤销已经成功执行的<code>sql</code>语句,回到开启事务之前的状态</li>
<li>只要提交事务,那么数据就会长久保存了,就不能回滚事务了。即提交或者回滚事务都是代表结束当前事务的操作</li>
</ul>
<h2>自动提交事务</h2>
<p>MySQL的每一条DML(增删改)语句都是一个单独的事务,每条语句都会自动开启一个事务,执行完毕自动提交事务,MySQL默认开始自动提交事务。自动提交,通过修改mysql全局变量<code>autocommit</code>进行控制。</p>
<p><strong>通过以下命令可以查看当前autocommit模式</strong></p>
<pre class="brush:php;toolbar:false">show variables like '%commit%';</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/d50eb658dc14e9bf60da47ceeaf721ba-15.png" alt="在这里插入图片描述"><br><strong>设置自动提交的参数为OFF</strong></p>
<pre class="brush:php;toolbar:false">set autocommit = 0; -- 0:OFF 1:ON</pre>
<p><img src="https://img.php.cn/upload/article/000/000/067/d50eb658dc14e9bf60da47ceeaf721ba-16.png" alt="在这里插入图片描述"><br><strong>案例演示</strong></p>
<pre class="brush:php;toolbar:false">-- 自动提交事务:每条sql语句就是一个事务,那么执行一条sql语句就会提交一次事务-- mysql数据库就是自动提交事务-- a给b转账100元update account set money=money-100 where name='a';update account set money=money+100 where name='b';-- 查看mysql是否自动提交事务-- autocommit的值是on表示自动提交事务,值是off表示关闭自动提交事务show variables like '%commit%';-- 我们可以使用命令临时设置mysql变为手动提交事务,即将自动提交事务关闭-- 下次重新连接mysql依然是自动提交事务set autocommit = 0; -- 0 表示关闭自动提交事务 1表示开启自动事务update account set money=money-100 where name='a'</pre>
<p><strong>注意:</strong></p>
<p>1)MySql默认自动提交。即执行一条sql语句提交一次事务。</p>
<p>2)设置autocommit为<code>off</code>状态,只是临时性的,下次重新连接mysql,autocommit依然变为<code>on</code>状态。</p>
<p>3)如果设置autocommit为<code>off</code>状态,那么当我们执行一条sql语句,就不会自动提交事务,重新启动可视化工具,数据并没有改变。</p>
<p>4)如果设置autocommit为<code>on</code>状态,如果我们先执行<code>start transaction</code> 然后在执行修改数据库的语句:</p>
<pre class="brush:php;toolbar:false">update account set money = money-100 where name='a';
update account set money = money+100 where name='b';</pre>
<p>那么此时就表示上述修改数据库的sql语句都在同一个事务中,此时必须手动提交事务,即<code>commit</code>;</p>
<p>换句话说,如果我们手动开启事务<code>start transaction</code>那么此时mysql就不会自动提交事务,必须手动提交事务。</p>
<p>5)如果设置autocommit为on状态,如果我们不执行<code>start transaction</code> 直接执行修改数据库的语句:</p>
<pre class="brush:php;toolbar:false">update account set money = money-100 where name='a';update account set money = money+100 where name='b';</pre>
<p>那么此时mysql就会自动提交事务,即上述每条sql语句就是一个事务</p>
<h2>事务原理和四大特征</h2>
<h2>事务原理</h2>
<p><img src="https://img.php.cn/upload/article/000/000/067/d50eb658dc14e9bf60da47ceeaf721ba-17.png" alt="在这里插入图片描述"><br><strong>原理说明</strong></p>
<ol>
<li>一个用户登录成功以后,服务器会创建一个临时日志文件。日志文件用来保存用户事务状态。</li>
<li>如果没有使用事务,则所有的操作直接写到数据库中,不会使用日志文件。</li>
<li>如果开启事务,将所有的写操作写到日志文件中。</li>
<li>如果这时用户提交了事务,则将日志文件中所有的操作写到数据库中。</li>
<li>如果用户回滚事务,则日志文件会被清空,不会影响到数据库的操作。</li>
</ol>
<h2>事务的四大特征</h2>
<p><strong>事务的四大特征(ACID)</strong></p>
<p>数据库的事务必须具备<strong>ACID</strong>特征,<strong>ACID</strong>是指<code>Atomicity</code>(原子性)、<code>Consistensy</code>(一致性)、<code>Isolation</code>(隔离性)和<code>Durabiliyt</code>(持久性)</p>
<p><strong>隔离性(Isolation)</strong></p>
<p>多个用户并发的访问数据库时,一个用户的事务不能被其他用户的事物干扰,多个并发的事务之间相互隔离<br><img src="https://img.php.cn/upload/article/000/000/067/1752463693924621c150302b0ef05d33-18.png" alt="在这里插入图片描述"><br><strong>持久性(Durability)</strong></p>
<p>指一个事务一旦被提交,它对数据库的改变是永久性的,哪怕数据库发生异常,重启之后数据依然会存在<br><img src="https://img.php.cn/upload/article/000/000/067/1752463693924621c150302b0ef05d33-19.png" alt="在这里插入图片描述"><br><strong>原子性(Atomicity)</strong></p>
<p>指事务包装的一组sql语句(一组业务逻辑)是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生</p>
<p><strong>一致性(Consistency)</strong></p>
<p>一致性是指数据处于一种语义上有意义且正确的状态;</p>
<p>事务一致性是指事务执行的结果必须是使数据从一个一致性状态变到另一个一致性状态。</p>
<p>事务的成功与失败,最终数据库的数据都是符合实际生活的<strong>业务逻辑</strong>。一致性绝大多数<strong>依赖业务逻辑</strong>和原子性<br><img src="https://img.php.cn/upload/article/000/000/067/1752463693924621c150302b0ef05d33-20.png" alt="在这里插入图片描述"></p>
<h2>事务的并发访问引发的三个问题(面试)</h2>
<p>事务在操作时的理想状态:多个事务之间互不影响,如果隔离级别设置不当就可能引发并发访问问题</p>
<table>
<thead><tr class="firstRow">
<th align="center">并发访问的问题</th>
<th align="left">含义</th>
</tr></thead>
<tbody>
<tr>
<td align="center">脏读</td>
<td align="left">一个事务读取到了另一个事务中尚<strong>未提交的数据</strong>。最严重,杜绝发生。</td>
</tr>
<tr>
<td align="center">不可重复读</td>
<td align="left">一个事务中两次读取的<strong>数据内容</strong>不一致,要求的是一个事务中多次读取时数据是不一致的,这是事务<strong>update时</strong>引发的问题</td>
</tr>
<tr>
<td align="center">幻读(虚读)</td>
<td align="left">一个事务内读取到了别的<strong>事务插入或者删除的数据</strong>,导致前后读取记录行数不同。这是<strong>insert或delete</strong>时引发的问题</td>
</tr>
</tbody>
</table>
<p><strong>1.脏读:指一个事务读取了另外一个事务未提交的数据。(非常危险)</strong><br><img src="https://img.php.cn/upload/article/000/000/067/1752463693924621c150302b0ef05d33-21.png" alt="在这里插入图片描述"><br><strong>2.不可重复读:在一个事务内多次读取表中的数据,多次读取的结果不同。</strong><br><img src="https://img.php.cn/upload/article/000/000/067/78eb789e1e140a75b7f32d4c6cf9bac7-22.png" alt="在这里插入图片描述"><br><strong>3.幻读(虚读):一个事务内读取到了别的事务插入或者删除的数据,导致前后读取记录行数不同</strong><br><img src="https://img.php.cn/upload/article/000/000/067/78eb789e1e140a75b7f32d4c6cf9bac7-23.png" alt="在这里插入图片描述"><br><strong>4.总结</strong></p>
<ul>
<li><p>赃读:一个事务读取另一个事务还没有提交的数据,一定避免。<br> 不可重复读:一个事务读取多次数据内容不一样,主要是update语句。事务已经提交了。 可以发生的。</p></li>
<li><p>幻读(虚读):一个事务读取多次数量不一样,主要是delete或者insert语句。事务已经提交了。可以发生的。</p></li>
</ul>
<h2>事务的隔离级别</h2>
<p>通过以上问题演示,我们发现如果不考虑事务的隔离性,会遇到脏读、不可重复读和虚读等问题。所以在数据库中我们要对上述三种问题进行解决。MySQL数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。</p>
<p><strong>事物隔离级别</strong></p>
<p>上面的级别最低,下面的级别最高。<code>是</code>表示会出现这种问题,<code>否</code>表示不会出现这种问题。</p>
<table>
<thead><tr class="firstRow">
<th>级别</th>
<th>名字</th>
<th>隔离级别</th>
<th>脏读</th>
<th>不可重复读</th>
<th>幻读</th>
<th>数据库默认隔离级别</th>
</tr></thead>
<tbody>
<tr>
<td>1</td>
<td>读未提交</td>
<td>read uncommitted</td>
<td>是</td>
<td>是</td>
<td>是</td>
<td><br></td>
</tr>
<tr>
<td>2</td>
<td>读已提交</td>
<td>read committed</td>
<td>否</td>
<td>是</td>
<td>是</td>
<td>Oracle和SQL Server</td>
</tr>
<tr>
<td>3</td>
<td>可重复读</td>
<td>repeatable read</td>
<td>否</td>
<td>否</td>
<td>是</td>
<td>MySQL</td>
</tr>
<tr>
<td>4</td>
<td>串行化</td>
<td>serializable</td>
<td>否</td>
<td>否</td>
<td>否</td>
<td><br></td>
</tr>
</tbody>
</table>
<p><strong>安全和性能对比</strong></p>
<ul>
<li><p>安全: 串行化>可重复读>读已提交>读未提交</p></li>
<li><p>性能: 串行化<可重复读<读已提交<读未提交</p></li>
</ul>
<blockquote>
<p>其实三个问题中,<strong>最严重的就是脏读</strong>(读取了错误数据),这个问题一定要避免;</p>
<p>关于不可重复读和虚读其实并不是<strong>逻辑上的错误</strong>,而是数据的<strong>时效性</strong>问题,所以这种问题并不属于很严重的错误;</p>
<p>如果对于数据的时效性要求不是很高的情况下,我们是可以接受不可重复读和虚读的情况发生的</p>
</blockquote>
<p>推荐学习:<a href="https://www.php.cn/course/list/51.html" target="_blank" textvalue="mysql视频教程">mysql视频教程</a></p></code></p>
以上是mysql设计概念及多表查询和事务操作的详细内容。更多信息请关注PHP中文网其他相关文章!