offer直通车(四)之数据库

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013234928/article/details/89417009

这里主要讲解mysql数据库,oracle请转场;学习mysql ,这里给大家推荐一本书《高性能MySQL(第3版)》,本人自己都还没看完,不过想成为高级开发的,这是必须看的一本书籍。

《高性能MySQL(第3版)PDF》链接: https://pan.baidu.com/s/1AbJT-xPg87ZrO_EqZGSs9Q 提取码: j9p8

MySQL数据库


一、存储引擎

​ 查看数据库支持的存储引擎:show engines;

​ 常见的引擎有4种:innoDB、MyISAM、Memory、Archive

InnoDB和MyISAM 的区别

  • InnoDB支持事务和行级锁 ,也支持主外键;MyISAM不支持事物,只支持表级锁
  • InnoDB不支持全文索引,MyISAM支持
  • InnoDB适用于大量的update和insert;MyISAM适用于大量的select,insert
  • InnoDB,基于磁盘的资源是InnoDB表空间数据文件和它的日志文件;MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型, .frm文件存储表定义,数据文件的扩展名为.MYD, 索引文件的扩展名是.MYI
  • InnoDB采用“聚集索引”的数据存储方式实现B-Tree索引,数据文件本身就是索引文件;MyISAM索引文件和数据文件是分离的;

注意:MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦


为什么不建议使用过长的字段作为主键

​ 因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大

为什么MyISAM会比Innodb 的查询速度快

INNODB在做SELECT的时候,要维护的东西比MYISAM引擎多很多;

1)数据块,INNODB要缓存,MYISAM只缓存索引块, 这中间还有换进换出的减少;

2)innodb寻址要映射到块,再到行,MYISAM 记录的直接是文件的OFFSET,定位比INNODB要快

3)INNODB还需要维护MVCC一致;虽然你的场景没有,但他还是需要去检查和维护

MVCC ( Multi-Version Concurrency Control )多版本并发控制


二、事物管理

1、ACID特性

总得来说,事务是为了保证数据的安全性,一致性,正确性。必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性

  • 原子性(atomic),事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行
  • 一致性(consistent),事务的执行结果,必须是从一个一致状态,变成另一个新的一致状态。事务的原子性保证其一致性
  • 隔离性(insulation),主要在并发时,各个事务之间互不影响。并发事务所作的修改必须与任何其它并发事务所作的修改隔离。
  • 持久性(Duration),事务一旦提交,数据就永久的保存在数据库,它对于系统的影响是永久性的。

2、安全问题

脏读、幻读、不可重复读

  • 脏读:一个事务读取到另一个事务未提交的数据,显然该未提交事务可能发生错误而回滚没有保存进数据库,那么读到的数据就是错误的数据。

  • 第一类更新丢失:开启一个事务,读取到一行数据,另一个事务也读到同一行数据。后一个事务提交事务保存到数据库后第一个事务更新时发生异常而回滚导致后一个事务的更新丢失

  • 第二类更新丢失:一个事务从数据库得到一行数据保存在内存中,这时另一个事务开发也读到同一行一样的数据,另一个事务更改了事务并提交保存到数据库。这时第一个事务依然是最开始的数据(没有重新去数据库读取)进行修改提交导致另一个事务提交的信息丢失。

  • 不可重复读:一个事务两次读取同一行数据,结果得到不同状态结果,如中间正好另一个事务更新了该数据,两次结果相异,不可信任。

  • 幻读: A事务读取B事务提交的新增数据,这时A事务将出现幻象读的问题

注:幻象读和不可重复读是 两个容易混淆的概念,前者是指读到了其它已经提交事务的新增数据,而后者是指读到了已经提交事务的更改数据(更改或删除)

3、隔离级别

四种事务隔离级别:

  • Read Uncommited :可读未提交,级别最低

  • Read Commited :可读已提交 (oracle,SQL Server 默认的隔离级别)

  • Repeatable Read :可重复读 (mysql 默认级别)

  • Serializable :串行化,级别最高

4、行级锁和表级锁

  • 行级锁(悲观锁):for update;基于索引实现的,如果不通过索引访问数据,InnoDB会使用表锁

    • 共享锁:lock in share mode
    • 排它锁:for update
    • 释放锁:COMMIT或ROLLBACK
  • 表级锁:

    • 加写锁LOCK TABLE test WRITE,
    • 加读锁LOCK table test READ;
    • 释放锁UNLOCK TABLES;
  • 乐观锁:不是数据库本身的锁,版本号和CAS

SET AUTOCOMMIT=0;
LOCAK TABLES t1 WRITE, t2 READ, ...;
[do something with tables t1 and here];
COMMIT;
UNLOCK TABLES;
-- 仅当autocommit=0、innodb_table_lock=1(默认设置)时,InnoDB层才能知道MySQL加的表锁

CAS:典型的实现方式就是 Compare and Swap ( CAS ):需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B。 CAS是非阻塞算法的一种常见实现


5、三大范式

  • 第一范式:数据库表中的字段都是单一属性的,不可再分

  • 第二范式:数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖,即符合第二范式。必须依赖主键

  • 第三范式:在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合3NF。 主键和非主键不能相互依赖


三、常用语法

关键字:PRIMARY、FOREIGN 、constraint 、references、column、AUTO_INCREMENT、DEFAULT、COMMENT

1、建表、索引、约束

CREATE TABLE USER(
	user_id INT NOT NULL AUTO_INCREMENT ,
	user_name VARCHAR(20) NOT NULL DEFAULT '' COMMENT '名字',
	PRIMARY KEY (user_id,user_name),
	INDEX user_name(user_name)
);

CREATE TABLE scode (
	scode_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
	scode  INT NOT NULL DEFAULT 0 COMMENT '分数',
	user_id INT NOT NULL DEFAULT 0 ,
	FOREIGN KEY(user_id) REFERENCES USER (user_id)
);

约束语法:

-- 索引
ALTER TABLE user ADD INDEX index_name (user_name)ALTER TABLE user ADD PRIMARY KEY ( user_name)ALTER TABLE user ADD UNIQUE unique_name ( user_name)-- 外键
ALTER  TABLE  table_1  ADD constraint 外键名称 foreign KEY (外键字段名)  references  mytab2 (主键字段名);
-- 字段
ALTER TABLE table_1 ADD column name VARCHAR(100) DEFAULT NULL COMMENT '姓名'

2、存储过程

关键字:delimiter、procedure、declare、cursor、continue、handle

流程控制:case when else、if 、while/loop/repeat (iterate:循环体中表示再次循环、leave:循环体 结束)

delimiter //  #设置结束符
create procedure in_param(in p_in int)
begin   #  begin 后面没有分号
declare tmpName varchar(20) default '' ;

declare flag BOOLEAN DEFAULT TRUE;
declare cur CURSOR FOR SELECT name FROM test ;
declare CONTINUE HANDLER FOR NOT FOUND SET flag  = FALSE; 

OPEN cur
FETCH cur INTO tmpName;
WHILE flag  DO 
	# ....执行体
	FETCH cur INTO tmpName;
END WHILE;	
COMMIT;     # 存储过程记得要提交commit;
CLOSE cur;
end // #  end 后面没有分号 


if val is null 	then select 'val is null'
	elseif  if val is 'ABC' 	then select 'val is ABC'
	else select 'val is Other'
end if;

3、常见函数

  • 聚集函数:min、max、sum、count、avg、round
  • 数值函数:
    • 绝对值函数:abs(x)
    • 向上取整函数:ceil(x)
    • 向下取整函数:floor(x)
    • 取模函数:mod(x,y)
    • 随机数函数:rand()
    • 四舍五入函数:round(x,y)
    • 数值截取函数:truncate(x,y)
  • 字符串函数:数据库字符串下标以数字1开始,而不是以0开始
    • 合并:concat(str1,str2,str3…)
    • 长度:length(str)
    • 截取:left/right(str, length)、substring()\substr(),substring_index
    • 替换:
  • 日期函数
    • 当前时间:
    • 当前日期
    • 年月日
    • 格式化

四、索引

​ 索引可以极大的提高查询访问速度,但是会降低插入,删除,更新表的速度,因为在执行写操作的时候还要操作索引文件。

1、索引分类

普通索引和唯一索引(主键索引)、单列索引和组合索引、全文索引、空间索引、覆盖索引

[UNIQUE|FULLTEXT|SPATIAL]  INDEX|KEY  [索引名](字段名1 [(长度)] [ASC | DESC])

按索引种类:

  • INDEX 普通索引:普通单列索引
  • PRIMARY/UNIQUE (唯一索引):列值唯一的单列索引,这包括主键索引
  • INDEX 联合索引:多列值组成一个索引, 有序性和最左前缀原则
  • FULLTEXT (全文索引):对文本的内容进行分词,进行搜索 ;FULLTEXT KEY index_name(column_name)
  • SPATIAL (空间索引):MyISAM 引擎支持,SPATIAL KEY index_name(column_name)

按照查询方式分类:

  • 前缀索引:对字段内文本的前几个字符建立索引(ADD KEY(column_name(prefix_length)));MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用作覆盖索引

  • 覆盖索引:如果一个索引包含(或覆盖)所有需要查询的字段的值,称为‘覆盖索引’。即只需扫描索引而无须回表

  • 合并索引:使用多个单列索引定位(联合索引)

explain 中Extra:Using index,说明MySQL正在使用覆盖索引

数据存储方式分类:聚簇索引、非聚簇索引(又叫二级索引)

  • 聚簇索引:

    • 每个叶子节点都包含了主键值、事物ID、回滚指针和 所有剩余列;即叶子结点存储了行的所有数据
    • 最多只能创建一个
    • InnoDB 通过主键聚集数据,没有主键,会选一个非空的唯一索引代替,再没有,会隐式定义一个主键
  • 聚簇索引优缺点:

    • 数据访问更快

在SQL语言中,建立聚簇索引使用CREATE INDEX语句,格式为:CREATE CLUSTER INDEX index_name ON table_name(column_name1,column_name2,…);

按索引类型分类:

  • B+树:联合索引
  • 哈希索引:只有memory引擎支持
  • 全文索引:只有MyISAM引擎支持,只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引
  • R-Tree:在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。

MySQL数据库索引选择使用B+树!


2、索引定级

**第一颗星:**索引将相关的记录放到一起,也就是where后面的等值谓词,可以匹配索引列顺序

第二颗星:如果索引中的数据顺序和查找中的排列顺序一致,也就是order by 中的排序和索引顺序是否一致

**第三颗星:**如果索引中的列包含了查询中的需要的全部列(覆盖索引)

意义:

  1. 第一颗星,where后面的谓词和索引列匹配的越多,索引片越窄,最终扫描的数据行也是越小
  2. 第二颗星,是避免排序,如果结果集采用现有顺序读取,那么就会避免一次排序,避免提前物化结果集
  3. 第三颗星,避免每一个索引行查询,都需要去聚簇索引进行一次随机IO查询

3、索引失效

索引失效:

  • 少用or,它会使联合索引失效
  • 单列索引列为null,组合索引全为null;使用 is null 或者 is not null 也不能使用索引
  • 不要在索引列上做任何操作:如left(name,1)或 运算包括(+,-,*,/,! 等),索引字段单独在 “ = ” 一侧
  • 范围条件后列上索引失效
  • 使用不等于(!= 或者<>)不能使用索引
  • like查询以%开头会索引失效
  • 如果列类型是字符串,那条件中一定要是字符型(虽然数字会自动转换,这不行),否则不使用索引

扩展:

为什么索引列无法存储Null值?

  • 索引是有序的。NULL值进入索引时,无法确定其应该放在哪里。
  • 如果需要把空值存入索引,方法有二:
    • 其一,把NULL值转为一个特定的值,在WHERE中检索时,用该特定值查找。
    • 其二,建立一个复合索引

4、索引优缺点

创建索引可以大大提高系统的性能。
第一, 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性
第二, 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
第三, 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
第四, 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
第五, 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

为什么不对表中的每一个列创建一个索引呢

第一, 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
第二, 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
第三, 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。

适合创建索引的列:

  • 经常需要搜索的列上,可以加快搜索的速度;
  • 作为主键的列上,强制该列的唯一性和组织表中数据的排列结构
  • 经常用在连接的列上,这 些列主要是一些外键,可以加快连接的速度
  • 在经常使用在WHERE子句中的列上面创建索引**,**加快条件的判断速度。

不适合的列:

  • 查询中很少使用或者参考的列不应该创建索引
  • 只有很少数据值的列也不应该增加索引
  • 当修改性能远远大于检索性能时,不应该创建索 引

5、优化器会分析不同执行顺序产生的性能消耗不同而动态调整执行顺序


五、主从同步

​ **原理:**读写分离

​ 主服务器(Master)负责网站NonQuery操作,从服务器负责Query操作,用户可以根据网站功能模特性块固定访问Slave服务器,或者自己写个池或队列,自由为请求分配从服务器连接。主从服务器利用MySQL的二进制日志文件,实现数据同步。二进制日志由主服务器产生,从服务器响应获取同步数据库。


六、SQL性能优化

1、优化方式

  • 添加索引
  • 查询时不要返回不需要的行、列
  • 每个字段都设置默认值,查询列值 is null,就将导致引擎放弃使用索引而进行全表扫描
  • 只需要一条数据时使用limit 1
  • 单表查询,减少多表查询
  • 将where 字句中滤掉最大数量记录的条件写在最右边,(SQL从右向左)
  • 减少数据访问次数
  • 用EXISTS替代IN、用NOT EXISTS替代NOT IN
  • 使用explain 分析
  • 分页优化时可以先查出最大偏移量的ID,再进行分页

七、Java-JDBC

1> 基本操作过程:

  • 第一步:注册一个数据库驱动
    • 方式1:Class.forName(“oracle.jdbc.driver.OracleDriver”);
    • 方式2:Driver drv = new oracle.jdbc.driver.OracleDriver(); DriverManager.registerDriver(drv);
    • 方式3:通过设置系统属性 jdbc.drivers,编译时在虚拟机中加载驱动.
  • **第二步:建立连接;**Connection conn=DriverManager.getConnection(“IP 地址及端口号和数据库实例名”, ”User”,” Pasword”);
    • 例: jdbc:mysql://192.168.8.21:3306/test
  • **第三步:获取Statement 对象;**Statement stmt = conn.createStatement();
  • **第四步:通过Statement 执行SQL;**stmt.executeQuery(String sql);
  • 第五步:处理结果集
  • 第六步:关闭数据库连接

PreparedStatement:预编译

调用存储过程:CallableStatement:cs = conn.prepareCall(“call sp_select_nofilter()”);

获取主键:st.getGeneratedKeys();

Statement st = conn.createStatement();
st.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = st.getGeneratedKeys();
if (rs.next()) {
	key = rs.getInt(1);
}

注意:

Connection实例线程安全问题

  • Connection不是线程安全的,它在多线程环境中使用时,会导致数据操作的错乱,特别是有事务的情况

2> 数据库连接池

好处:

  • 使用连接池来管理Connection,和重复使用,减少了资源开销

在Java中,连接池使用javax.sql.DataSource接口来表示连接池/数据源

常用的DataSource的实现:

  • C3P0: Hibernate推荐的,但是该连接池在07年之后就不再更新了,不建议使用:性能太太差了。
  • DBCP: Apache组织的项目,Spring推荐的. 真心不错。
  • Druid: 阿里巴巴的项目(德鲁伊),世界上最好连接池。

DataSource(数据源)和连接池(Connection Pool)是同一个。

使用连接池和不使用连接池的区别在哪里?
获取连接对象:

  • 没有连接池: 通过DriverManager来获取;连接对象,DriverManager.getConnection(url,username,password)
  • 存在连接池: 直接通过连接池来获取连接对象,Connection conn = DataSource对象.getConnection();

释放连接对象:

  • 没有连接池: conn.close():和数据库服务器(DBMS)断开连接。
  • 存在连接池: conn.close():把Connection对象归还给连接池,并没有和DBMS断开。

八、sql 脚本

关键字:PRIMARY、FOREIGN 、constraint 、references、column、AUTO_INCREMENT、DEFAULT、COMMENT

一、SQL

1、建表、索引、约束

CREATE TABLE USER(
	user_id INT NOT NULL AUTO_INCREMENT ,
	user_name VARCHAR(20) NOT NULL DEFAULT '' COMMENT '名字',
	PRIMARY KEY (user_id,user_name),
	INDEX user_name(user_name)
);

CREATE TABLE scode (
	scode_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
	scode  INT NOT NULL DEFAULT 0 COMMENT '分数',
	user_id INT NOT NULL DEFAULT 0 ,
	FOREIGN KEY(user_id) REFERENCES USER (user_id)
);

约束语法:

-- 索引
ALTER TABLE user ADD INDEX index_name (user_name)ALTER TABLE user ADD PRIMARY KEY ( user_name)ALTER TABLE user ADD UNIQUE unique_name ( user_name)-- 外键
ALTER  TABLE  table_1  ADD constraint 外键名称 foreign KEY (外键字段名)  references  mytab2 (主键字段名);
-- 字段
ALTER TABLE table_1 ADD column name VARCHAR(100) DEFAULT NULL COMMENT '姓名'

2、常见查询

  • 聚集函数:min、max、sum、count、avg、round
  • 数值函数:
    • 绝对值函数:abs(x)
    • 向上取整函数:ceil(x)
    • 向下取整函数:floor(x)
    • 取模函数:mod(x,y)
    • 随机数函数:rand()
    • 四舍五入函数:round(x,y)
    • 数值截取函数:truncate(x,y)
  • 字符串函数:数据库字符串下标以数字1开始,而不是以0开始
    • 合并:concat(str1,str2,str3…)
    • 长度:length(str)
    • 截取:left/right(str, length)、substring()\substr(),substring_index
    • 替换:
  • 日期函数
    • 当前时间:
    • 当前日期
    • 年月日
    • 格式化

九、常见问题

经典题型

1、mysql分页有什么优化
2、悲观锁、乐观锁
3、组合索引,最左原则
4、mysql 的表锁、行锁
5、mysql 性能优化
6、mysql的索引分类:B+,hash;什么情况用什么索引
7、事务的特性和隔离级别

count(*) 和 count(1)和count(列名)区别

执行效果上:

  • count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL
  • count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL
  • count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。

执行效率上:

  • 列名为主键,count(列名)会比count(1)快
  • 列名不为主键,count(1)会比count(列名)快
  • 如果表多个列并且没有主键,则 count(1) 的执行效率优于 count( * )
  • 如果有主键,则 select count(主键)的执行效率是最优的
  • 如果表只有一个字段,则 select count( * )最优。

猜你喜欢

转载自blog.csdn.net/u013234928/article/details/89417009