zoukankan      html  css  js  c++  java
  • 数据库知识归纳(面试向)

    ---->数据库分类

       数据库可分为关系型数据库(Sql)和非关系型数据库(NoSql)

       Sql: mysql、sqlserver、oracle

       NoSql:

      1)键值对数据库:redis、memcache
      2)列存储数据库:hbase
      3)文档型数据库:mongdb
      4)图形数据库:graph
    ?--- 区别
       1.关系型数据库通过外键关联来建立表与表之间的关系,
       2.非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定,
    NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题,包括超大规模数据的存储。
    ?--- 比较
     优点:
       1)成本:nosql数据库简单易部署,基本都是开源软件,不需要像使用oracle那样花费大量成本购买使用,相比关系型数据库价格便宜。
       2)查询速度:nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及nosql数据库。
       3)存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。
       4)扩展性:关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。
    缺点:
       1)维护的工具和资料有限,因为nosql是属于新的技术,不能和关系型数据库10几年的技术同日而语。
       2)不提供对sql的支持,如果不支持sql这样的工业标准,将产生一定用户的学习和使用成本。
       3)不提供关系型数据库对事物的处理。

    ---->索引

        索引是对数据库表中一个或多个列的值进行排序的结构,要想理解索引的,需要了解一种数据结构 B+树,我们平时建表的时候都会加上主键,在某些数据库中,如果没有加上主键甚至都不能都创建表,事实上,一个加了主键的表,并不能说是我们所想的“表”,一个没加主键的表,它的数据无序的放置在磁盘存储器上,一行一行的排列的很整齐,反而很接近理想中的“表”,如果给表上了主键,那么表在磁盘上的存储结构就由整齐排列的结构转变成了树状结构,也就是上面说的「平衡树」结构,换句话说,就是整个表就变成了一个索引。也就是所谓的「聚集索引」。 这就是为什么一个表只能有一个主键, 一个表只能有一个「聚集索引」,因为主键的作用就是把「表」的数据格式转换成「索引(平衡树)」的格式放置
    ?--- 优劣
    优点:
    • 大大地加快了查询速率
    • 创建唯一性索引,保证数据库表中每一行数据的唯一性
    • 加速表和表之间的连接
    • 在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
      1)大大减少服务器需要扫描的数据量
      2)帮助服务器避免排序和临时表
      3)将所及I/O变为顺序I/O
    缺点:
    • 索引需要占用数据表以外的物理存储空间
    • 创建索引和维护索引要花费一定的时间
    • 当对表进行更新操作时,索引需要被重建,这样降低了数据的维护速度(这是因为索引的数据结构是个平衡树, 因为平衡树这个结构必须一直维持在一个正确的状态, 增删改数据都会改变平衡树各节点中的索引数据内容,破坏树结构, 因此,在每次数据改变时, DBMS必须去重新梳理树(索引)的结构以确保它的正确,这会带来不小的性能开销)
       因此,可见虽然使用索引查询数据会比全表扫描要快,但是索引的使用是需要付出一定的成本,以及需要定期的维护,所以索引查询不一定能够提高查询性能。
    ?--- 分类
       普通索引:最基本的的索引方式
    –直接创建索引
    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 SET utf8 COLLATE utf8_general_ci NOT NULL ,
    `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
    `time` int(10) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`),
    INDEX index_name (title(length))
    )
    –删除索引
    DROP INDEX index_name ON table

      唯一索引:关键字unique,索引列的值必须唯一,但允许有空值(注意和主键不同)。如果是组合索引,则列值的组合必须唯一,创建方法和普通索引类似。

    –创建唯一索引
    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 SET utf8 COLLATE utf8_general_ci NOT NULL ,
    `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
    `time` int(10) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`),
    UNIQUE indexName (title(length))
    );

    主键索引:关键字primary key,数据库表经常有一列或多列组合,其值唯一标识表中的每一行。该列称为表的主键。在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。

     全文索引:关键字FULLTEXT,对于大容量的数据表,生成全文索引是一个非常消耗时间非常消耗硬盘空间的做法。
    –创建表的适合添加全文索引
    CREATE TABLE `table` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
    `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
    `time` int(10) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`),
    FULLTEXT (content)
    );
    –修改表结构添加全文索引
    ALTER TABLE article ADD FULLTEXT index_content(content)
    –直接创建索引
    CREATE FULLTEXT INDEX index_content ON article(content)

    ~~~聚簇索引与非聚簇索引(重点细说)

       聚簇索引:索引的顺序与表中记录的物理顺序相同。类似于假如用新华字典找一个字时候,会先从这个字的音序开始,然后接着再找到音节,从而找到目标,具备有一定具体的物理顺序步骤。 它反映的是真实数据的物理顺序,所以一张表只能创建一个聚簇索引
      聚簇索引十分适合用于那些经常要搜索范围值的列,当应用程序执行一个查询经常检索某一个日期范围的记录,则使用聚簇索引可以迅速找到开始日期的执行,然后检索表中所有相邻的行,直到结束日期
     非聚簇索引:非聚簇索引记录的物理顺序与逻辑顺序没有必然的联系,与数据的存储物理结构没有关系。一个表对应的非聚簇索引可以有多条,根据不同列的约束可以建立不同要求的非聚簇索引
      何时使用聚集索引或非聚集索引?
    动作描述 使用聚集索引 使用非聚集索引
    列经常被分组排序 使用 使用
    返回某范围内的数据 使用 不使用
    一个或极少不同值 不使用 不使用
    小数目的不同值 使用 不使用
    大数目的不同值 不使用 使用
    频繁更新的列 不使用 使用
    外键列 使用 使用
    主键列 使用 使用
    频繁修改索引列 不使用 使用
     
      复合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。例如索引是key index (a,b,c)。 可以支持a | a,b| a,b,c 3种组合进行查找,但不支持 b,c进行查找 ,当最左侧字段是常量引用时,索引就十分有效。
    create table test(
    a int,
    b int,
    c int,
    KEY a(a,b,c)
    );
    
    优: select * from test where a=10 and b>50
    差: select * from test where a50
    
    优: select * from test where order by a
    差: select * from test where order by b
    差: select * from test where order by c
    
    优: select * from test where a=10 order by a
    优: select * from test where a=10 order by b
    差: select * from test where a=10 order by c
    
    优: select * from test where a>10 order by a
    差: select * from test where a>10 order by b
    差: select * from test where a>10 order by c
    
    优: select * from test where a=10 and b=10 order by a
    优: select * from test where a=10 and b=10 order by b
    优: select * from test where a=10 and b=10 order by c
    
    优: select * from test where a=10 and b=10 order by a
    优: select * from test where a=10 and b>10 order by b
    差: select * from test where a=10 and b>10 order by c
    
    索引原则
    1.索引越少越好
    原因:主要在修改数据时,第个索引都要进行更新,降低写速度。
    2.最窄的字段放在键的左边
    3.避免file sort排序,临时表和表扫描.
     参考一:MySql数据库复合索引

     ?--- 失效情况

    • 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描
    • 对索引列运算,运算包括(+、-、*、/、!、<>、%、like'%_'(%放在前面)、or、in、exist等),导致索引失效 
    • 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如: select id from t where num/2=100 应改为: select id from t where num=100*2;
    • 应尽量避免在 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;
    • is null 索引失效;is not null Betree索引生效
    • 下面的查询也将导致全表扫描: select id from t where name like ‘%abc%’;
    • 复合索引失效,where条件没有按照符合索引的顺序排列
    • 对索引使用内部函数,这种情况下应该建立基于函数的索引
    • 不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引

     ?---使用原则

    • 不要设置过多的索引,消耗的成本比较高,同时维护的也比较麻烦,反而会降低了效率
    • 对于小的表建立索引,实现的效果不高 
    • 对于经常要用到的列,可以考虑聚集索引

     ---->MySql常用引擎

         在MySQL数据库中,主要的引擎是 Innodb和MyIASM 。在5.1之前,默认是MyIASM,而在这之后采用了Innodb引擎。

         简单来说,Innodb引擎支持事务,并且提供了行级锁以及外键约束,由于锁的粒度小,写操作是不会锁定全表的,所以在并发度较高的场景下使用会提升效率的。但是该引擎并不会提供全文搜索,当进行Select count(*) from table指令的时候,需要进行扫描全表。Innodb引擎的索引的数据结构也是B+树,只不过数据结构中存储的都是实际的数据,这种索引有被称为聚集索引。(常用于大容量的数据集,以及需要事务控制)

        MyIASM 引擎不会提供事务支持,也不支持行级锁和外键。因此当执行Insert插入和Update更新语句时,即执行写操作的时候需要锁定这个表。所以会导致效率会降低。不过和Innodb不同的是,MyIASM引擎是保存了表的行数,于是当进行Select count(*) from table语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的。可以将MyIASM作为数据库引擎的首先对于MyIASM引擎来说,B+树的数据结构中存储的内容实际上是实际数据的地址值。也就是说它的索引和实际数据是分开的,只不过使用索引指向了实际数据。这种索引的模式被称为非聚集索引。(常用于不需要事务支持,插入不频繁、查询十分频繁)

    ---->事务(ACID)

       即一个逻辑单元内所有操作,要不全部执行生效,要不完全不执行,以达成一致的效果。

       原子性(Atomicity):一个事务在执行的过程中,要不就完全执行,要不就完全不执行,不会在中间的某个环节结束。如果事务的执行过程中发生错误,就会触发回滚操作,即撤销当前操作回到事务前的状态。

      一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的默认规则,这包含资料的精准度、串联新以及后续数据库可以自发性地完成预定的工作。A转账给B,执行事务之前:A+B=100,执行事务之后:A+B=100

       隔离性(Isolation):当两个或以上事务并发访问的时候(此处访问指查询和修改的操作)数据库的同一数据时所表现出的互相关系。事务隔离分为不同的级别,包括读不提交(Read uncommitted)、读提交(Read committed)、可重复读(Repeatable read)和串行化(Serializable)。多个事务并发访问相互之间不影响,A事务的结果不会覆盖B事务的结果

      持久性(Durability):在事务完成以后,该事务对数据库所作的更改便持久地保存在数据库之中,而且是完全的事务一旦提交,对数据库数据的改变就是永久性的

     ? ---事务隔离级别

      事务隔离级别

    • 读未提交(Read uncommitted):允许读取还未提交的改变了的数据。可能导致脏、幻、不可重复读。
    • 读已提交(Read committed):允许在并发事务已经提交后读取。可防止脏读,但是幻读和不可重复读仍可发生。
    • 可重复读(Repeatable read):对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但是幻读仍可能发生。
    • 串行化(Serializable):完全服从数据库四种隔离级别,确保不发生脏、幻、不可重复读。速度最慢,它是通过完全锁定在事务中涉及的数据表来完成的,事务按顺序串行执行。
    不考虑隔离性,会引发一下问题:
    • 脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。读到无效数据,导致查询结果前后不一致。
    • 不可重复读:在同一个事务中,读到另一个事务已经提交更新的数据。读到更新的数据,导致多次读取同一个数据返回的结果有所不同。
    • 幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。在后来的查询中,第一个事务就会发现有些原来没有的记录。读取到新的记录
    MySQL的默认事务隔离级别是Repeatable read

     ? ---事务实现方式

       目前主要有两种方式实现ACID:第一种是Write ahead logging,也就是日志的方式。第二种是Shadow paging。

       相对于WAL(write ahead logging)技术,shadow paging技术实现起来比较简单,消除了写日志记录的开销恢复的速度也快(不需要redo和undo)。shadow paging的缺点就是事务提交时要输出多个块,这使得提交的开销很大,而且以块为单位,很难应用到允许多个事务并发执行的情况——这是它致命的缺点。

        Write ahead logging(预写日志): 

      1. 事务所引起的所有改动都要记录在日志中,在事务提交完成之前,所有的这些记录必须被写入硬盘; 
      2. 一个数据库的缓冲页直到被记入日志后才能发生修改。直到缓冲页对应的日志被写入硬盘后,该缓冲页才会存入硬盘; 
      3. 当缓冲页被修改和日志被更新修改时,必须加上互斥锁,以保证改动被记录到日志中的顺序与它发生的顺序是一致的;
      4.因为我们知道在出现崩溃的情况下, 我们可以用日志来恢复数据库:任何尚未附加到数据页的记录 都将先从日志记录中重做(这叫向前滚动恢复,也叫做 REDO) 然后那些未提交的事务做的修改将被从数据页中删除 (这叫向后滚动恢复 - UNDO)

    ---->数据库的三大范式

       三范式指的是第一(1NF)、第二(2NF)和第三范式(3NF),其作用:解决数据冗余,为数据有效性检查,提高存储效率考虑

    ?---认识几个键

      超键:在关系中能唯一标识元组的属性集称为关系模式的超键。注意:(属性集,说明可以是多个)

      候选键:不含有多余属性的超键

      主键:用户选作记录标识的候选键

    举个栗子

     有一张基础的学生表
    
     那么超键就可以有  (学号)唯一;(姓名)唯一;(学号,年龄)唯一;(学号,姓名,年龄)唯一 。(可以看出超键的组合是唯一的,但是不是最小的唯一)
    
     候选键:学号,唯一且没有多余的属性;姓名,唯一且没有多余的属性。
    
     既可以选择学号,也可以选择姓名(前提是规定没有重名的)作为主键,所以主键是选中的一个候选键。

    ?---函数依赖

        我们都知道,在数据库中,属性之间都是会发生联系。例如,每个学生只有一个姓名,每门课程只有一个任课教师,每个学生学一门课程只能有一个总评成绩。等等,这类联系,我们都可以将其称为函数依赖(FD)。

       三种函数依赖形式:

    • 平凡与非平凡依赖:若对于R(U)的任意两个可能的关系r1、r2,若r1[x]=r2[x],则r1[y]=r2[y],或者若r1[x]不等于r2[x],则r1[y]不等于r2[y],称X决定Y,或者Y依赖X。通俗的说:就是我们知道一个之后可以推导出另外一个。
    • 局部依赖与非局部依赖:若x->y 并且,存在X的真子集x1,使得x1->y,则 y部分依赖于 x;若x->y并且,对于x的任何一个真子集x1,都不存在x1->y 则称y完全依赖于x。例子:{a,b}->{c} 同时{a}->{c}或者{b}->{c} 则{c}部分依赖于{a,b}                    
    • 传递依赖X>Y,Y—>A,Y/>XA不属Y,那么称X>A是传递依赖

    ?---范式理解

      第一范式:要求每个关系的属性为原子性,不可再分(能分就分,分到不能再分为止!)

       例如:R(学号,姓名,性别)

      第二范式:主要是要消除局部依赖

       例如:有关系模式为:R(学号、课程编号、成绩、教师号、教师职称)等。

       R上有两个FD:(学号,课程号)—>(教师号,教师职称) 课程编号>(教师工号,教师职称)。由此,可得出前面一个是局部依赖。所以R关系模式不是第二范式模式。此时R的关系就会出现冗余和异常现象。例如,如果一门课程有10个学生选修,那么在关系中就存有10条记录,教师工号和职称也会重复10次。  

       解决方案:将R分解成R1(课程编号,教师号,教师职称)和R2(学号,课程号,成绩)。此时,R1R2都是第二范式模式。

      第三范式:主要是消除传递依赖

       例如:R(课程编号,教师工号,教师职称)

       如果课程编号—>教师工号,教师工号—>教师职称,那么课程编号—>教师职称就是一个传递依赖。所以不是第三范式。此时R就会出现冗余和异常操作。例如,一个教师开设五门课程,那么关系中就会出现五条记录,教师职称就会重复五次。

        解决方案:把R分解为R1(教师工号,教师职称)和R2(课程编号,教师工号)  

    ---->悲观锁与乐观锁

        数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。

       悲观锁:每次去拿数据的时候都会认为别人会在自己操作的过程中偷偷修改,所以每次自己拿数据的时候加上了锁,这样别人想拿到这个数据就会触发阻塞机制直到拿到了锁。比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。

       乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。

    ---->Sql注入

    ?---概念理解

        sql作为一种解释型语言,在运行时是由一个运行时组件解释语言代码并执行其中包含的指令的语言。基于这种执行方式,产生了一系列叫做代码注入(code injection)的漏洞 。它的数据其实是由程序员编写的代码和用户提交的数据共同组成的。程序员在web开发时,没有过滤敏感字符,绑定变量,导致攻击者可以通过sql灵活多变的语法,构造精心巧妙的语句,不择手段,达成目的,或者通过系统报错,返回对自己有用的信息。

    ?---注入形式

     1.数字注入 

        在浏览器地址栏输入:learn.me/sql/article.php?id=1,这是一个get型接口,发送这个请求相当于调用一个查询语句:

    $sql = "SELECT * FROM article WHERE id =",$id

       正常情况下,应该返回一个id=1的文章信息。那么,如果在浏览器地址栏输入learn.me/sql/article.php?id=-1 OR 1 =1

       这样的就算是一个sql注入了。因为id=-1是false,而 1=1是true的,所以其实调用的查询语句是:

    $sql = "SELECT * FROM article 

    2.字符注入

       这种经常发生于登录操作页面,即一个登录页面,有用户名和密码,以及提交表单信息按钮,通过与数据库的连接,页面发起Post请求,后台向数据库提供数据进行比对校验。

       假如我们登录操作中的sql如下:

    select * from users where username='tarena' and password=md5('admin')

       这种乍一看是没什么问题的,当用户名和密码输入就成功登录。但是,由于用户名和密码都是字符串,SQL注入方法即把参数携带的数据变成mysql中注释的字符串。有两种注入方式:

      1)'#':'#'后所有的字符串都会被当成注释来处理

       如上,当用户输入 tarena'# ,密码此时可以就可以随便输入,因为该查询语句变成了:

    select * from users where username='tarena'#' and password=md5('admin')

        实际上相当于:

    select * from users where username='tarena'

      这样的话就直接忽略了密码的验证操作。甚至还有一种更为牛逼的方式:

      比如:在用户名输入框中输入:’or 1=1#,密码随便输入,即查询语句变成:

    select * from users where username='' or 1=1#' and password=md5('')

      由于'#'后边全部都被注释,而且 1=1 是为true的,所以最终效果如下:

    select * from users

      这样就直接查出了整个表的所有数据,对于网站的安全影响极大。

    2)'-- ' (--后面有个空格):'-- '后面的字符串都会被当成注释来处理

      这个道理和上边的使用办法类似,当用户输入tarena'--,密码随意,即查询sql如下:

    select * from users where username='tarena'--' and password=md5('admin')

      相当于:

    select * from users where username='tarena'

    ?---如何避免

         避免数据变成代码被执行,对sql语句进行预编译和查询参数绑定,在SQL语句中放置占位符'?',然后将带有占位符的SQL语句传给数据库编译,执行的时候才将用户输入的数据作为执行的参数传给用户。这样的操作不仅使得SQL语句在书写的时候不再需要拼接,看起来也更直接,而且用户输入的数据也没有机会被送到数据库的SQL解释器被编译执行,也不会越权变成代码。

        使用PreparedStatement,它是在创建语句对象的同时给出要执行的sql语句。这样,sql语句就会被系统进行预编译,执行的速度会有所增加,尤其是在执行大语句的时候,效果更加理想。而且PreparedStatement中绑定的sql语句是可以带参数的。

    附加:

       

    其他: MySQL的分库分表总结

              在一个千万级的数据库查寻中,如何提高查询效率?

              数据库系统的结构(三级模式结构)

      参考众多文章总结,仅供个人日常查阅使用,主要参考资料如下:

    https://blog.csdn.net/u012294820/article/details/78732771

    https://www.cnblogs.com/aspwebchh/p/6652855.html

    http://feiyan.info/16.html

    https://www.xuebuyuan.com/3190517.html

  • 相关阅读:
    (转)Epoll模型详解
    (转)彻底学会使用epoll(一)——ET模式实现分析
    (转)Linux内核 TCP/IP、Socket参数调优
    Nodejs RESTFul架构实践之api篇(转)
    创业笔记-Node.js入门之阻塞与非阻塞
    创业笔记-Node.js入门之基于事件驱动的回调
    创业笔记-Node.js入门之一个完整的基于Node.js的web应用
    创业笔记-Node.js入门之JavaScript与Node.js
    在CentOS6.5下配置安装LVS
    zend studio 添加xdebug调试php代码
  • 原文地址:https://www.cnblogs.com/liangyueyuan/p/9757330.html
Copyright © 2011-2022 走看看