数据库

143 阅读16分钟

1、聚合函数

聚合函数是对一组值进行计算并返回单一的值的函数,它经常与select语句中的 group by 子句一同使用。

  1. avg():返回的是指定组中的平均值,空值被忽略。
  2. count():返回的是指定组中的项目个数。
  3. max():返回指定数据中的最大值。
  4. min():返回指定数据中的最小值。
  5. sum():返回指定数据的和,只能用于数字列,空值忽略。
  6. group by():对数据进行分组,对执行完 group by 之后的组进行聚合函数的运算,计算每一组的值。最后用 having 去掉不符合条件的组,having 子句中的每一个元素必须出现在select列表中(只针对于mysql)。

2、#{}和${}的区别是什么?

#{}是预编译处理,${}是字符串替换。使用#{}可以有效的防止 SQL 注入,提高系统安全性。

3、char和varchar的区别

char的长度是不可变的,而varchar的长度是可变的。

4、索引

索引(Index)是帮助 MySQL 高效获取数据的数据结构。常见的查询算法, 顺序查找,二分查找,二 叉排序树查找,哈希散列法,分块查找,平衡多路搜索树B树(B-tree)
MySQL数据库几个基本的索引类型:普通索引、唯一索引、主键索引、全文索引、组合索引

  1. 普通索引

    • 直接创建索引

      CREATE INDEX index_name ON table(column[length]))
      
    • 修改表结构的方式添加索引

      `ALTER TABLE table_name ADD INDEX index_name ON(column[length]))`
      
    • 创建表的时候同时创建索引

      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`)
          INDEX index_name (title[length])
      )
      
    • 删除索引

      `DROP INDEX index_name ON table`
      
  2. 唯一索引:与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

    • 创建唯一索引

      CREATE UNIQUE INDEX indexName ON table(column[length])
      
    • 修改表结构

      ALTER TABLE table_name ADD UNIQUE indexName ON (column[length])
      
    • 创建表的时候直接指定

      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 ,
          UNIQUE indexName (title[length])
      );
      
  3. 主键索引 :是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。

    CREATE TABLE `table` (
        `id` int(11) NOT NULL AUTO_INCREMENT ,
        `title` char(255) NOT NULL ,
        PRIMARY KEY (`id`)
    );
    
  4. 组合索引指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。

     `ALTER TABLE `table` ADD INDEX name_city_age (name,city,age);`     
     
    
  5. 全文索引 主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext 索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where 语句的参数匹配。fulltext 索引配合 match against 操作使用,而不是一般的 where 语句加 like。它可以在 create table,alter table ,create index使用,不过目前只有 char.varchar,text 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用 CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多。

    • 创建表的适合添加全文索引
    PRIMARY KEY (`id`), FULLTEXT (content));
    
    • 修改表结构添加全文索引
    ALTER TABLE article ADD FULLTEXT index_content(content)
    
    • 直接创建全文索引
    CREATE FULLTEXT INDEX index_content ON article(content)
    

5、索引的优点与缺点

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

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

6、常见索引原则

  • 选择唯一性索引:唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
  • 为经常需要排序、分组和联合操作的字段建立索引,为常作为查询条件的字段建立索引。
  • 限制索引的数目:越多的索引,会使更新表变得很浪费时间。
  • 尽量使用数据量少的索引:如果索引的值很长,那么查询的速度会受到影响。
  • 尽量使用前缀来索引:如果索引字段的值很长,最好使用值的前缀来索引。
  • 删除不再使用或者很少使用的索引
  • 最左前缀匹配原则:即最左优先,在检索数据时从联合索引的最左边开始匹配。所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则。
  • 尽量选择区分度高的列作为索引:区分度的公式是表示字段不重复的比例索引列不能参与计算,保持列“干净”:带函数的查询不参与索引。
  • 尽量的扩展索引,不要新建索引。

7、数据库三范式

第一范式:第一范式的目标是确保每列的原子性:如果每列都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式(1NF)

第二范式:首先满足第一范式,并且表中非主键列不存在对主键的部分依赖。第二范式要求每个表只描述一件事情。

第三范式:第三范式定义是,满足第二范式,并且表中的列不存在对非主键列的传递依赖。

8、数据库事务

是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行,要么都不执行。事务是一个不可分割的工作逻辑单元 事务必须具备以下四个属性,简称 ACID 属性:

  • 原子性(Atomicity):事务是一个完整的操作。事务的各步操作是不可分的(原子的);要 么都执行,要么都不执行。
  • 一致性(Consistency):当事务完成时,数据必须处于一致状态。
  • 隔离性(Isolation):对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独 立的,它不应以任何方式依赖于或影响其他事务。
  • 永久性(Durability):事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务 的永久性。

9、事务的四种隔离级别

  1. Read uncommitted:读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。
  2. Read committed:读已提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数
  3. Repeatable read:可重复读,就是在开始读取数据(事务开启)时,不再允许修改操作
  4. Serializable 序列化:Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读.不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。在 MySQL 数据库中,支持上面四种隔离级别,默认的为 Repeatable read (可重复读);而在 Oracle 数据库中,只支持 Serializable (串行化)级别和 Readcommitted (读已提交)这两种级别,其中默认的为 Read committed 级别。
级别名字隔离级别脏读不可重复读幻读数据库默认隔离级别
1读未提交read uncommitted
2读已提交read committedOracle和SQL Server
3可重复读repeatable readMySQL
4串行化serializable

10、数据库并发策略

并发控制一般采用三种方法,分别是乐观锁和悲观锁以及时间戳。

乐观锁:乐观锁认为一个用户读数据的时候,别人不会去写自己所读的数据;悲观锁88就刚好相反,觉得自 己读数据库的时候,别人可能刚好在写自己刚读的数据,其实就是持一种比较保守的态度;时间 戳就是不加锁,通过时间戳来控制并发出现的问题。

悲观锁:悲观锁就是在读取数据的时候,为了不让别人修改自己读取的数据,就会先对自己读取的数据加锁,只有自己把数据读完了,才允许别人修改那部分数据,或者反过来说,就是自己修改某条数据的时候,不允许别人读取该数据,只有等自己的整个事务提交了,才释放自己加上的锁,才允许其他用户访问那部分数据。

两种锁的使用场景:乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

11、CAS算法(乐观锁)

即 compare and swap(比较与交换),是一种有名的无锁算法。

无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blockingSynchronization)。

CAS 算法涉及到三个操作数

  • 需要读写的内存值V
  • 进行比较的值 A
  • 拟写入的新值 B

当且仅当 V 的值等于 A 时,CAS 通过原子方式用新值 B 来更新 V 的值, 否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。

12、乐观锁的缺点

ABA问题:如果一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回 A,那 CAS 操作就会误认为它从来没有被修改过。这个问题被称为 CAS 操作的 "ABA"问题。

循环时间长开销大:自旋 CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给 CPU 带来非常大的执行开销。 如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起 CPU 流水线被清空(CPUpipeline flush),从而提高 CPU 的执行效率。

只能保证一个共享变量的原子操作:CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时CAS 无效。但是从 JDK 1.5 开始,提供了 AtomicReference 类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作。所以我们可以使用锁或者利用 AtomicReference 类把多个共享变量合并成一个共享变量来操作。

CAS 与 synchronized 的使用情景:简单的来说 CAS 适用于写比较少的情况下(多读场景,冲突一般较少),synchronized 适用于写比较多的情况下(多写场景,冲突一般较多)。

  • 对于资源竞争较少(线程冲突较轻)的情况,使用 synchronized 同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而 CAS 基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
  • 对于资源竞争严重(线程冲突严重)的情况,CAS 自旋的概率会比较大,从而浪费更多的 CPU 资源,效率低于 synchronized。

补充: Java 并发编程这个领域中 synchronized 关键字一直都是元老级的角色,很久之前很多人都会称它为 “重量级锁” 。但是,在 JavaSE 1.6 之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后变得在某些情况下并不是那么重了。synchronized 的底层实现主要依靠Lock-Free的队列,基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。

13、时间戳

时间戳就是在数据库表中单独加一列时间戳,比如“TimeStamp”,每次读出来的时候,把该字段也读出来,当写回去的时候,把该字段加1,提交之前,跟数据库的该字段比较一次,如果比数据库的值大的话,就允许保存,否则不允许保存,这种处理方法虽然不使用数据库系统提供的锁机制,但是这种方法可以大大提高数据库处理的并发量,以上悲观锁所说的加“锁”,其实分为几种锁,分别是:排它锁(写锁)和共享锁(读锁)。

14、触发器

触发器是一段能自动执行的程序,是一种特殊的存储过程,触发器和普通的存储过程的区别是:触发器是当对某一个表进行操作时触发。诸如:update、insert、delete这些操作的时候,系统会自动调用执行该表上对应的触发器。触发器可以分为两类:DML触发器和DDL触发器,其中DDL触发器它们会影响多种数据定义语言语句而激发,这些语句有create、alter、drop语句。

15、数据库锁

  1. 行级锁:行级锁是一种排他锁,防止其他事务修改此行;在使用以下语句时,Oracle会自动应用行级锁。
  2. 表级锁:表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL 引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
  3. 页级锁:页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。BDB引擎支持页级锁。

16、分区分表

  • 垂直切分:将表按照功能模块,关系密切程度划分出来,部署到不同的库上。
  • 水平切分:当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,例如userID散列,进行划分,然后存储到多个结构相同的表,和不同的库上。

17、MySQL常用SQL查询语句优化方法

  1. 应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。

  2. 对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引。

  3. 应尽量避免在 where 子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描。

  4. 尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描。

  5. 尽量避免查询中使用前置百分号,这也将导致全表扫描。

  6. in 和 not in 也要慎用,否则会导致全表扫描。对于连续的数值,能用 between 就不要用 in 了。

  7. 如果在 where 子句中使用参数,也会导致全表扫描。

  8. 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

    select id from t where num/2=100
    

    应改为:

    select id from t where num=100*2
    
  9. 应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。

  10. 不要在 where 子句中的“=”左边进行函数.算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

  11. 在使用索引字段作为条件时,如果该索引是组合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

  12. 很多时候用 exists 代替 in 是一个好的选择。

  13. 尽量避免大事务操作,提高系统并发能力。