批量插入
在插入多条数据时,不要一句一句的SQL语句插入,而是一次性插入多条数据
INSERT INTO 表名 (字段名1,字段名2,...) VALUES (值一,值二,...),(值一,值二,...),(值一,值二,...);``
INSERT INTO 表名 VALUES (值一,值二,...),(值一,值二,...),(值一,值二,...);``
但是批量插入一次最多不要超过1000条,大概就是500到1000条,如果一次性要插入几万条数据,那么可以将其分为多条insert语句插入
手动提交事务
可以优化插入速度,在插入前手动开启事务,插入完成后手动结束事务
start transaction;
insert into 表名 values(具体数据1);
insert into 表名 values(具体数据2);
insert into 表名 values(具体数据3);
...
commit;
大批量插入数据
如果一次性插入大批量数据(万级的),使用insert语句插入性能较低,此时可以使用MySQL提供的load指令插入,通过load指令可以将本地磁盘中的数据全部加载到数据库当中
使用load指令的步骤:
1.在客户端连接服务端的时候,加上参数 – local-infile
mysql--local-infile -u root -p
2.设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local infile=1;
3.执行load指令将准备好的数据,加载到表结构中
-- 这里的'/root/sql1.log'是本地文件
-- fields teminated by ',':表示每一个字段之间使用 , 分割
-- lines teminated by '\n';表示每一行数据之间使用 \n 分割
load data local infile '本地文件' into table '表名' fields teminated by ',' lines teminated by '\n';
可以看到,默认情况下local_infile这个全局变量是关闭的
注意这里插入数据时,由于是虚拟机,所以要现在finalshall中上传数据,把数据上传到虚拟机中
这里插入100万条数据只需要耗时16秒,很强
使用load插入时也需要主键顺序插入,顺序插入数据高于乱序插入
主键顺序插入性能高于乱序插入
InnoDB中数据的组织方式:
在InnoDB中,表的数据都是根据主键的顺序组织存放的,这种存储方式被称为索引组织表。(即每行数据在页中都是顺序存放)
page(页)是innoDB磁盘管理的最小单元,一个extent(区)中可以包含64个页
页分裂
页可以为空,也可以填充一半,也可以全部填满,但是一个页中最少包含2行数据,如果某行的数据较大,超出了页的阈值之后,就会出现行溢出的现象,
如果顺序插入,就不会出现页分裂,乱序插入就会出现页分裂的现象,导致插入时要多操作页,自然插入的时间就会变长
页删除
在InnoDB中当删除一行数据时,实际上数据并没有被物理删除,而是数据被标记为删除并且他的空间变的允许其他数据使用
上图的13,14,15,16就是打上删除的标记了
当页中删除的数据达到一个阈值(MERGE_THRESHOLD)时(默认为页的50%),innoDB会开始寻找最靠近的页(前或后面的页)看看是否可以将两个页进行合并以优化空间使用
达到阈值
然后合并
再插入数据时就会往下一个页中插入数据
这里阈值(MERGE_THRESHOLD)可以自己设置,默认为50%
主键的设计原则
满足业务需求的情况下,尽量降低主键的长度
插入数据时,尽量使用顺序插入,尽量使用auto_increment自增主键
尽量不要使用UUID做主键或者其他自然主键(如身份证号)
业务操作时,尽量避免对主键的修改
MySQL中的两种排序方式
using index的效率较高
-- 此时age和phone都没有索引,使用order by排序时都是Using filesort,效率较低
mysql> explain select id,age ,phone from tb_user_s1 order by age;
+----------------+
| Extra |
+----------------+
| Using filesort |
+----------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select id,age ,phone from tb_user_s1 order by age,phone;
+----------------+
| Extra |
+----------------+
| Using filesort |
-+----------------+
1 row in set, 1 warning (0.00 sec)
-- 为age和phone建立联合索引后,就会变为Using index
-- 若在查询时,order by之后的联合索引的排序不同,例如,一个顺序,一个倒序,
-- 也会出现Using filesort的情况
-- 这里age升序排列,phone倒序排列
select id,age ,phone from tb_user_s1 order by age asc,phone desc;
-- 这种情况的解决方式就是在创建联合索引时就把顺序定好
-- 下面就是在创建联合索引时就确定age和phone的排列方式
create index idx_user_age_phone_ad on tb_user_s1(age asc ,phone desc)
注意:上述的所有排序优化都有一个条件,就是覆盖索引,如果不是覆盖索引就不行
在分组操作时,建立适当的索引来提升效率
没有索引时,直接分组效率是较低的,使用索引对分组进行优化的话,就尽量使用联合索引,注意联合索引的最左前缀法则
在分组操作时,索引的使用也是需要满足最左前缀法则的
limit一个常见的问题就是,在大数据量的情况下,越往后查询数据,limit的效率月底,例如limit 2000000,10,此时需要mysql排序前2000010记录,但是仅仅返回2000000和2000010之间的记录,其他的记录丢弃,查询排序的代价非常大
官方给出的优化方式是,通过覆盖索引和子查询的方式优化
-- 直接使用limit查询,效率很低,大概要19秒多
select *from tb_user_s2 limit 9000000,10;
-- 使用覆盖索引和子查询的方式优化
-- 先在子查询中找到对应的主键,然后再使用主键进行查询数据
-- 但是这种方式好像在mysql8.0.26语法不支持
select * from tb_user_s2 where id in(select id from tb_user_s2 order by in limit 9000000,10);
-- 那么可以使用另外的语法实现这个效果
-- 把select id from tb_user_s2 order by in limit 9000000,1
-- 返回的结果看成一张表,然后使用多表查询
select s.* from tb_user_s2 s, (select id from tb_user_s2 order by in limit 9000000,10) a where a.id=s.id;
-- 这样写大概查询是10秒,提高9秒的效率
当数据量很大时,count一次也是很耗时的,
这里MyISAM的前提条件是没有where条件,如果有where条件,那么MyISAM也会比较慢
对于count来说,优化策略就是自己计数,在插入或删除数据时自己记录下来
count是一个聚合函数,对于返回的结果集,一行行的判断,如果count函数的参数不是NULL,累计值就加1,否则不加,最后返回累计值,即count参数会统计数据的数量
count的用法:
1.count(*)
2.count(主键)
3.count(字段)
4.count(1)
用法的演示:
-- count(*)就是求取某个表中数据的总体数量
mysql> select count(*)from tb_user_s1;
+----------+
| count(*) |
+----------+
| 16 |
+----------+
1 row in set (0.00 sec)
-- count(字段),会统计字段的数量,对字段里的值统计前会先判断是不是null,
-- 不是null才会统计进去,是null就不会统计
mysql> select count(profession)from tb_user_s1;
+----------+
| count(*) |
+----------+
| 16 |
+----------+
1 row in set (0.00 sec)
-- count(1)的使用和count(*)是一样的,也是会统计数据的总体数量
count(主键)的话,InnoDB会遍历整张表,将所有主键都拿出来,然后累加返回具体数量
count(字段),没有not null约束的话,InnoDB会遍历整张表把字段值拿出来,然后判断每个值是否为null,不为null,数量增加,为null,数量不增加,有not null约束的话,就跟count(主键)类似,InnoDB会遍历整张表把字段值拿出来,然后累加获得字段值的数量
count(1),InnoDB遍历整张表,不取值,对每一行放一个数字1进去,直接按行进行累加
count(*),InnoDB并不会把全部字段值取出来,已经优化过了,不取值,直接按行累加
需要规避的一个问题是:在更新字段时需要根据带有索引的字段进行更新,即where后的条件字段尽量是带有索引的,否则,就会出现行锁升级为表锁,锁住整张表,从而使并发性能降低
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁
视图(View)是一种虚拟存在的表,视图中的数据并不在数据库中真实存在,视图中行和列数据都来自于定义视图的查询中使用的表,并且是在使用视图时动态生成的。
定义视图时使用的表也被叫做基表
简单来说,视图不保存数据,它里面只保存了查询的SQL逻辑,不保存查询结果,所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上
-- 这里的select语句就是视图所封装的数据 ,select所查询的表就叫做视图的基表
CREATE [OR REPLACE] VIEW 视图名称[(列名列表)] AS SELECT 语句
[WITH[CASCADED | LOCAL]CHECK OPTION]
create [or replace] view 视图名称[(列名列表)] as SELECT 语句
[with[cascaded | local]check option]
查看创建视图的语句:SHOW CREATE VIEW 视图名称;
查看视图数据:SELECT *FROM 视图名称.....;(就是传统的select查表语句)
-- 这里修改的方式1与创建的不同就是,必须加上or replace
方式1:CREATE OR REPLACE VIEW 视图名称[(列名列表)] AS SELECT 语句
[WITH[CASCADED | LOCAL]CHECK OPTION]
create [or replace] view 视图名称[(列名列表)] as SELECT 语句
[with[cascaded | local]check option]
方式2:ALTER VIEW 视图名称[(列名列表)] AS SELECT 语句
[WITH[CASCADED | LOCAL]CHECK OPTION]
alter view 视图名称[(列名列表)] as SELECT 语句
[with[cascaded | local]check option]
DROP VIEW [IF EXISTS] 视图名称[,视图名称];
-- 创建视图
create or replace view name_v_1 as select id,name from tb_user_s1 where id<=10;
-- 查询视图
show create view name_v_1;
select*from name_v_1 ;
-- 修改视图
create or replace view name_v_1 as select id,name,age from tb_user_s1 where id<=10;
alter view name_v_1 as select id,age from tb_user_s1 where id<=10;
-- 删除视图
drop view if exists name_v_1;
注意:视图在数据库中并不存在,虽然可以往视图里添加数据,但是实际上数据是被添加到视图所对应的基表中去了,并且视图只会显示符合创建条件时的数据,超出条件范围的数据不会视图中,即在视图中找不到超出创建视图条件的数据
-- 创建视图
create or replace view name_v_1 as select id,name from tb_user_s1
where id<=18;
-- 往视图中插入数据实际上就是往视图所对应的基表中插入数据
-- 这里所对应的基表就是tb_user_s1
insert into name_v_1 values (17,'Tom');
insert into name_v_1 values (20,'Jack');
-- 上面的两条数据只有(17,'Tom');能在视图中显示出来,因为(20,'Jack');超出了
-- 创建视图的条件
为了避免上面那种情况的发生,可以在创建视图的时候在后面加上
with cascaded check option / with local check option
,这样就不会出现超出条件的数据了,因为只要有数据插入时超出条件,就会报错
create or replace view name_v_1 as select id,name from tb_user_s1
where id<=18 with cascaded check option;
-- 往视图中插入数据实际上就是往视图所对应的基表中插入数据
insert into name_v_1 values (17,'Tom');
-- 此时运行这里就会报错
insert into name_v_1 values (20,'Jack');
上面的with cascaded check option就是视图中检查选项的应用
即在创建视图时使用with … check option的话,mysql就会检查视图中正在改变的每一行的数据,如果有数据不满足创建视图时的条件,那么就会报错,不允许视图改变,这里默认值就是 cascaded 不加也可以
cascaded与local的区别:
cascaded:作用
创建视图时(这里新创建的视图记作s1),视图的基表可以是另一个视图(另一个视图记作s2),如果创建时使用cascaded,那么这个新创建的视图(s1)里的数据既要满足s1的条件,又要满足s2的条件,并且,s2在创建时并不一定有with ... check option
语句,
如果以s2为基表又创建一个s3的话,s3在创建时没加with ... check option
语句的话,s3里的数据就需要满足s1和s2的条件,不一定需要满足s3的条件(因为s3没有加with ... check option
语句),如果s3中也添加了with ... check option
语句,那么就需要同时满足s1,s2,s3的条件
例如:
-- 这里name_v_1就是s2,name_v_2就是s1,
-- 那么,添加在s1的数据既要满足id<=18,又要满足id>10
create or replace view name_v_1 as select id,name from tb_user_s1
where id<=18 ;
create or replace view name_v_2 as select id,name from name_v_1
where id>10 with cascaded check option ;
-- 这条数据满足两个条件,可以插入
insert into name_v_1 values (17,'Tom');
-- 此时运行这里就会报错,因为不满足s2的条件
insert into name_v_1 values (20,'Jack');
local:作用
同样的,创建视图时(这里新创建的视图记作s1),视图的基表可以是另一个视图(另一个视图记作s2),如果创建时使用local,那么这个新创建的视图(s1)里的数据要满足s1的条件,然后看s2中有没有with ... check option
语句,如果有的话,就既要满足s1,又要满足s2,如果没有,那么就不用管基表(s2)的条件,只满足s1的条件即可。
至于这里再以s2作为基表创建新视图s3的规则就跟上面一样了,也满足local的规则
例如:
-- 这里name_v_1就是s2,name_v_2就是s1,
-- 那么,添加在s1的数据就要满足id>10,
-- 由于s2中没有``with ... check option``语句,所以,不用满足s2的条件
create or replace view name_v_1 as select id,name from tb_user_s1
where id<=18 ;
create or replace view name_v_2 as select id,name from name_v_1
where id>10 with cascaded check option ;
-- 这条数据满足s1条件,可以插入
insert into name_v_1 values (17,'Tom');
-- 这条数据也满足s1条件,可以插入,
-- 如果s2有``with ... check option``语句,那么这条程序就会失败
insert into name_v_1 values (20,'Jack');
视图的更新:要使视图可更新,视图中的行与基础表(基表)中的行之间必须存在一对一的关系,如果视图包含以下任何一项,则该视图不可更新
比如:
在视图创建时里面包含一些
1.聚合函数(sum,min,max,count等),
2.DISTINCT–distinct关键字
3.GROUP BY – group by
4.HAVING – having
5.UNION或UNION ALL
1.操作简单
视图可以简化用户对数据的理解和操作,那些被经常使用查询的表就可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件
2.安全
数据库可以授权,但是最低也只能授权到表上,不能授权到数据库中特定行和特定列上,但是通过视图用户只能查询和修改他们所能见到的数据
比如,我只想让某一个用户看到并修改学生表的id和name字段,那么就可以使用视图来使这些用户只看到id和name
3.数据独立
视图可以帮助用户屏蔽真实表结构的变化带来的影响
-- 1
create or replace view name_v_1 as select id,name,profession from
tb_user_s1 with cascaded check option;
-- 2
create table student(
id int primary key auto_increment comment 'id',
name varchar(10) comment 'name',
courseId int comment '课程id'
)comment '学生表';
create table course(
id int primary key auto_increment comment 'id',
courseName varchar(10) comment '课程名'
)comment '课程表';
create table stu_cou(
id int primary key auto_increment comment 'id',
stuId int comment 'stuId',
souId int comment 'couId'
)comment '关系表';
alter table stu_cou add constraint fk_stu_id foreign key (stuId) references student(id);
alter table stu_cou add constraint fk_cou_id foreign key (souId) references course(id);
alter table student drop courseId;
insert into student values (1,'tom'),(2,'jack'),(3,'mike'),(4,'arthur');
select *from student;
insert into course values (1,'java'),(2,'js'),(3,'python'),(4,'c#');
insert into stu_cou values (1,1,1),(2,1,2),(3,1,4),(4,2,2),(5,2,3),(6,3,1),(7,3,2),(8,3,3),(9,4,1),(10,4,2),(11,4,3);
create or replace view vi_stu_cou as select distinct s.name,c.courseName from student s,stu_cou sc,course c where s.id=sc.stuId and c.id=sc.souId;
select *from vi_stu_cou;