MYSQL基础篇

文章目录

  • 一、SQL查询语言
    • 1.DDL
    • 2.DML
    • 3.DCL
  • 二、MYSQL专题
    • 1.存储引擎
    • 2.索引及底层实现
    • 3.事务
    • 4.锁机制
    • 5.触发器和存储过程


一、SQL查询语言

1.DDL

DDL语言又称为数据定义语言,主要的操作有create、drop、alter、show。

创建一个student数据库

create database if not exists student;

查询该数据库的创建信息

show create database student;

删除数据库

drop database if exists student;

在对数据库中的表进行一系列操作时首先需要指定一个库

use student;

创建表

create table 表名(字段名 字段类型 字段约束 COMMENT "注释");

在student这个库下创建一个grade表

create table grade (id char(20) primary key COMMENT "学生学号",
                    name varchar(20) not null COMMENT "学生姓名",
                    age int not null COMMENT "学生年龄",
                    grade int not null COMMENT "学生成绩");

查询grade表的字段信息

desc grade;

查询grade表的创建信息

show create table grade;

字段约束有五种:

  1. 主键约束:字段不能为空且不能重复 primary key
  2. 外键约束:该字段在另外的表中一定存在 references grade(age)
  3. 唯一约束:这一列字段不允许重复
  4. 非空约束:字段值不能为空
  5. 默认值约束:给定一个默认值 default

删除grade这个表

drop table grade;

修改表的字段类型:修改grade表的年龄字段类型为char(10)

alter table grade modify age char(10);

修改表的字段名称:修改grade表的id字段为sid字段

alter table grade change id sid char(20);

给grade表添加字段id_card

alter table grade add id_card char(18) unique;

指定字段的添加位置:将card字段添加到name字段的后面

alter table grade add card char(18) unique after name;

删除grade表中的card字段

alter table grade drop card;

修改grade表为information表

alter table grade rename information;

2.DML

DML又称为数据操作语言,主要操作有insert、delete、update、select。

向grade表中添加一行数据

insert into grade values("001","zhangli","18",98);

给grade表的指定字段插入数据

insert into grade (sid,name,age,grade) values("002","lili","19",97);

给grade表中一次性插入多行数据

insert into grade values("003","manman","18",87),
                        ("004","keke","18",95),
                        ("005","dada","20",76);

将grade表中学号为004的学生的姓名修改为zhangsan

update grade set name="zhangsan" where id="004";

查询grade表中的所有数据

select * from grade;

查询grade表中成绩大于95的学生的信息

select * from grade where grade>95;

去重查询:查询grade表中不同的年龄

select distinct age from grade;

按升序查询grade表中的学生学号和姓名

select id,name from grade order by grade asc;

按降序查询grade表中的学生学号和姓名

select id,name from grade order by grade desc;

删除grade表中学号为002的这一行数据

delete from grade where id="002";

删除grade表中的所有数据

delete from grade;

分组查询:按找年龄对grade表中的学生分组,查询他们的总分

select age,SUM(grade) from grade group by age;

等值查询:查询grade表和stu表中id相等的id。

select grade.id,stu.id from grade,stu where grade.id=stu.id;

求grade表中平均分大于80的学生的平均成绩(当有聚合函数为条件时,条件关键词为having而不是where)

select id,avg(grade) from grade having avg(grade)>80 group by id;

查询grade表和result的所有信息(包含重复的信息)

select * from grade
union all
select *from result;

查询grade表和result的所有信息(不包含重复的信息)

select * from grade
union
select * from result;

在grade表中查询姓李的学生的所有信息

select * from grade where name like "李%";

3.DCL

DCL又称为数据控制语言

grant 给用户授权

revoke 代表回收权限

自主权限管理:管理员可自由分配权限
强制权限管理:权限是固定的,拥有一定等级就拥有相应权限。

二、MYSQL专题

1.存储引擎

存储引擎就是mysql上的一个驱动程序,此部分介绍四种存储引擎:

(1)MyISAM

不支持外键,不支持事务,支持全文索引,特点是表锁设计,对于一些在线分析处理操作速度快。

其索引是由B+树实现的,文件组成是由 myd 存放数据的 ,由myi存放索引的。

(2)InnoDB

支持事务,支持外键,不支持全文索引。

主要是面向在线事务处理方面的应用,特点是行锁设计。

Innodb采用聚集索引的方式。没有主键,没有唯一键,为每一行生产一个6字节的行id,作为主键。索引也是由B+树实现的。

(3)Memory

将数据放在内存中,如果数据库重启或者宕机,表数据就会丢失。

Memory存储引擎非常适合存储一些临时表,默认的是哈希索引,不是B+树索引,varchar()默认是按照char()存储的,浪费内存。

不支持text和BLOB类型。如果数据中有text和BLOB类型,数据库会把这些数字转换到磁盘上。

(4)Archive

只支持INSERT和SELECT操作,使用压缩算法将数据进行压缩后存储,压缩比例一般是1:10,主要提供插入和压缩功能。

2.索引及底层实现

简单的来说,索引是一种数据结构,它的目的是为了提高查询数据的效率。

索引就相当于一本书的目录,有了这个目录,我们在书中要查找的内容就会容易很多,否则如果书本内容较多想要查找自己要找的内容无异于大海捞针。

索引分为聚簇索引非聚簇索引两种,聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快。

MySQL索引的类型:

  1. 普通索引:是最基本的索引,它没有任何限制, MyIASM中默认的BTREE类型的索引,也是我们大多数情况下用到的索引。
  2. 唯一索引:与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值(注意和主键不同)。如果是组合索引,则列值的组合必须唯一。
  3. 全文索引:对于较大的数据集,将资料输入一个没有全文(FULLTEXT)索引的表中,然后创建索引,其速度比把资料输入现有FULLTEXT索引的速度更为快。不过对于大容量的数据表,生成全文索引是一个非常消耗时间非常消耗硬盘空间的做法。
  4. 单列索引、多列索引:多个单列索引与单个多列索引的查询效果不同,因为执行查询时,MySQL只能使用一个索引,会从多个索引中选择一个限制最为严格的索引。
  5. 组合索引(最左前缀):平时用的SQL查询语句一般都有比较多的限制条件,所以为了进一步榨取MySQL的效率,就要考虑建立组合索引。

MYSQL索引的缺点:

虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。
因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。建立索引会占用磁盘空间的索引文件。
一般情况这个问题不太严重,但如果在一个大表上创建了多种组合索引,索引文件的会膨胀很快。
索引只是提高效率的一个因素,如果MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。

MYSQL索引的优化:

1.何时使用聚集索引或非聚集索引?

MYSQL基础篇_第1张图片
2. 索引不会包含有NULL值的列

只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。

3.使用短索引

对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是唯一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

4.索引列排序

MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

5.like语句操作
一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。

6.不要在列上进行运算

例如:select * from users where YEAR(adddate)<2007,将在每个行上进行运算,这将导致索引失效而进行全表扫描。

3.事务

事务是一组sql语句的集合,事务有四个特性ACID

A原子性:一个事务的执行被视为一个不可分割的最小单元。事务里面的操作,要么全部成功执行,要么全部失败回滚,不可以只执行其中的一部分。

C一致性:一个事务的执行不应该破坏数据库的完整性约束。

I隔离性:通常来说,事务之间的行为不应该互相影响。然而实际情况中,事务相互影响的程度受到隔离级别的影响。

D持久性:事务提交之后,需要将提交的事务持久化到磁盘。即使系统崩溃,提交的数据也不应该丢失。

事务不隔离会产生什么问题

1)第一类丢失更新:在没有事务隔离的情况下,两个事务都同时更新一行数据,但是第二个事务却中途失败退出, 导致对数据的两个修改都失效了。

例如:

张三的工资为5000,事务A中获取工资为5000,事务B获取工资为5000,汇入100,并提交数据库,工资变为5100,

随后
事务A发生异常,回滚了,恢复张三的工资为5000,这样就导致事务B的更新丢失了。

2)脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

例如:
  张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。
与此同时,
  事务B正在读取张三的工资,读取到张三的工资为8000。
  随后,
  事务A发生异常,而回滚了事务。张三的工资又回滚为5000。
  最后,
  事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。

3)不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

例如:
  在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。
  与此同时,
  事务B把张三的工资改为8000,并提交了事务。
  随后,
  在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。

4)第二类丢失更新:不可重复读的特例。有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。

例如:

在事务A中,读取到张三的存款为5000,操作没有完成,事务还没提交。
  与此同时,
  事务B,存储1000,把张三的存款改为6000,并提交了事务。
  随后,
  在事务A中,存储500,把张三的存款改为5500,并提交了事务,这样事务A的更新覆盖了事务B的更新。

5)幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
例如:
  目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。
  此时,
  事务B插入一条工资也为5000的记录。
  这是,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。

四种隔离级别:

1.READ UNCOMMITTED(未提交读):在RU的隔离级别下,事务A对数据做的修改,即使没有提交,对于事务B来说也是可见的,这种问题叫脏读。这是隔离程度较低的一种隔离级别,在实际运用中会引起很多问题,因此一般不常用。

2.READ COMMITTED(提交读):在RC的隔离级别下,不会出现脏读的问题。事务A对数据做的修改,提交之后会对事务B可见,举例,事务B开启时读到数据1,接下来事务A开启,把这个数据改成2,提交,B再次读取这个数据,会读到最新的数据2。在RC的隔离级别下,会出现不可重复读的问题。这个隔离级别是许多数据库的默认隔离级别。

3.REPEATABLE READ(可重复读):在RR的隔离级别下,不会出现不可重复读的问题。事务A对数据做的修改,提交之后,对于先于事务A开启的事务是不可见的。举例,事务B开启时读到数据1,接下来事务A开启,把这个数据改成2,提交,B再次读取这个数据,仍然只能读到1。在RR的隔离级别下,会出现幻读的问题。幻读的意思是,当某个事务在读取某个范围内的值的时候,另外一个事务在这个范围内插入了新记录,那么之前的事务再次读取这个范围的值,会读取到新插入的数据。Mysql默认的隔离级别是RR,然而mysql的innoDB引擎间隙锁成功解决了幻读的问题。

4.SERIALIZABLE(可串行化):可串行化是最高的隔离级别。这种隔离级别强制要求所有事物串行执行,在这种隔离级别下,读取的每行数据都加锁,会导致大量的锁征用问题,性能最差。

MYSQL基础篇_第2张图片

事务的实现是基于数据库的存储引擎。不同的存储引擎对事务的支持程度不一样。mysql中支持事务的存储引擎有innoDB和NDB。innoDB是mysql默认的存储引擎,默认的隔离级别是RR,并且在RR的隔离级别下更进一步,通过多版本并发控制(MVCC,Multiversion Concurrency Control )解决不可重复读问题,加上间隙锁(也就是并发控制)解决幻读问题。因此innoDB的RR隔离级别其实实现了串行化级别的效果,而且保留了比较好的并发性能。
事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志实现。说到事务日志,不得不说的就是redo和undo

1.redo log

在innoDB的存储引擎中,事务日志通过重做(redo)日志和innoDB存储引擎的日志缓冲(InnoDB Log Buffer)实现。事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是DBA们口中常说的“日志先行”(Write-Ahead Logging)。当事务提交之后,在Buffer Pool中映射的数据文件才会慢慢刷新到磁盘。此时如果数据库崩溃或者宕机,那么当系统重启进行恢复时,就可以根据redo log中记录的日志,把数据库恢复到崩溃前的一个状态。未完成的事务,可以继续提交,也可以选择回滚,这基于恢复的策略而定。
在系统启动的时候,就已经为redo log分配了一块连续的存储空间,以顺序追加的方式记录Redo Log,通过顺序IO来改善性能。所有的事务共享redo log的存储空间,它们的Redo Log按语句的执行顺序,依次交替的记录在一起。

2.undo log

undo log主要为事务的回滚服务。在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。单个事务的回滚,只会回滚当前事务做的操作,并不会影响到其他的事务做的操作。

4.锁机制

共享锁和排他锁

MYSQL中的共享锁也叫做读锁和写锁。

  • 共享锁:不堵塞,多个用户可以同时读一个资源。互不干扰。
  • 排他锁:一个写锁会阻塞其他的读锁和写锁,这样可以只允许一个用户进行写入,防止其他用户读取正在写入的资源。

表锁和行锁

  • 表锁:粒度大,系统开销小,并发性低,会锁定整张表,效率低,MyIsam存储引擎使用表锁
  • 行锁:粒度小,开销大,最大程度支持并发处理,InnoDB存储引擎使用行锁。

悲观锁和乐观锁

-悲观锁:

  • 它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
  • 在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它事务无法读取这些数据。

乐观锁:

  • 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
  • 而乐观锁机制在一定程度.上解决了这个问题。乐观,锁,大多是基于数据版本( Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,- -般是通过为数据库表增加一个"version"字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

死锁

多数情况下,可以认为如果一个资源被锁定,它总会在以后某个时间被释放。而死锁发生在当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。简单的说,进程A等待进程B释放他的资源,B又等待A释放他的资源,这样就互相等待就形成死锁。

虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件:

  • 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程,占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
  • 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
  • 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
  • 环路等待条件:指在发生死锁时,必然存在一个进程–资源的环形链,即进程集合{P0,P1,P2,.,Pn}中的PO正在等待- -个P1占用的资源; P1正在等待P2占用的资源,Pn正在等待已被PO占用的资源。

最大限度降低死锁的方法:

  • 设置获得锁的超时时间
  • 按照同一顺序访问对象
  • 避免事务中的用户交互
  • 保持事务简短并在一个批处理中
  • 使用低隔离级别
  • 使用绑定连接

5.触发器和存储过程

  1. 触发器是一个特殊的存储过程,不同的是存储过程要用CALL来调用,而触发器不需要使用CALL,也不需要手工启动,只要当一个预定义的事件发生的时候,就会被MYSQL自动调用。
  2. MySQL 除了对 INSERT、UPDATE、DELETE 基本操作进行定义外,还定义了 LOAD DATA 和 REPLACE 语句。
  3. LOAD DATA 语句用于将一个文件装入到一个数据表中,相当与一系列的 INSERT 操作。
  4. REPLACE 语句一般来说和 INSERT 语句很像,只是在表中有 primary key 或 unique 索引时,如果插入的数据和原来 primary key 或 unique 索引一致时,会先删除原来的数据,然后增加一条新数据,也就是说,一条 REPLACE 语句有时候等价于一条。
  5. INSERT 语句,有时候等价于一条 DELETE 语句加上一条 INSERT 语句。

创建触发器

CREATE TRIGGER trigger_name trigger_time trigger_event
    ON tbl_name FOR EACH ROW trigger_stmt;
  • 触发程序是与表有关的命名数据库对象,当表上出现特定事件时,将激活该对象。
  • 触发程序与命名为tbl_name的表相关。tbl_name必须引用永久性表。不能将触发程序与临时表或视图关联起来。
  • trigger_time是触发程序的动作时间。它可以是BEFORE或AFTER,以指明触发程序是在激活它的语句之前或之后触发。
  • trigger_event指明了激活触发程序的语句的类型。
  • trigger_stmt是当触发程序激活时执行的语句。
    如果你打算执行多个语句,可使用BEGIN … END复合语句结构。这样,就能使用存储子程序中允许的相同语句。

trigger_event可以是下述值之一:

  • INSERT:将新行插入表时激活触发程序,例如,通过INSERT、LOAD DATA和REPLACE语句。
  • UPDATE:更改某一行时激活触发程序,例如,通过UPDATE语句。
  • DELETE:从表中删除某一行时激活触发程序,例如,通过DELETE和REPLACE语句。

注意,trigger_event与以表操作方式激活触发程序的SQL语句并不很类似。
例如,关于INSERT的BEFORE触发程序不仅能被INSERT语句激活,也能被LOAD DATA语句激活。

创建一个单执行语句的触发器

CREATE TABLE account(acct_num INT ,amount DECIMAL(10,2));
CREATE TRIGGER ins_sum BEFORE INSERT ON account
FOR EACH ROW SET @SUM=@SUM+new.amount;

首先创建一个account表,表中有两个字段,分别为:acct_num字段(定义为int类型),amount字段(定义成浮点类型);其次创建一个名为ins_sum的触发器,触发的条件是向数据表account插入数据之前,对新插入的amount字段值进行求和计算。

创建有多个执行语句的触发器

CREATE TRIGGER testref BEFORE INSERT ON test1
  FOR EACH ROW BEGIN
    INSERT INTO test2 SET a2 = NEW.a1;
    DELETE FROM test3 WHERE a3 = NEW.a1;  
    UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;
  END

查看触发器

show triggers;

删除触发器

drop trigger trigger_name;

总结:

对于相同的表,相同的事件只能创建一个触发器,比如对表account创建了BEFORE INSERT触发器
那么如果对表account再次创建一个BEFORE INSERT触发器,MYSQL就会报错,此时,只可以在表account上
创建AFTER INSERT或者BEFORE UPDATE类型的触发器

存储过程

存储过程是一组可编程的函数,是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。

存储过程的优点:

  1. 将重复性很高的一些操作,封装到一个存储过程中,简化了对这些SQL的调用
  2. 批量处理:SQL+循环,减少流量,也就是“跑批”
  3. 统一接口,确保数据的安全

你可能感兴趣的:(MYSQL基础篇)