数据库三范式

第一范式

是指在关系模型中,对于添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。

第二范式

第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

第三范式

第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。

反范式

规范化的优点是明显的,它避免了大量的数据冗余,节省了存储空间,保持了数据的一致性。当一个库里的数据经常发生变化时,达到3NF的库可以使用户不必在超过两个以上的地方更改同一个值。那么是不是只要把所有的表都规范为3NF后,数据库的设计就是最优的呢?这可不一定。范式越高意味着表的划分更细,一个数据库中需要的表也就越多,用户不得不将原本相关联的数据分摊到多个表中。当用户同时需要这些数据时只能采用连接表的形式将数据重新合并在一起。同时把多个表联接在一起的花费是巨大的,尤其是当需要连接的两张或者多张表数据非常庞大的时候,表连接操作几乎是一个噩梦,这严重地降低了系统运行性能。

目的原则

规范化目的是使结构更合理,消除存储异常,使数据冗余尽量小。便于插入、删除和更新。
遵从概念单一化“一事一地”原则,即一个关系模式描述一个实体或实体间的一种联系。
规范的实质就是概念的单一化。

概念术语

sql server的触发器是什么?怎么使用触发器?

触发器是数据库中由一定事件触发的特殊的存储过程,他不是由程序掉用也不是手工启动的。
触发器的执行可以由对一个表的insert,delete, update等操作来触发,触发器经常用于加强数据的完整性约束和业务规则等等。

介绍存储过程基本概念和它的优缺点

存储过程是一个预编译的SQL语句,他的优点是允许模块化的设计,也就是说只需创建一次,在该程序中就可以调用多次。例如某次操作需要执行多次SQL,就可以把这个SQL做一个存储过程,因为存储过程是预编译的,所以使用存储过程比单纯SQL语句执行要快。缺点是可移植性差,交互性差。

索引的作用?和它的优点缺点是什么?

索引就是一种特殊的查询表,数据库的搜索引擎可以利用它加速对数据的检索。
索引很类似与现实生活中书的目录,不需要查询整本书内容就可以找到想要的数据对应的存储位置,再依实际存储位置获得数据。
缺点是它减慢了数据录入的速度,同时也增加了数据库的尺寸大小。

使用索引有哪些需要注意的地方?

  1. 创建索引的的字段尽量小,最好是数值,比如整形int等;
  2. 对于频繁修改的字段,尽量不要创建索引,维护索引的成本很高,而且更容易产生索引碎片;
  3. 定期的索引维护,如索引碎片的修复等;
  4. 不要建立或维护不必要的重复索引,会增加修改数据(新增、修改、删除数据)的成本;
  5. 使用唯一性高的字段创建索引,切不可在性别这样的低唯一性的字段上创建索引;
  6. 在SQL语句中,尽量不要在Where条件中使用函数、运算符或表达式计算,会造成索引无法正常使用;
  7. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描;
  8. 应尽量避免在 where 子句中使用!=或<>操作符,否则将导致引擎放弃使用索引而进行全表扫描;

    索引碎片是如何产生的?有什么危害?又该如何处理?

    索引在使用一段时间后(主要是新增、修改、删除数据,如果该页已经存储满了,就要进行页的拆分,频繁的拆分,会产生较多的索引碎片)会产生索引碎片。
    索引碎片会严重影响数据的查询效率,如碎片太多,索引可能不会被使用。
    碎片的处理方式主要有两种:
    第一种是预防:设置页的填充因子
    意思就是在页上设置一段空白区域,在新增数据的时候,可以使用这段空白区域,可以一定的避免页的拆分,从而减少索引碎片的产生。
    第二种是索引修复:定期对索引进行检查、维护,写一段SQL检查索引的碎片比例,如果碎片过多,进行碎片修复或重建,定期执行即可。具体可以参考本文末尾的相关参考资料。

    聚集索引和非聚集索引区别?

    聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致

  9. 查询速度不同

聚集索引查询数据速度快。
非聚集索引查询数据速度慢。

  1. 占用内存大小不同:

聚集索引需要较少的内存来进行操作。
非聚集索引需要更多的内存用于操作。

视图的作用,视图可以更改么?

视图是虚拟的表,与包含数据的表不一样,视图只包含使用时动态检索的数据,不包含任何列或数据。
使用视图可以简化复杂的sql操作,隐藏具体的细节,保护数据;
视图创建后,可以像使用与表相同的方式利用它们;
视图的目的在于简化检索,保护数据,并不用于更新操作。

维护数据库的完整性和一致性,你喜欢用触发器还是自写业务逻辑?为什么?

  1. 尽可能使用约束,如check、主键、外键、非空字段等来约束。这样做效率最高,也最方便。
    2. 其次是使用触发器,这种方法可以保证,无论什么业务系统访问数据库都可以保证数据的完整新和一致性。
    3. 最后考虑的是自写业务逻辑,但这样做麻烦,编程复杂,效率低下。

    锁的目的是什么?

    主要解决多个用户同时对数据库的并发操作时会带来以下数据不一致的问题:

  2. 丢失更新,同时修改一条数据;

  3. 读脏,A修改了数据后,B读取后A又取消了修改,B读脏;
  4. 不可重复读,A用户读取数据,随后B用户读取该数据并修改,此时A用户再读取数据时发现前后两次的值不一致;
  5. 还有一种是幻读,这个情况好像不多。

并发控制的主要方法是封锁,锁就是在一段时间内禁止用户做某些操作以避免产生数据不一致。

锁的粒度有哪些?

数据库锁:锁定整个数据库,这通常发生在整个数据库模式改变的时候。
表锁:锁定整个表,这包含了与该表相关联的所有数据相关的对象,包括实际的数据行(每一行)以及与该表相关联的所有索引中的键。
区段锁:锁定整个区段,因为一个区段由8页组成,所以区段锁是指锁定控制了区段、控制了该区段内8个数据或索引页以及这8页中的所有数据行。
页锁:锁定该页中的所有数据或索引键。
行或行标识符:虽然从技术上将,锁是放在行标识符上的,但是本质上,它锁定了整个数据行。

SQL相关

第一题

Student(S#,Sname,Sage,Ssex) 学生表:S#:学号;Sname:学生姓名;Sage:学生年龄;Ssex:学生性别
Course(C#,Cname,T#) 课程表:C#,课程编号;Cname:课程名字;T#:教师编号
SC(S#,C#,score) 成绩表:S#:学号;C#,课程编号;score:成绩
Teacher(T#,Tname) 教师表:T#:教师编号; Tname:教师名字

  1. 查询“001”课程比“002”课程成绩高的所有学生的学号;

    1. select a.S# from (select s#,score from SC where C#='001') a,(select s#,score
    2. from SC where C#='002') b
    3. where a.score>b.score and a.s#=b.s#;
  2. 查询平均成绩大于60分的同学的学号和平均成绩;

    1. select S#,avg(score)
    2. from sc
    3. group by S# having avg(score) >60;
  3. 查询所有同学的学号、姓名、选课数、总成绩;

    1. select Student.S#,Student.Sname,count(SC.C#),sum(score)
    2. from Student left Outer join SC on Student.S#=SC.S#
    3. group by Student.S#,Sname
  4. 查询姓“李”的老师的个数;

    1. select count(distinct(Tname))
    2. from Teacher
    3. where Tname like '李%';

    NO SQL

    非关系型数据库和关系型数据库区别,优势比较?

    非关系型数据库的优势:

  5. 性能

NO SQL是基于键值对的,不需要经过SQL层的解析,所以性能非常高。

  1. 可扩展性

NO SQL同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。

关系型数据库的优势:

  1. 复杂查询

可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。

  1. 事务支持

使得对于安全性能很高的数据访问要求得以实现。

总结

对于这两类数据库,对方的优势就是自己的弱势,反之亦然

redis缓存相关

redis缓存穿透,缓存击穿,缓存雪崩原因

  • 缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
  • 缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
  • 缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力

    缓存穿透解决方案

    一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
    有很多种方法可以有效地解决缓存穿透问题最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

    缓存击穿解决方案

    key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。

  • 使用互斥锁(mutex key)

业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

缓存雪崩解决方案

与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key。
缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件

读写分离&主从复制

主从复制的几种方式

  1. 同步复制

所谓的同步复制,意思是master的变化,必须等待slave-1,slave-2,…,slave-n完成后才能返回。 这样,显然不可取,也不是MySQL复制的默认设置。比如,在WEB前端页面上,用户增加了条记录,需要等待很长时间。

  1. 异步复制

如同AJAX请求一样。master只需要完成自己的数据库操作即可。至于slaves是否收到二进制日志,是否完成操作,不用关心,MySQL的默认设置。

  1. 半同步复制

master只保证slaves中的一个操作成功,就返回,其他slave不管。 这个功能,是由google为MySQL引入的。

主从复制分析的 7 个问题

  1. master的写操作,slaves被动的进行一样的操作,保持数据一致性,那么slave是否可以主动的进行写操作?

假设slave可以主动的进行写操作,slave又无法通知master,这样就导致了master和slave数据不一致了。因此slave不应该进行写操作,至少是slave上涉及到复制的数据库不可以写。实际上,这里已经揭示了读写分离的概念。

  1. 主从复制中,可以有N个slave,可是这些slave又不能进行写操作,要他们干嘛?

以实现数据备份。
类似于高可用的功能,一旦master挂了,可以让slave顶上去,同时slave提升为master。
异地容灾,比如master在北京,地震挂了,那么在上海的slave还可以继续。
主要用于实现scale out,分担负载,可以将读的任务分散到slaves上。
【很可能的情况是,一个系统的读操作远远多于写操作,因此写操作发向master,读操作发向slaves进行操作】

  1. 主从复制中有master,slave1,slave2,…等等这么多MySQL数据库,那比如一个JAVA WEB应用到底应该连接哪个数据库?

当 然,我们在应用程序中可以这样, insert/delete/update这些更新数据库的操作,用 connection(formaster)进行操作, select用connection(forslaves)进行操作。那我们的应用程序还要完成怎么从slaves选择一个来执行select,例如使用简单的轮循算法。
这样的话,相当于应用程序完成了SQL语句的路由,而且与MySQL的主从复制架构非常关联,一旦master挂了,某些slave挂了,那么应用程序就要修改了。能不能让应用程序与MySQL的主从复制架构没有什么太多关系呢?
找一个组件,application program只需要与它打交道,用它来完成MySQL的代理,实现SQL语句的路由。
MySQL proxy并不负责,怎么从众多的slaves挑一个?可以交给另一个组件(比如haproxy)来完成。
这就是所谓的 MySQLREAD WRITE SPLITE,MySQL的读写分离。

  1. 如果MySQL proxy , direct , master他们中的某些挂了怎么办?

总统一般都会弄个副总统,以防不测。同样的,可以给这些关键的节点来个备份。

  1. 当master的二进制日志每产生一个事件,都需要发往slave,如果我们有N个slave,那是发N次,还是只发一次?

如果只发一次,发给了slave-1,那slave-2,slave-3,…它们怎么办?
显 然,应该发N次。实际上,在MySQL master内部,维护N个线程,每一个线程负责将二进制日志文件发往对应的slave。master既要负责写操作,还的维护N个线程,负担会很重。可以这样,slave-1是master的从,slave-1又是slave-2,slave-3,…的主,同时slave-1不再负责select。 slave-1将master的复制线程的负担,转移到自己的身上。这就是所谓的多级复制的概念。

  1. 当一个select发往MySQL proxy,可能这次由slave-2响应,下次由slave-3响应,这样的话,就无法利用查询缓存了。

应该找一个共享式的缓存,比如memcache来解决。将slave-2,slave-3,…这些查询的结果都缓存至mamcache中。

  1. 随着应用的日益增长,读操作很多,我们可以扩展slave,但是如果master满足不了写操作了,怎么办呢?

scale on ?更好的服务器? 没有最好的,只有更好的,太贵了。。。
scale out ? 主从复制架构已经满足不了。
可以分库【垂直拆分】,分表【水平拆分】。
[

](https://blog.csdn.net/lanzhupi/article/details/112503851)