MySQL数据库 #5

文章目录

一、Python操作MySQL

MySQL本身就是一款C/S架构,有服务端、有客户端,自身带了有客户端:mysql.exe
Python这门语言成为了MySQL的客户端(对于一个服务端来说,客户端可以有很多)

1.pymysql的基本操作

import pymysql  需要导入pymysql模块,没有该模块的需要自行安装

# 1.连接MySQL服务端
conn = pymysql.connect(  # 实例化出一个对象
    host='127.0.0.1',   # 本地地址
    port=3306,          # 默认端口号
    user='root',		# 用户权限
    password='123123',  # 登录密码
    db='school',  # 只能指定一个库‘school’,不能做全局
    charset='utf8mb4'   # 字符编码
)
# 2.产生一个游标对象,游标对象就是我们在终端中输入命令的那一个光标栏
# cursor = conn.cursor()  # 括号内不写参数,数据是元组,指定性不强
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)  # 加了参数后变成了字典数据,KV键值对,指定性变强了

# 3.编写SQL语句
sql = 'select * from teacher;'  # 在pycharm中会出现语句包黄提示,不用管

# 4.发送SQL语句
base = cursor.execute(sql)  # execute也有返回值,接受的是SQL语句影响的行数
print(base)

# 5.获取SQL语句执行之后的结果
res = cursor.fetchall()  # 获取全部内容
print(res)

2.pymysql补充说明

1.查看补充

fetchone() 获取所有结果
fetchall() 获取结果的第一个数据集
fetchmany() 获取指定数量的结果

注意三者都有类似于文件光标的移动特性

2. 增删改,自动确认

autocommit=True 直接添加在配置中

3.SQL注入问题

import pymysql

# 1.连接服务端
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123123',
    db='db11',
    charset='utf8mb4',
    autocommit=True
)

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)  # 2.产生游标对象

# 3.编写登录功能
in_use = input('username>>>>')
in_pwd = input('password>>>>')

sql = "select * from userinfo where username = '%s' and password = '%s' " % (in_use, in_pwd)  # 验证账号密码是否存在

cursor.execute(sql)  # 获取执行结果
res = cursor.fetchall()  # 获取所有结果
if res:
    print(res)
    print('登录成功')

else:
    print('登录失败')
1.输入对的用户名就可登录

在这里插入图片描述
在这里插入图片描述
当拷贝到Navicat里查询的时候会发现从–的数据都变成了注释

2.输入错的用户名也可以登录

在这里插入图片描述
在这里插入图片描述
当拷贝到Navicat里查询的时候会发现or只要有一个条件成立就行,从–的数据都变成了注释

结论:SQL注入利用特殊符号的组合产生特殊含义,从而避开正常的业务逻辑

针对上述的SQL注入问题,核心在于手动拼接了关键数据

解决办法就是交个execute来帮你完成拼接
把占位符的引号去掉,只需要留%s即可

sql = "select * from userinfo where username = %s and password = %s "  # 验证账号密码是否存在

cursor.execute(sql, (in_use, in_pwd))  # 获取执行结果

这样可完美解决SQL注入问题

二、视图

1.什么是视图

视图就是通过查询得到一张虚拟表,然后保存下来,下次直接使用即可。

2. 为什么要用视图

如果要频繁使用一张虚拟表,可以不用重复查询。

3.如何用视图

正常两张表连接情况(拼接)查询 use db4

create view work2department as
select * from work inner join department on work.dep_id = department.id

4.引入视图的作用

当我们频繁的去查询连表,反复拼接,会出现繁琐的现象,影响效率

5.解决办法

我们可以使用视图来进行操作,将拼接的连表保存起来

create view work2department as select * from work inner join department on work.dep_id = department.id

视图的应用

1.创建视图 use db4
create view 创建视图名字 as 执行连表的SQL语句
create view work2department as select * from work inner join department on work.dep_id = department.id;

注意:当字段名相同时,会产生冲突,导致无法建立视图
 alter table department change id cid int;  # 修改字段名

在这里插入图片描述

2.查询视图层
select * from 视图名字
	eg: select * from work2department;

在这里插入图片描述

3.查询Navicat视图层位置

在这里插入图片描述

4.删除是图表
删除视图表格式:
	drop view 视图名;
5. 使用视图注意事项(强调)

1.在硬盘中,视图只有表结构文件,没有表数据文件(数据来源于 )

2.视图通常用于查询,尽量不要修改视图中的数据(修改视图回影响到原表)

3.开发过程中能不会去使用视图的,因为视图时MySQL的功能,如果你的项目里大量使用视图,那么意味着你后期想要扩展某个功能的时候,这个功能恰好又需要视图进行修改,意味着你需要在MySQL这边将视图修改一下,然后再去应用层修改对应的SQL语句,这就涉及到跨部门沟通的问题,所有通常不会使用视图,而是通过重新修改sql语句来扩展功能。

在这里插入图片描述

三、触发器

1.什么是触发器

在满足对某张表数据的增、删、改的情况下,自动触发的功能称之为触发器。

2.为什么要用触发器

触发器专门针对我们对某一张表的增insert,删delete,改update的行为,这类行为一旦执行 就会触发触发器的执行,既自动运行另外一段sql语句。

3.触发器trigger

满足条件就会自动执行

MySQL关于增、删、改的情况有六种情况

3.1 针对表的增加
	增前、增后
3.2 针对表的改
	改前、改后
3.3 针对表的删
	删前、删后

4.触发器语法结构

create trigger 触发器名字 before/after insert/update/delete on 表名 for ench row
begin
	sql语句
end

5.触发器命名有一定的规律,见名知意

tri_before_insert_t1
tri_after_delete_t2
tri_after_update_t3

6.触发三种状态(六种演示)

1.表的增加:
create trigger tri_after_insert_t1 after insert on 表名 for each row
begin
	 sql语句
end	 # 增加之后触发

create trigger tri_before_insert_t1 before insert on 表名 for each row
begin
	 sql语句
end  # 增加之前触发


2.表的修改:
create trigger tri_after_update_t1 after update on 表名 for each row
begin
	 sql语句
end  # 修改之后触发

create trigger tri_before_insert_t1 before insert on 表名 for each row
begin
	sql语句
end # 修改之前触发


3.表的删除:
create trigger tri_after_delete_t1 after delete on 表名 for each row
begin
	sql语句
end # 删除之前触发

create trigger tri_before_delete_t1 defore delete on 表名 for each row
begin
	sql语句
end # 删除之后触发

7.临时修改(只在当前窗口有用)

在书写sql代码的时候结束符是;而整个触发器的结束符也是;,这就会出现语法冲突,这个时候需要我们临时修改结束符号

delimiter $$  #  只在当时窗口有效

记得要修改回来 delimiter;

在这里插入图片描述

8.触发器实战案例

1. MySQL 中NEW作用
在MySQL中NEW特指数据对象可以通过点的方式获取字段对应的数据
	id    name	pwd  hobby
	1     kiki  123  sing
	NEW.name  >>>  kiki
2.创建cmd命令表与报错日志
# 模拟cmd命令表
CREATE TABLE cmd (
    id INT PRIMARY KEY auto_increment,
    USER CHAR (32),
    priv CHAR (10),
    cmd CHAR (64),     # cmd命令字段
    sub_time datetime, # 提交时间
    success enum ('yes', 'no') #0代表执行失败
);

# 报错日志表
CREATE TABLE errlog (
    id INT PRIMARY KEY auto_increment,
    err_cmd CHAR (64),
    err_time datetime
);
3.创建触发器
1.创建之前修改结束符
delimiter $$  # 将mysql默认的结束符由;换成$$

# 创建触发器
create trigger tri_after_insert_cmd after insert on cmd for each row
begin
	# 触发器启动 判断cmd中success字段内容记录是否为no,为no则执行以下代码
    if NEW.success = 'no' then  # 新记录都会被MySQL封装成NEW对象
        insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
    end if;
end $$

2.创建之后修改结束符
delimiter ;  # 结束之后记得再改回来,不然后面结束符就都是$$了。
4.cmd表内插入记录(数据)
#往表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');
5.验证触发器(查询结果)
查询errlog表记录
select * from errlog;

解析:
1.当cmd命令行后台被插入数据时,if判断出现为‘on’ 报错的数据时
2.会自动触发 触发器会执行代码,将为‘on’错误代码 插入到《errlog报错日志》
如下图

在这里插入图片描述

6.查看触发器和删除触发器
# 查看所有的触发器
show triggers;
# 删除触发器
drop trigger tri_after_insert_cmd;

在这里插入图片描述

四、事物

事物知识

1.事务的四大特征(ACID)

A:原子性

​ 每个事物都是不可分割的最小单位(同一个事务内的多个操作要么同时成功要么同时失败)

C:一致性

​ 事务必须是数据库从一个一致性状态变到另一个一致性状态,一致性与原子性是密切相关的

I:隔离性

​ 事务与事务之间彼此不干扰

D:持久性

​ 一个事务一旦提交,它会数据库中的数据的改变应该就是永久性,如,A支付宝转钱给另一个人B,钱的拥有者就是另一个人B

2.事物存在的必要性

比如:kiki要给kimi转账1000元。银行向数据库发送请求,将kiki的账户减少了1000元,在发送的请求过程出现了信息错乱(断电/断网/不可抗力),导致请求失败。

总结:kiki的账户减少了1000元,但是kimi的账户并没有添加1000元,导致无法正常转账。

解决方法: 事物的必要性,要么同时成功要么同时失败

3.事务关键字transaction、rollback、commit****

开启事物:start transaction;
回滚:    rollback;
确认      commit;

如果在使用修改表述数据num=100>>>:num=88,如果没有再次确认的话,可以通过回滚rollback,拿到修改数据之前的数据num =100,
底层原理就是:我们修改的数据只是在内存修改的,回滚之后能拿到之前数据num=100,但是如果确认数据库里的num=88,那么我们拿到的就是数据库里num=88

3.事务关键字transaction、rollback、commit****

开启事物:start transaction;
回滚:    rollback;
确认      commit;

如果在使用修改表述数据num=100>>>:num=88,如果没有再次确认的话,可以通过回滚rollback,拿到修改数据之前的数据num =100,
底层原理就是:我们修改的数据只是在内存修改的,回滚之后能拿到之前数据num=100,但是如果确认数据库里的num=88,那么我们拿到的就是数据库里num=88

4.事物案例

1.创建事物
create table user(
	id int primary key auto_increment,
	name char(32),
	balance int
);
2.插入数据
insert into user(name,balance)
values
('kiki',1000),
('kevin',1000),
('kimi',1000);
3.开启事务
# 修改数据之前先开启事务操作
start transaction;
4.修改操作update
# 修改操作
update user set balance=900 where name='kiki'; #买支付100元
update user set balance=1010 where name='kevin'; #中介拿走10元
update user set balance=1090 where name='kimi'; #卖家拿到90元
5.回滚rollback
# 回滚到上一个状态
rollback;

在这里插入图片描述

6.再次确认commit
# 开启事务之后,只要没有执行commit操作,数据其实都没有真正刷新到硬盘
commit;

在这里插入图片描述

7.总结事务

​ 开启事务之后,只要没有执行commit操作,数据其实都没有真正刷新到硬盘。开启事务检测操作是否完整,不完整主动滚回上一个状态,如果完整就应该执行commit操作。

8.使用python,来完善的伪代码的逻辑
try:  # 异常捕获(判断一旦以下代码出现一行报错则执行)
	update user set balance=900 where name='jason';  # 买家支付100元
	update user set balance=1010 where name='egon';  # 中介拿走10元
	update user set balance=1090 where name='tank';  # 卖家拿到90元
except 异常:  # 如果有异常就滚回去
	rolback;
else:  # 无异常则保存解释事务
	commit;

五、SQL事务四种隔离级别

1.事务隔离级别介绍

在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改

InnoDB支持所有隔离级别
	set transaction isolation level 级别

2.四种隔离级别

1.read uncommitted(未提交读)

事务中的修改即使没有提交,对其他事务也都是可见的,事务可以读取未提交的数据,这一现象也称之为"脏读"

​ 简单理解:事务中数据修改即使没有提交,在内存中,没有提交到硬盘,别人可能也是引用的我内存中的数据,读的是修改的但是没有提交的,该现象称之为“脏读”

2.read committed(提交读)

大多数数据库系**统默认的隔离级别
​ 一个事务从开始直到提交之前所作的任何修改对其他事务都是不可见的,这种级别也叫做"不可重复读"

​ 简单理解:该事务修改了,只要没有提交,别人是不可读的,其他人用的就是没有改之前原表里面的数据,两者之间不会发生冲突,该现象称之为“不可重复读”

3.repeatable read(可重复读) # MySQL默认隔离级别
能够解决"脏读"问题,但是无法解决"幻读"

所谓幻读指的是当某个事务在读取某个范围内的记录时另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录会产生幻行,InnoDB和XtraDB通过多版本并发控制(MVCC)及间隙锁策略解决该问题

4.serializable(可串行读)

​ 强制事务串行执行,很少使用该级别

3.案例理解

MVCC只能在read committed(提交读)、repeatable read(可重复读)两种隔离级别下工作,其他两个不兼容(read uncommitted:总是读取最新  serializable:所有的行都加锁)

InnoDB的MVCC通过在每行记录后面保存两个隐藏的列来实现MVCC
	一个列保存了行的创建时间
  一个列保存了行的过期时间(或删除时间)  # 本质是系统版本号
每开始一个新的事务版本号都会自动递增,事务开始时刻的系统版本号会作为事务的版本号用来和查询到的每行记录版本号进行比较

例如
刚插入第一条数据的时候,我们默认事务id1,实际是这样存储的
    username		create_version		delete_version
    jason						1					
可以看到,我们在content列插入了kobe这条数据,在create_version这列存储了11是这次插入操作的事务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.什么是存储过程

存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql,类似于python中的自定义函数。

2.存储过程语法格式

关键字: procedure
格式:
create procedure 函数名(参数)
begin
	功能体代码
end

调用其内部sql代码格式
call 函数名()

3.无参存储过程

# 无参函数
delimiter $$  # 修改结束符
create procedure p1()
begin
	select * from cmd;
end $$
delimiter ;  # 修改回来结束符

# 调用  通过p1()调用其内部sql代码
call p1()

在这里插入图片描述

4.有参存储过程

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;  # 定变量名为res,变量值为10
select @res;  # 查看res变量值为10
call p2(1,4,@res);  # 调用p2
select @res;  # 查看存储过程是否执行,满足条件执行res=0

"""
 大前提:存储过程在哪个库下面创建的只能在对应的库下面才能使用!!!
 # 1、直接在mysql中调用
set @res=10  # res的值是用来判断存储过程是否被执行成功的依据,所以需要先定义一个变量@res存储10
call p1(2,4,10);  # 报错
call p1(2,4,@res);  

# 查看结果
select @res;  # 执行成功,@res变量值发生了变化
"""

在这里插入图片描述

5.查看、删除存储过程

查看存储过程具体信息
	show create procedure pro1;
查看所有存储过程
	show procedure status;
删除存储过程
	drop procedure pro1;

6.pyMySQL调用存储过程

import pymysql

# 创建链接
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    passwd='123',
    db='db6',
    charset='utf8',
    autocommit=True  # 涉及到增删改,二次确认
)

# 生成一个游标对象(操作数据库)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 调用存储过程固定语法callproc('p2',(1,3,10))
cursor.callproc('p2',(1,3,10))  # 内部原理 @_p1_0=1,@_p1_1=3,@_p1_2=10;
# 查看结果
print(cursor.fetchall())

# 查看变量对应的值
cursor.execute('select @_p1_2')
# 查看结果
print(cursor.fetchall())

七、MySQL函数

1.MySQL什么是函数

相当于python中内置函数 注意与存储的过程的区别,MySQL内置的函数只能在sql语句中使用。

2.通过help查看函数帮助

help 函数名 查看帮助信息

3.移除指定字符

Trim	: 移出左右两边指定字符
LTrim	: 移出左侧指定字符
RTrim	: 移出右侧指定字符

4.大小写转换

Lower : 全部转换为小写
Upper : 全部转换为大写

5.获取左右起始指定个数字符

Left : 从左起始
Right : 从右起始

6. 返回读音相似值(英文效果)

解析:
客户表中有一个顾客登记的用户名为J.Lee
但如果这是输入错误真名其实叫J.Lie,可以使用soundex匹配发音类似的
where Soundex(name)=Soundex('J.Lie')

关键字:
Soundex

时间格式实战案例

日期格式:date_format
'''在MySQL中表示时间格式尽量采用2022-11-11形式'''
日期格式: date_format
%Y:%m:%d:%H:%M:%S:%X: 时分秒
1.创建表
CREATE TABLE blog (
    id INT PRIMARY KEY auto_increment,
    NAME CHAR (32),
    sub_time datetime
);
2.插入时间数据
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');

3.查询数据
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;


# 更多日期处理相关函数 
	date	年月日
    where date(sub_time) = '2015-03-01'
    year	年份
    where year(sub_time) = 2016
    month	月份
    where month(sub_time) = 07
    
	adddate	增加一个日期 
	addtime	增加一个时间
	datediff计算两个日期差值
    

流程控制

if分支
# 分支结构
declare i int default 0;
IF i = 1 THEN
	SELECT 1;
ELSEIF i = 2 THEN
	SELECT 2;
ELSE
	SELECT 7;
END IF;
while分支
# 循环结构
DECLARE num INT ;
SET num = 0 ;
WHILE num < 10 DO
	SELECT num ;
	SET num = num + 1 ;
END WHILE ;

八、索引与慢查询优化

索引相关概念

1.什么是索引?

​ 简单的理解为可以帮助你加快数据查询速度的工具 也可以把索引比喻成书的目录,它能让你更快的找到自己想要的内容;让获取的数据更有目的性,从而提高数据库检索数据的性能。

2.在MySQL种索引的作用

索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构

primary key
unique key
index key

1.上述的三个key都可以加快数据查询
2.primary key和unique key除了可以加快查询本身还自带限制条件而index key很单一就是用来加快数据查询
3.外键不属于索引键的范围 是用来建立关系的 与加快查询无关  foreign key 

3.索引加快查询的本质

	id int primary key auto_increment, # 主键
 	name varchar(32) unique,
  	province varchar(32)
 	age int
 	phone bigint
 	
	select name from user where phone=18818888888;  # 一页页的翻
	select name from user where id=99999;  # 按照目录确定页数找

虽然索引可以加快数据查询,但是会降低增删的速度。

4.建立索引的作用

通常情况下我们频繁使用某些字段查询数据,为了提升查询的速度可以将该字段建立索引。

5.索引分类

聚集索引(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*叶子节点和枝节点都有指向其他节点的指针

辅助索引在查询数据的时候最会还是需要借助于聚集索引
	辅助索引叶子节点存放的是数据的主键值

有时候就算采用索引字段查询数据 也可能不会走索引!!!
	最好能记三个左右的特殊情况

等值查询与范围查询都快
二叉树->平衡二叉树->B树->B+树

HASH索引

将数据打散再去查询
等值查询快,范围查询慢

FULLTEXT:全文索引
只可以用在MyISAM引擎
通过关键字的匹配来进行查询,类似于like的模糊匹配
like + %在文本比较少时是合适的
但是对于大量的文本数据检索会非常的慢
全文索引在大量的数据面前能比like快得多,但是准确度很低
百度在搜索文章的时候使用的就是全文索引,但更有可能是ES
RTREE:R树索引
索引测试

数据准备

#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;
  	全部更新失败
  """
主键
查看当前表主键自增到的值(表当前主键值减一)
	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
"""
读锁(共享锁)
	多个用户同一时刻可以同时读取同一个资源互不干扰
写锁(排他锁)
	一个写锁会阻塞其他的写锁和读锁
死锁
	1.多个事务试图以不同的顺序锁定资源时就可能会产生死锁
  2.多个事务同时锁定同一个资源时也会产生死锁
	# Innodb通过将持有最少行级排他锁的事务回滚
事物日志

事务日志可以帮助提高事务的效率
存储引擎在修改表的数据时只需要修改其内存拷贝再把该修改记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘

事务日志采用的是追加方式因此写日志操作是磁盘上一小块区域内的顺序IO而不像随机IO需要次哦按的多个地方移动磁头所以采用事务日志的方式相对来说要快的多

事务日志持久之后内存中被修改的数据再后台可以慢慢刷回磁盘,目前大多数存储引擎都是这样实现的,通常称之为"预写式日志"修改数据需要写两次磁盘

MVCC多版本控制
MVCC只能在read committed(提交读)、repeatable read(可重复读)两种隔离级别下工作,其他两个不兼容(read uncommitted:总是读取最新  serializable:所有的行都加锁)

InnoDB的MVCC通过在每行记录后面保存两个隐藏的列来实现MVCC
	一个列保存了行的创建时间
  一个列保存了行的过期时间(或删除时间)  # 本质是系统版本号
每开始一个新的事务版本号都会自动递增,事务开始时刻的系统版本号会作为事务的版本号用来和查询到的每行记录版本号进行比较

例如
刚插入第一条数据的时候,我们默认事务id1,实际是这样存储的
    username		create_version		delete_version
    jason						1					
可以看到,我们在content列插入了kobe这条数据,在create_version这列存储了11是这次插入操作的事务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变更技术)工具,简单方便的执行上述过程
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值