MySQL-知识点补充
阅读原文时间:2023年07月09日阅读:1
简单实现利用数据库实现注册登录功能:
import pymysql

conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123',
    database='20230127th',
    charset='utf8mb4',
    autocommit=True
)

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
username = input('username>>>:').strip()
password = input('password>>>').strip()
sql = "select * from userinfo where username='%s' and password='%s'"%(username,password)
print(sql)
cursor.execute(sql)
res = cursor.fetchall()
if res:
    print('登陆成功')
else:
    print('用户名或密码错误')

但是会出现以上问题:按照以上格式输入可以利用sql语句的语法注释,把密码注掉,相当于不用输密码就可以登陆成功。这种现象叫做sql注入问题。解决
import pymysql

conn = pymysql.connect(
    host='127.0.0.1',
    user='root',
    password='123',
    database='db11',
    charset='utf8mb4',
    autocommit=True
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

username = input('请输入您的用户名>>>:').strip()
userpwd = input('请输入您的密码>>>:').strip()
sql = "select * from userinfo where username=%s and password=%s"
cursor.execute(sql,(username, userpwd))
'''
为了防止随意输入在mysql中产生注释导致登陆成功的行为,数据处理交给软件来做
'''
res = cursor.fetchall()
# print(res)  # [{'username': 'max', 'password': '123'}]
if res:
    print('登陆成功', res)
else:
    print('用户名或密码错误')


1.视图就是通过查询得到一张虚拟表,然后保存下来,下次直接使用。
语法结构:
create view teacher2course as select * from teacher inner join course on teacher.tid=course.teacher_id;
"""
1.视图的表只能用来查询不能用作其他增删改操作
2.在终端中添加视图之后可以看到视图被当做一个表新增在了库当中,而在Navicat中库中无法显示视图
3.视图尽量少用,会跟真正的表产生混淆,从而干扰操作者
"""


达到某个条件之后自动触发执行,在MySQL中更加详细的说明是触发器,针对表的增添、删、该操作能够自动触发主要有六种情况:增前、增后、删前、删后、改前、改后。
"""
主要是针对增删改做一些附加的操作,提醒修改的人达到这些门槛才能去增删改这个表。
"""
1.触发器语法结构:
    create trigger 触发器名字 before/after insert/update/delete on 表名 each row
begin
    sql 语句
end

2.触发器命名规则有一定规律:
tri_before_insert_t1
tri_after_delete_t2
tri_after_update_t2

3.临时修改SQL语句的结束符:
delimiter 其他符号  # 修改过之后;就失效,需要采用其他符号代码才执行
delimiter ;  # 使用完成之后要改回成;

4.触发器实际应用:
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;


1.事务:多个操作同时进行,那么同时成功,那么同时失败。

2.事物的特性:
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;
'''
执行事务只是改了内存的事务,所以硬盘中的数据没有被真正改掉
'''

# 开启事务之后,只要没有执行commit操作,数据其实都没有真正刷新到硬盘,commit操作之后文件会保存到硬盘当中
commit;

"""
事务相关关键字:
    start transaction:开启事务操作
    rollback:回滚到上一状态
    commit:确认数据修改
    savepoint:普通的事务rollback回退是撤销整步操作,如果是想在中间做几个标记,让事务撤回到指定的点就需要用到savepoint,也叫部分支持事务。但是savepoint不是完整的事务。
savepoint使用:创建占位符:saveppoint sp01;回退到占位符地址:rollback to sp01。
"""
隔离级别:事物的隔离性规定事务之间是独立的,但是在sql中可以修改其隔离级别。在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改:
InnoDB支持所有隔离级别
    set transaction isolation level 级别
1.read uncommitted(未提交读)
    事务中的修改即使没有提交,对其他事务也都是可见的,事务可以读取未提交的数据,这一现象也称之为"脏读"
2.read committed(提交读)
    大多数数据库系统默认的隔离级别
    一个事务从开始直到提交之前所作的任何修改对其他事务都是不可见的,这种级别也叫做"不可重复读"。事务中的数据如果没有最终提交,对其他事物可见的都是硬盘当中真正的数据。
3.repeatable read(可重复读)        # MySQL默认隔离级别
    能够解决"脏读"问题,但是无法解决"幻读"
  所谓幻读指的是当某个事务在读取某个范围内的记录时另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录会产生幻行,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表示这个参数必须只能是传入不能被返回出去
    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 p2(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中的内置函数
"""
ps:可以通过help 函数名查看帮助信息
"""
1.移除指定字符
Trim、LTrim、RTrim
Trim用法1:去掉左边的指定字符
select trim(leading 'x' from 'xxxbbbxxx');  # bbbxxx

Trim用法2:去掉左右边的指定字符
select trim(trailing 'x' from 'xxxbbbxxx');  # xxxbbb

Trim用法3:去掉两边的指定字符
select trim(both 'x' from 'xxxbbbxxx');  # bbb

RTrim:去掉字符当中左边的空格:
select rtrim('aaa    ');  # aaa

LTrim:去掉字符当中右边的空格:
mysql> select rtrim('aaa    ');

2.Soundex:匹配谐音字符(只适用于英文)
"""
eg:客户表中有一个顾客登记的用户名为J.Lee
        但如果这是输入错误真名其实叫J.Lie,可以使用soundex匹配发音类似的
        where Soundex(name)=Soundex('J.Lie')
"""

3.upper/lower:转化为大小写:
select upper/lower(字段名) from 表名;

4.切片操作
select left('jason',3);  # jas  # 从左往右拿到'jason'的前三个字符
select left(字段名,拿到的字符数)from 表名;  # 拿到表里该字段下所有数据的前指定位数个字符

5.日期操作:data_format:日期格式化
实现博客园上面具体日期发布的文章数量
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.select date_format(sub_time,'%Y-%m'),count(id) from blog where Date(sub_time) = '2015-03-01' group by date_format(sub_time,'%Y-%m');  # 查询2015年3月1日发布的篇数
2.where Year(sub_time)=2016 AND Month(sub_time)=07;  # 查询2016年7月发布的篇数

# 更多日期处理相关函数
    adddate 增加一个日期
    addtime 增加一个时间
    datediff计算两个日期差值


1.分支结构:
declare i int default 0;
IF i = 1 THEN
    SELECT 1;
ELSEIF i = 2 THEN
    SELECT 2;
ELSE
    SELECT 7;
END IF;

2.循环结构:
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 + sql语句,进行模拟优化器来执行分析。

从上往下依次从差变好:
all
index        尽量避免
range
ref
eq_ref
const
system
null


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';

准备

#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;

# 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变更技术)工具,简单方便的执行上述过程