SQL注入问题
| 怪像1:输对用户名就可以登录成功 |
| 怪像2:不需要对的用户名和密码也可以登录成功 |
| |
| SQL注入:利用特殊符合的组合产生特殊的含义 从而避开正常的业务逻辑 |
| select * from userinfo where name='jason' -- kasdjksajd' and pwd='' |
| select * from userinfo where name='xyz' or 1=1 -- aksdjasldj' and pwd='' |
| |
| 针对上述的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; |
| |
| 1.视图的表只能用来查询不能做其他增删改操作 |
| 2.视图尽量少用 会跟真正的表产生混淆 从而干扰操作者 |
触发器
| 达到某个条件之后自动触发执行 |
| 在MySQL中更加详细的说明是触发器:针对表继续增、删、改操作能够自动触发 |
| 主要有六种情况:增前、增后、删前、删后、改前、改后 |
| |
| create trigger 触发器的名字 before/after insert/update/delete on 表名 for each row |
| begin |
| sql语句 |
| end |
| |
| 1.触发器命名有一定的规律 |
| tri_before_insert_t1 |
| tri_after_delete_t2 |
| tri_after_update_t2 |
| 2.临时修改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') |
| ); |
| |
| CREATE TABLE errlog ( |
| id INT PRIMARY KEY auto_increment, |
| err_cmd CHAR (64), |
| err_time datetime |
| ); |
| |
| delimiter $$ |
| create trigger tri_after_insert_cmd after insert on cmd for each row |
| begin |
| if NEW.success = 'no' then |
| insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time); |
| end if; |
| end $$ |
| delimiter ; |
| |
| |
| 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'); |
| |
| |
| 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'; |
| update user set balance=1010 where name='kevin'; |
| update user set balance=1090 where name='tank'; |
| |
| |
| rollback; |
| |
| |
| commit; |
| |
| """ |
| 事务相关关键字 |
| start transaction; |
| rollback |
| commit |
| savepoint |
| """ |
| 在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改 |
| InnoDB支持所有隔离级别 |
| set transaction isolation level 级别 |
| 1.read uncommitted(未提交读) |
| 事务中的修改即使没有提交,对其他事务也都是可见的,事务可以读取未提交的数据,这一现象也称之为"脏读" |
| 2.read committed(提交读) |
| 大多数数据库系统默认的隔离级别 |
| 一个事务从开始直到提交之前所作的任何修改对其他事务都是不可见的,这种级别也叫做"不可重复读" |
| 3.repeatable read(可重复读) |
| 能够解决"脏读"问题,但是无法解决"幻读" |
| 所谓幻读指的是当某个事务在读取某个范围内的记录时另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录会产生幻行,InnoDB和XtraDB通过多版本并发控制(MVCC)及间隙锁策略解决该问题 |
| 4.serializable(可串行读) |
| 强制事务串行执行,很少使用该级别 |
| |
| |
| 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 n int, |
| out res int |
| ) |
| begin |
| select * from cmd where id > m and id < n; |
| set res=0; |
| end $$ |
| delimiter ; |
| |
| |
| set @res=10; 定义 |
| select @res; 查看 |
| call p1(1,5,@ res) 调用 |
| select @res 查看 |
| |
| """ |
| 查看存储过程具体信息 |
| show create procedure pro1; |
| 查看所有存储过程 |
| show procedure status; |
| 删除存储过程 |
| drop procedure pro1; |
| """ |
| |
| |
| |
| |
| set @res=10 |
| call p1(2,4,10); |
| call p1(2,4,@res); |
| |
| |
| select @res; |
| |
| |
| pymysql链接mysql |
| 产生的游表cursor.callproc('p1',(2,4,10)) |
| cursor.excute('select @_p1_2;') |
函数
| 可以看成是python中的内置函数 |
| |
| "ps:可以通过help 函数名 查看帮助信息!" |
| |
| Trim、LTrim、RTrim |
| |
| |
| Lower、Upper |
| |
| |
| Left、Right |
| |
| |
| Soundex |
| """ |
| eg:客户表中有一个顾客登记的用户名为J.Lee |
| 但如果这是输入错误真名其实叫J.Lie,可以使用soundex匹配发音类似的 |
| where Soundex(name)=Soundex('J.Lie') |
| """ |
| |
| |
| '''在MySQL中表示时间格式尽量采用2022-11-11形式''' |
| CREATE TABLE blog ( |
| id INT PRIMARY KEY auto_increment, |
| NAME CHAR (32), |
| sub_time datetime |
| ); |
| INSERT INTO blog (NAME, sub_time) |
| VALUES |
| ('第1篇','2015-03-01 11:31:21'), |
| ('第2篇','2015-03-11 16:31:21'), |
| ('第3篇','2016-07-01 10:21:31'), |
| ('第4篇','2016-07-22 09:23:21'), |
| ('第5篇','2016-07-23 10:11:11'), |
| ('第6篇','2016-07-25 11:21:31'), |
| ('第7篇','2017-03-01 15:33:21'), |
| ('第8篇','2017-03-01 17:32:21'), |
| ('第9篇','2017-03-01 18:31:21'); |
| select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m'); |
| |
| 1.where Date(sub_time) = '2015-03-01' |
| 2.where Year(sub_time)=2016 AND Month(sub_time)=07; |
| |
| adddate 增加一个日期 |
| addtime 增加一个时间 |
| datediff计算两个日期差值 |
流程控制
| |
| 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 ; |
索引相关概念
| 1)索引就好比一本书的目录,它能让你更快的找到自己想要的内容 |
| 2)让获取的数据更有目的性,从而提高数据库检索数据的性能 |
| |
| 索引在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*树...... |
| |
| 二叉树 |
| 二叉树里面还可以细分成很多领域 我们简单的了解即可 |
| 二叉意味着每个节点最大只能分两个子节点 |
| B树 |
| 所有的节点都可以存放完整的数据 |
| B+\*树 |
| 只有叶子节点才会存放真正的数据 其他节点只存放索引数据 |
| B+叶子节点增加了指向其他叶子节点的指针 |
| B*叶子节点和枝节点都有指向其他节点的指针 |
| |
| 辅助索引在查询数据的时候最会还是需要借助于聚集索引 |
| 辅助索引叶子节点存放的是数据的主键值 |
| |
| 有时候就算采用索引字段查询数据 也可能不会走索引!!! |
| 最好能记三个左右的特殊情况 |
慢查询优化
| explain |
| |
| 1)index 尽量避免 |
| 2)range |
| 3)ref |
| 4)eq_ref |
| 5)const |
| 6)system |
| 7)null |
测试索引
准备
| #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'; # 速度变快 |
总结:上面这些操作,你感兴趣可以敲一敲,不感兴趣你就可以不用敲了,权当看个乐呵。理论掌握了就行了
慢查询日志
设定一个时间检测所有超出该时间的sql语句,然后针对性的进行优化!
全文检索
| MySQL的全文检索功能MYISAM存储引擎支持而InnoDB存储引擎不支持 |
| 一般在创建表的时候启用全文检索功能 |
| create table t1( |
| id int primary key auto_increment, |
| content text |
| fulltext(content) |
| )engine=MyISAM; |
| |
| |
| 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; |
| |
安全管理
| 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 用户名; |
| |
| 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(可重复读) |
| 能够解决"脏读"问题,但是无法解决"幻读" |
| 所谓幻读指的是当某个事务在读取某个范围内的记录时另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录会产生幻行,InnoDB和XtraDB通过多版本并发控制(MVCC)及间隙锁策略解决该问题 |
| 4.serializable(可串行读) |
| 强制事务串行执行,很少使用该级别 |
锁
| 读锁(共享锁) |
| 多个用户同一时刻可以同时读取同一个资源互不干扰 |
| 写锁(排他锁) |
| 一个写锁会阻塞其他的写锁和读锁 |
| 死锁 |
| 1.多个事务试图以不同的顺序锁定资源时就可能会产生死锁 |
| 2.多个事务同时锁定同一个资源时也会产生死锁 |
| |
事务日志
| 事务日志可以帮助提高事务的效率 |
| 存储引擎在修改表的数据时只需要修改其内存拷贝再把该修改记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘 |
| 事务日志采用的是追加方式因此写日志操作是磁盘上一小块区域内的顺序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值,这表示在事务开始之后这行记录才被删除。 |
| """ |
转换表的引擎
| 主要有三种方式,并各有优缺点! |
| |
| alter table t1 engine=InnoDB; |
| """ |
| 适用于任何存储引擎 但是需要执行很长时间 MySQL会按行将数据从原表赋值到一张新的表中,在复制期间可能会消耗系统所有的IO能力,同时原表会加读锁 |
| """ |
| |
| """ |
| 使用mysqldump工具将数据导出到文件,然后修改文件中相应的SQL语句 |
| 1.引擎选项 |
| 2.表名 |
| """ |
| |
| """ |
| 综合了第一种方案的高效和第二种方案的安全 |
| 1.先创建一张新的表 |
| 2.利用insert ... select语法导数据 |
| 数据量不大这样做非常合适 数据量大可以考虑分批处理 针对每一段数据执行事务提交操作避免产生过多的undo |
| """ |
| ps:上述操作可以使用pt-online-schema-change(基于facebook的在线schema变更技术)工具,简单方便的执行上述过程 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码