数据库相关知识整理
事务及事务的四大特征
1、事务
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。
2、事务四大特征
- A:原子性(Atomicity) 事务操作要么完成,要么不完成,执行过程发生错误会回滚。
- C:一致性(Correspondence) 事务前后数据库完整性、约束性没被破坏。如完整性约束a+b=10,若a变,必须改变b保证a+b=10。
- I:隔离性(Isolation) 隔离状态执行事务,确保每一事务在系统中认为只有该事务在使用系统,事务的串行化,这个主要是关于事务并发的概念。
- D:持久性(Durability) 在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
关系型数据库、非关系型数据库简介、区别
-
关系型数据库:指采用了关系模型(如mysql表)来组织数据的数据库。
存在的问题:
(1)网站的用户并发性非常高,对于传统关系型数据库来说,硬盘I/O是一个很大的瓶颈.
(2)数据库的横向扩展难度较大,当需要对数据库系统进行升级和扩展时,往往需要停机维护和数据迁移。
(3)ACID特性导致设计以及关联查询有一定难度。 -
非关系型数据库:以键值对存储,且结构不固定,分布式,且**一般不保证遵循ACID原则的数据存储系统。**非关系型数据库严格上来说是一种数据结构化存储方法的集合。
-
两类代表:redis与mysql,为什么redis比mysql快?
(1) Redis存储的是k-v格式的数据。绝大部分操作(MGET是O(N))复杂度O(1),而MySQL引擎的底层实现是B+Tree,时间复杂度是O(logn)。
(2) MySql基于磁盘存储,redis基于内存,读写速度差别大。
MySql的索引
-
索引就是为了加快查找而引入的数据结构。在MySql中有两种实现方式:B+树和hash。
-
采用B+(innoDB和MyISAM都用b+)的原因:一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。评价数据结构作为索引的优劣性指标就是磁盘I/O。B+相较于红黑树节点层数少,相较于B树只有叶子节点存有data域,且链表维护最后一层叶子节点,方便查找全部数据。mongoDB用B树。
-
为什么说B+树比B树更适合做操作系统的数据库索引和文件索引?
(1)B+树的磁盘读写的代价更低
B+树内部结点没有指向关键字具体信息的指针,这样内部结点相对B树更小。
(2)B+树的查询更加的稳定
因为非终端结点并不是最终指向文件内容的结点,仅仅是作为叶子结点中关键字的索引。这样所有的关键字的查找都会走一条从根结点到叶子结点的路径。所有的关键字查询长度都是相同的,查询效率相当。 -
哈希索引,是根据索引的键值计算出响应的hash值,然后根据hash表中的地址来定位数据。效率较高,可一次定位,不像B+树索引要从根节点到枝节点。但是(1)hash索引只能用于“=”“IN”“<=>(严格比较两个NULL值是否相等,相等为1,否则为0)”查询,不能使用范围查询。(2)同理,由于哈希索引中存放的是键值经过哈希运算后的hash值,不保留键值大小关系,所以无法用索引避免排序运算。 (3)不同键值索引存在相同的hash值,不可避免对表进行扫描。
聚集索引与非聚集索引
- (主键索引)聚集索引:数据行的物理顺序与列值的逻辑顺序相同。非聚集索引存在二次查询为题,且非聚集索引不唯一。
- 聚集索引插入数据时速度要慢(时间花费在“物理存储的排序”上,也就是首先要找到位置然后插入),查询数据比非聚集数据的速度快。
- B+树中只有叶子节点会带有指向记录的指针,而B树则所有节点都带有。
- B+树索引可以分为聚集索引和非聚集索引。
- mysql使用B+树,其中Myisam是非聚集索引,innoDB是聚集索引
- 聚集索引的叶节点就是数据节点;而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。(二次查询)
InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。
Mysql Innodb中的索引数据结构是 B+ 树,普通索引,也叫做辅助索引,叶子节点存放的是主键值。主键上的索引叫做聚集索引,表里的每一条记录都存放在主键的叶子节点上。
唯一索引本质上是辅助索引,然后加了唯一约束。
————————————————
MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
MySql最左前缀匹配原则
在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。
a | b | rowid |
---|---|---|
1 | 2 | 1 |
1 | 3 | 2 |
… | … | 3 |
2 | 2 | 256 |
创建一个索引
create index index_1 on t(a,b)
当 select * from T where a = 1 and b = 2的时候, 数据库系统可以直接从索引文件中直接二分法找到 A = 1 的记录,然后再 b = 2 的记录。但如果直接where b = 2 则需要遍历这个索引表的全部(索引失效)。
MySql数据库的优化
- 选取合适的字段属性,如邮政编码字段char(6)与char(255)的比较。
- 使用join代替子查询。
- 用UNION代替手动创建的临时表
- 建立索引,一般,索引应建立在那些将用于JOIN,WHERE判断和ORDER BY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。
- NOT IN操作会扫描全表,尽量避免使用
- 应尽量避免在 where 子句中对字段进行 null 值判断、使用>、<、=操作符、or连接条件,否则将导致引擎放弃使用索引而进行全表扫描。
索引失效情况:
- like 以%开头,索引无效;当like前缀没有%,后缀有%时,索引有效。
- or语句前后没有同时使用索引。当or左右查询字段只有一个是索引,该索引失效,只有当or左右查询字段均为索引时,才会生效
- 组合索引,不是使用第一列索引,索引失效。
- 数据类型出现隐式转化。如varchar不加单引号的话可能会自动转换为int型,使索引无效,产生全表扫描。
- 在索引字段上使用not,<>,!=。不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。 优化方法: key<>0 改为 key>0 or key<0。
- 当全表扫描速度比索引速度快时,mysql会使用全表扫描,此时索引失效。
MySql事务的并发和事务隔离级别
https://www.pianshen.com/article/7321786855/ 这篇文章图画的不错
- 脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
- 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
- 幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。不可重复读侧重于修改,幻读侧重于新增或删除
mysql默认隔离级别为可重复读。
-
脏读,事务B修改后,还没正式提交前,事务A就已经读到修改内容了
-
不可重复读,事务B修改多次内容且提交后,事务A在多次读的过程中发现每一次读到的都不一样。
-
幻读
-
Redis持久化
Redis是一个内存数据库,数据保存在内存中**(不同于mysql,mysql是持久化储存,放在磁盘里)**,由于内存的数据变化是很快,容易发生丢失。Redis还为我们提供了持久化的机制,分别是RDB(Redis DataBase)和AOF(Append Only File)
-
RDB:
RDB其实就是把数据以快照的形式保存在磁盘上。RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。
RDB优势:(1)RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
(2)生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
(3)RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
RDB劣势:在快照持久化期间修改数据可能容易导致数据丢失。 -
AOF机制:全量备份总是耗时的,有时候我们提供一种更加高效的方式AOF,工作机制很简单,redis会将每一个收到的写命令都通过write函数追加到文件中。通俗的理解就是日志记录。
-
每当有一个写命令过来时,就直接保存在我们的AOF文件中。
AOF优点:AOF可以更好的保护数据不丢失,即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
缺点:对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
单线程redis
Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程。其中执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会进入一个队列中,然后逐个被执行。并且多个客户端发送的命令的执行顺序是不确定的。但是可以确定的是不会有两条命令被同时执行,不会产生并发问题,这就是Redis的单线程基本模型。
(1) 绝大部分请求是纯粹的内存操作(非常快速)
(2) 采用单线程,避免了不必要的上下文切换和竞争条件
(3) 非阻塞IO - IO多路复用,Redis采用epoll做为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接,读写,关闭都转换为了时间,不在I/O上浪费过多的时间。
…更新中,如有相关建议,欢迎您提出,谢谢。
Redis高并发原理
(1)纯内存访问。数据存放在内存中,内存的响应时间大约是100纳秒,这是Redis每秒万亿级别访问的重要基础。
(2)非阻塞I/O,Redis采用epoll做为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接,读写,关闭都转换为了事件,不在I/O上浪费过多的时间。
(3)单线程避免了线程切换和竞态产生的消耗。
注意 在创建请求(建立连接请求)以及返回结果不一定是单线程。执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会进入一个队列中,然后逐个被执行
https://zhuanlan.zhihu.com/p/91539644
待看 https://www.cnblogs.com/jack1995/p/10914058.html
Redis类型
-
string:做简单的kv缓存
-
hash : hash是一个string类型的field和value的映射表,hash特别适合用于存储对象(应为对象可能会包含很多属性)
-
list : 字符串列表,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。
-
set : 集合,差、并、交集操作,全局去重
-
sorted set : 有序集合,排行榜,用户列表,内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,跳跃表按score从小到大保存所有集合元素。使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。时间复杂度与红黑树相同,增加、删除的操作较为简单。如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员是比较长的字符串(字符串长占内存,原始链表中存储的有可能是很大的对象,而索引结点只需要存储关键值值和几个指针,并不需要存储对象)时, Redis就会使用跳跃表来作为有序集合健的底层实现。
-
功能:作为***缓存***,配合其它数据库作为存储层,利用Redis支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
redis淘汰策略
当Redis的内存(maxmemory参数配置)已满时,它会根据淘汰策略(maxmemory-policy参数配置)进行相应的操作。
- 不删除策略(no-eviction) 达到最大内存限制直接报错
- lru
- lfu
- random 随机删除一部分key
redis删除策略
Redis是 key-value 数据库,可以设置Redis缓存的key的过期时间。Redis的过期删除策略就是指当Redis中的key过期了,Redis是如何进行处理的。
- 定时删除 : 在设置key的过期时间的同时,Redis会创建一个定时器,当key达到过期时间时,立即删除该键。
- 惰性删除: 放任键过期不管,只有当获取键时,才检查获取的键是否过期,若过期删除该键;若没过期,就返回值。
- 定期删除: 每隔一段时间(默认100ms),程序就对数据库进行一次检查,删除过期键。
redis采用的定期删除和惰性删除
Redis线程模型
基于reactor模式,因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。
Redis异常
- 缓存雪崩:同一时间内大量键过期(失效),导致所有请求瞬间都落在了数据库中导致连接异常而崩掉
如何解决缓存雪崩
给缓存数据的过期时间设置随机机,防止同一时间大量数据过期。
给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,若标记失效,则更新缓存数据。
并发量不大时,可以使用加锁排队。
对于"Redis挂掉了,请求全部走数据库"这种情况,我们可以有以下的思路:
事发前:实现Redis的高可用(主从架构+Sentinel或者Redis集群),尽量避免Redis挂掉
事发中:设置本地缓存(ehcache)+限流(hystrix),尽量避免数据挂掉,保证服务能正常工作
事发后:redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据
-
缓存穿透:恶意请求缓存中不存在的数据,导致所有请求都落在数据库,造成短时间承受大量请求而崩掉。
可以考虑通过把可能的数据哈希到bitmap拦截。 -
缓存击穿:并发查询同一条缓存中没有但是数据库有的数据
可通过设置热点数据用不过期来解决。
利用互斥锁:在缓存失效的时,先获取锁,得到锁后再去请求数据库。没有得到锁,则休眠一段时间在重试。