一、哪些地方适合用索引?哪些不适合?
where,order by,group by中频繁出现,且数据分布比较离散的列适合创建索引。比如有个用户表,用户名,手机号经常会作为查询条件,且不同用户用户名,手机号都不同(数据较离散),因此适合创建索引。但是用户的性别,虽然也会经常作为查询条件,但是因为性别只有男,女,未知等几种,数据不够离散,因此不适合创建索引。
索引也是文件,当修改数据时要动态的修改索引,所以修改次数明显比查询要多的列不适合创建索引。
二、索引的底层实现
索引的目的在于提高查询效率。
通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
当数据量相当大时,二叉树搜索树已经不足以满足我们的要求。所以我们引进了B+树这种为磁盘存储而设计的数据结构。
b+树可以把磁盘IO次数控制在一个很小的数量级(常数数量级)。
上图就是一颗b+树,b+树是一颗多叉搜索树,期中除了叶子节点其他节点存的只是范围,就像一本书的目录一样(图中蓝色代表范围,黄色代表指针),叶子节点中存储的才是真实的数据。
通常情况下,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
1.索引字段要尽量的小:通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。简单的说,字段越大,b+树的高度就会越高,查询效果就越差。
2.索引的最左匹配特性(即从左往右匹配):当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据
三、B树和B+树的区别
B树:所有节点都包含key和data,所有节点组成这棵树,并且叶子节点指针为null。
B+树:只有叶子节点存储真实数据,其他节点只存储范围和指针。叶子节点不存储指针。
四、红黑树相比二叉搜索树有什么优点?
二叉搜索树在最坏情况下会变成一中链式结构,这种情况是最坏情况,使二叉搜索树的一般操作时间复杂度变为O(n).
红黑树虽然本质上是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,保证了存在n个节点的红黑树始终保持着logn的高度,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。
五、数据库隔离级别
不同的隔离级别对并发问题的解决情况如图:
注意:事务的隔离级别和数据库并发性是成反比的,隔离级别越高,并发性越低。
六、脏读、幻读、不可重复读、撤销覆盖、提交覆盖
1、脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
例如:
张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。
与此同时,
事务B正在读取张三的工资,读取到张三的工资为8000。
随后,
事务A发生异常,而回滚了事务。张三的工资又回滚为5000。
最后,
事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。
2、不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
例如:
在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。
与此同时,
事务B把张三的工资改为8000,并提交了事务。
随后,
在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。
3、幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
例如:
目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。
此时,
事务B插入一条工资也为5000的记录。
这是,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。
4、提醒
不可重复读的重点是修改:
同样的条件,你读取过的数据,再次读取出来发现值不一样了
幻读的重点在于新增或者删除:
同样的条件,第 1 次和第 2 次读出来的记录数不一样
5、第一类丢失更新(撤销覆盖)
A事务撤销时,把已经提交的B事务的更新数据覆盖了。例如:
这时候取款事务A撤销事务,余额恢复为1000,这就丢失了更新。
6、第二类丢失更新(更新覆盖)
A事务覆盖B事务已经提交的数据,造成B事务所做的操作丢失
七、get和post的区别
1. GET 请求的数据会附在 URL 之后(就是把数据放置在 HTTP 协议头中),以?分割 URL 和传输数据,参数之间
以&相连,如:login.action?name=zhagnsan&password=123456。
POST 把提交的数据则放置在是 HTTP 包的包体中。
2. GET 方式提交的数据最多只能是 1024 字节。(get的长度限制主要原因是提交数据在url上,实际上url不存在长度限制,但是有些浏览器会限制url的长度,如Netscape、FireFox 等,理论上没有长度限制,其限制取决于操作系统的支持。)
理论上 POST 没有限制,可传较大量的数据。
3.POST 的安全性要比 GET 的安全性高。(主要是因为请求的数据会显示到url上,而浏览器有缓存)
4.Get 是向服务器发索取数据的一种请求,而 Post 是向服务器提交数据的一种请求,在 FORM(表单)中,Method
默认为"GET",实质上,GET 和 POST 只是发送机制不同,并不是一个取一个发!
八、cookie和session介绍一下区别
Cookie 是 web 服务器发送给浏览器的一块信息,浏览器会在本地一个文件中给每个 web 服务器存储 cookie。以后浏览器再给特定的 web 服务器发送请求时,同时会发送所有为该服务器存储的 cookie。
Session 是存储在 web 服务器端的一块信息。session 对象存储特定用户会话所需的属性及配置信息。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。
cookie和session区别:
1、无论客户端做怎样的设置,session 都能够正常工作。当客户端禁用 cookie 时将无法使用 cookie.
2、在存储的数据量方面:session 能够存储任意的 java 对象,cookie 只能存储 String 类型的对象。
3、Cookie是属于Session对象的一种。但cookie是存在于客户端的,不会占用服务器资源;而Session会占用服务器资源。
4、虽然很多著名网站都采用cookie。但cookie的安全性是存在问题的,虽然Cookies是保存在本机上的,但是其信息的完全可见性且易于本地编辑性,往往可以引起很多的安全问题。而Session是安全的、可靠的。
cookie典型应用场景:
(一):判断用户是否登陆过网站,以便下次登录时能够直接登录。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
(二):另一个重要的应用是“购物车”中类的处理和设计。用户可能在一段时间内在同一家网站的不同页面选择不同的商品,可以将这些信息都写入cookie,在最后付款时从cookie中提取这些信息,当然这里面有了安全和性能问题需要我们考虑了。
九、hash索引和B+树索引的应用场景
hash结构的特点:检索效率非常高,索引的检索可以一次到位,O(1)。B树需要从根节点到枝节点,最后才能到叶节点进行多次I/O操作,所以hash的效率远远高于B树的效率。
但hash也有很多弊端,导致用到的场景并不多。
1、首先,hash无法完成范围查询和排序,而b+树可以。
2、hash索引也不支持联合索引的最左匹配原则。
3、当有很多重复键值的情况下,容易发生hash冲突的问题。
十、Redis的持久化方案
Redis提供了RDB持久化和AOF持久化
1、RDB模式
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式。
可以通过配置设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置
save 900 1 #900秒内如果超过1个key被修改,则发起快照保存
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000
(1)优势
- 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这样非常方便进行备份。比如你可能打算没1天归档一些数据。
- 方便备份,我们可以很容易的将一个一个RDB文件移动到其他的存储介质上
- RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
- RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。
(2)劣势
- 还是有可能存在数据丢失的情况,两次存储之间有真空期。
-
每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。
2、AOF模式
redis会将每一个收到的写命令都通过write函数追加到文件中
当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要写入磁盘的时间间隔。(默认是:每秒fsync一次)
(1)优势
- AOF默认策略为每秒fsync一次,即时发生停机故障,也最多丢失一秒钟的数据。
-
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
-
AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单
(2)劣势
-
对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
-
速度一般也会慢于RDB策略
-
AOF 在过去曾经发生过这样的 bug : 因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。 (举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。)虽然不常见,但RDB是不会出现这样的问题的。
3、抉择
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果可以承受数分钟以内的数据丢失,那么可以只使用 RDB 持久化。速度也会更快一些。
如果想要数据尽量不要丢失,使用AOF。