视图
视图就是一条 SELECT 语句执行后返回的结果集,将查询语句包装起来
CREATE VIEW sel_news AS
SELECT n.id,n.title,nt.name
FROM news n
LEFT JOIN newstype nt ON n.type=nt.id
SELECT * FROM sel_news
DROP VIEW sel_news
存储过程
数据库中也可以和java一样有逻辑处理功能,就可以将逻辑事先编辑好存在数据库中,使用时直接调用, 减少数据在数据库和应用服务器之间的传输,提高数据处理的效率
优点:处理某个逻辑的过程直接存储在数据库中,运行速度较快
缺点:对数据库依赖程度较高,可移植性差
DELIMITER $$
CREATE PROCEDURE save_admin(IN p_account VARCHAR(10),IN p_password VARCHAR(10),OUT p_result VARCHAR(10))
BEGIN
DECLARE v_count INT DEFAULT 0;
SELECT COUNT(*) INTO v_count FROM admin WHERE account = p_account;
IF v_count = 0 THEN
INSERT INTO admin(account,PASSWORD)VALUES(p_account,p_password);
SET p_result = "保存成功";
ELSE
SET p_result = "账号已存在";
SELECT p_result;
END IF;
END$$
DELIMITER ;
CALL save_admin('admin','111',@p_result);
mybatis中使用存储过程
指定parameterMap,指定输入输出参数
<parameterMap type="map" id=“usermap">
<parameter property="account" jdbcType="VARCHAR" mode="IN"/>
<parameter property="password" jdbcType="VARCHAR" mode="IN"/>
<parameter property="result" jdbcType="VARCHAR" mode="OUT"/>
</parameterMap>
<insert id="saveAdmin" parameterMap="usermap" statementType="CALLABLE"> {call saveuser(?, ? ,?)}
</insert >
Map<String, Object> parms = new HashMap<String, Object>(); parms.put("account","jim");
parms.put("password","000");
userDao.saveAdmin(parms);
String result = parms.get(“result”);
函数
类似存储过程,主要用于查询
DELIMITER $$
CREATE FUNCTION findType(p_type INT) RETURNS VARCHAR(10)
BEGIN
DECLARE v_type VARCHAR(10) DEFAULT '';
IF p_type = 0 THEN
SET v_type = '超级管理员';
ELSE
SET v_type = '管理员';
END IF;
RETURN v_type;
END$$
DELIMITER ;
SELECT id,account,findType(TYPE)TYPE FROM admin
DELIMITER $$
CREATE FUNCTION find_news_type(p_typeid INT) RETURNS VARCHAR(10)
BEGIN
DECLARE v_type VARCHAR(10) DEFAULT '';
SELECT NAME INTO v_type FROM newstype WHERE id = p_typeid;
RETURN v_type;
END$$
DELIMITER ;
SELECT id,title,find_news_type(TYPE) TYPE FROM news;
触发器
类似存储过程,函数,与表相关,像事件
对表 新增,修改,删除前后自动触发
特征
- 与表相关联:触发器定义在特定的表上,这个表称为触发器表
- 自动激活触发器:当对表中的数据执行 INSERT、UPDATE 或 DELETE 操作时,如果对表上的这个特定操作定义了触发器,该触发器自动执行,这是不可撤销的。
- 不能直接调用:与存储过程不同,触发器不能被直接调用,也不能传递或接受参数。
- 作为事务的一部分:触发器与激活触发器的语句一起做为对一个单一的事务来对待,可以从触发器中的任何位置回滚
语法解析
- 触发器名称:是用来标识触发器的,由用户自定义
- 触发时机:其值是 before 或 after
- 触发事件:其值是 insert,update 和 delete
- 表名称:标识建立触发器的表名,即在哪张表上建立触发器
- 语句:是触发器程序体,触发器程序可以使用 begin 和 end 作为开始和结束,中间包含多条语句;
在行级触发器代码中,可以使用 old 和 new 访问到该行的旧数据和新数据,old和 new 是对应表的行记录类型变量
DELIMITER $$
CREATE
TRIGGER delete_admin_role
BEFORE
DELETE
ON admin
FOR EACH ROW
BEGIN
DELETE FROM admin_role WHERE admin_id = old.id;
END$$
DELIMITER ;
DELETE FROM admin WHERE id = 46
DELIMITER $$
CREATE
TRIGGER insert_admin_log
AFTER
INSERT
ON admin
FOR EACH ROW
BEGIN
INSERT INTO admin_log(id,account,oper_time)VALUES(new.id,new.account,NOW());
END$$
DELIMITER ;
INSERT INTO admin(account)VALUES('admin888')
MySQL架构
- 连接层:负责与客户端和程序建立连接,认证
- 服务层:SQl 接口、解析器、查询优化器、缓存
- 引擎:负责与数据文件系统连接,读写数据
- 物理文件层 :负责存储数据表,日志文件(mysql事务依赖于日志)
MySQL 引擎
引擎是数据库中具体与文件交互的技术,不同引擎的实现方式是有区别的
SHOW ENGINES;
SHOW TABLE STATUS LIKE 'admin'
索引
为什么使用数据库索引?
如果不使用索引的话,查询(where id=100)从第一行开始,逐行向后查询,直到查询到我们需要的数据,如果数据量非常大的情况下效率就很低
什么是索引?
索引类似于书的目录,通过目录快速查找到我们需要的数据
索引是帮助 MySQL 高效获取数据的数据结构,排好序的快速查找的数据结构
在一个数据结构中将数据维护着,方便查找
索引的原理
与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数,本质上都是通过不断地缩小查询范围
索引的优势劣势
优势
- 提高数据检索的效率,降低数据库的 IO 成本
- 通过索引列对数据进行排序,降低数据排序的成本,降低了 CPU 的消耗
劣势
创建索引的原则
创建
- 主键自动建立唯一索引
- 频繁作为查询条件的字段应该创建索引(where 后面的语句)
- 外键建议索引
- 查询中作为排序、分组条件的字段
不创建
- 表记录太少
- 经常增删改的表
- 不作为查询条件的字段
- 数据重复且分布均匀的字段 (性别)
索引的分类
主键索引
设置为主键后会自动建立索引,不能为空,一个表只能有一个主键
SHOW INDEX FROM admin;
DROP INDEX admin_account ON admin;
单值索引
一个索引只包含单个列,一个表可以有多个单值索引 (name / account)
CREATE INDEX admin_account ON admin(account);
唯一索引
索引列 数据不能重复,允许为null
CREATE UNIQUE INDEX admin_account ON admin(account);
组合索引
一个索引中包含多个列,比单值索引开销更小(对于相同的多个列建索引),列数远大于行数使用
组合索引最左前缀原则:列如表中有 a,b,c 3 列,为 a,b 两列创建组合索引,只有在最左侧索引列出现在查询条件中才会生效(a);
全文索引
模糊查询时,一般索引无效,使用全文索引
CREATE FULLTEXT INDEX news_title ON news(title) WITH PARSER ngram;
SELECT * FROM news WHERE MATCH(title) AGAINST('汇总')
索引数据结构
InnoDB默认使用B+ 树 作为数据结构存储索引
- 排好序的,一个节点可以存储多个数据. 横向扩展, 使得树的高度降低了.
- 非叶子节点不存储数据,只存储索引,可以放更多的索引.
- 数据记录都存放在叶子节点中, 找到了索引,也就找了数据.
- 所有叶子节点之间都有一个链指针,非常适合区间查询 age>20 age<50
聚簇索引和非聚簇索引
聚簇索引: 找到了索引,就找到了数据
- 主键可以直接找到数据
- 根据学号只查询学号 可以直接命中学号 此种场景学号就是聚簇的
非聚簇索引 : 找到了索引但没有找到数据,需要根据主键再次回表查询
根据学号只查询学号,姓名; 虽然学号加了索引,但是还需要查询姓名,需要根据学号找到主键,通过主键回表查询
而 MyISAM 引擎采用的是非聚簇式设计,即使是主键索引,因为索引是单独维护在一个文件中,而InnoDB主键索引连着数据
事务
事务就是一次完整的数据库操作,这个操作中的多条sql 执行是一个整体,要么都成功或不成功
MySQL只有InnoDB支持事务,事务用来管理增删改语句
例如转账操作,从A账号向B账号赚钱,数据库就需要分两步操作,这两个操作就不可分
事务特性
事务是必须满足 4 个条件(ACID):原子性(Atomicity,或称不可分割性)、持久性(Durability)、隔离性(Isolation,又称独立性)、一致性(Consistency)
原子性:一次事务中的多个操作要么都成功,要么都失败
持久性:事务一旦提交,数据就不可改变,即使数据库服务出现问题
隔离性:数据库允许有多个事务进行访问,这时就需要对多个事物间的操作进行隔离,四个级别
- 读未提交 问题 脏读
- 读已提交 解决 脏读 问题 不可重复读
- 可重复度 解决 不可重读 问题 幻读
- 串行化 解决一切问题 加锁 效率低
一致性:在事务开始前和事务结束后,数据库完整性没有被破坏
事务设置
SET GLOBAL autocommit=0;
SHOW GLOBAL VARIABLES LIKE 'autocommit';
BEGIN;
INSERT INTO test(NAME)VALUES("aaaa");
ROLLBACK;
SELECT * FROM test;
COMMIT;
并发事务问题
脏读
A事务读到了B事务未提交的数据
- 事务 B 更新年龄 18
- 事务 A 读取数据库信息,年龄是 18
- 事务 B 回滚
不可重复读
在事务 A 中先后两次读取同一个数据,B事务在期间修改了数据并提交,A两次读取的结果不一样,预期是一样的
幻读
在事务 A 中先后两次读取同一个数据,B事务在期间添加了数据行数并提交,A两次读取的行数不同,一般幻读出现在范围查询
事务的隔离级别
只有 InnoDB 支持事务,所以这里说的事务隔离级别是指 InnoDB 下的事务隔离级别
SELECT @@global.transaction_isolation,@@transaction_isolation;
SET SESSION/GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
读未提交
(read uncommitted):一个事务可以读取到另一个事务未提交的修改。
这会带来脏读,幻读,不可重复读问题
读已提交
(read committed):一个事务只能读取另一个事务已经提交的修改。
其避免了脏读,仍然存在不可以重复读和幻读问题
可重复读
(repeatable read MySQL 默认隔离级别):同一个事务中多次读取相同的数据返回的结果是一样的。
其避免了脏读和不可重复读问题,MySQL8 后也解决幻读问题
串行化
(serializable):事务串行执行,避免了以上所有问题,类似加锁效率低
MVCC
多版本并发控制 Multi-Version Concurrent Control
为了提升MySQL 读-写 写-读 两个操作同时进行,写-写MySQL支持行级锁,不能同时操作一行数据
每次对表中的记录操作时,会保存一个日志(undolog) 里面会记录事务的id号.
如果有多个事务操作时,他们就会根据事务id,找到自己操作的版本记录.
不同的隔离级别在读数据时, 会根据版本链生成一个ReadView(临时读视图) 版本链快照
- 读已提交:每次读取数据前都生成一个 ReadView 产生不可重复读 其中数据发生改变,版本链中也会发生修改, 每次读的时候ReadView中的数据就发生改变,所有不可重复读
- 可重复读:在第一次读取数据时生成一个 ReadView,之后数据发生改变,版本链发生变化,没有关系,第一次读的时候,已经拍过照了
锁机制
MySQL中的锁主要针对写写操作
行锁
某个事物对某行记录操作时,会把当前行锁住,其他事务就不能对当前行操作。
粒度最小,并发是最高的,频繁加锁释放锁
表锁
当某个事物
开销小,加锁快,粒度大,并发度最低,锁冲突概率高
间隙锁
在条件范围操作时,会给满足条件的区间数据加锁
共享锁 / 排他锁
共享锁:读锁
排他锁:写锁
在查询时,必要情况下,也可以为读操作加排他锁 select … from admin for update
乐观锁 / 悲观锁
乐观锁:就是没有加锁,通过版本号区分
悲观锁:加锁 (行锁,表锁,间隙锁)
SQL优化
- 正确的使用索引(查询条件列、排序列)
- 避免索引失效
- 在where 子句中 避免 where num is null
- 在 where 子句中使用!=或<>操作符
- 在 where 子句中使用 or 来连接条件
- 在where 使用运算符、函数 (where num/2=100、 substring() )
- 建议使用主键自增 合理利用索引结构
- 索引不宜建立太多 ,一般一张表6个左右 可以考虑组合索引 最左前缀原则
- 状态,类型…一般建议使用数字类型 int
- 变长varchar代替 定长char
- 不建议使用 select * -->查询哪些列…
- 一次性不要查询数据过多 ,可用分页查询,降低每次查询数据量
- 避免字段值为null null是占空弄间的 可以给默认值 ’ ’
|