mysql随笔
⚫ MySQL 中有哪几种锁?
MYISAM支持表锁,InnoDB支持表锁和行锁,默认为行锁。
1、表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
2、行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
3、页
面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
⚫ 死锁是怎么出现的?
什么是死锁
(1) 打个比方,假设有 P1 和 P2 两个进程,都需要 A 和 B 两个资源,现在 P1 持有 A 等 待 B 资源,而 P2 持有 B 等待 A 资源,两个都等待另一个资源而不肯释放资源,就
这样无限等待中,这就形成死锁,这也是死锁的一种情况。给死锁下个定义,如
果一组进程中每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,
那么该组进程是死锁的。
(2) 竞争不可抢占资源引起死锁
(3) 也就是我们说的第一种情况,而这都在等待对方占有的不可抢占的资源。
64、死锁的发生必须具备 4 个条件
(4) 互斥条件: 其实就是进程对所分配到的资源进行排它性使用,是指在一段时间内
某个资源只能由一个进程占用。如果此时还有其它进程请求资源,那么其它线程
只能等待,直到占有资源的进程用完被释放掉。
(5) 请求和保持条件: 指进程已经保持至少一个资源,但又提出了新的资源请求,而
该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保
持不放。
(6) 不剥夺条件: 指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用
完时由自己释放。
(7) 环路等待条件: 指在发生死锁时候,一定存在一个进程相当于资源的环形链,也
就是进程的集合像{P0,P1,P2,···,Pn}中的 P0 正在等待一个 P1 占用的资
源;P1 正在等待 P2 占用的资源,……,Pn 正在等待已被 P0 占用的资源
65、如何避免死锁四种方法
(1) 检测死锁
这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安
全区,此方法允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,
及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源,然后采取适当
措施,从系统中将已发生的死锁清除掉。
(2) 解除死锁。
这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将进程
从死锁状态中解脱出来。常用的实施方法是撤销或挂起一些进程,以便回收一些资
源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。
死锁的检测和解除措施,有可能使系统获得较好的资源利用率和吞吐量,但在实现
上难度也最大。
⚫ SQL 之聚合函数
聚合函数是对一组值进行计算并返回单一的值的函数,它经常与 select 语句中的 group by 子句一同使用。
- avg():返回的是指定组中的平均值,空值被忽略。
- count():返回的是指定组中的项目个数。
- max():返回指定数据中的最大值。
- min():返回指定数据中的最小值。
- sum():返回指定数据的和,只能用于数字列,空值忽略。
- group by():对数据进行分组,对执行完 group by 之后的组进行聚合 函数的运算,计算每一组的值。最后用 having 去掉不符合条件的组, having 子句中的每一个元素必须出现在 select 列表中(只针对于 mysql)。
⚫ SQL Select 语句完整的执行顺序
查 询 中 用 到 的 关 键 词 主 要 包 含 六 个 , 并 且 他 们 的 顺 序 依 次 为 select--from--where--group by--having--order by 其中 select 和 from 是必须的,其他关键词是可选的, 这六个关键词 的执行顺序如下: from: 需要从哪个数据表检索数据 where: 过滤表中数据的条件 group by: 如何将上面过滤出的数据分组算结果 order by : 按照什么样的顺序来查看返回的数据
⚫ 简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别
- InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
- InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
- InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
- InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
- Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高
⚫ MySQL 中 InnoDB 支持的四种事务隔离级别名称,以及逐级之间的区别?
SQL 标准定义的四个隔离级别为:
1、read uncommited :读到未提交数据
2、read committed:脏读,不可重复读
3、repeatable read:可重读
4、serializable :串行事物
⚫ 常见索引原则有
- 选择唯一性索引,唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
- 为经常需要排序、分组和联合操作的字段建立索引。
- 为常用作为查询条件的字段建立索引。
- 限制索引的数目:越多的索引,会使更新表变得很浪费时间。尽量使用数据量少的索引
- 如果索引的值很长,那么查询的速度会受到影响。尽量使用前缀来索引
- 如果索引字段的值很长,最好使用值的前缀来索引。
- 删除不再使用或者很少使用的索引
- 最左前缀匹配原则,非常重要的原则。
- 尽量选择区分度高的列作为索引区分度的公式是表示字段不重复的比例
- 索引列不能参与计算,保持列“干净”:带函数的查询不参与索引。
- 尽量的扩展索引,不要新建索引
⚫ 索引
索引(Index)是帮助 MySQL 高效获取数据的数据结构。 常见的查询算法,顺序查找,二分查找,二叉排序树查找,哈希散列法,分块查找,平衡多路搜索树 B 树(B-tree) ,索引是对数据库表中一个或多个列的值进行排序的结构,建立索引有助于快速获取信息。
你也可以这样理解:索引就是加快检索表中数据的方法。数据库的索引类似于书籍的索引。在书籍中,索引允许用户不必翻阅完整个书就能迅速地找到所需要的信息。在数据库中,索引也允许数据库程序迅速地找到表中的数据,而不必扫描整个数据库
mysql 有4种不同的索引:
主键索引(PRIMARY)
唯一索引(UNIQUE)
普通索引(INDEX)
全文索引(FULLTEXT)
索引并非是越多越好,创建索引也需要耗费资源,一是增加了数据库的存储空间,二是在插入和删除时要花费较多的时间维护索引索引加快数据库的检索速度索引降低了插入、删除、修改等维护任务的速度唯一索引可以确保每一行数据的唯一性通过使用索引,可以在查询的过程中使用优化隐藏器,提高系统的性能索引需要占物理和数据空间
:CREATE 【UNIQUE】INDEX
index_name ON table_name (column_list);或者 ALTER TABLE table_name ADD INDEX
index_name (id,name);修改用:ALTER TABLE table_name REBUILD INDEX index_name
(column_list);删除用:DROP INDEX index_name ON talbe_name
或者:ALTER TABLE table_name DROP INDEX index_name
查看用:select * from all_indexes where table_name='student';
⚫ CHAR 和 VARCHAR 的区别?
1、CHAR 和 VARCHAR 类型在存储和检索方面有所不同
2、CHAR 列长度固定为创建表时声明的长度,长度值范围是 1 到 255 当 CHAR值被存储时,它们被用空格填充到特定长度,检索 CHAR 值时需删除尾随空格。
⚫ 主键和候选键有什么区别?
表格的每一行都由主键唯一标识,一个表只有一个主键。主键也是候选键。按照惯例,候选键可以被指定为主键,并且可以用于任何外键引用。
⚫ myisamchk 是用来做什么的?
它用来压缩 MyISAM 表,这减少了磁盘或内存使用。
⚫ MyISAM Static 和 MyISAM Dynamic 有什么区别?
在 MyISAM Static 上的所有字段有固定宽度。动态 MyISAM 表将具有像 TEXT,BLOB 等字段,以适应不同长度的数据类型。
MyISAM Static 在受损情况下更容易恢复。
⚫ 可以使用多少列创建索引?
任何标准表最多可以创建 16 个索引列
⚫ NOW()和 CURRENT_DATE()有什么区别?
NOW()命令用于显示当前年份,月份,日期,小时,分钟和秒。
CURRENT_DATE()仅显示当前年份,月份和日期。
⚫ MySQL 数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?
1、设计良好的数据库结构,允许部分数据冗余,尽量避免 join 查询,提高效率。
2、选择合适的表字段数据类型和存储引擎,适当的添加索引。
3、MySQL 库主从读写分离。
4、找规律分表,减少单表中的数据量提高查询速度。
5、添加缓存机制,比如 memcached,apc 等。
6、不经常改动的页面,生成静态页面。
7、书写高效率的 SQL。比如 SELECT * FROM TABEL 改为 SELECT field_1,field_2, field_3 FROM TABLE.
⚫ 锁的优化策略
1、读写分离
2、分段加锁
3、减少锁持有的时间
4.多个线程尽量以相同的顺序去获取资源不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效
⚫ 数据库中的事务是什么?
事务(transaction)是作为一个单元的一组有序的数据库操作。如果组中的所有操作都成功,则认为事务成功,即使只有一个操作失败,事务也不成功。如果所有操作完成,事务则提交,其修改将作用于所有其他数据库进程。如果一个操作失败,则事务将回滚,该事务所有操作的影响都将取消。
事务特性:
1、原子性:即不可分割性,事务要么全部被执行,要么就全部不被执行。
2、一致性或可串性。事务的执行使得数据库从一种正确状态转换成另一种正确状态
3、隔离性。在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事物
4、持久性。事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。
或者这样理解:
事务就是被绑定在一起作为一个逻辑工作单元的 SQL 语句分组,如果任何一个语句操作失败那么整个操作就被失败,以后操作就会回滚到操作前状态,或者是上有个节点。为了确保要么执行,要么不执行,就可以使用事务。要将有组语句作为事务考虑,就需要通过 ACID 测试,即原子性,一致性,隔离性和持久性。
框架中,我们一般把事物交给 spring 来管理。spring 配置事务的方式一般有两
种,一个是声明式事务,一个是注解式事务。注解事务,比较简单灵活,在 spring
配置文件中配置一个tx:annotation-driven 饿里忒甚的注解,然后在需要的方法
上加@Transactional 注解就可以了。声明式事务,切点一般是扫描 service 层
实现类,通过方法名匹配配置传播特性,决定哪些方法上加事务,哪些不需要事
物。事务主要有五大隔离级别和 7 种传播特性;五大隔离级别由低到高:主要控
制是否出现脏读,不可重复读和幻觉读;7 种传播特性主要决定是新建事务,还
是取当前事务;
➢ 脏读:
指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,
另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据, 那
么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
➢ 不可重复读:
指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数
据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读
到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是
不可重复读。
➢ 幻觉读:
指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进
行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表
中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个
事务的用户
⚫ 简述spring 的事务传播行为和 隔离级别
spring 的事务传播行为: Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:
PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
Spring 的隔离级别
1、Serializable:最严格的级别,事务串行执行,资源消耗最大;
2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
4、Read Uncommitted:保证了读取过程中不会读取到非法数据。
⚫ 写出删除表中重复记录的语句 oracle
delete from people
where peopleId in (select peopleId from people group by peopleId having count(peopleId) > 1) and rowid not in (select min(rowid) from people group
⚫ truncate与delete的区别?(delete from table和truncate table tablea的区别!)
truncate是DDL語言.delete是DML語言 DDL語言是自動提交的.命令完成就不可回滾.truncate的速度也比delete要快得多.
详细说明:
相同点:truncate和不带where子句的delete, 以及drop都会删除表内的数据
不同点:
- truncate和 delete只删除数据不删除表的结构(定义)
drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger),索引(index); 依赖于该表的存储过程/函数将保留,但是变为invalid状态.
2.delete语句是dml,这个操作会放到rollback segement中,事务提交之后才生效;如果有相应的trigger,执行的时候将被触发.
truncate,drop是ddl, 操作立即生效,原数据不放到rollback segment中,不能回滚. 操作不触发trigger.
3.delete语句不影响表所占用的extent, 高水线(high watermark)保持原位置不动
显然drop语句将表所占用的空间全部释放
truncate 语句缺省情况下见空间释放到 minextents个 extent,除非使用reuse storage; truncate会将高水线复位(回到最开始).
4.速度,一般来说: drop> truncate > delete
5.安全性:小心使用drop 和truncate,尤其没有备份的时候.否则哭都来不及
使用上,想删除部分数据行用delete,注意带上where子句. 回滚段要足够大.
想删除表,当然用drop
想保留表而将所有数据删除. 如果和事务无关,用truncate即可. 如果和事务有关,或者想触发trigger,还是用delete.
⚫ 简单描述 MySQL 中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响(从读写两方面)
索引是一种特殊的文件(InnoDB 数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
普通索引(由关键字 KEY 或 INDEX 定义的索引)的唯一任务是加快对数据的访问速度。
普通索引允许被索引的数据列包含重复的值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用
关键字 UNIQUE 把它定义为一个唯一索引。也就是说,唯一索引可以保证数据记录的唯一性。
主键,是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字 PRIMARY KEY 来创建。
索引可以覆盖多个数据列,如像 INDEX(columnA, columnB)索引,这就是联合索引。
索引可以极大的提高数据的查询速度,但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作索引文件
⚫ 优化数据库的方法
1、选取最适用的字段属性,尽可能减少定义字段宽度,尽量把字段设置 NOTNULL,例如’省份’、’性别’最好适用 ENUM
2、使用连接(JOIN)来代替子查询
3、适用联合(UNION)来代替手动创建的临时表
4、事务处理
5、锁定表、优化事务处理
6、适用外键,优化锁定表
7、添加适当的索引,索引对查询速度影响很大,必须添加索引。主键索引,唯一索引,普通索引,全文索引
8、优化查询语句
9、分表分区,分库分表
10、读写分离,部署主从数据库
11、选择合适的数据库引擎
⚫ redis中的事务和mysql中的事务有什么区别?
⚫ 索引的底层实现原理和优化
B+树,经过优化的 B+树主要是在所有的叶子结点中增加了指向下一个叶子节点的指针,因此 InnoDB 建议为大部分表使用默认自增的主键作为主索引。
⚫ MySQL 里记录货币用什么字段类型好
NUMERIC 和 DECIMAL 类型被 MySQL 实现为同样的类型,这在 SQL92 标准允许。他们被用于保存值,该值的准确精度是极其重要的值,例如与金钱有关的数据。当声明一个类是这些类型之一时,精度和规模的能被(并且通常是)指定。
例如:
在这个例子中,9(precision)代表将被用于存储值的总的小数位数,而 2(scale)代表将被用于存储小数点后的位数。因此,在这种情况下,能被存储在 salary 列中的值的范围是从-9999999.99 到
9999999.99。 salary DECIMAL(9,2)
在这个例子中,9(precision)代表将被用于存储值的总的小数位数,而 2(scale)代表将被用于存储小数点后的位数。因此,在这种情况下,能被存储在 salary 列中的值的范围是从-9999999.99 到
9999999.99
⚫ MyISAM 表格将在哪里存储,并且还提供其存储格式?
每个 MyISAM 表格以三种格式存储在磁盘上:·“.frm”文件存储表定义·数据文件具有“.MYD”(MYData)扩展名索引文件具有“.MYI”(MYIndex)扩展名
⚫ 什么情况下,索引会失效?
1、以“%”开头的 LIKE 语句,模糊匹配
2、OR 语句前后没有同时使用索引
3、数据类型出现隐式转化(如 varchar 不加单引号的话可能会自动转换为 int 型)率不如一次加一把大锁。
4、空值会导致不走索引,因为hashSet不能存空值。
索引列有函数运算,不走索引,可以在索引列上建立一个函数的索引。
5、!=或者<>可能导致不走索引。
6、not in,not exist不走索引
⚫ 对于关系型数据库而言,索引是相当重要的概念,请回答有关索引的几个问题:
1、索引的目的是什么?
快速访问数据表中的特定信息,提高检索速度创建唯一性索引,保证数据库表中每一行数据的唯一性。加速表和表之间的连接使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间
2、索引对数据库系统的负面影响是什么?
创建索引和维护索引需要耗费时间,这个时间随着数据量的增加而增加;索引需要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间;当对表进行增、删、改、的时候索引也要动态维护,这样就降低了数据的维护速度。
3、为数据表建立索引的原则有哪些?
在最频繁使用的、用以缩小查询范围的字段上建立索引。在频繁使用的、需要排序的字段上建立索引
4、什么情况下不宜建立索引?
对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引。对于一些特殊的数据类型,不宜建立索引,比如文本字段(text)等
⚫ 什么是锁?
答:数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。
基本锁类型:锁包括行级锁和表级锁
⚫ 什么是存储过程?用什么来调用?
答:存储过程是一个预编译的 SQL 语句,优点是允许模块化的设计,就是说只需创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次 SQL,使用存储过程比单纯 SQL 语句执行要快。可以用一个命令对象来调用存储过程。
⚫ 如何通俗地理解三个范式?
第一范式:1NF 是对属性的原子性约束,要求属性具有原子性,不可再分解;
第二范式:2NF 是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
第三范式:3NF 是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。。
范式化设计优缺点:
优点:
可以尽量得减少数据冗余,使得更新快,体积小
缺点:对于查询需要多个表进行关联,减少写得效率增加读得效率,更难进行索引优化
反范式化:
优点:可以减少表得关联,可以更好得进行索引优化
缺点:数据冗余以及数据异常,数据得修改需要更多的成本
⚫ 简单说一说drop、delete与truncate的区别
SQL中的drop、delete、truncate都表示删除,但是三者有一些差别delete和truncate只删除表的数据不删除表的结构速度,一般来说: drop> truncate >deletedelete语句是dml,这个操作会放到rollback segement中,事务提交之后才生效;如果有相应的trigger,执行的时候将被触发. truncate,drop是ddl, 操作立即生效,原数据不放到rollbacksegment中,不能回滚. 操作不触发trigger
⚫ 并发事务带来哪些问题?
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复读和幻读区别:
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了
⚫ 事务隔离级别有哪些?MySQL的默认隔离级别是?
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读
⚫ 数据库存储引擎
数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。存储引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated 。
⚫ InnoDB、Mysaim的特点?
a、InnoDB:
1、支持事务处理。
2、支持外健。
3、支持行锁。
4、不支持FULLTEXT类型的索引。
5、不保存表的具体行数,扫描表来计算有多少行。
6、对于AUTO_INCREMENT类型的字段,必须包含只有该字段的索引。
7、DELETE表时,是一行一行的删除
8、InnoDB把数据和索引存放在表空间里面
9、跨平台可以直接拷贝使用
10、表格很难被压缩
b、MyISAM
1、不支持事务处理。
2、不支持外健。
3、支持全文搜索
4、保存表的具体行数,不带where时,直接返回保存的行数。
7、DELETE表时,先drop表,然后在重建表。
8、MyISAM表被存放在三个文件。frm文件存放表格定义,数据文件是MYD(MYData)。索引文件是MYI(MYindex)引伸。
9、跨平台很难直接拷贝使用
6、对于AUTO_INCREMENT类型的字段可以和其他字段一起建立联合索引
10、表格很难被压缩
c、选择:因为MySAM相对简单所以在效率上要优于InnoDB,如果系统读多写少。选MySAM最好。且MySAM恢复速度快。可直接用备份覆盖恢复。如果系统读少写多,选InnoDB最好。
⚫ 大表如何优化?
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:限定数据的范围务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内;读/写分离经典的数据库拆分方案,主库负责写,从库负责读;垂直分区根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了垂直拆分的优点: 可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;
⚫ 水平分区
保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。水平拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库 。水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。
- 下面补充一下数据库分片的两种常见方案:客户端代理: 分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。 当当网的Sharding-JDBC 、阿里的TDDL是两种比较常用的实现。
- 中间件代理: 在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。 我们现在谈的 Mycat 、360的Atlas、网易的DDB等等都是这种架构的实现。
⚫ 分库分表之后,id 主键如何处理
因为要是分成多个表之后,每个表都是从 1 开始累加,这样是不对的,我们需要一个全局唯一的 id 来支持。生成全局 id 有下面这几种方式:
UUID:不适合作为主键,因为太长了,并且无序不可读,查询效率低。比较适合用于生成唯一的名字的标示比如文件的名字。数据库自增 id : 两台数据库分别设置不同步长,生成不重复ID的策略来实现高可用。这种方式生成的 id 有序,但是需要独立部署数据库实例,成本高,还会有性能瓶颈。
利用 redis 生成 id : 性能比较好,灵活方便,不依赖于数据库。但是,引入了新的组件造成系统更加复杂,可用性降低,编码更加复杂,增加了系统成本。
⚫ 存储过程(特定功能的 SQL 语句集)
一组为了完成特定功能的 SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。
⚫ 存储过程优化思路
- 尽量利用一些 sql 语句来替代一些小循环,例如聚合函数,求平均函数等。
- 中间结果存放于临时表,加索引。
- 少使用游标。 sql 是个集合语言,对于集合运算具有较高性能。而 cursors 是过程运算。比如对一个 100 万行的数据进行查询。游标需要读表 100 万次,而不使用游标则只需要少量几次读取。
- 事务越短越好。 sqlserver 支持并发操作。如果事务过多过长,或者隔离级别过高,都会造成并发操作的阻塞,死锁。导致查询极慢,cpu 占用率极地。
- 使用 try-catch 处理错误异常。
- 查找语句尽量不要放在循环内
⚫ 数据库并发策略
并发控制一般采用三种方法,分别是乐观锁和悲观锁以及时间戳。
⚫ 什么是悲观锁
悲观锁就是在读取数据的时候,为了不让别人修改自己读取的数据,就会先对自己读取的数据加锁,只有自己把数据读完了,才允许别人修改那部分数据,或者反过来说,就是自己修改某条数据的时候,不允许别人读取该数据,只有等自己的整个事务提交了,才释放自己加上的锁,才允许其他用户访问那部分数据。
⚫ 乐观锁的业务场景及实现方式
每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都
不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。如果
数据被其他线程修改,则不进行数据更新,如果数据没有被其他线程修改,则进
行数据更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。
乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数
据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新
获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。
⚫ 什么是时间戳
时间戳就是在数据库表中单独加一列时间戳,比如“TimeStamp”, 每次读出来的时候,把该字段也读出来,当写回去的时候,把该字段加1,提交之前 ,跟数据库的该字段比较一次,如果比数据库的值大的话,就允许保存,否则不允许保存,这种处理方法虽然不使用数据库系统提供的锁机制,但是这种方法可以大大提高数据库处理的并发量,以上悲观锁所说的加“锁”,其实分为几种锁,分别是: 排它锁(写锁)和共享锁(读锁) 。
⚫ Memory
Memory(也叫 HEAP)堆内存:使用存在内存中的内容来创建表。每个 MEMORY 表只实际对应一个磁盘文件。 MEMORY 类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH 索引。但是一旦服务关闭,表中的数据就会丢失掉。 Memory 同时支持散列索引和 B 树索引, B树索引可以使用部分查询和通配查询,也可以使用<,>和>=等操作符方便数据挖掘,散列索引相等的比较快但是对于范围的比较慢很多什么是乐观锁乐观锁认为一个用户读数据的时候,别人不会去写自己所读的数据;悲观锁就刚好相反,觉得自己读数据库的时候,别人可能刚好在写自己
刚读的数据,其实就是持一种比较保守的态度;时间戳就是不加锁,通过时间戳来控制并发出现的问题。
⚫ 索引
索引(Index)是帮助 MySQL 高效获取数据的数据结构。常见的查询 算法, 顺序查找,二分查找,二 叉排序树查找,哈希散列法,分块查找,平衡多路搜索 树 B 树(B-tree) 索引就是加快检索表中数据的方法。数据库的索引类似于书籍的索引。 在书籍中,索引允许用户不必翻阅完整个书就能迅速地找到所需要的信息。在数 据库中,索引也允许数据库程序迅速地找到表中的数据,而不必扫描整个数据库。 MySQL 数据库几个基本的索引类型:普通索引.唯一索引.主键索引.全 文索引.组合索引
**⚫ 普通索引 **
是最基本的索引,它没有任何限制。它有以下几种创建方式: (
1) 直接创建索引 CREATE INDEX index_name ON table(column[length]))
2) 修改表结构的方式添加索引 ALTER TABLE table_name ADD INDEX index_name ON (column[length]))
3) 创建表的时候同时创建索引
4) 删除索引 DROP INDEX index_name ON table
⚫ 唯一索引
与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。 如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:
1) 创建唯一索引 CREATE UNIQUE INDEX indexName ON table(column[length])
2) 修改表结构 ALTER TABLE table_name ADD UNIQUE indexName ON (column[length])
3) 创建表的时候直接指定
⚫ 主键索引
是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。 一般是在建表的时候同时创建主键索引:
⚫ 组合索引
指多个字段上创建的索引,只有在查询条件中使用了创建索 引时的第一个字段,索引才会被使用。使用组合索引时遵循 最左前缀集合 ALTER TABLE table
ADD INDEX name_city_age (name,city,age);
**⚫ 全文索引 **
主要用来查找文本中的关键字,而不是直接与索引中的 值相比较。fulltext 索引跟其它索引大不相同,它更像是一个搜 索引擎,而不是简单的 where 语句的参数匹配。fulltext 索引 配合 match against 操作使用,而不是一般的 where 语句加 like。它可以在 create table,alter table ,create index 使 用,不过目前只有 char.varchar,text 列上可以创建全文索引。 值得一提的是,在数据量较大时候,现将数据放入一个没有全局 索引的表中,然后再用 CREATE index 创建 fulltext 索引,要 比先为一张表建立 fulltext 然后再将数据写入的速度快很多。
(1)创建表的适合添加全文索引
CREATE TABLE table
(
id
int(11) NOT NULL AUTO_INCREMENT ,
title
char(255) CHARACTER NOT NULL ,
content
text CHARACTER NULL ,
time
int(10) NULL DEFAULT NULL ,
PRIMARY KEY (id
),
FULLTEXT (content)
);
(2) 修改表结构添加全文索引 ALTER TABLE article ADD FULLTEXT index_content(content)
(3) 直接创建全文索引 CREATE FULLTEXT INDEX index_content ON article(content)
⚫ 索引的优点
创建唯一索引,保证数据库表中每一行数据的唯一性 大大加快数据的检索速度,这也是创建索引的最主要的原因 加速表和表之间的连接,特别是在实现数据的参考完整 性方面特别有意义。在使用分组和排序子句进行数据检 索时,同样可以显著减少查询中分组和排序的时间。 通过使用索引,可以在查询的过程中使用优化隐藏器,提高系统的性能。
**⚫ 索引的缺点 **
创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加 索引需要占物理空间,除了数据表占数据空间之外,每一个 索引还要占一定的物理空间,如果要建立聚簇索引,那么需 要的空间就会更大 当对表中的数据进行增加.删除和修改的时候,索引也 要动态的维护,降低了数据的维护速度
**⚫ 常见索引原则 **
选择唯一性索引:唯一性索引的值是唯一的,可以更快 速的通过该索引来确定某条记录。 为经常需要排序.分组和联合操作的字段建立索引. 为 常作为查询条件的字段建立索引。 限制索引的数目:越多的索引,会使更新表变得很浪费 时间。 尽量使用数据量少的索引:如果索引的值很长,那么查 询的速度会受到影响。尽量使用前缀来索引:如果索引字段的 值很长,最好使用值的前缀来索引。删除不再使用或者很少使 用的索引最左前缀匹配原则,非常重要的原则。 尽量选择区分度高的列作为索引:区分度的公式是表示 字段不重复的比例索引列不能参与计算,保持列“干净”:带 函数的查询不参与索引。 尽量的扩展索引,不要新建索引。
⚫ MySQL 常用 30 种 SQL 查询语句优化方法
- 应尽量避免在 where 子句中使用!=或<>操作符, 否则引擎将放弃使用索引而进行全表扫描。
- 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
- 应尽量避免在 where 子句中对字段进行 null 值 判断,否则将导致引擎放弃使用索引而进行全表扫描。如: select id from t where num is null 可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询: select id from t where num=0
- 尽量避免在 where 子句中使用 or 来连接条件, 否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num=10 or num=20 可以这样查询: select id from t where num=10 union all select id from t where num=20
- 下面的查询也将导致全表扫 描:(不能前置百分号) select id from t where name like ‘%c%’ 下面走索引 select id from t where name like ‘c%’ 若要提高效率,可以考虑全文 检索。
- in 和 not in 也要慎用,否则会 导致全表扫描,如: select id from t where num in(1,2,3) 对于连续的数值,能用 between 就不要用 in 了: select id from t where num between 1 and 3
- 如果在 where 子句中使用参数,也会导致全表扫 描。因为 SQL 只有在运行时才会解析局部变量,但优化程序不 能将访问计划的选择推迟到运行时;它必须在编译时进行选择。 然 而,如果在编译时建立访问计划,变量的值还是未知的, 因而无法作为索引选择的输入项。如下面语句将进行全表扫 描:select id from t where num=@num 可以改为强制查询使 用索引: select id from t with(index(索引名)) where num=@num
- 应尽量避免在 where 子句中对字段进行表达式操 作,这将导致引擎放弃使用索引而进行全表扫描。如: select id from t where num/2=100 应改为: select id from t where num=100*2
- 应尽量避免在 where 子句中对字段进行函数操作,这 将导致引擎放弃使用索引而进行全表扫描。如: select id from t where substring(name,1,3)=’ abc’ –name 以 abc 开头的 id select id from t where datediff(day,createdate,’ 2005-11-30′)=0 –’ 2005-11-30′生成的 id 应改为: select id from t where name like ‘abc%’ select id from t where createdate>=’2005-11-30′ and createdate<’ 2005-12-1′
- 不要在 where 子句中的“=”左边进行函数.算术 运算或其他表达式运算,否则系统将可能无法正确使用索 引。11. 在使用索引字段作为条件时,如果该索引是复合索 引,那么必须使用到该索引中的第一个字段作为条件时才能保 证系统使用该索引,否则该索引将不会被使 用,并且应尽可 能的让字段顺序与索引顺序相一致。
- 不要写一些没有意义的查询,如需要生成一个空表结 构: select col1,col2 into #t from t where 1=0 这类代码不会返回任何结果集,但是会消耗系统资 源的,应改成这样: create table #t(…) 13. 很多时候用 exists 代替 in 是一个好的选择: select num from a where num in(select num from b) 用下面的语句替换: select num from a where exists(select 1 from b where num=a.num)
- 并不是所有索引对查询都有效,SQL 是根据表中数据来进 行查询优化的, 当索引列有大量数据重复时,SQL 查询可能不会去利用索引,
如一表中有字段sex,male.female 几乎各一半,那么即使在 sex 上建了索引也对查询效率起不了作用。 - 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因 为 insert 或 update 时有可能会重建索引,所以怎样建索引 需要慎重考虑,视具体情况而定。一个表的索引数较好不要超过 6 个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。
- 应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一 旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大 的资源。若应用系统需要频繁更新 clustered 索引数据列, 那么需要考虑是否应将该索引建为 clustered 索引。
- 尽量使用数字型字段,若只含数值信息的字段尽量不要 设计为字符型, 这会降低查询和连接的性能,并会增加存储开销。 这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个 字符,而对于数字型而言只需要比较一次就够了。
- 尽可能的使用 varchar/nvarchar 代 替 char/nchar ,因为首先变长字段存储空间小,可以节省存储 空间,其次对于查询来说,在一个相对较小的字段内搜索效率 显然要高些。
- 任何地方都不要使用 select * from ,用具体的字 段列表代替“*”,不要返回用不到的任何字段。
- 尽量使用表变量来代替临时表。如果表变量包含大量数 据,请注意索引非常有限(只有主键索引)。
- 避免频繁创建和删除临时表,以减少系统表资源的消耗。
- 临时表并不是不可使用,适当地使用它们可以使某些例程更有 效,例如, 当需要重复引用大型表或常用表中的某个数据集时。 但是,对于一次性事件,较好使 用导出表。
- 在新建临时表时,如果一次性插入数据量很大,那么可以 使用 select into 代 替 create table,避免造成大量 log , 以提高速度;如果数据量不大, 为了缓和系统表的资源,应先 create table,然后 insert。
- 如果使用到了临时表,在存储过程的最后务必将所有的临时表 显式删除, 先 truncate table ,然后 drop table ,这样可以 避免系统表的较长时间锁定。
- 尽量避免使用游标,因为游标的效率较差,如果游标操 作的数据超过 1 万行,那么就应该考虑改写。
- 使用基于游标的方法或临时表方法之前,应先寻找基于 集的解决方案来解决问题,基于集的方法通常更有效。
- 与临时表一样,游标并不是不可使用。对小型数据集使 用FAST_FORWARD 游标通常要优于其他逐行处理方法,尤 其是在必须引用几个表才能获得所需的数据时。在结果集中包括 “合计”的例程通常要比使用游标执行的速度快。如果开发时 间 允许,基于游标的方法和基于集的方法都可以尝试一下,看哪 一种方法的效果更好。
- 在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。 无需在执行存储过程和触发器的每个语句后向客户端发送 DONEINPROC 消息。
- 尽量避免向客户端返回大数据量,若数据量过大,应该 考虑相应需求是否合理。
- 尽量避免大事务操作,提高系统并发能力。
⚫ 数据库优化方案整理
- SQL 以及索引的优化 首先要根据需求写出结构良好的 SQL,然后根据 SQL 在表 中建立有效的索引。但是如果索引太多,不但会影响写入的效率, 对查询也有一定的影响。
- 合理的数据库设计 根据数据库三范式来进行表结构的设计。设计表结构时, 就需要考虑如何设计才能更有效的查询。 数据库三范式: i. 第一范式:数据表中每个字段都必须是不可拆分的最小 单元,也就是确保每一列的原子性; ii. 第二范式:满足一范式后,表中每一列必须有唯一 性,都必须依赖于主键; iii. 第三范式:满足二范式后,表中的每一列只与主键 直接相关而不是间接相关(外键也是直接相关),字段 没有冗余。 注意:没有最好的设计,只有最合适的设计,所以不要过分注重 理论。三范式可以作为一个基本依据,不要生搬硬套。 有时候可以根据场 景合理地反规范化: A:分割表。 B:保留冗余字段。当两个或多个表在查询中经常需要连 接时,可以在其中一个表上增加若干冗余的字段,以 避免表
之间的连接过于频繁,一般在冗余列的数据不经常变动的情 况下使用。 C:增加派生列。派生列是由表中的其它多个列的计算所 得,增加派生列可以减少统计运算,在数据汇总时可以大大 缩短运算时间。 数据库五大约束: A:PRIMARY key: 设 置 主 键 约 束 ; B:UNIQUE:设置唯一性约束,不能有重复值; C:DEFAULT 默认值约束 D:NOT NULL:设置非空约束,该字段不能为空; E:FOREIGN key :设置外键约束。 字段类型选择: A:尽量使用 TINYINT.SMALLINT.MEDIUM_INT 作为 整数类型而非 INT,如果非负则加上 UNSIGNED B:VARCHAR 的长度只分配 真正需要的空间 C:使用枚举或整数代替字符 串类型D:尽量使用 TIMESTAMP 而非DATETIME E:单表不要有太多字段,建 议在 20 以内 F:避免使用 NULL 字段,很难查询优化且占用额外索 引空间 - 硬件优化 更快的 IO.更多的内存。一般来说内存越大,对于数据库 的操作越好。但是CPU 多就不一定了,因为他并不会用到太多 的 CPU 数量,有很多的查询都是单CPU。另外使用高的 IO
(SSD.RAID),但是 IO 并不能减少数据库锁的机制。所以说 如果查询缓慢是因为数据库内部的一些锁引起的,那么硬件优化 就没有什么意义。 - 系统配置的优化 例如:MySQL 数据库 my.cnf
⚫ 优化方案
- 代码优化 之所以把代码放到第一位,是因为这一点最容易引起技术 人员的忽视。很多技术人员拿到一个性能优化的需求以后,言 必称缓存.异步.JVM 等。实际上,第一步就应该是分析相关的 代码,找出相应的瓶颈,再来考虑具体的优化策略。有一些性能 问题,完全是由于代码写的不合理,通过直接修改一下代码就能 解决问题的,比如 for 循环次数过多.作了很多无谓的条件判 断.相同逻辑重复多次等。 举例说明: 一个 update 操作,先查询出 entity,再执行 update, 这样无疑多了一次数据库交互。还有一个问题,update 语 句可能会操作一些无需更新的字段。 我们可以将表单中涉及到的属性,以及 updateTime, updateUser 等赋值到 entity,直接通过 pdateByPrimaryKeySelective,去 update 特定字段
- 定位慢 SQL,并优化 这是最常用.每一个技术人员都应该掌握基本的 SQL 调优手段(包括方法. 工具.辅助系统等)。这里以 MySQL 为 例,最常见的方式是,由自带的慢查询日志或者开源的慢查询 系统定位到具体的出问题的 SQL,然后使用explain.profile 等工具来逐步调优,最后经过测试达到效果后上线。
- SqlServer 执行计划: 通过执行计划,我们能得到哪些信息: A:哪些步骤花费的成本比较高 B:哪些步骤产生的数据量多,数据量的多少用线条的粗细表示,很直观 C:每一步执行了什么动作 4. 具体优化手段:
A:尽量少用(或者不用)sqlserver 自带的函数 select id from t where substring(name,1,3) = ’abc’ select id from t where datediff(day,createdate,’ 2005-11-30′) = 0 可以这样查询: select id from t where name like ‘abc%’ select id from t where createdate >= ‘2005-11-30’ and createdate < ‘2005-12-1’
B:连续数值条件,用BETWEEN 不用 IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5
C:Update 语句,如果只更改 1.2 个字段,不要 Update 全部字段,否则频繁调用会引起明显的性能消耗
D:尽量使用数字型字段,若只含数值信息的字段尽量 不要设计为字符型
E:不建议使用 select * from t , 用具体的字段列表代替“”,不要返回 用不到的任何字段。尽量避免向客户 端返回大数据量,若 数据量过大,应该考虑相应需求是否合理
F:表与表之间通过一个冗余字段来关联,要比直接使用 JOIN 有更好 的性能
G:select count() from table;这样不带任何条件的 count 会引起全 表 扫描连接池调优 我们的应用为了实现数据库连接的高效获取.对数据库连接
的限流等目的, 通常会采用连接池类的方案,即每一个应用 节点都管理了一个到各个数据库的连接池。随着业务访问量或 者数据量的增长,原有的连接池参数可能不能很好地满足需 求,这个时候就需要结合当前使用连接池的原理.具体的连接 池监控数据和当前的业务量作一个综合的判断,通过反复的几 次调试得到最终的调优参数。 合理使用索引 索引一般情况下都是高效的。但是由于索引是以空间换时 间的一种策略,索引本身在提高查询效率的同时会影响插入. 更新.删除的效率,频繁写的表不宜建索引。 选择合适的索引列,选择在 where,group by,order by, on 从句中出现的列作为索引项,对于离散度不大的列没有必 要创建索引。
如何选择索引字段
A:字段出现在查询条件中,并且查询条件可 以使用索引
B:通常对数字的索引和检索要比对字符串的 索引和检索效率更高
C:语句执行频率高,一 天会有几千次以上
D:通过字段条件可筛选的记录集很小
无效索引
A:尽量不要在 where 子句中对字段进行 null 值判 断,否则将导致引擎放弃使用索引而进行全表扫描
B:应尽量避免在 where 子句中使用 != 或 <> 操作 符,否则将引擎放弃使用索引而进行全表扫描。
C:应尽量避免在 where 子句中使用 or 来连接条件, 如果一个字段有索引,一个字段没有索引,将导致引擎放弃 使用索引而进行全表扫描 select id from t where num=10 or Name = ‘admin’ 可以这样查询: select id from t where num = 10 union select id from t where Name = ‘admin’ union all 返回所有数据,不管是不是重复。 union 会 自动压缩,去除重复数据。
D:不做列运算where age + 1 = 10,任何对列的操作 都将导致表扫描,它包括数据库教程函数.计算表达式等 E:查询 like,如果是 ‘%aaa’ 不会使用到索引 分表分表方式 水平分割(按行).垂直分割(按列) 分表场景 A: 根据经验,MySQL 表数据一般达到百万级别,查询效 率就会很低。 B: 一张表的某些字段值比较大并且很少使用。可以将这些 字段隔离成单独一张表,通过外键关联,例如考试成绩, 我们通常关注分数,不关注考试详情。水平分表策略 C:按时间分表:当数据有很强的实效性,例如微博的数据,
可以按月分割。按区间分表:例如用户表 1 到一百万用一张 表,一百万到两百万用一张表。hash 分表:通过一个原始目标 id 或者是名称按照一定的 hash 算法计算出数据存储的表 名。 - 读写分离
当一台服务器不能满足需求时,采用读写分离【写: update/delete/add】的方式进行集群。 一台数据库支持最大连接数是有限的,如果用户的并发 访问很多,一台服务器无法满足需求,可以集群处理。 MySQL 集群处理技术最常用的就是读写分离。 主从同步:数据库最终会把数据持久化到磁盘,集群必 须确保每个数据库服务器的数据是一致的。从库读主库写, 从库从主库上同步数据。 读写分离:使用负载均衡实现,写操作都往主库上写,读操 作往从服务器上读。 - 缓存缓存分类 本地缓存: HashMap/ConcurrentHashMap.Ehcache.Guava Cache 等缓存服务:Redis/Tair/Memcache 等
- 使用场景 短时间内相同数据重复查询多次且数据更新不频繁,这个 时候可以选择先从缓存查询,查询不到再从数据库加载并回设到 缓存的方式。此种场景较适合用单机缓存。 高并发查询热点数据,后端数据库不堪重负,可以用缓存来扛。 缓存作用:减轻数据库的压力,减少访问时间。 缓存选择:如果数据量小,并且不会频繁地增长又清空(这 会导致频繁地垃圾回收),那么可以选择本地缓存。具体的话, 如果需要一些策略的支持(比如缓存满的逐出策略),可以考
虑 Ehcache;如不需要,可以考虑 HashMap;如需要考 虑多线程并发的场景,可以考虑 ConcurentHashMap。 其他情况,可以考虑缓存服务。目前从资源的投入度. 可运维性.是否能动态扩容以及配套设施来考虑,我们优先考虑 Tair。除非目前 Tair 还不能支持的场合 (比如分布式锁.Hash 类型的 value),我们考虑用 Redis。 缓存穿透一般的缓存系统,都是按照 key 去缓存查询, 如果不存在对应的value,就应该去后端系统查找(比如 DB)。 如果 key 对应的 value 是一定不存在的,并且对该 key 并 发请求量很大,就会对后端系统造 成很大的压力。这就叫做 缓存穿透。 对查询结果为空的情况也进行缓存,缓存时间设置短 点,或者该 key 对应的数据 insert 了之后清理缓存。 缓存并发有时候如果网站并发访问高,一个缓存如果失效, 可能出现多个进程同时查询 DB,同时设置缓存的情况, 如果并发确实很大,这也可能造成 DB 压力过大,还有缓存频 繁更新的问题。对缓存查询加锁,如果 KEY 不存在,就加 锁,然后查 DB 入缓存,然后解 锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入 DB 查 询。缓存雪崩(失效) 当缓存服务器重启或者大量缓存集中在某一个时间段失效, 这样在失效的时候,也会给后端系统(比如 DB)带来很大压 力。不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀. 防止缓存空间不够用 (1) 给缓存服务,选择合适的缓存逐出算法,比如最常见的 LRU。 (2) 针对当前设置的容量,设置适当的警戒值,比如 10G 的缓存,当缓存数据达到 8G 的时候,就开始发出报 警,提前排查问题或者扩容。 (3) 给一些没有必要长期保存的 key,尽量设置过期时间。 我们看下图,在 WebServe(r Dao 层)和 DB 之间加一层 cache,这层
cache 一般选取的介质是内存,因为我们都知道存入数据库的数据 都具有持久化的特点,那么读写会有磁盘 IO 的操作,内存 的读写速度远比磁盘快得多。(选用存储介质,提高访问速度: 内存>>磁盘;减少磁盘 IO 的操作,减少重复查询,提高吞吐量) 常用开源的缓存工具有:ehcache.memcache.Redis。
ehcache 是一个纯 Java 的进程内缓存框架,hibernate 使用其做二级缓存。同时,ehcache 可以通过多播的方式实现 集群。本人主要用于本地的缓存,数据库上层的缓存。 memcache 是一套分布式的高速缓存系统,提供 key-value 这样简单的数据储存,可充分利用 CPU 多核, 无持久化功能。在做 web 集群中可以用做session 共享, 页面对象缓存。 Redis 高性能的 key-value 系统,提供丰富的数据类型, 单核 CPU 有抗并发能力,有持久化和主从复制的功能。本人 主要使用 Redis 的 Redis sentinel, 根据不同业务分为多 组。
搜索引擎 例如:solr,elasticsearch
⚫ oracle和mysql的分页语句?
oracle: select * from (select * from (select s.*,rownum rn from student s ) where rn<=5) where rn>0
mysql: select * from table_name where 1= 1 limit (start,end)
⚫ JDBC 流程
1、 加载 JDBC 驱动程序:
在连接数据库之前,首先要加载想要连接的数据库的驱动到 JVM(Java 虚
拟机),
这通过 java.lang.Class 类的静态方法 forName(String className)实现。
2、 提供 JDBC 连接的 URL
连接 URL 定义了连接数据库时的协议、子协议、数据源标识。
- 书写形式:协议:子协议:数据源标识
协议:在 JDBC 中总是以 jdbc 开始 子协议:是桥连接的驱动程序或是数据
库管理系统名称。
数据源标识:标记找到数据库来源的地址与连接端口。
3、创建数据库的连接
要连接数据库,需要向 java.sql.DriverManager 请求并获得 Connection 对
象, 该对象就代表一个数据库的连接。 - 使用 DriverManager 的 getConnectin(String url , String username , String
password )方法传入指定的欲连接的数据库的路径、数据库的用户名和 密码来
获得。
4、 创建一个 Statement
要执行 SQL 语句,必须获得 java.sql.Statement 实例,Statement 实例分为
以下 3 种类型:
1、执行静态 SQL 语句。通常通过 Statement 实例实现。
2、执行动态 SQL 语句。通常通过 PreparedStatement 实例实现。
3、执行数据库存储过程。通常通过 CallableStatement 实例实现。
具体的实现方式:
Statement stmt = con.createStatement() ; PreparedStatement pstmt =
con.prepareStatement(sql) ; CallableStatement cstmt =
con.prepareCall(“{CALL demoSp(? , ?)}”) ;
5、执行 SQL 语句
Statement 接口提供了三种执行 SQL 语句的方法:executeQuery 、
executeUpdate 和 execute
1、ResultSet executeQuery(String sqlString):执行查询数据库的 SQL 语句 ,
返回一个结果集(ResultSet)对象。
2、int executeUpdate(String sqlString):用于执行 INSERT、UPDATE 或
DELETE 语句以及 SQL DDL 语句,如:CREATE TABLE 和 DROP TABLE 等 3、execute(sqlString):用于执行返回多个结果集、多个更新计数或二者组合的 语
句。 具体实现的代码:
ResultSet rs = stmt.executeQuery(“SELECT * FROM …”) ; int rows =
14
stmt.executeUpdate(“INSERT INTO …”) ; boolean flag = stmt.execute(String
sql) ;
6、处理结果
两种情况:
1、执行更新返回的是本次操作影响到的记录数。
2、执行查询返回的结果是一个 ResultSet 对象。
• ResultSet 包含符合 SQL 语句中条件的所有行,并且它通过一套 get 方法
提供了对这些 行中数据的访问。
• 使用结果集(ResultSet)对象的访问方法获取数据:
while(rs.next()){
String name = rs.getString(“name”) ;
String pass = rs.getString(1) ; // 此方法比较高效
}
(列是从左到右编号的,并且从列 1 开始)
7、关闭 JDBC 对象
操作完成以后要把所有使用的 JDBC 对象全都关闭,以释放 JDBC 资源,
关闭顺序和声 明顺序相反:
1、关闭记录集
2、关闭声明
3、关闭连接对象
⚫ 数据库表纵表转横表
a) 表结构
1、横表: 主键、字段 1、字段 2……
如:姓名 语文 英语……
优点:一行表示了一个实体记录,清晰可见,一目了然。
缺点:如果现在要给这个表加一个字段,那么就必须重建表结构。
2、纵表:主键、字段 1/2/3、字段值。
如:姓名 课程 成绩
优点:如果现在要给这个表加一个字段,只需要添加一些记录。
缺点:数据描述不是很清晰,而且会造成数据库数据很多。另如果需要分
组统计,要先 group by,较繁琐。
b )方法一:
聚合函数(max 或者 sum)配合 case 语句
例子:select 姓名,sum(case 课程 where ’语文’ then 成绩 end)
as 语文 from 表名 group by 姓名
方法二:
使用 pivot
select * from 表名 pivot (max(成绩)for 课程 in(语文,数学,英语)) 临时表
c) oracle SQL 实现竖表转横表
方法一:
--用 decode 实现,
SELECT 姓名,
SUM(DECODE(课程, '语文', 成绩)) 语文 FROM 表名 GROUP BY 姓名
方法二:
--用 case when 实现
SELECT 姓名,
SUM(CASE 课程 WHEN '语文' THEN 成绩 ELSE 0 END) 语文 FROM 表名GROUP
BY 姓名
区别:如果条件是单一值时,用 decode 比较简便,如果判断条件比较复杂是用 case when
实现
⚫ CHAR和VARCHAR的区别?
1、在存储和检索方面有所不同。
2、CHAR列长度固定为创建表时申明的长度,长度值范围时1到255
3、当CHAR值被储存时,他们被用空格填充到特定长度,检索CHAR值时需要删除尾随空格
⚫ 一张表里面有ID自增主键,当insert了17条记录之后,删除了第15,16,17条记录,再把mysql重启,再insert一条记录,这条记录的ID是18还是15 ?
一般情况下,我们创建的表的类型是InnoDB,如果新增一条记录(不重启mysql的情况下),这条记录的id是18;但是如果重启(文中提到的)MySQL的话,这条记录的ID是15。因为InnoDB表只把自增主键的最大ID记录到内存中,所以重启数据库或者对表OPTIMIZE操作,都会使最大ID丢失。
但是,如果我们使用表的类型是MylSAM,那么这条记录的ID就是18。因为MylSAM表会把自增主键的最大ID记录到数据文件里面,重启MYSQL后,自增主键的最大ID也不会丢失。
⚫ mysql有关权限的表是哪几个?
MySQL 服务器通过权限表来控制用户对数据库的访问,权限表存放在 MySQL 数
据库里,由 MySQL_install_db 脚本初始化。这些权限表分别 user,db,table_priv,columns_priv 和 host。
⚫ 索引的数组结构(b数,hash)?
⚫ Hash索引和B+树索引有什么区别或者优缺呢?
首先要知道Hash索引和B+树索引的底层实现原理:
hash索引底层就是hash表,进行查询时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据.
B+树底层实现原理是多路平衡查找树,对于每一次的查询都是从根节点出发,查询到叶子节点方可以获得所查键值,然后查询判断是否需要回表查询.
区别:
hash索引
1:hash索引进行等值查询更快(一般情况下)但是却无法进行范围查询.因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询.
2:hash索引不支持模糊查询以及多列索引的最左前缀匹配,因为hash函数的不可预测,eg:AAAA和AAAAB的索引没有相关性.
3:hash索引任何时候都避免不了回表查询数据.
4:hash索引虽然在等值上查询叫快,但是不稳定,性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时查询效率可能极差.
5:hash索引不支持使用索引进行排序,因为hash函数的不可预测.
B+树
1:B+树的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似)自然支持范围查询.
2:在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询.不需要回表查询.
3:查询效率比较稳定,对于查询都是从根节点到叶子节点,且树的高度较低.
结论
大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度,而不需要使用Hash索引.
⚫ 数据库为什么使用B+树而不是B数?
⚫ 什么是聚簇索引呢?
B+树在满足聚簇索引和覆盖索引的时候不需要回表查询数据,什么是聚簇索引呢?
聚簇索引:表数据按照索引的顺序来储存的,也就是说索引项的顺序与表中记录的物理顺序一致.在B+树的索引中叶子节点可能储存了当前的key值,也可能储存了当前的key值以及整行的数据,在一张表上最多只能创建一个聚簇索引,因为真实数据的物理顺序只有一种.
非聚簇索引:表数据存储顺序与索引顺序无关,对于非聚簇索引,叶子节点包含索引字段值及指向数据页数据行的逻辑指针.
聚簇索引和非聚簇索引总结: 聚簇索引是一种稀疏索引,数据页的上一级的索引页储存的是页指针,而不是行指针,而非聚簇索引,则是密集索引,在数据页的上一级索引页它为每一个数据行存储一条索引记录.
在InnoDB中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引,如果没有唯一键,则隐式的生成一个键建立索引.
当查询使用聚簇索引时,在对应的叶子节点,可以获得到整行数据,因此不用再次进行回表查询.
非聚簇索引一定会回表查询吗?
不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必在进行回表查询了.
⚫ MyISAM和InnoDB存储引擎使用的锁?
MyISAM采用表级锁
InnoDB支持行级锁和表级锁,默认行级锁
⚫ mysql数据库的索引的工作机制是什么?
数据库索引,是数据库管理系统中的一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树以及B+树。
⚫ ?