存储引擎
什么是存储引擎?有什么用?
存储引擎是MYSQL中特有的一个术语,其它数据库没有(Oracle中有,但不叫这个名字)
存储引擎实际上是一个表存储/组织数据的方式,不同的存储引擎,表存储数据的方式不同。
怎么给表添加/指定“存储引擎”呢?
show create table t_student;
可以在建表的时候给表指定存储引擎:
CREATE TABLE `t_student` (
`no` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`cno` int(11) DEFAULT NULL,
PRIMARY KEY (`no`),
KEY `cno` (`cno`),
CONSTRAINT `t_student_ibfk_1` FOREIGN KEY (`cno`) REFERENCES `t_class` (`classno`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8
在建表的时候可以在最后小括号的")"的后面使用:ENGIN
来指定存储引擎;CHARSET
来指定这张表的字符编码方式。
结论:mysql 默认的存储引擎是:InnoDB,默认的字符编码方式是:utf8
建表时指定存储引擎,以及字符编码方式。
create table t_product(
id int primary key,
name varchar(255)
)engine = InnoDB default charset = utf8;
mysql> show create table t_product;
CREATE TABLE `t_product` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
怎么查看MySQL支持哪些引擎?
show engines \G
*************************** 1. row ***************************
Engine: InnoDB
Support: DEFAULT
Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
XA: YES
Savepoints: YES
*************************** 2. row ***************************
Engine: MRG_MYISAM
Support: YES
Comment: Collection of identical MyISAM tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 3. row ***************************
Engine: MEMORY
Support: YES
Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 4. row ***************************
Engine: BLACKHOLE
Support: YES
Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
XA: NO
Savepoints: NO
*************************** 5. row ***************************
Engine: MyISAM
Support: YES
Comment: MyISAM storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 6. row ***************************
Engine: CSV
Support: YES
Comment: CSV storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 7. row ***************************
Engine: ARCHIVE
Support: YES
Comment: Archive storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 8. row ***************************
Engine: PERFORMANCE_SCHEMA
Support: YES
Comment: Performance Schema
Transactions: NO
XA: NO
Savepoints: NO
*************************** 9. row ***************************
Engine: FEDERATED
Support: NO
Comment: Federated MySQL storage engine
Transactions: NULL
XA: NULL
Savepoints: NULL
mysql 支持九大存储引擎。
关于mysql常用的存储引擎:
-
MyISAM存储引擎
它管理的表具有以下特征:
使用三个文件表示每个表:
-
格式文件 - 存储表结构的定义(mytable.frm)
-
数据文件 - 存储表行的内容(mytable.MYD)
-
索引文件 - 存储表上索引(mytable.MYI):索引相当于一本书的目录,缩小扫描范围,提高检索效率。
提示:对于一张表来说,只要是主键,后者加有unique约束的字段上会自动创建索引。
MYISAM存储引擎的特点:
- 可被转换为压缩,只读表 来节省空间,是这种存储引擎的优势。
MYISAM不支持事务,安全性低。
-
-
InnoDB存储引擎
这是MySQL的默认存储引擎,同时也是一个重量级的存储引擎。
InnoDB支持事务,支持数据库崩溃后的自动恢复机制。
InnoDB存储引擎最主要的特点:非常安全。
它管理的表具有下列主要特征:
– 每个InnoDB表在数据库目录中以
.frm
格式文件表示– InnoDB表空间 tablespace 被用于存储表的内容 (表空间是一个逻辑名称,表空间存储数据+索引)
– 提供一组用来记录事务性活动的日志文件
– 用COMMIT(提交)、SAVEPOINT及**ROLLBACK(回滚)**支持事务处理
– 提供全ACID兼容 (ACID:原子性、一致性、隔离性、持久性)
– 在MySQL服务器崩溃后提供自动恢复
– 多版本(MVCC)和行级锁定
– 支持外键及引用的完整性,包括级联删除和更新
InnoDB最大的特点就是支持事务:以保证数据的安全,效率不是很高,不能压缩,不能转换为只读,不能很好的节省存储空间。
-
MEMORY存储引擎
使用MEMORY存储引擎的表,其数据存储在内存中,且行的长度固定,这两个特点使得MEMORY存储引擎非常快。
MEMORY存储引擎管理的表具有下列特征:
– 在数据库目录内,每个表均以.frm格式的文件表示。
– 表数据及索引被存储在内存中。
– 表级锁机制。
– 不能包含TEXT或BLOB字段。
MEMORY存储引擎以前被称为HEAP 引擎。
MEMORY引擎的优点:查询效率是最高的,不需要和硬盘交互。
MEMORY引擎的缺点:不安全,关机之后数据消失。因为数据和索引都是在内存当中的。(内存是直接取,电流的速度。硬盘上取是机械行为)
!!!事务
什么是事务?
一个事务就一个完整的业务逻辑。是一个最小的工作单元,要么同时成功,要么同时失败,不可再分。
什么是一个完整的业务逻辑?
假设转账:从A账户向B账户中转账10000
将A账户的钱减去10000(update语句)
将B账户的钱加上10000(update语句)
以上就是一个完整的业务逻辑。此操作是一个最小的工作单元,要么同时成功,要么同时失败,不可再分。
这两个update语句要求必须同时成功或者同时失败,这样才能保证钱是正确的。
DML语句有事务
只有DML语句才会有事务这一说,其它语句和事务无关。
insert delete update 这三个语句和事务有关,因为只有这三个语句是数据库表中数据进行增、删、改的。
只要操作涉及到数据的增、删、改,那么就一定要考虑安全问题。数据安全是第一位!
假设所有的业务,只要一条DML语句就能完成,还有必要存在事务机制吗?
正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事儿都能一条DML语句完成,那么事务则没有存在的价值了。
本质上,一个事务其实就是多条DML语句同时成功,或者同时失败。
事务:就是批量的DML语句同时成功,或者同时失败!
事务如何实现批量DML语句同时成功或失败的
InnoDB存储引擎:提供一组用来记录事务性活动的日志文件
在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。在事务的执行过程中,我们可以提交事务,也可以回滚事务。
提交事务:清空事务活动的日志文件,将数据全部彻底永久化到数据库表中。标志着事务的结束,并且是一种全部成功的结束。
回滚事务:将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件。标志着事务的结束,并且是一种全部失败的结束。
如何提交事务/回滚事务
- 提交事务:commit; 语句
- 回滚事务:rollback; 语句 (回滚永远只能回到上一次的提交点)
- 事务(Transaction):一批操作(一组DML)
- 开启事务(Start Transaction)
- SET AUTOCOMMIT:禁用或启用事务的自动提交模式
事务对应的单词:transaction
测试一下,在mysql当中默认的事务行为是什么样的?
mysql默认情况下是支持自动提交事务的。每执行一条DML语句,则提交一次。(这种自动关机提交是不符合我们的开发习惯的,因为一个业务通常是需要多条DML语句共同执行才能完成的,为了保证数据的安全,必须要求同时成功之后再提交,所以不能执行一条就提交一条。)
如何将mysql中的自动提交机制关闭呢?
关闭自动提交机制:start transaction;
回滚事务
mysql> delete from dept_bake;
mysql> start transaction;
mysql> insert into dept_bake(deptno,dname,loc) values(10,'SALES','BEIJING');
mysql> insert into dept_bake(deptno,dname,loc) values(10,'SALES','BEIJING');
mysql> insert into dept_bake(deptno,dname,loc) values(10,'SALES','BEIJING');
mysql> insert into dept_bake(deptno,dname,loc) values(10,'SALES','BEIJING');
mysql> insert into dept_bake(deptno,dname,loc) values(10,'SALES','BEIJING');
mysql> select * from dept_bake;
+--------+-------+---------+
| DEPTNO | DNAME | LOC |
+--------+-------+---------+
| 10 | SALES | BEIJING |
| 10 | SALES | BEIJING |
| 10 | SALES | BEIJING |
| 10 | SALES | BEIJING |
| 10 | SALES | BEIJING |
+--------+-------+---------+
5 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
提交事务
mysql> select * from dept_bake;
Empty set (0.00 sec)
mysql> start transaction; #开启事务
mysql> insert into dept_bake values(10,'abc','bj');
mysql> insert into dept_bake values(10,'abc','bj');
Query OK, 2 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from dept_bake;
+--------+-------+------+
| DEPTNO | DNAME | LOC |
+--------+-------+------+
| 10 | abc | bj |
| 10 | abc | bj |
+--------+-------+------+
2 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from dept_bake;
+--------+-------+------+
| DEPTNO | DNAME | LOC |
+--------+-------+------+
| 10 | abc | bj |
| 10 | abc | bj |
+--------+-------+------+
2 rows in set (0.00 sec)
事务的四个特性
事务的4个特性:ACID
-
A:原子性(Atomicity)
说明事务是最小的工作单元,不可再分。
-
C:一致性(Consistency)
所有事务要求,在同一个失误当中,所有操作必须同时成功,或者同时失败。以保证数据的一致性。
-
I:隔离性(Isolation)
A事务与B事务之间具有一定的隔离。A事务在操作一张表的时候,另一张事务B也操作这张表。多线程并发访问同一张表,线程安全问题。
-
D:持久性(Durability)
事务最终结束的一个保障,事务提交:相当于将没有保存到硬盘上的数据保存到硬盘上。
事务的隔离性
A教室和B教室中间有一道墙,这道墙可以很厚,也可以很薄。这就是事务的隔离级别。这道墙越厚,表示隔离级别越高。
事务的隔离级别分别有4个:
-
读未提交:read uncommited(最低的隔离级别)「没有提交就读到了」
事务A可以读取事务B未提交的数据。
这种隔离级别存在的问题就是:脏读现象(Dirty Read)我们称读到了脏数据。
这种隔离级别一般都是理论上的,大多数的数据库隔离级别都是二档起步。
-
读已提交:read commited 「提交之后才能读到」
事务A只能读到事务B提交之后的数据
这种隔离级别解决了脏读现象
这种隔离级别存在不可重复读取数据的问题。
在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读取
这种隔离级别比较真实的数据,每一次读到的数据是绝对的真实。Oracle数据库默认的隔离级别是:read commited
-
可重复读:repeatable read 「提交之后也读不到,永远读取的都是刚开启事务时的数据」
事务A开启之后,无论多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改了,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。
可重复度解决了不可重复读的问题;可重复读可能会出现幻影读,每一次读取的数据都是幻想,不够真实。加排它锁(for update)可以解决幻读的问题。
MySQL中默认的事务级别就是可重复读:repeatable read
-
序列化/串行化:serializable(最高的隔离级别)
这是最高隔离级别,效率最低,解决了所有的问题。这种隔离级别表示事务排队,不能并发!
有点像synchronized,线程同步(事务同步)。每一次读取到的数据都是最真实的,并且效率是最低的。
查看隔离级别
select @@tx_isolation;
或者
SELECT @@session.tx_isolation;
:查看会话级的当前隔离级别
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
mysql> select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| REPEATABLE-READ |
+------------------------+
REPEATABLE-READ :mysql默认的隔离级别
SELECT @@global.tx_isolation;
:查看全局级的当前隔离级别
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
验证隔离级别
被测试的表:t_user
验证 read uncommited – (Dirty Read 脏读)
set global transaction isolation level read uncommitted;
//设置全局的事务隔离级别为 读未提交
事务A | 事务B |
---|---|
use bjpowernode; | use bjpowernode; |
start transaction; | |
select * from t_user; #没查到 | start transaction; |
insert into t_user values('zhangsan'); | |
select * from t_user; #查到了 | |
rollback; | |
select * from t_user; #空表 |
验证 read commited
set global transaction isolation level read committed;
//设置全局的事务隔离级别为 读已提交
事务A | 事务B |
---|---|
use bjpowernode; | |
use bjpowernode; | |
start transaction; | |
start transaction; | |
select * from t_user; #为空 | |
insert into t_user values('jack'); | |
select * from t_user; #为空 | |
commit; #提交 | |
select * from t_user; #有数据-jack |
验证 repeatable read --(幻读)
set global transaction isolation level repeatable read;
//设置全局的事务隔离级别为 可重复读
事务A | 事务B |
---|---|
use bjpowernode; | |
use bjpowernode; | |
start transaction; | |
start transaction; | |
select * from t_user; #jack | |
insert into t_user values('lisi'); | |
insert into t_user values('wangwu'); | |
select * from t_user; #jack | |
commit; #提交 | |
select * from t_user; #jack | |
selcet * from t_user for update; #显示全部已添加的数据 | |
insert into t_user values('abc'); #光标暂停 等待事务提交 | |
commit; #事务提交 | |
#abc 插入成功 |
**注意:**在表t_user
后面添加for update
就是添加排它锁来解决 可重复读的幻读问题。
验证 serializable
set global transaction isolation level serializable;
//设置全局的事务隔离级别为 序列化/串行化
事务A | 事务B |
---|---|
use bjpowernode; | |
use bjpowernode; | |
start transaction; | |
start transaction; | |
select * from t_user; # | |
insert into t_user values('abc'); | |
select * from t_user; #卡光标,在等待事务提交 | |
commit | |
#显示添加的数据----abc |
索引(index)
什么是索引
索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制。一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。
索引相当于一本书的目录,为了缩小扫描范围而存在的一种机制。
t_user
id(idIndex) | name(nameIndex) | email(emailIndex) | address(emailAddressIndex) |
---|---|---|---|
1 | jack | …@…com | … |
2 | lisi | …@…com | … |
3 | wangwu | …@…com | … |
4 | zhaoliu | …@…com | … |
5 | hanmeimei | …@…com | … |
6 | jack | …@…com | … |
select * from t_user where name = 'jack';
如果name字段上没有添加索引(目录),或者说没有给name字段创建索引,MySQL会进行全扫描,会将name字段上的每一个值都比对一遍,效率比较低。
MySQL在查询方面主要有两种方式:
- 第一种方式:全表扫描
- 第二种方式:根据索引扫描
注意:在MySQL数据库当中索引是需要排序的,并且这个索引的排序和TreeSet数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树。
在mysql中索引是一个B-Tree数据结构。遵循左小右大原则存放,采用中序遍历方式遍历取数据。
索引的实现原理
t_user
id(PK) | name | 每一行记录在硬盘上都有物理存储编号 |
---|---|---|
100 | zhangsan | 0x1111 |
120 | lisi | 0x2222 |
99 | wangwu | 0x8888 |
88 | zhaoliu | 0x9999 |
101 | jack | 0x6666 |
55 | lucy | 0x5555 |
130 | tom | 0x7777 |
提醒1:在任何数据库当中主键上都会自动添加索引对象,id字段上自动有索引。另外在mysql当中,一个字段上如果有unique约束的话,也会自动创建索引对象。
提醒2:在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。
提醒3:在mysql中,索引是一个单独的对象,不同的存储引擎以不同的形式存在。在MYISAM存储引擎中,索引存储在一个.MYI
文件中。在InnoDB存储引擎中,索引存储在一个逻辑名称叫做 tablespase 当中。在MEMORY 存储引擎当中索引被存储在内存当中。不管索引存储在哪里,索引在mysql中都是以一个树的形式存在。(自平衡二叉树B-Tree)
在什么情况下,我们才会给字段添加索引呢?
- 情况1:数据量庞大
- 情况2:该字段经常出现在where后面,以条件的形式存在,这个字段总是被扫描。
- 情况3:该字段很少DML(insert delete update)操作。(因为DML操作之后,索引需要重新排序)
建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。建议通过主键查询,建议通过unique约束的字段进行扫描,效率是比较高的。
索引的创建
create index emp_ename_index on emp(ename);
//给emp表的ename字段添加索引,起名:emp_name_index
索引的删除
drop index emp_ename_index on emp;
//将emp表上的emp_ename_index索引对象删除
查看一条SQL语句是否使用了索引进行检索
在查询语句前加上一个关键字explain
explain select * from emp where ename = 'king';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
扫描14条记录:说明没有使用索引。type=ALL
create index emp_enmae_index on emp(ename);
explain select * from emp where ename = 'king';
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | emp | NULL | ref | emp_enmae_index | emp_enmae_index | 13 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
索引的结构:B+树的结构
索引的失效
失效的第1种情况:
select * from emp where ename like '%T';
mysql> explain select * from emp where ename = '%T';
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | emp | NULL | ref | emp_enmae_index | emp_enmae_index | 13 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
ename即使加了索引,也不会进行索引的检索,因为模糊匹配当中以"%"开头了。
所以尽量避免模糊查询的时候以”%“开始。------这是一种优化的策略。
失效的第2种情况:
使用or的时候会失效,如果使用or那么要求or两边的条件字段都要有索引,才会进行过索引检索。如果其中一边的字段没有添加索引,那么另一个字段上的索引也会失效,所以不建议使用or。
explain select * from emp where ename = 'king' or job = 'manager';
#尽量少用or,可以用union来提高检索效率。
+----+-------------+-------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | emp | NULL | ALL | emp_enmae_index | NULL | NULL | NULL | 14 | 16.43 | Using where |
+----+-------------+-------+------------+------+-----------------+------+---------+------+------+----------+-------------+
失效的第3种情况:
使用复合索引(两个字段,或者更多的字段联合起来添加一个索引)的时候,没有使用左侧的列查找,索引会失效。
create index emp_job_index on emp(job,sal);
//符合索引
explain select * from emp where job = 'MANAGER';
+----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | emp | NULL | ref | emp_job_index | emp_job_index | 12 | const | 3 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
#rows = 3 key = emp_job_index 用到了索引
explain select * from emp where sal = 800;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
#key = null 没有用索引
总结:复合索引的时候,用右边的字段查找,索引会失效。
失效的第4种情况:
在where当中索引列参加了数学运算
create index emp_sal_index on emp(sal);
//给sal关键字添加索引
explain select * from emp where sal = 800;
+----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | emp | NULL | ref | emp_sal_index | emp_sal_index | 9 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
explain select * from emp where sal+1 = 800;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
失效的第5种情况:
在where当中,索引列使用了函数
explain select * from emp where lower(ename) = 'smith';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
索引的分类
索引是各种数据库进行优化的重要手段。优化的时候优先考虑的因素就是索引。
索引在数据库中分了很多类:
- 单一索引:一个字段上添加索引
- 复合索引:两个字段或更多字段上添加索引
- 主键索引:主键上添加索引
- 唯一性索引:具有unique约束的字段上添加索引。
注意:唯一性比较弱的字段上添加索引的用处不大。
视图(view)
视图的概述
视图是一种根据查询(也就是SELECT表达式)定义的数据库对象,用于获取想要看到和使用的局部数据。
视图有时也被成为“虚拟表”。
视图可以被用来从常规表(称为“基表”)或其他视图中查询数据。
相对于从基表中直接获取数据,视图有以下好处:
- 访问数据变得简单
- 可被用来对不同用户显示不同的表的内容
用来协助适配表的结构以适应前端现有的应用程序
view:站在不同的角度去看待同一份数据。
视图的操作(创建、删除)
create table dept2 as select * from dept;
// 表复制
select * from dept2;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
创建视图:
create view dept2_view as select * from dept2;
**注意:**只有DQL语句才能以view的形式创建。create view dept2_view as
后面跟的必须是DQL语句。
删除视图:
drop view dept2_view;
视图的作用
视图作用:
- 提高检索效率,方便、简化开发,利于维护
- 隐藏表的实现细节【面向视图检索】
我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致对原表被操作。
视图的特点:通过对视图的操作,会影响到原表数据。
select * from dept2_view;
//面向视图查询
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
insert into dept2_view(deptno,dname,loc) values(60,'SALES','BEIJING');
//面向视图插入
select * from dept2;
//查询原表数据
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
| 60 | SALES | BEIJING |
+--------+------------+----------+
delete from dept2_view;
//面向视图删除
select * from dept2;
//查询原表数据
Empty set (0.00 sec)
创建视图对象:
create view
emp_dept_view
as
select
e.ename,e.sal,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;
查询视图对象
select * from emp_dept_view;
+--------+---------+------------+
| ename | sal | dname |
+--------+---------+------------+
| SMITH | 800.00 | RESEARCH |
| ALLEN | 1600.00 | SALES |
| WARD | 1250.00 | SALES |
| JONES | 2975.00 | RESEARCH |
| MARTIN | 1250.00 | SALES |
| BLAKE | 2850.00 | SALES |
| CLARK | 2450.00 | ACCOUNTING |
| SCOTT | 3000.00 | RESEARCH |
| KING | 5000.00 | ACCOUNTING |
| TURNER | 1500.00 | SALES |
| ADAMS | 1100.00 | RESEARCH |
| JAMES | 950.00 | SALES |
| FORD | 3000.00 | RESEARCH |
| MILLER | 1300.00 | ACCOUNTING |
+--------+---------+------------+
面向视图更新
update emp_dept_view set sal = 1000 where dname = 'ACCOUNTING';
mysql> select * from emp_dept_view;
+--------+---------+------------+
| ename | sal | dname |
+--------+---------+------------+
| SMITH | 800.00 | RESEARCH |
| ALLEN | 1600.00 | SALES |
| WARD | 1250.00 | SALES |
| JONES | 2975.00 | RESEARCH |
| MARTIN | 1250.00 | SALES |
| BLAKE | 2850.00 | SALES |
| CLARK | 1000.00 | ACCOUNTING |
| SCOTT | 3000.00 | RESEARCH |
| KING | 1000.00 | ACCOUNTING |
| TURNER | 1500.00 | SALES |
| ADAMS | 1100.00 | RESEARCH |
| JAMES | 950.00 | SALES |
| FORD | 3000.00 | RESEARCH |
| MILLER | 1000.00 | ACCOUNTING |
+--------+---------+------------+
#所有的ACCOUNTING部门的 sal --> 1000
查询原表
select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 1000.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 1000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1000.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
# 原表的 ACCOUNTING部门 的 sal 也改了
视图在实际开发中的作用:
视图是用来简化SQL语句的。
假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用,每一次使用这个sql语句的时候都需要重新编写,很长很麻烦:
这个时候可以把这条复杂的SQL语句以视图对象的形式新建。在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发,并且利于后期的维护。因为修改的时候也只需要修改视图对象所映射的SQL语句即可。
使用视图的时候可以像使用table一样,视图不是存在在内存当中,视图对象也是以一种文件形式存储在硬盘上的,不会消失。
DBA常用命令
新建用户
CREATE USER username IDENTIFIED BY 'password';
说明:username——你将创建的用户名, password——该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器。
授权(grant)
grant all privileges on dbname.tbname to 'username'@'login ip' identified by 'password' with grant option;
dbname=*
:表示所有数据库tbname=*
:表示所有表login ip=%
:表示任何ippassword为空
:表示不需要密码即可登录with grant option;
: 表示该用户还可以授权给其他用户
细粒度授权
首先以root用户进入mysql,然后键入命令:grant select,insert,update,delete on *.* to p361 @localhost Identified by "123";
如果希望该用户能够在任何机器上登陆mysql,则将localhost改为 “%” 。
粗粒度授权
我们测试用户一般使用该命令授权,
GRANT ALL PRIVILEGES ON *.* TO 'p361'@'%' Identified by "123";
注意:用以上命令授权的用户不能给其它用户授权,如果想让该用户可以授权,用以下命令:
GRANT ALL PRIVILEGES ON *.* TO 'p361'@'%' Identified by "123" WITH GRANT OPTION;
privileges包括:
- alter:修改数据库的表
- create:创建新的数据库或表
- delete:删除表数据
- drop:删除数据库/表
- index:创建/删除索引
- insert:添加表数据
- select:查询表数据
- update:更新表数据
- all:允许任何操作
- usage:只允许登录
回收权限(revoke)
revoke privileges on dbname[.tbname] from username;
revoke all privileges on *.* from p361;
use mysql
select * from user
进入 mysql库中
修改密码;
update user set password = password('qwe') where user = 'p646';
刷新权限;
flush privileges
导出
在dos命令窗口中执行:mysqldump bjpowernode > D:\ bjpowernode.sql -uroot –proot
注意:MySQL 5.6版本以后为了安全,导出有所改变:
mysqldump bjpowernode > D:\ bjpowernode.sql -uroot –p
Enter password: ****
导出指定的表:
mysqldump bjpowernode emp> D:\ bjpowernode.sql -uroot –proot
导入
先登录到mysql服务器上
创建数据库:create database bjpowernode;
使用数据库:use bjpowernode;
然后初始化数据库:source D:\bjpowernode.sql
mysql> show tables;
+-----------------------+
| Tables_in_bjpowernode |
+-----------------------+
| dept |
| dept2 |
| dept2_view |
| dept_bake |
| emp |
| emp2 |
| emp_dept_view |
| emp_veiw |
| mytable |
| salgrade |
| t_class |
| t_product |
| t_student |
| t_user |
| t_vip |
+-----------------------+
数据库设计三范式
数据库设计范式是数据库表的设计依据,如何进行数据库表的设计
数据库的设计范式:
- 第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。
- 第二范式:建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。
- 第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。
设计数据库表的时候,需要按照以上的范式进行,可以避免表中数据的冗余,空间的浪费。
第一范式
最核心,最重要的范式,所有表的设计都需要满足:必须有主键,并且每一个字段都是原子性不可再分。
学生编号 | 学生姓名 | 联系方式 |
---|---|---|
1001 | 张三 | zs@gmail.com,1359999999 |
1002 | 李四 | ls@gmail.com,13699999999 |
1001 | 王五 | ww@163.net,13488888888 |
以上是学生表不满足第一范式,1、没有主键。2、联系方式可以分为邮箱地址和电话
修改之后,以下的表就满足了第一范式。
学生编号 (PK) | 学生姓名 | 邮箱地址 | 联系电话 |
---|---|---|---|
1001 | 张三 | zs@gmail.com | 1359999999 |
1002 | 李四 | ls@gmail.com | 13699999999 |
1001 | 王五 | ww@163.net | 13488888888 |
第二范式
建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。
这张表描述了学生和老师的关系:(1个学生可能有多个老师,1个老师可能有多个学生)多对多的关系
学生编号 | 学生姓名 | 教师编号 | 教师姓名 |
---|---|---|---|
1001 | 张三 | 001 | 王老师 |
1002 | 李四 | 002 | 赵老师 |
1003 | 王五 | 001 | 王老师 |
1001 | 张三 | 002 | 赵老师 |
不满足第一范式:没有主键
修改后:
学生编号+ | 教师编号 (PK) | 学生姓名 | 教师姓名 |
---|---|---|---|
1001 | 001 | 张三 | 王老师 |
1002 | 002 | 李四 | 赵老师 |
1003 | 001 | 王五 | 王老师 |
1001 | 002 | 张三 | 赵老师 |
学生编号 教师编号 ,两个字段联合做主键,复合主键(PK:学生编号+教师编号)满足第一范式。但是不满足第二范式,“张三”依赖1001,“王老师”依赖001,产生了部分依赖,导致数据冗余,空间浪费。
为了让以上的表满足第二范式,需要这样设计:
-
使用三张表来表示多对多的关系。
学生表
学生编号(PK) 学生姓名 1001 张三 1002 李四 1003 王五 教师表
教师编号(PK) 教师姓名 001 王老师 002 赵老师 学生教师关系表
id(PK) 学生编号(FK) 教师编号(FK) 1 1001 001 2 1002 002 3 1003 001 4 1001 002
诀窍:多对多,三张表,关系表两个外键。
第三范式
建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。
班级和学生的关系:(一对多关系)
学生编号(PK) | 学生姓名 | 班级编号 | 班级名称 |
---|---|---|---|
1001 | 张三 | 01 | 一年一班 |
1002 | 李四 | 02 | 一年二班 |
1003 | 王五 | 03 | 一年三班 |
1004 | 赵六 | 03 | 一年三班 |
分析以上表是否满足第一范式?
满足第一范式,有主键
分析以上表是否满足第二范式?
满足第二范式,因为主键不是复合主键,没有产生部分依赖。主键是单一的主键。
分析以上表是否满足第三范式?
第三范式要求:不要产生传递依赖
一年一班依赖01,01依赖1001,产生了传递依赖
因此不符合第三范式的要求。产生了数据的冗余。
为了符合第三范式,以上表需要这样设计:
班级表:
班级编号 (PK) | 班级名称 |
---|---|
01 | 一年一班 |
02 | 一年二班 |
03 | 一年三班 |
学生表:
学生编号(PK) | 学生姓名 | 班级编号 (FK) |
---|---|---|
1001 | 张三 | 01 |
1002 | 李四 | 02 |
1003 | 王五 | 03 |
1004 | 赵六 | 03 |
一对多:两张表,多的表加外键。
总结表的设计
- 一对多:一对多,两张表,多的表加外键。
- 多对多:多对多,三张表,关系表两个外键。
- 一对一:一对一,外键唯一。
庞大的一张表:
t_user
id | login_name | login_pwd | real_name | address… | |
---|---|---|---|---|---|
1 | zhangsan | 123 | 张三 | zhangsan@xxx.com | … |
2 | lisi | 123 | 李四 | lisi@xxx.com | … |
… |
拆分为两个表:
t_login 登录信息表
id(PK) | login_name | login_pwd |
---|---|---|
1 | zhangsan | 123 |
2 | lisi | 123 |
t_user 用户详细信息表
id(PK) | real_name | address… | login_id(fk+unique) | |
---|---|---|---|---|
100 | 张三 | zhangsan@xxx.com | … | 1 |
200 | 李四 | lisi@xxx.com | … | 2 |
数据库设计的三范式是理论上的。实践和理论还是有偏差的,最终目的都是为了满足客户的需求,有时候会拿冗余换执行速度。
因为在mysql当中,表和表之间的连接次数越多,效率越低(笛卡尔积)
有的时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,并且对于开发人员来说,sql语句的编写难度也会降低。