事务表和非事务表
事务表故名思义就是支持事务的表,支不支持事务和 MySQL 的存储类型有关,一般情况下,InnoDB
存储引擎的表是支持事务的,关于 InnoDB 的知识,我们会在后面详细介绍。
非事务表相应的就是不支持事务的表,在 MySQL 中,存储引擎 MyISAM
是不支持事务的,非事务表的特点是不支持回滚。
对于回滚的话,还要讲一点就是 SAVEPOINT
,它能指定事务回滚的一部分,但是不能指定事务提交的一部分。SAVEPOINT 可以指定多个,在满足不同条件的同时,回滚不同的 SAVEPOINT。需要注意的是,如果定义了两个相同名称的 SAVEPOINT,则后面定义的 SAVEPOINT 会覆盖之前的定义。如果 SAVEPOINT 不再需要的话,可以通过 RELEASE SAVEPOINT
来进行删除。删除后的 SAVEPOINT 不能再执行 ROLLBACK TO SAVEPOINT 命令。
我们通过一个示例来进行模拟不同的 SAVEPOINT
首先先启动一个事务 ,向 cxuan005 中插入一条数据,然后进行查询,那么是可以查询到这条记录的
start transaction; insert into cxuan005(id,info) values(666,'cxuan666'); select * from cxuan005 where id = 666;
查询之后的记录如下
然后我们定义一个 SAVEPOINT,如下所示
savepoint test;
然后继续插入一条记录
insert into cxuan005(id,info) values(777,'cxuan777');
此时就可以查询到两条新增记录了,id 是 666 和 777 的记录。
select * from cxuan005 where id = 777;
那么我们可以回滚到刚刚定义的 SAVEPOINT
rollback to savepoint test;
再次查询 cxuan005 这个表,可以看到,只有 id=666 的这条记录插入进来了,说明 id=777 这条记录已经被回滚了。
此时我们看到的都是 mysql01 中事务还没有提交前的状态,所以这时候 mysql02 中执行查询操作是看不到 666 这条记录的。
然后我们在 mysql01 中执行 commit 操作,那么此时在 mysql02 中就可以查询到这条记录了。
SQL 安全问题
SQL 安全问题应该是我们程序员比较忽视的一个地方了。日常开发中,我们一般只会关心 SQL 能不能解决我们的业务问题,能不能把数据查出来,而对于 SQL 问题,我们一般都认为这是 DBA 的活,其实我们 CRUD 程序员也应该了解一下 SQL 的安全问题。
SQL 注入简介
SQL 注入就是利用某些数据库的外部接口将用户数据插入到实际的 SQL 中,从而达到入侵数据库
的目的。SQL 注入是一种常见的网络攻击的方式,它不是利用操作系统的 BUG 来实现攻击的。SQL 主要是针对程序员编写时的疏忽来入侵的。
SQL 注入攻击有很大的危害,攻击者可以利用它读取、修改或者删除数据库内的数据,获取数据库中的用户名和密码,甚至获得数据库管理员的权限。并且 SQL 注入一般比较难以防范。
SQL Mode
MySQL 可以运行在不同的 SQL Mode 模式下,不同的 SQL Mode 定义了不同的 SQL 语法,数据校验规则,这样就能够在不同的环境中使用 MySQL ,下面我们就来介绍一下 SQL Mode。
SQL Mode 解决问题
SQL Mode 可以解决下面这几种问题
- 通过设置 SQL Mode,可以完成不同严格程度的数据校验,有效保障数据的准确性。
- 设置 SQL Mode 为
ANSI
模式,来保证大多数 SQL 符合标准的 SQL 语法,这样应用在不同数据库的迁移中,不需要对 SQL 进行较大的改变 - 数据在不同数据库的迁移中,通过改变 SQL Mode 能够更方便的进行迁移。
下面我们就通过示例来演示一下 SQL Mode 用法
我们可以通过
select @@sql_mode;
来查看默认的 SQL Mode,如下是我的数据库所支持的 SQL Mode
涉及到很多 SQL Mode,下面是这些 SQL Mode 的解释
ONLY_FULL_GROUP_BY
:这个模式会对 GROUP BY 进行合法性检查,对于 GROUP BY 操作,如果在SELECT 中的列,没有在 GROUP BY 中出现,那么将认为这个 SQL 是不合法的,因为列不在 GROUP BY 从句中
同样举个例子,我们现在查询一下 cxuan005 的 id 和 info 字段。
select id,info from cxuan005;
这样是可以运行的
然后我们使用 GROUP BY 字句进行分组,这里只对 info 进行分组,我们看一下会出现什么情况
select id,info from cxuan005 group by info;
我们可以从错误原因中看到,这条 SQL 语句是不符合 ONLY_FULL_GROUP_BY 的这条 SQL Mode 的。因为我们只对 info 进行分组了,没有对 id 进行分组,我们把 SQL 语句改成如下形式
select id,info from cxuan005 group by id,info;
这样 SQL 就能正确执行了。
当然,我们也可以删除 sql_mode = ONLY_FULL_GROUP_BY 的这条 Mode,可以使用
SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
来进行删除,删除后我们使用分组语句就可以放飞自我了。
select id,info from cxuan005 group by info;
但是这种做法只是暂时的修改,我们可以修改配置文件 my.ini 中的 sql_mode= STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
STRICT_TRANS_TABLES
:这就是严格模式,在这个模式下会对数据进行严格的校验,错误数据不能插入,报error 错误。如果不能将给定的值插入到事务表中,则放弃该语句。对于非事务表,如果值出现在单行语句或多行语句的第1行,则放弃该语句。
当使用 innodb 存储引擎表时,考虑使用 innodb_strict_mode 模式的 sql_mode,它能增量额外的错误检测功能。
NO_ZERO_IN_DATE
:这个模式影响着日期中的月份和天数是否可以为 0(注意年份是非 0 的),这个模式也取决于严格模式是否被启用。如果这个模式未启用,那么日期中的零部分被允许并且插入没有警告。如果这个模式启用,那么日期中的零部分插入被作为 0000-00-00
并且产生一个警告。
这个模式需要注意下,如果启用的话,需要 STRICT_TRANS_TABLES
和 NO_ZERO_IN_DATE
同时启用,否则不起作用,也就是
set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE';
然后我们换表了,使用 cxuan003 这张表,表结构如下
我们主要测试日期的使用,在 cxuan003 中插入一条日期为 0000-00-00
的数据
insert into cxuan003 values(111,'study','0000-00-00');
发现能够执行成功,但是把年月日各自变为 0 之后再进行插入,则会插入失败。
insert into cxuan003 values(111,'study','2021-00-00');
insert into cxuan003 values(111,'study','2021-01-00');
这些组合有很多,我这里就不再细致演示了,读者可以自行测试。
如果要插入 0000-00-00
这样的数据,必须设置 NO_ZERO_IN_DATE
和 NO_ZERO_DATE
。
ERROR_FOR_DIVISION_BY_ZERO
:如果这个模式未启用,那么零除操作将会插入空值并且不会产生警告;如果这个模式启用,零除操作插入空值并产生警告;如果这个模式和严格模式都启用,零除从操作将会产生一个错误。
NO_AUTO_CREATE_USER
:禁止使用 grant 语句自动创建用户,除非认证信息被指定。
NO_ENGINE_SUBSTITUTION
:此模式指定当执行 create 语句或者 alter 语句指定的存储引擎没有启用或者没有编译时,控制默认存储引擎的自动切换。默认是启用状态的。