SQL注入问题
简介
- 针对SQL注入的攻击行为可描述为通过用户可控参数中注入SQL语法,破坏原有SQL结构,达到编写程序意料之外结果的攻击行为。
其成因可归结为以下两个原理叠加造成:
-
程序编写者在处理程序和数据库交互时,使用字符串凭借的方式构造SQL语句
-
未对用户可控参数进行足够的过滤便将参数内容拼接进入到SQL语句中
怪像1:输对用户名就可以登录成功
怪像2:不需要对的用户名和密码也可以登录成功
select * from userinfo where name='jason' -- kasdjksajd' and pwd=''
select * from userinfo where name='xyz' or 1=1 -- aksdjasldj' and pwd=''
解决sql注入问题
- 针对上述的SQL注入问题 核心在于手动拼接了关键数据 交给execute处理即可
sql = " select * from userinfo where name=%s and pwd=%s "
cursor.execute(sql, (username, password))
'''
补充说明
executemany(sql,[(),(),(),()...])
'''
视图
视图就是通过查询得到一张虚拟表,然后保存下来,下次直接使用
create view teacher2course as
select * from teacher inner join course on teacher.tid = course.teacher_id;
- 视图的表只能用来查询不能做其他增删改操作
- 视图尽量少用 会跟真正的表产生混淆 从而干扰操作者
触发器
什么是触发器
- 达到某个条件之后自动触发执行
在MySQL中更加详细的说明是触发器:针对表继续增、删、改操作能够自动触发
主要有六种情况:增前、增后、删前、删后、改前、改后
创建触发器语法
create trigger 触发器的名字before/after insert/update/delete on 表名 for each row
begin
sql语句
end
- 触发器命名有一定的规律
tri_before_insert_t1
tri_after_delete_t2
tri_after_update_t2
- 临时修改SQL语句的结束符
因为有些操作中需要使用分号
触发器实际应用
CREATE TABLE cmd (
id INT PRIMARY KEY auto_increment,
USER CHAR (32),
priv CHAR (10),
cmd CHAR (64),
sub_time datetime, #提交时间
success enum ('yes', 'no') #0代表执行失败
);
CREATE TABLE errlog (
id INT PRIMARY KEY auto_increment,
err_cmd CHAR (64),
err_time datetime
);
delimiter $$ # 将mysql默认的结束符由;换成$$
create trigger tri_after_insert_cmd after insert on cmd for each row
begin
if NEW.success = 'no' then # 新记录都会被MySQL封装成NEW对象
insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
end if;
end $$
delimiter ; # 结束之后记得再改回来,不然后面结束符就都是$$了
#往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志
INSERT INTO cmd (
USER,
priv,
cmd,
sub_time,
success
)
VALUES
('kevin','0755','ls -l /etc',NOW(),'yes'),
('kevin','0755','cat /etc/passwd',NOW(),'no'),
('kevin','0755','useradd xxx',NOW(),'no'),
('kevin','0755','ps aux',NOW(),'yes');
# 查询errlog表记录
select * from errlog;
# 查看所有的触发器
show triggers;
# 删除触发器
drop trigger tri_after_insert_cmd;
事务
事务是什么?
$多个操作同时进行 那么同时成功 那么同时失败 这就是事务$
事务的四大特性(ACID)
- A:
原子性
事务中的各项操作是不可分割的整体 要么同时成功要么同时失败
- C:
一致性
使数据库从一个一致性状态变到另一个一致性状态 - I:
隔离性
多个事务之间彼此不干扰 - D:
持久性
也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的 - 注意:只有当前三条性质都满足了,才能保证事务的一致性
create table user(
id int primary key auto_increment,
name char(32),
balance int
);
insert into user(name,balance)
values
('jason',1000),
('kevin',1000),
('tank',1000);
# 修改数据之前先开启事务操作
start transaction;
# 修改操作
update user set balance=900 where name='jason'; #买支付100元
update user set balance=1010 where name='kevin'; #中介拿走10元
update user set balance=1090 where name='tank'; #卖家拿到90元
# 回滚到上一个状态
rollback;
# 回滚到上一个状态
rollback;
# 开启事务之后,只要没有执行commit操作,数据其实都没有真正刷新到硬盘
commit;
"""
事务相关关键字
start transaction;
rollback
commit # 提交事务处理
savepoint
"""
事物的隔离级别
mysql中的四种事务隔离级别如下:
-
read uncommitted
(读未提交数据):允许事务读取未被其他事务提交的变更(脏读、不可重复读和幻读的问题都会出现) -
read committed
(读已提交数据):只允许事务读取已经被其他事务提交的变更(可以避免脏读,但不可重复读和幻读的问题仍然可能出现) -
repeatable read
(可重复读):确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新(update).(可以避免脏读和不可重复读,但幻读仍然存在) -
serializable
(串行化):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,所有并发问题都可避免,但性能十分低下(因为你不完成就都不可以弄,效率太低)了解:
oracle支持两种事务隔离级别:read committed、serializable oracle默认的事务隔离级别是:read committed mysql的默认事务隔离级别是:repeatable read MVCC只能在read committed(提交读)、repeatable read(可重复读)两种隔离级别下工作,其他两个不兼容(read uncommitted:总是读取最新 serializable:所有的行都加锁) InnoDB的MVCC通过在每行记录后面保存两个隐藏的列来实现MVCC 一个列保存了行的创建时间 一个列保存了行的过期时间(或删除时间) # 本质是系统版本号 每开始一个新的事务版本号都会自动递增,事务开始时刻的系统版本号会作为事务的版本号用来和查询到的每行记录版本号进行比较 例如 刚插入第一条数据的时候,我们默认事务id为1,实际是这样存储的 username create_version delete_version jason 1 可以看到,我们在content列插入了kobe这条数据,在create_version这列存储了1,1是这次插入操作的事务id。 然后我们将jason修改为jason01,实际存储是这样的 username create_version delete_version jason 1 2 jason01 2 可以看到,update的时候,会先将之前的数据delete_version标记为当前新的事务id,也就是2,然后将新数据写入,将新数据的create_version标记为新的事务id 当我们删除数据的时候,实际存储是这样的 username create_version delete_version jason01 2 3 """ 由此当我们查询一条记录的时候,只有满足以下两个条件的记录才会被显示出来: 1.当前事务id要大于或者等于当前行的create_version值,这表示在事务开始前这行数据已经存在了。 2.当前事务id要小于delete_version值,这表示在事务开始之后这行记录才被删除。 """
存储过程
- 可以看成是python中的自定义函数
# 无参函数
delimiter $$
create procedure p1()
begin
select * from cmd;
end $$
delimiter ;
# 调用
call p1()
# 有参函数
delimiter $$
create procedure p2(
in m int, # in表示这个参数必须只能是传入不能被返回出去
in n int,
out res int # out表示这个参数可以被返回出去,还有一个inout表示即可以传入也可以被返回出去
)
begin
select * from cmd where id > m and id < n;
set res=0; # 用来标志存储过程是否执行
end $$
delimiter ;
# 针对res需要先提前定义
set @res=10; 定义
select @res; 查看
call p1(1,5,@res) 调用
select @res 查看
"""
查看存储过程具体信息
show create procedure pro1;
查看所有存储过程
show procedure status;
删除存储过程
drop procedure pro1;
"""
# 大前提:存储过程在哪个库下面创建的只能在对应的库下面才能使用!!!
# 1、直接在mysql中调用
set @res=10 # res的值是用来判断存储过程是否被执行成功的依据,所以需要先定义一个变量@res存储10
call p1(2,4,10); # 报错
call p1(2,4,@res);
# 查看结果
select @res; # 执行成功,@res变量值发生了变化
# 2、在python程序中调用
pymysql链接mysql
产生的游表cursor.callproc('p1',(2,4,10)) # 内部原理:@_p1_0=2,@_p1_1=4,@_p1_2=10;
cursor.excute('select @_p1_2;')
函数
- 可以看成是python中的内置函数
字符函数
1.length(str):统计字符串的字节数(取决于编码方式,utf8汉字3字节,gbk汉字2字节)
2.concat(str1, str2):拼接字符
3.substr/substring(str, n1 [,n2]):切割字符,n1起始位置(mysql下标从1开始),n2可选,表示切割长度
4.instr(str1, str2):返回str2在str1中首次出现的位置;如果没有找到,则返回0。不区分大小写。
5.trim(str [, substr]):取出字符串前后的substr字符串,默认为空格
6.upper(str):字母变大写
7.lower(str):字母变小写
8.lpad(str1,length,str2)。其中str1是第一个字符串,length是结果字符串的长度,str2是一个填充字符串。如果str1的长度没有length那么长,则使用str2填充;如果str1的长度大于length,则截断。
9.rpad(str1, length, str2):参考lpad
10.replace(object,search,replace):把object对象中出现的的search全部替换成replace。
数学函数
1.round(num, 2):四舍五入,保留两位小数
2.ceil(num):向上取整
3.floor(num):向下取整
4.truncate(num, 2):从小数点后两位直接截断,比truncate(3.1415926, 3) = 3.141
5.mod(num, 2):求余数 num % 2
日期函数
1.now():当前系统时间,1997-06-03 19:23:12
2.curdate():当前系统时间的日期,1997-06-03
3.curtime():当前系统时间的时间,19:23:12
4.year():年份,1997
5.month():月,6
6.monthname():英文形式,June
7.day():日, 3
8.hour():小时, 19
9.minute():分钟, 23
10.second():秒, 12
11.str_to_date(“1997-06-03 19:23:12”, "%Y-%m-%d %H:%i:%s):通过后面的模式将前面的字符串进行转换成date格式
12.date_format(“1997/6/3”, “%Y年%m月%d日”):将日期转为字符,1997年6月3日
13.datediff(date1, date2):
流程控制
流程就分为三大类:
顺序结构
: 程序从上往下依次执行分支结构
:程序按条件进行选择执行,从两条或多条路径中选择一条执行循环结构
: 程序满足一定条件下,重复执行一组语句
注意: 针对于MySQL的流程控制语句主要有 3 类
条件判断语句
:IF语句和 CASE 语句循环语句
: LOOP,WHILE 和 REPEAR 语句跳转语句
: ITERATE 和 LEAVE 语句
分支结构
declare i int default 0;
IF i = 1 THEN
SELECT 1;
ELSEIF i = 2 THEN
SELECT 2;
ELSE
SELECT 7;
END IF;
循环结构
DECLARE num INT ;
SET num = 0 ;
WHILE num < 10 DO
SELECT num ;
SET num = num + 1 ;
END WHILE ;
索引相关概念
什么是索引
索引是存储引擎用于提高数据库表的访问速度的一种数据结构
索引的优缺点
优点:
- 加快数据查找的速度
- 为用来排序或者是分组的字段添加索引,可以加快分组和排序的速度
- 加快表与表之间的连接
缺点:
- 建立索引需要占用物理空间
- 会降低表的增删改的效率,因为每次对表记录进行增删改,需要进行动态维护索引,导致增删改时间变长
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构
* primary key
* unique key
* index key
1.上述的三个key都可以加快数据查询
2.primary key和unique key除了可以加快查询本身还自带限制条件而index key很单一就是用来加快数据查询
3.外键不属于索引键的范围 是用来建立关系的 与加快查询无关
索引加快查询的本质
id int primary key auto_increment,
name varchar(32) unique,
province varchar(32)
age int
phone bigint
select name from userinfo where phone=18818888888; # 一页页的翻
select name from userinfo where id=99999; # 按照目录确定页数找
聚集索引(primary key)
主键、主键索引
辅助索引(unique,index)
除主键意外的都是辅助索引
覆盖索引
select name from user where name='jason';
非覆盖索引
select age from user where name='jason';
索引数据结构
简介
索引底层其实是树结构>>>:树是计算机底层的数据结构
树有很多中类型:二叉树、b树、b+树、B*树......
二叉树
- 顾名思义,二叉搜索树,即指最多拥有两个叉,这里的叉即为所有非叶子结点的儿子(Lift和Right);
- 所有的结点存储一个关键字;
- 非叶子结点的左指针指向小于其关键字的结点,右指针指向对于其关键字的结点
- 二叉搜索树的搜索,从根结点开始,如果查询的关键字与结点关键字相等,则该结点为查询的结点,如果查询关键字比结点关键字小,则进入左子树,反之则进入右子树;如果左子树为空或者右子树为空,则返回查找不到响应的关键字
B树
B-树就是B树(可能有部分人会习惯上把B-树读为B减树,其实并不存在B减树,只是读法上的不同而已),B就是balanced
,平衡的意思。B-树就是指的B树
B+树
B+树是对B树的一种变形树,它与B树的差异在于:
- 有k个子结点的结点必然有k个关键码
- 非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中
- 树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录
B+树和B树的区别
- B树:有序数组+平衡多叉树;
- B+树:有序数组*链表*+平衡多叉树;
但是B树也有优点,其优点在于,由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速
B*树
B树是B+树一种变形 它是在B+树的基础上 将索引层以指针连接起来 使搜索取值更加快捷
B树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为2/3代替B+树的1/2);
B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;
树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;
所以B树相对于B+树,空间利用率上有所提高,查询速率也有所提高
总结:
二叉搜索树:二叉树,每个结点只存储一个关键字且值大于左子树,小于右子树。
B(B-)树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点; 所有关键字在整颗树
中出现,且只出现一次,非叶子结点可以命中;
B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点
中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率
从1/2提高到2/3;
慢查询优化
type | 含义 |
---|---|
index | Full Index Scan,index 与 ALL 区别为 index 类型只遍历索引树 |
range | 索引范围扫描,常见于 between、<、> 等的查询 |
ref | 非唯一性索引扫描,返回匹配某个单独值的所有记录,本质上也是一种索引访问 |
eq_ref | 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描 |
const | 通过主键或者唯一索引来定位一条记录 |
system | system 是 const 类型的特例,当查询的表只有一行的情况下,使用 system |
null | MySQL 在优化过程中分解语句,执行时甚至不用访问表或索引 |
- 从上到下,性能从差到好,一般来说需要保证查询至少达到 range 级别, 最好达到 ref
测试索引
1.准备表
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);
2.创建存储过程,实现批量插入记录
delimiter $$ #声明存储过程的结束符号为$$
create procedure auto_insert1()
BEGIN
declare i int default 1;
while(i<3000000)do
insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));
set i=i+1;
end while;
END$$ #$$结束
delimiter ; #重新声明分号为结束符号
3.查看存储过程
show create procedure auto_insert1\G
4.调用存储过程
call auto_insert1();
# 表没有任何索引的情况下
select * from s1 where id=30000;
# 避免打印带来的时间损耗
select count(id) from s1 where id = 30000;
select count(id) from s1 where id = 1;
# 给id做一个主键
alter table s1 add primary key(id); # 速度很慢
select count(id) from s1 where id = 1; # 速度相较于未建索引之前两者差着数量级
select count(id) from s1 where name = 'jason' # 速度仍然很慢
"""
范围问题
"""
# 并不是加了索引,以后查询的时候按照这个字段速度就一定快
select count(id) from s1 where id > 1; # 速度相较于id = 1慢了很多
select count(id) from s1 where id >1 and id < 3;
select count(id) from s1 where id > 1 and id < 10000;
select count(id) from s1 where id != 3;
alter table s1 drop primary key; # 删除主键 单独再来研究name字段
select count(id) from s1 where name = 'jason'; # 又慢了
create index idx_name on s1(name); # 给s1表的name字段创建索引
select count(id) from s1 where name = 'jason' # 仍然很慢!!!
"""
再来看b+树的原理,数据需要区分度比较高,而我们这张表全是jason,根本无法区分
那这个树其实就建成了“一根棍子”
"""
select count(id) from s1 where name = 'xxx';
# 这个会很快,我就是一根棍,第一个不匹配直接不需要再往下走了
select count(id) from s1 where name like 'xxx';
select count(id) from s1 where name like 'xxx%';
select count(id) from s1 where name like '%xxx'; # 慢 最左匹配特性
# 区分度低的字段不能建索引
drop index idx_name on s1;
# 给id字段建普通的索引
create index idx_id on s1(id);
select count(id) from s1 where id = 3; # 快了
select count(id) from s1 where id*12 = 3; # 慢了 索引的字段一定不要参与计算
drop index idx_id on s1;
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';
# 针对上面这种连续多个and的操作,mysql会从左到右先找区分度比较高的索引字段,先将整体范围降下来再去比较其他条件
create index idx_name on s1(name);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 并没有加速
drop index idx_name on s1;
# 给name,gender这种区分度不高的字段加上索引并不难加快查询速度
create index idx_id on s1(id);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 快了 先通过id已经讲数据快速锁定成了一条了
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 慢了 基于id查出来的数据仍然很多,然后还要去比较其他字段
drop index idx_id on s1
create index idx_email on s1(email);
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 快 通过email字段一剑封喉
联合索引
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';
# 如果上述四个字段区分度都很高,那给谁建都能加速查询
# 给email加然而不用email字段
select count(id) from s1 where name='jason' and gender = 'male' and id > 3;
# 给name加然而不用name字段
select count(id) from s1 where gender = 'male' and id > 3;
# 给gender加然而不用gender字段
select count(id) from s1 where id > 3;
# 带来的问题是所有的字段都建了索引然而都没有用到,还需要花费四次建立的时间
create index idx_all on s1(email,name,gender,id); # 最左匹配原则,区分度高的往左放
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 速度变快
全文检索
MySQL的全文检索功能MYISAM存储引擎支持而InnoDB存储引擎不支持
一般在创建表的时候启用全文检索功能
create table t1(
id int primary key auto_increment,
content text
fulltext(content)
)engine=MyISAM;
# match括号内的值必须是fulltext括号中定义的(单个或者多个)
select content from t1 where match(content) against('jason')
'''上述语句可以用like实现但是查询出来的结果顺序不同 全文检索会以文本匹配的良好程度排序数据再返回效果更佳'''
# 查询扩展
select note_text from productnotes where Math(note_text) Against('jason' with query expansion);
"""
返回除jason外以及其他jason所在行相关文本内容行数据
eg:
jason is handsome and cool,every one want to be cool,tony want to be more handsome;
二三句虽然没有jason关键字 但是含有jason所在行的cool和handsome
"""
# 布尔文本搜索
即使没有定义fulltext也可以使用,但是这种方式非常缓慢性能低下
select note_text from productnotes where Match(note_text) Against('jason' in boolean mode);
# 注意事项
1.三个及三个以下字符的词视为短词,全文检索直接忽略且从索引中排除
2.MySQL自身自带一个非用词列表,表内词默认均被忽略(可以修改该列表)
3.出现频率高于50%的词自动作为非用词忽略,该规则不适用于布尔搜索
4.针对待搜索的文本内容不能少于三行,否则检索不返回任何结果
5.单引号默认忽略
插入数据
数据库经常被多个用户访问,insert操作可能会很耗时(特别是有很多索引需要更新的时候)而且还可能降低等待处理的select语句性能
如果数据检索是最重要的(一般都是),则可以通过在insert与into之间添加关键字low_priority指示MySQL降低insert语句优先级
insert low_priority into
insert还可以将一条select语句的结果插入表中即数据导入:insert select
eg:想从custnew表中合并数据到customers表中
insert into customers(contact,email) select contact,email from custnew;
更新数据
如果使用update语句更新多列值,并且在更新这些列中的一列或者多列出现一个错误会导致整个update操作被取消,如果想发生错误也能继续执行没有错误的更新操作可以采用
update ignore custmoers ...
"""
update ignore set name='jason1',id='a' where id=1;
name字段正常修改
update set name='jason2',id='h' where id=1;
全部更新失败
"""
删除数据
delete语句从表中删除数据,甚至可以是所有数据但是不会删除表本身
并且如果想从表中删除所有的行不要使用delete可以使用truncate速度更快并且会重置主键值(实际是删除原来的表并重新创建一个表而不是逐行删除表中的数据)
主键
查看当前表主键自增到的值(表当前主键值减一)
select last_insert_id();
外键
MySQL存储引擎可以混用,但是外键不能跨引擎即使用一个引擎的表不能引用具有使用不同引擎表的外键
重命名表
rename关键字可以修改一个或者多个表名
rename table customer1 to customer2;
rename table back_cust to b_cust,
back_cust1 to b_cust1,
back_cust2 to b_cust2;
事务
MySQL提供两种事务型存储引擎InnoDB和NDB cluster及第三方XtraDB、PBXT
事务处理中有几个关键词汇会反复出现
事务(transaction)
回退(rollback)
提交(commit)
保留点(savepoint)
为了支持回退部分事务处理,必须能在事务处理块中合适的位置放置占位符,这样如果需要回退可以回退到某个占位符(保留点)
创建占位符可以使用savepoint
savepoint sp01;
回退到占位符地址
rollback to sp01;
# 保留点在执行rollback或者commit之后自动释放
安全管理
1.创建用户
create user 用户名 identified by '密码';
"""修改密码"""
set password for 用户名 = Password('新密码');
set password = Password('新密码'); # 针对当前登录用户
2.重命名
rename user 新用户名 to 旧用户名;
3.删除用户
drop user 用户名;
4.查看用户访问权限
show grants for 用户名;
5.授予访问权限
grant select on db1.* to 用户名;
# 授予用户对db1数据库下所有表使用select权限
6.撤销权限
revoke select on db1.* from 用户名;
"""
整个服务器
grant all/revoke all
整个数据库
on db.*
特定的表
on db.t1
"""
隔离级别
在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改
InnoDB支持所有隔离级别
set transaction isolation level 级别
1.read uncommitted(未提交读)
事务中的修改即使没有提交,对其他事务也都是可见的,事务可以读取未提交的数据,这一现象也称之为"脏读"
2.read committed(提交读)
大多数数据库系统默认的隔离级别
一个事务从开始直到提交之前所作的任何修改对其他事务都是不可见的,这种级别也叫做"不可重复读"
3.repeatable read(可重复读) # MySQL默认隔离级别
能够解决"脏读"问题,但是无法解决"幻读"
所谓幻读指的是当某个事务在读取某个范围内的记录时另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录会产生幻行,InnoDB和XtraDB通过多版本并发控制(MVCC)及间隙锁策略解决该问题
4.serializable(可串行读)
强制事务串行执行,很少使用该级别
锁
读锁(共享锁)
多个用户同一时刻可以同时读取同一个资源互不干扰
写锁(排他锁)
一个写锁会阻塞其他的写锁和读锁
死锁
1.多个事务试图以不同的顺序锁定资源时就可能会产生死锁
2.多个事务同时锁定同一个资源时也会产生死锁
# Innodb通过将持有最少行级排他锁的事务回滚
事务日志
事务日志可以帮助提高事务的效率
存储引擎在修改表的数据时只需要修改其内存拷贝再把该修改记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘
事务日志采用的是追加方式因此写日志操作是磁盘上一小块区域内的顺序IO而不像随机IO需要次哦按的多个地方移动磁头所以采用事务日志的方式相对来说要快的多
事务日志持久之后内存中被修改的数据再后台可以慢慢刷回磁盘,目前大多数存储引擎都是这样实现的,通常称之为"预写式日志"修改数据需要写两次磁盘
MVCC多版本控制
MVCC只能在read committed(提交读)、repeatable read(可重复读)两种隔离级别下工作,其他两个不兼容(read uncommitted:总是读取最新 serializable:所有的行都加锁)
InnoDB的MVCC通过在每行记录后面保存两个隐藏的列来实现MVCC
一个列保存了行的创建时间
一个列保存了行的过期时间(或删除时间) # 本质是系统版本号
每开始一个新的事务版本号都会自动递增,事务开始时刻的系统版本号会作为事务的版本号用来和查询到的每行记录版本号进行比较
例如
刚插入第一条数据的时候,我们默认事务id为1,实际是这样存储的
username create_version delete_version
jason 1
可以看到,我们在content列插入了kobe这条数据,在create_version这列存储了1,1是这次插入操作的事务id。
然后我们将jason修改为jason01,实际存储是这样的
username create_version delete_version
jason 1 2
jason01 2
可以看到,update的时候,会先将之前的数据delete_version标记为当前新的事务id,也就是2,然后将新数据写入,将新数据的create_version标记为新的事务id
当我们删除数据的时候,实际存储是这样的
username create_version delete_version
jason01 2 3
"""
由此当我们查询一条记录的时候,只有满足以下两个条件的记录才会被显示出来:
1.当前事务id要大于或者等于当前行的create_version值,这表示在事务开始前这行数据已经存在了。
2.当前事务id要小于delete_version值,这表示在事务开始之后这行记录才被删除。
"""
转换表的引擎
主要有三种方式,并各有优缺点!
# 1.alter table
alter table t1 engine=InnoDB;
"""
适用于任何存储引擎 但是需要执行很长时间 MySQL会按行将数据从原表赋值到一张新的表中,在复制期间可能会消耗系统所有的IO能力,同时原表会加读锁
"""
# 2.导入导出
"""
使用mysqldump工具将数据导出到文件,然后修改文件中相应的SQL语句
1.引擎选项
2.表名
"""
# 3.insert ... select
"""
综合了第一种方案的高效和第二种方案的安全
1.先创建一张新的表
2.利用insert ... select语法导数据
数据量不大这样做非常合适 数据量大可以考虑分批处理 针对每一段数据执行事务提交操作避免产生过多的undo
"""
ps:上述操作可以使用pt-online-schema-change(基于facebook的在线schema变更技术)工具,简单方便的执行上述过程
标签:结点,name,数据库,事务,完结,MySQL,where,id,select
From: https://www.cnblogs.com/oiqwyig/p/16936480.html