导图社区 数据存储
数据存储、首先要明白索引(index)是在存储引擎(storage engine)层面实现的,而不是server层面。不是所有的存储引擎都支持所有的索引类型。即使多个存储引擎支持某一索引类型,它们的实现和行为也可能有所差别。
编辑于2022-11-10 10:10:48时间管理-读书笔记,通过学习和应用这些方法,读者可以更加高效地利用时间,重新掌控时间和工作量,实现更高效的工作和生活。
本书是法兰教授的最新作品之一,主要阐明了设计史的来源、设计史现在的状况以及设计史的未来发展可能等三个基本问题。通过对设计史学科理论与方法的讨论,本书旨在促进读者对什么是设计史以及如何写作一部好的设计史等问题的深入认识与反思。
《计算机组成原理》涵盖了计算机系统的基本组成、数据的表示与运算、存储系统、指令系统、中央处理器(CPU)、输入输出(I/O)系统以及外部设备等关键内容。通过这门课程的学习,学生可以深入了解计算机硬件系统的各个组成部分及其相互之间的连接方式,掌握计算机的基本工作原理。
社区模板帮助中心,点此进入>>
时间管理-读书笔记,通过学习和应用这些方法,读者可以更加高效地利用时间,重新掌控时间和工作量,实现更高效的工作和生活。
本书是法兰教授的最新作品之一,主要阐明了设计史的来源、设计史现在的状况以及设计史的未来发展可能等三个基本问题。通过对设计史学科理论与方法的讨论,本书旨在促进读者对什么是设计史以及如何写作一部好的设计史等问题的深入认识与反思。
《计算机组成原理》涵盖了计算机系统的基本组成、数据的表示与运算、存储系统、指令系统、中央处理器(CPU)、输入输出(I/O)系统以及外部设备等关键内容。通过这门课程的学习,学生可以深入了解计算机硬件系统的各个组成部分及其相互之间的连接方式,掌握计算机的基本工作原理。
数据存储
数据存储
ElasticSearch
架构图
ES集群核心概念
集群(cluster)
一个ES集群由多个节点(node)组成, 每个集群都有一个共同的集群名称最为标识
节点(node)
一个es实例即为一个节点,一台机器可以有多个节点,正常使用下每个实例都应该会部署在不同的机器上。
ES的配置文件中可以通过node.master、 node.data 来设置节点类型
node.master: true/false 表示节点是否具有成为主节点的资格
node.data: true/false 表示节点是否为存储数据
node节点的组合方式
主节点+数据节点: 默认方式,节点既可以作为主节点,又存储数据
数据节点: 节点只存储数据,不参与主节点选举
客户端节点: 不会成为主节点,也不存储数据,主要针对海量请求时进行负载均衡
分片(shard):
如果我们的索引数据量很大,超过硬件存放单个文件的限制,就会影响查询请求的速度,ES引入了分片技术。一个分片本身就是一个完成的搜索引擎,文档存储在分片中,而分片会被分配到集群中的各个节点中,随着集群的扩大和缩小,ES会自动的将分片在节点之间进行迁移,以保证集群能保持一种平衡。分片有以下特点:
ES的一个索引可以包含多个分片(shard);
每一个分片(shard)都是一个最小的工作单元,承载部分数据;
每个shard都是一个lucene实例,有完整的简历索引和处理请求的能力;
增减节点时,shard会自动在nodes中负载均衡;
一个文档只能完整的存放在一个shard上
一个索引中含有shard的数量,默认值为5,在索引创建后这个值是不能被更改的。
优点:水平分割和扩展我们存放的内容索引;分发和并行跨碎片操作提高性能/吞吐量;
每一个shard关联的副本分片(replica shard)的数量,默认值为1,这个设置在任何时候都可以修改。
副本:replica
副本(replica shard)就是shard的冗余备份,它的主要作用:
冗余备份,防止数据丢失;
shard异常时负责容错和负载均衡;
索引文档的过程
文档写入包含:单文档写入和批量 bulk 写入,这里只解释一下:单文档写入流程
第一步:客户写集群某节点写入数据,发送请求。( 如果没有指定路由/协调节点, 请求的节点扮演路由节点的角色。)
第二步: 节点 1 接受到请求后, 使用文档_id 来确定文档属于分片 0。请求会被转到另外的节点, 假定节点 3。因此分片 0 的主分片分配到节点 3 上。
第三步: 节点 3 在主分片上执行写操作, 如果成功, 则将请求并行转发到节点 1 和节点 2 的副本分片上, 等待结果返回。所有的副本分片都报告成功, 节点 3 将向协调节点( 节点 1) 报告成功, 节点 1 向请求客户端报告写入成功。
第二步中的文档获取分片的过程?
借助路由算法获取,路由算法就是根据路由和文档 id 计算目标的分片 id 的过程。
1shard = hash(_routing) % (num_of_primary_shards)
lucense
tu
ES依赖一个重要的组件Lucene,关于数据结构的优化通常来说是对Lucene的优化,它是集群的一个存储于检索工作单元
在Lucene中,分为索引(录入)与检索(查询)两部分,索引部分包含分词器、过滤器、字符映射器等,检索部分包含查询解析器等。
一个Lucene索引包含多个segments,一个segment包含多个文档,每个文档包含多个字段,每个字段经过分词后形成一个或多个term。
NOSQL
redis (厨房)
优势和场景【洗碗机】
全景
问题画像
优点
对于 redis 和 memcached 我总结了下面四点。现在公司一般都是用 redis 来实现缓存,而且 redis 自身也越来越强大了!
1. redis支持更丰富的数据类型(支持更复杂的应用场景):
Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。
2. Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中。
3. 集群模式:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 redis 目前是原生支持 cluster 模式的.
5. Redis使用单线程的多路 IO 复用模型。
高效率的原因
1)纯内存操作(主要)
2)核心是基于非阻塞的IO多路复用机制(主要)
3)单线程反而避免了多线程的频繁上下文切换问题
6.
使用场景
1、会话缓存( Session Cache)
2、全页缓存( FPC)
除基本的会话 token 之外, Redis 还提供很简便的 FPC 平台。回到一致性问题, 即使重启了 Redis 实例, 因为有磁盘的持久化, 用户也不会看到页面加载速度的下降,这是一个极大改进,类似 PHP 本地 FPC。 再次以 Magento 为例,Magento 提供一个插件来使用 Redis 作为全页缓存后端。 此外, 对 WordPress 的用户来说, Pantheon 有一个非常好的插件 wp-redis, 这个插件能帮助你以最快速度加载你曾浏览过的页面。
3、队列
Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作, 这使得 Redis 能作为一个很好的消息队列平台来使用。Redis 作为队列使用的操作,就类似于本地程序语言( 如 Python)对 list 的 push/pop 操作。 如果你快速的在 Google 中搜索“ Redis queues”, 你马上就能找到大量的开源项目, 这些项目的目的就是利用 Redis 创建非常好的后端工具, 以满足各种队列需求
4, 排行榜/计数器
Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合( Set) 和有序集合( Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。所以, 我们要从排序集合中获取到排名最靠前的 10 个用户– 我们称之为“ user_scores”, 我们只需要像下面一样执行即可: 当然, 这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数, 你需要这样执行: ZRANGE user_scores 0 10 WITHSCORES Agora Games 就是一个很好的例子, 用 Ruby 实现的, 它的排行榜就是使用 Redis 来存储数据的, 你可以在这里看到。
5、发布/订阅
最后( 但肯定不是最不重要的)是 Redis 的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用, 还可作为基于发布/订阅的脚本触发器, 甚至用 Redis 的发布/订阅功能来建立聊天系统!
性能问题【油烟机】
常见问题
1、Master 最好不要写内存快照,如果 Master 写内存快照,save 命令调度 rdbSave函数, 会阻塞主线程的工作, 当快照比较大时对性能影响是非常大的, 会间断性暂停服务
2、如果数据比较重要, 某个 Slave 开启 AOF 备份数据, 策略设置为每秒同步一
3、为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网
4、尽量避免在压力很大的主库上增加从库
5、主从复制不要用图状结构, 用单向链表结构更为稳定, 即:Master <- Slave1<- Slave2 <- Slave3 … 这样的结构方便解决单点故障问题,实现 Slave 对 Master 的替换。如果 Master 挂了, 可以立刻启用 Slave1 做 Master, 其他不变。
优化
fork耗时导致高并发请求延时
fork耗时跟redis主进程的内存有关系,一般控制redis的内存在10GB以内,slave -> master,全量复制
主从复制风暴问题
如果一下子让多个slave从master去执行全量复制,一份大的rdb同时发送到多个slave,会导致网络带宽被严重占用
如果一个master真的要挂载多个slave,那尽量用树状结构,不要用星型结构
缓存雪崩
原因
解决方案
图
事前:redis高可用,主从+哨兵,redis cluster,避免全盘崩溃
事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL被打死
事后:redis持久化,快速恢复缓存数据
数据倾斜
分类
数据量倾斜:
某个实例上保存了 bigkey
bigkey 的操作一般都会造成实例 IO 线程阻塞,如果 bigkey 的访问量比较大,就会影响到这个实例上的其它请求被处理的速度。
对策:
我们在业务层生成数据时,要尽量避免把过多的数据保存在同一个键值对中。
如果 bigkey 正好是集合类型,把 bigkey 拆分成很多个小的集合类型数据,分散保存在不同的实例上。
Slot 分配不均衡
如果集群运维人员没有均衡地分配 Slot,就会有大量的数据被分配到同一个 Slot 中,而同一个 Slot 只会在一个实例上分布,这就会导致,大量数据被集中到一个实例上,造成数据倾斜。
我们可以通过运维规范,在分配之前,我们就要避免把过多的 Slot 分配到同一个实例。
如果是已经分配好 Slot 的集群,先查看 Slot 和实例的具体分配关系,判断是否有过多的 Slot 集中到了同一个实例。
如果有的话,就将部分 Slot 迁移到其它实例,从而避免数据倾斜。
Hash Tag
Hash Tag 是指加在键值对 key 中的一对花括号{}。这对括号会把 key 的一部分括起来,客户端在计算 key 的 CRC16 值时,只对 Hash Tag 花括号中的 key 内容进行计算。如果没用 Hash Tag 的话,客户端计算整个 key 的 CRC16 的值。
举个例子,假设 key 是 user:profile:3231,我们把其中的 3231 作为 Hash Tag,此时,key 就变成了 user:profile:{3231}。当客户端计算这个 key 的 CRC16 值时,就只会计算 3231 的 CRC16 值。否则,客户端会计算整个“user:profile:3231”的 CRC16 值。
Hash Tag 的好处是,如果不同 key 的 Hash Tag 内容都是一样的,那么,这些 key 对应的数据会被映射到同一个 Slot 中,同时会被分配到同一个实例上。
Hash Tag 应用场景
用在 Redis Cluster 和 Codis 中,支持事务操作和范围查询。因为 Redis Cluster 和 Codis 本身并不支持跨实例的事务操作和范围查询,当业务应用有这些需求时,就只能先把这些数据读取到业务层进行事务处理,或者是逐个查询每个实例,得到范围查询的结果。
这样操作起来非常麻烦,所以,使用 Hash Tag 把要执行事务操作或是范围查询的数据映射到同一个实例上,这样就能很轻松地实现事务或范围查询了
该怎么应对这种问题呢?我们就需要在范围查询、事务执行的需求和数据倾斜带来的访问压力之间,进行取舍了。
数据访问倾斜:
虽然每个集群实例上的数据量相差不大,但是某个实例上的数据是热点数据,被访问得非常频繁。
淘汰策略【垃圾桶】
1、定时删除:在设置键的过期时间的同时,创建一个定时器 timer. 让定时器在键的过期时间来临时, 立即执行对键的删除操作。
2、惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是 否过期, 如果过期的话, 就删除该键;如果没有过期, 就返回该键。
3、定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至 于要删除多少过期键, 以及要检查多少个数据库, 则由算法决定。
Redis 的回收策略(淘汰策略)
volatile-lru:从已设置过期时间的数据集( server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl: 从已设置过期时间的数据集( server.db[i].expires) 中挑选将要过期的数据淘汰
volatile-random: 从已设置过期时间的数据集( server.db[i].expires) 中任意选择数据淘汰
allkeys-lru: 从数据集( server.db[i].dict) 中挑选最近最少使用的数据淘汰
allkeys-random: 从数据集( server.db[i].dict) 中任意选择数据淘汰
no-enviction( 驱逐) : 禁止驱逐数据
注意这里的 6 种机制,volatile 和 allkeys 规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据, 后面的 lru、ttl 以及 random 是三种不同的淘汰策略, 再加上一种 no-enviction 永不回收的策略。
高可用【洗衣机】
哨兵和集群
Redis Sentinal 着眼于高可用, 在 master 宕机时会自动将 slave 提升为master, 继续提供服务。
Redis Cluster 着眼于扩展性, 在单个 redis 内存不足时, 使用 Cluster 进行分片存储。
Redis 可以使用主从同步,从从同步。第一次同步时,主节点做一次 bgsave, 并同时将后续修改操作记录到内存 buffer, 待完成后将 rdb 文件全量同步到复制节点, 复制节点接受完成后将 rdb 镜像加载到内存。加载完成后, 再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
图
集群
架构图
一个Redis集群通常由多个节点组成,最初每个节点都是独立的,它们都处于只包含自己的集群之中,当通过CLUSTER MEET <ip> <port>命令将各个独立的节点连接起来之后,它们就组成了一个集群。
一个节点其实就是一个运行在集群模式下的Redis服务器。其所提供的功能与普通的Redis服务器一致的。
作用
读拓展
一个 master 用于写,多个 slave 用于分摊读的压力。
图
高可用
Redis Cluster
原因
1.主从复制不能实现高可用
2.业务需要更高的QPS,而主从复制中单机的QPS可能无法满足业务需求
3.数据量的考虑,现有服务器内存不能满足业务数据的需要时,单纯向服务器添加内存不能达到要求,此时需要考虑分布式需求,把数据分布到不同服务器上
4.网络流量需求:业务的流量已经超过服务器的网卡的上限值,可以考虑使用分布式来进行分流
5.离线计算,需要中间环节缓冲等别的需求
优缺点
优点:
(1)无需Sentinel哨兵监控,如果Master挂了,Redis Cluster内部自动将Slave切换Master
(2)可以进行水平扩容
(3)支持自动化迁移,当出现某个Slave宕机了,那么就只有Master了,这时候的高可用性就无法很好的保证了,万一master也宕机了,咋办呢? 针对这种情况,如果说其他Master有多余的Slave ,集群自动把多余的Slave迁移到没有Slave的Master 中。
缺点:
(1)批量操作是个坑
(2)资源隔离性较差,容易出现相互影响的情况。
限制Redis Cluster规模的关键因素
实例间的通信开销会随着实例规模增加而增大,在集群超过一定规模时(比如 800 节点),集群吞吐量反而会下降。
Gossip 协议的工作原理可以概括成两点。
一是,每个实例之间会按照一定的频率,从集群中随机挑选一些实例,把 PING 消息发送给挑选出来的实例,用来检测这些实例是否在线,并交换彼此的状态信息。PING 消息中封装了发送消息的实例自身的状态信息、部分其它实例的状态信息,以及 Slot 映射表。
二是,一个实例在接收到 PING 消息后,会给发送 PING 消息的实例,发送一个 PONG 消息。PONG 消息包含的内容和 PING 消息一样。
实例间使用 Gossip 协议进行通信时,通信开销受到通信消息大小和通信频率这两方面的影响,
每个实例在发送一个 Gossip 消息时,除了会传递自身的状态信息,默认还会传递集群十分之一实例的状态信息。
一个 Gossip 消息的大小:nodename 和 ip 这两个字节数组的长度是 40 字节和 46 字节,我们再把结构体中其它信息的大小加起来,就可以得到一个 Gossip 消息的大小了,即 104 字节
PING 消息大小:对于一个包含了 1000 个实例的集群来说,每个实例发送一个 PING 消息时,会包含 100 个实例的状态信息,总的数据量是 10400 字节,再加上发送实例自身的信息,一个 Gossip 消息大约是 10KB。
此外,为了让 Slot 映射表能够在不同实例间传播,PING 消息中还带有一个长度为 16,384 bit 的 Bitmap,这个 Bitmap 的每一位对应了一个 Slot,如果某一位为 1,就表示这个 Slot 属于当前实例。这个 Bitmap 大小换算成字节后,是 2KB。我们把实例状态信息和 Slot 分配信息相加,就可以得到一个 PING 消息的大小了,大约是 12KB。
PONG 消息和 PING 消息的内容一样,所以,它的大小大约是 12KB。每个实例发送了 PING 消息后,还会收到返回的 PONG 消息,两个消息加起来有 24KB。
主从复制
作用
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
原理
重同步(resync)
完整重同步(full resynchronization)
slave 通过 SYNC 或 PSYNC 命令,向 master 发起同步请求。
master 执行 BGSAVE 命令,将当前数据库状态保存为 RDB 文件。
生成 RDB 文件完毕后,master 将该文件发送给 slave。
slave 收到 RDB 文件后,将其加载至内存。
master 将 backlog 中缓冲的命令发送给 slave(一开始在 BGSAVE 时记录了当时的 offset)。
slave 收到后,逐个执行这些命令。
部分重同步(partial resynchronization)
slave 通过 PSYNC <replication-id> <offset> 命令,向 master 发起「部分重同步」请求。
master 返回 CONTINUE 告知 slave 同意执行「部分重同步」,先决条件为:
<replication-id> 是 master 的 replication-id,并且 slave 给的 <offset> 在 master 的「复制积压缓冲区」backlog 里面
master 将 backlog 中缓冲的命令发送给 slave(根据 slave 给的 offset)。
slave 收到后,逐个执行这些命令。
复制积压缓冲区
是 master 维护的一个固定长度(fixed-sized)的先进先出(FIFO)的内存队列。值得注意的是:
队列的大小由配置 repl-backlog-size 决定,默认为 1MB。当队列长度超过 repl-backlog-size 时,最先入队的元素会被弹出,用于腾出空间给新入队的元素。7
队列的生存时间由配置 repl-backlog-ttl 决定,默认为 3600 秒。如果 master 不再有与之相连接的 slave,并且该状态持续时间超过了 repl-backlog-ttl,master 就会释放该队列,等到有需要(下次又有 slave 连接进来)的时候再创建。
命令传播(command propagate)
「命令传播」用于在 master 的数据库状态被修改时,将导致变更的命令传播给 slave,从而让 slave 的数据库状态与 master 保持一致。
原理步骤
建立连接
保存主节点信息
建立socket连接
发送ping命令
身份验证
发送从节点端口信息
同步数据
命令传播
全量复制和部分复制
全量复制:用于初次复制或其他无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作。
(1)从节点判断无法进行部分复制,向主节点发送全量复制的请求;或从节点发送部分复制的请求,但主节点判断无法进行部分复制;具体判断过程需要在讲述了部分复制原理后再介绍。
(2)主节点收到全量复制的命令后,执行bgsave,在后台生成RDB文件,并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令
(3)主节点的bgsave执行完成后,将RDB文件发送给从节点;从节点首先清除自己的旧数据,然后载入接收的RDB文件,将数据库状态更新至主节点执行bgsave时的数据库状态
(4)主节点将前述复制缓冲区中的所有写命令发送给从节点,从节点执行这些写命令,将数据库状态更新至主节点的最新状态
(5)如果从节点开启了AOF,则会触发bgrewriteaof的执行,从而保证AOF文件更新至主节点的最新状态
部分复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效。需要注意的是,如果网络中断时间过长,导致主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制。
心跳机制
PING
每隔指定的时间,主节点会向从节点发送PING命令,这个PING命令的作用,主要是为了让从节点进行超时判断。
REPLCONF ACK
实时监测主从节点网络状态
检测命令丢失
辅助保证从节点的数量和延迟
主要出现的问题
(1)数据同步阶段:在主从节点进行全量复制bgsave时,主节点需要首先fork子进程将当前数据保存到RDB文件中,然后再将RDB文件通过网络传输到从节点。如果RDB文件过大,主节点在fork子进程+保存RDB文件时耗时过多,可能会导致从节点长时间收不到数据而触发超时;此时从节点会重连主节点,然后再次全量复制,再次超时,再次重连……这是个悲伤的循环。为了避免这种情况的发生,除了注意Redis单机数据量不要过大,另一方面就是适当增大repl-timeout值,具体的大小可以根据bgsave耗时来调整。
(2)命令传播阶段:如前所述,在该阶段主节点会向从节点发送PING命令,频率由repl-ping-slave-period控制;该参数应明显小于repl-timeout值(后者至少是前者的几倍)。否则,如果两个参数相等或接近,网络抖动导致个别PING命令丢失,此时恰巧主节点也没有向从节点发送数据,则从节点很容易判断超时。
(3)慢查询导致的阻塞:如果主节点或从节点执行了一些慢查询(如keys *或者对大数据的hgetall等),导致服务器阻塞;阻塞期间无法响应复制连接中对方节点的请求,可能导致复制超时。
哨兵
Sentinel(哨兵)是用于监控redis集群中Master状态的工具,是Redis 的高可用性解决方案,sentinel哨兵模式已经被集成在redis2.4之后的版本中。
sentinel是redis高可用的解决方案,sentinel系统可以监视一个或者多个redis master服务,以及这些master服务的所有从服务;当某个master服务下线时,自动将该master下的某个从服务升级为master服务替代已下线的master服务继续处理请求。
主要功能
(1)集群监控,负责监控redis master和slave进程是否正常工作
(2)消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
(3)故障转移,如果master node挂掉了,会自动转移到slave node上
(4)配置中心,如果故障转移发生了,通知client客户端新的master地址
工作原理
1)每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个PING命令。
2)如果一个实例(instance)距离最后一次有效回复PING命令的时间超过 own-after-milliseconds 选项所指定的值,则这个实例会被Sentinel标记为主观下线。
3)如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。
4)当有足够数量的Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态,则Master会被标记为客观下线。
5)在一般情况下,每个Sentinel 会以每10秒一次的频率向它已知的所有Master,Slave发送 INFO 命令。
6)当Master被Sentinel标记为客观下线时,Sentinel 向下线的 Master 的所有Slave发送 INFO命令的频率会从10秒一次改为每秒一次。
7)若没有足够数量的Sentinel同意Master已经下线,Master的客观下线状态就会被移除。 若 Master重新向Sentinel 的PING命令返回有效回复,Master的主观下线状态就会被移除。
仲裁会
这个区别看起来很微妙,但是很容易理解和使用。例如,集群中有5个sentinel,票数被设置为2,当2个sentinel认为一个master已经不可用了以后,将会触发failover,但是,进行failover的那个sentinel必须先获得至少3个sentinel的授权才可以实行failover。
领头Sentinel
一个redis服务被判断为客观下线时,多个监视该服务的sentinel协商,选举一个领头sentinel,对该redis服务进行故障转移操作。
持久化【冰箱】
Redis 提供两种持久化机制 RDB 和 AOF 机制:
1、RDB (Redis DataBase)持久化方式: 是指用数据集快照的方式半持久化模式) 记录 redis 数据库的所有键值对,在某个时间点将数据写入一个临时文件, 持久化结束后, 用这个临时文件替换上次持久化的文件, 达到数据恢复。
优点:
1、只有一个文件 dump.rdb, 方便持久化。
2、容灾性好, 一个文件可以保存到安全的磁盘。
3、性能最大化, fork 子进程来完成写操作, 让主进程继续处理命令, 所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能)
4.相对于数据集大时, 比 AOF 的启动效率更高。
缺点:
1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障, 会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)
2、AOF (Append-only file)持久化方式: 是指所有的命令行记录以 redis 命令请求协议的格式完全持久化存储)保存为 aof 文件。
优点:
1、数据安全, aof 持久化可以配置 appendfsync 属性, 有 always, 每进行一次命令操作就记录到 aof 文件中一次。
2、通过 append 模式写文件, 即使中途服务器宕机, 可以通过 redis-check-aof 工具解决数据一致性问题。
3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前( 文件过大时会对命令进行合并重写), 可以删除其中的某些命令( 比如误操作的 flushall))
缺点:
1、AOF 文件比 RDB 文件大, 且恢复速度慢。
2、数据集大的时候, 比 rdb 启动效率低。
数据恢复
重启Redis时,rdbLoad函数就会被执行,它将读取RDB文件,并将RDB文件数据载入到内存中。但是我们很少用选择某一时间的RDB快照方式来恢复数据,因为这样会丢失很多数据,较好的选择就是通过AOF日志来恢复数据,但是回放AOF日志文件是相对很耗时的一个操作。我们可以采用手动RDB+AOF的方式恢复:选某一时间点的RDB文件恢复数据后,跟RDB最后生成时间通过运维工具去修改AOF日志文件,选择RDB文件时间之后的AOF指令保存为新的AOF文件回放(为了保证数据的完整性,一般会选择RDB时间前一点点)。到了Redis4.0它给运维同学带来了解放双手的混合持久化模式,它将RDB文件和增量的AOF日志存放在一起,这时候AOF不在是全量日志,而是自这次持久化开始到持久化结束时间发生的增量日志。这样就很大程度的提高了数据恢复的速度也减少了手工运维的烦恼。
结构组成
架构图
文件事件处理器
文件事件处理器是单线程模式运行的,但是通过IO多路复用机制监听多个socket,可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了redis内部的线程模型的简单性。
多个socket,
IO多路复用程序,
文件事件分派器,
事件处理器
命令处理器
命令回复器
连接应答器
数据结构
数据结构
启动操作
图
mongodb
数据结构
架构图
数据库-->collection-->record
MongoDB在数据存储上按命名空间来划分,一个collection是一个命名空间,一个索引也是一个命名空间。
同一个命名空间的数据被分成很多个Extent,Extent之间使用双向链表连接。
在每一个Extent中,保存了具体每一行的数据,这些数据也是通过双向链接连接的。
每一行数据存储空间不仅包括数据占用空间,还可能包含一部分附加空间,这使得在数据update变大后可以不移动位置。
索引以BTree结构实现。
如果你开启了jorunaling日志,那么还会有一些文件存储着你所有的操作记录。
特性
概述
所用语言:C++
特点:保留了SQL一些友好的特性(查询,索引)。
使用许可: AGPL(发起者: Apache)
协议: Custom, binary( BSON)
Master/slave复制(支持自动错误恢复,使用 sets 复制)
内建分片机制
支持 javascript表达式查询
可在服务器端执行任意的 javascript函数
update-in-place支持比CouchDB更好
在数据存储时采用内存到文件映射
对性能的关注超过对功能的要求
建议最好打开日志功能(参数 --journal)
在32位操作系统上,数据库大小限制在约2.5Gb
空数据库大约占 192Mb
采用 GridFS存储大数据或元数据(不是真正的文件系统)
2.MongoDB优点:
1)更高的写负载,MongoDB拥有更高的插入速度。
2)处理很大的规模的单表,当数据表太大的时候可以很容易的分割表。
3)高可用性,设置M-S不仅方便而且很快,MongoDB还可以快速、安全及自动化的实现节点 (数据中心)故障转移。
4)快速的查询,MongoDB支持二维空间索引,比如管道,因此可以快速及精确的从指定位置 获取数据。MongoDB在启动后会将数据库中的数据以文件映射的方式加载到内存中。如果内 存资源相当丰富的话,这将极大地提高数据库的查询速度。
5)非结构化数据的爆发增长,增加列在有些情况下可能锁定整个数据库,或者增加负载从而 导致性能下降,由于MongoDB的弱数据结构模式,添加1个新字段不会对旧表格有任何影响, 整个过程会非常快速。
3.MongoDB缺点:
1)不支持事务。
2)MongoDB占用空间过大 。
3)MongoDB没有成熟的维护工具。
4.MongoDB应用场景
1.)适用于实时的插入、更新与查询的需求,并具备应用程序实时数据存储所需的复制及高度伸缩性;
2) 非常适合文档化格式的存储及查询;
3.)高伸缩性的场景:MongoDB 非常适合由数十或者数百台服务器组成的数据库。
4.)对性能的关注超过对功能的要求。
通信方式
多线程方式,主线程监听新的连接,连接后,启动新的线程做数据的操作(IO切换)。
数据同步
图
上图是 MongoDB 采用 Replica Sets 模式的同步流程
红色箭头表示写操作写到 Primary 上,然后异步同步到多个 Secondary 上
蓝色箭头表示读操作可以从 Primary 或 Secondary 任意一个上读
各个 Primary 与 Secondary 之间一直保持心跳同步检测,用于判断 ReplicaSets 的状态
持久化存储
MMap方式把文件地址映射到内存的地址空间,直接操作内存地址空间就可以操作文件,不用再调用write,read操作,性能比较高。
mongodb调用mmap把磁盘中的数据映射到内存中的,所以必须有一个机制时刻的刷数据到硬盘才能保证可靠性,多久刷一次是与sync delay参数相关的。
journal(进行恢复用)是Mongodb中的redo log,而Oplog则是负责复制的binlog。
如果打开journal,那么即使断电也只会丢失100ms的数据,这对大多数应用来说都可以容忍了。从1.9.2+,mongodb都会默认打开journal功能,以确保数据安全。
而且journal的刷新时间是可以改变的,2-300ms的范围,使用 --journalCommitInterval 命令。
Oplog和数据刷新到磁盘的时间是60s,对于复制来说,不用等到oplog刷新磁盘,在内存中就可以直接复制到Sencondary节点。
可以把movechunk目录里的旧文件删除吗?
没问题,这些文件是在分片(shard)进行均衡操作(balancing)的时候产生的临时文件.一旦这些操作已经完成,相关的临时文件也应该被删除掉.但目前清理工作是需要手动的,所以请小心地考虑再释放这些文件的空间
如果块移动操作(movechunk)失败了,我需要手动清除部分转移的文档吗?
不需要,移动操作是一致(consistent)并且是确定性的(deterministic);一次失败后,移动操作会不断重试;当完成后,数据只会出现在新的分片里(shard).
分片
数据在什么时候才会扩展到多个分片里?
Mongodb分片是基于区域的,所以一个集合中的所有的对象都被存放在一个块中,只有当存在多余一个块的时候,才会有多个分片获取数据的选项,现在每个默认块的大小是64mb,所以至少64mb的空间才可以实施一个迁移。
如果在一个分片(shard)停止或者很慢的时候,我发起一个查询会怎样?
如果一个分片停止了,除非查询设置了“partial”选项,否则查询会返回一个错误,如果一个分片响应很慢,Mongodb则会等待他的响应
HA集群
用的比较多的是Replica Sets,采用选举算法,自动进行leader选举,在保证可用性的同时,可以做到强一致性要求。
当然对于大量的数据,mongodb也提供了数据的切分架构Sharding。
HBASE
HBase 特点
1) 存储容量大,一个表可以容纳上亿行,上百万列;
2.)可通过版本进行检索,能搜到所需的历史版本数据;
3.)负载高时,可通过简单的添加机器来实现水平切分扩展,跟Hadoop的无缝集成保障了其数据可靠性(HDFS)和海量数据分析的高性能(MapReduce);
4.)在第3点的基础上可有效避免单点故障的发生。
4.HBase 缺点
1. 基于Java语言实现及Hadoop架构意味着其API更适用于Java项目;
2. node开发环境下所需依赖项较多、配置麻烦(或不知如何配置,如持久化配置),缺乏文档;
3. 占用内存很大,且鉴于建立在为批量分析而优化的HDFS上,导致读取性能不高;
4. API相比其它 NoSql 的相对笨拙。
5.HBase 适用场景
bigtable类型的数据存储;
对数据有版本查询需求;
应对超大数据量要求扩展简单的需求。
mysql
MySQL架构
MySQL与众不同主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。
tu
连接层
主要完成一些类似连接处理、授权认证及相关安全方案;
引入线程池的概念,为通过认证安全连接的客户端提供线程;
可以实现基于SSL的安全连接,服务器也会为安全接入的客户端验证它所具有的操作权限。
服务层
主要完成大部分的核心服务功能, 包括查询解析、分析、优化、缓存、以及所有的内置函数;
所有跨存储引擎的功能也都在这一层实现,包括触发器、存储过程、视图等;
引擎层
存储引擎真正的负责了MySQL中数据的存储和提取;
服务器通过API与存储引擎进行通信;
不同的存储引擎具有的功能不同,我们可以根据实际需要进行选取;
存储层
主要是将数据存储在运行于该设备的文件系统之上,并完成与存储引擎的交互。
面试题
MySQL查询的具体流程?或一条SQL语句在MySQL中如何执行的?
流程图
流程
客户端请求
连接器(验证用户身份,给予权限)
查询缓存(存在缓存则直接返回,不存在则执行后续操作)
分析器(对SQL进行词法分析和语法分析操作)
优化器(主要对执行的sql优化选择最优的执行方案方法)
执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)
去引擎层获取数据返回(如果开启查询缓存则会缓存查询结果)
存储引擎
存储引擎是MySQL的组件,用于处理不同表类型的SQL操作。一个数据库中多个表可以使用不同引擎以满足各种性能和实际需求。
查看存储引擎
查看支持的存储引擎
查看默认存储引擎
准确查看某个数据库中的某一表所使用的存储引擎
设置存储引擎
建表时指定存储引擎(默认的就是INNODB,不需要设置)
修改存储引擎
修改默认存储引擎,也可以在配置文件my.cnf中修改默认引擎
存储引擎对比
InnoDB支持事务;MyISAM不支持事务;
InnoDB支持外键;MyISAM不支持;对一个包含外键的InnoDB表转MyISAM会失败;
InnoDB是聚簇索引;MyISAM是非聚簇索引;
InnoDB不保存表的具体行数,执行 select count(*) from table 需要全表扫描;MyISAM用一个变量保存了整个表的行数,执行上述语句读出该变量即可,速度非常快;
InnoDB最小的锁粒度是行锁,适合高并发;MyISAM最小的锁粒度是表锁,不适合高并发;一个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限。
InnoDB不仅缓存索引还缓存真实数据,对内存要求较高,内存大小对性能有决定性的影响;MyISAM只缓存索引,不缓存真实数据;
InnoDB表占用空间大;MyISAM表占用空间小;
InnoDB关注点在事务;MyISAM关注点在性能;
面试题
一张表,里面有ID自增主键,当insert了17条记录之后,删除了第15,16,17条记录,再把Mysql重启,再insert一条记录,这条记录的ID是18还是15 ?
如果表的引擎是MyISAM,那么是18。因为MyISAM表会把自增主键的最大ID 记录到数据文件中,重启MySQL自增主键的最大ID也不会丢失;
如果表的引擎是InnoDB,那么是15。因为InnoDB 表只是把自增主键的最大ID记录到内存中,所以重启数据库或对表进行OPTION操作,都会导致最大ID丢失。
哪个存储引擎执行 select count(*) 更快,为什么?
MyISAM更快,因为MyISAM内部维护了一个计数器,把表的总行数存储在磁盘上,可以直接调取;
InnoDB没有将表的总行数存储在磁盘上,需要全表扫描累加行数,所以数据表越大越耗时;InnoDB这样做的苦衷与其支持事务有关,由于多版本并发控制(MVCC)的原因,InnoDB表“应该返回多少行”是不确定的。
数据类型
整数类型
浮点数类型
字符串类型
日期类型
其他数据类型
面试题
char 和 varchar 的区别?
char 是固定长度;varchar 长度可变;
相同点
char(n),varchar(n)中的n都代表字符的个数;
超过char,varchar最大长度n的限制后,字符串会被截断;
不同点
char不论实际存储的字符数都会占用n个字符的空间,而varchar只会占用实际字符应该占用的字节空间加1(实际长度length,0<=length<255)或加2(length>255)。
能存储的最大空间限制不一样:char的存储上限为255字节。
char在存储时会截断尾部的空格,而varchar不会。
BLOB和TEXT有什么区别?
BLOB是一个二进制对象,可以容纳可变数量的数据。有四种类型的BLOB:TINYBLOB、BLOB、MEDIUMBLO和 LONGBLOB
TEXT是一个不区分大小写的BLOB。四种TEXT类型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。
BLOB 保存二进制数据,TEXT 保存字符数据。
索引
索引介绍
MYSQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构,所以说索引的本质是:数据结构。
索引的目的在于提高查询效率,可以类比字典、 火车站的车次表、图书的目录等 。
简单理解为“排好序的快速查找的数据结构”。数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
索引本身也很大,不可能全部存储在内存中,一般以索引文件的形式存储在磁盘上
平常说的索引,没有特别指明的话,就是B+树(多路搜索树,不一定是二叉树)结构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。此外还有哈希索引等。
图例说明
下图是一种可能的索引方式示例
左边的数据表,一共有两列七条记录,最左边的是数据记录的物理地址。
为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值,和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到对应的数据,从而快速检索出符合条件的记录。
基本语法
创建
创建索引
修改表结构(添加索引)
删除
查看
常用的 alter 命令
ALTER TABLE tableName ADD PRIMARY KEY (column_list): 添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。
ALTER TABLE tableName ADD UNIQUE indexName (column_list) 创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。
ALTER TABLE tableName ADD INDEX indexName (column_list) 添加普通索引,索引值可出现多次。
ALTER TABLE tableName ADD FULLTEXT indexName (column_list)指定了索引为 FULLTEXT ,用于全文索引。
索引分类
数据结构角度
B+树索引
Hash索引
Full-Text全文索引
R-Tree索引
物理存储角度
聚簇索引(clustered index)
非聚簇索引(non-clustered index),也叫 辅助索引(secondary index)
以上两个索引都是B+树结构
逻辑角度
主键索引:主键索引是一种特殊的唯一索引,不允许有空值;
普通索引/单列索引:每个索引只包含单个列,一个表可以有多个单列索引;
多列索引(复合索引、联合索引):复合索引指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用复合索引时遵循最左前缀集合;
唯一索引:限制值必须唯一,可以有一个NULL值;
空间索引:空间索引是对空间数据类型的字段建立的索引;
索引结构
首先要明白索引(index)是在存储引擎(storage engine)层面实现的,而不是server层面。不是所有的存储引擎都支持所有的索引类型。即使多个存储引擎支持某一索引类型,它们的实现和行为也可能有所差别。
B+Tree索引
认识 B-Tree (B树)
3阶B树示例
每个节点最多有m个孩子;
除根节点和叶子节点外,其他每个节点至少有Ceil(m/2)个孩子;
若根节点不是子节点,则至少有2个孩子;
所有叶子节点都在同一层,且不包含其他关键字信息;
每个非终端节点包含n个关键字信息,m/2 <= n <= m,n=孩子个数-1;
每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划。
看图好理解
认识B+Tree(B+树)
B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。
4阶B+树示例
有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
除根节点和子节点外,每个节点至少有 (m+1)/2个孩子。
所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
B+Tree 与 B-Tree 的几点不同
非叶子节点只存储键值信息;
所有叶子节点之间都有一个链指针;
数据记录都存放在叶子节点中;
B+Tree的优势(相对B-Tree)
单一节点存储更多的元素,使得查询的IO次数更少。
所有查询都要查找到叶子节点,查询性能稳定。
所有叶子节点形成有序链表,便于范围查询。
MyISAM 和 InnoDB 引擎索引都采用B+索引
MyISAM 主键索引与辅助索引
MyISAM索引图示(索引和数据存放位置分离)
MyISAM引擎的索引文件和数据文件是分离的。这样的索引称为"非聚簇索引"。
MyISAM引擎索引结构的叶子节点的数据域,存放的并不是实际的数据记录,而是数据记录的地址。
MyISAM的主索引与辅助索引区别并不大,只是主键索引不能有重复的关键字。
InnoDB主键索引与辅助索引
InnoDB主键索引图示
InnoDB辅助(非主键)索引图示
辅助索引检索过程(以上图name字段为例)
先在辅助索引上检索name,到达其叶子节点获取对应的主键;
②再使用主键在主索引上再进行对应的检索操作。
InnoDB的数据文件就是主键索引文件(索引和数据存放在一起),这种索引被称为“聚簇索引”,一个表只能有一个聚簇索引。
InnoDB引擎索引结构的叶子节点的数据域,存放的就是实际的数据记录(对于主索引,此处会存放表中所有的数据记录;对于辅助索引此处会引用主键,检索的时候通过主键到主键索引中找到对应数据行)。
Hash索引
主要就是通过Hash算法,将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储。
检索算法:在检索查询时,就再次对待查关键字再次执行相同的Hash算法,得到Hash值,到对应Hash表对应位置取出数据即可,如果发生Hash碰撞,则需要在取值时进行筛选。
MySQL目前有Memory引擎和NDB引擎支持Hash索引。
Full-Text全文索引
全文索引是MyISAM的一种特殊索引类型,主要用于全文索引;InnoDB从MYSQL5.6版本提供对全文索引的支持。
它用于替代效率较低的LIKE模糊匹配操作,而且可以通过多字段组合的全文索引一次性全模糊匹配多个字段。
同样使用B-Tree存放索引数据,但使用的是特定的算法,将字段数据分割后再进行索引(一般每4个字节一次分割),索引文件存储的是分割前的索引字符串集合,与分割后的索引信息,对应Btree结构的节点存储的是分割后的词信息以及它在分割前的索引字符串集合中的位置。
R-Tree空间索引
空间索引是MyISAM的一种特殊索引类型,主要用于地理空间数据类型。
MySQL高效索引
覆盖索引(Covering Index)
select 的数据列只用从索引中就能够获取到,不必根据索引再次读取数据文件。查询列要被所建的索引覆盖。
判断标准
使用explain,可以通过输出的extra列来判断,对于一个索引覆盖查询,显示为 using index。
面试题
为什么要用索引?
通过唯一性索引可确保数据的唯一性。
加快数据的检索速度(大大减少检索的数据量),这是建索引最主要的原因。
加快表之间的连接。
在使用分组和排序字句进行数据检索的时候,可以减少分组和排序时间。
可以在查询过程中,使用优化隐藏器,提供系统的性能。
创建索引有哪些缺点?
创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加;
索引需要占物理空间;
当对表的数据进行增删改的时候,索引也要动态的维护,这样降低了数据的维护速度;
数据库索引的原理,为什么要用 B+树,为什么不用二叉树?
当从算法逻辑上讲,二叉树的查找速度和比较次数是最小的;
但是由于数据库索引是存储在磁盘上的,所以必须考虑磁盘IO的问题,磁盘IO是比较耗时的操作;
当数据量比较大的时候,索引的大小可能有几个G,是不可能全部加载到内存中的;
做法是逐一加载每一个磁盘页,这里的磁盘页对应着索引树的节点;
索引树的高度(层级)就是需要的磁盘IO次数;
在相同数据量的情况下,B+树的高度是小于二叉树的,数据量越大差距越明显。
聚簇索引和非聚簇索引的区别?
聚簇索引(InnoDB特有,Myisam没有)
聚簇索引具有唯一性:将数据跟索引结构放到一块,因此一个表仅有一个聚簇索引。
误区:把主键自动设置为聚簇索引
聚簇索引默认是主键,如果表中没有定义主键,InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键来昨晚聚簇索引。如果已经设置了主键为聚簇索引,必须先删除主键,然后添加想要的聚簇索引,最后恢复设置主键即可。
优点
聚簇索引的查询速度非常的快,因为整个B+树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。
缺点
依赖于有序的数据,不是有序的数据插入或查找的速度比较慢;
更新代价大;
非聚簇索引
优点
更新代价比聚簇索引小;
缺点
聚簇索引与非聚簇索引 检索过程对比
案例数据表
检索过程图
聚簇索引
InnoDB使用的是聚簇索引,将主键组织到一颗B+树中,而行数据存储在叶子节点上,若使用“where id=7” 这样的条件查找主键,则按B+树的检索算法即可查找到对应的节点,直接获得行数据。
若对Name列进行条件搜索,则需要两个步骤:1、在辅助索引B+树中检索Name,到达其他叶子节点获取对应的主键。2、使用主键在主索引B+树再执行一次B+树检索操作,最终到达叶子节点即可获得整行数据。
非聚簇索引
MyISAM使用的是非聚簇索引,非聚簇索引的两颗B+树看上去没什么不同,节点的结构完全一致,只是存储的内容不同;
主键索引B+树的节点存储了主键,辅助索引B+树存储了辅助键;
表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别;
由于索引树是独立的,通过辅助键检索无需访问主键的索引树。
使用场景
为什么推荐使用整型自增主键而不是选择UUID?
UUID是字符串,比整型消耗更多的存储空间;
在B+树中进行查找时需要跟经过节点值比较大小,整型数据的比较运算比字符串更快速;
自增的整型索引在磁盘中会连续存储,在读取一页数据时也是连续;UUID是随机产生的,读取的上下两行数据存储是分散的,不适合执行按范围条件(如:where id>5 and id<10)查询语句。
在插入或删除数据时,整型自增主键会在叶子结点的末尾建立新的叶子节点,不会破坏左侧子树的结构;UUID主键很容易出现这样的情况:B+树为了维持自身的特性,有可能会进行结构的重构,消耗更多的时间。
为什么InnoDB非主键索引结构叶子节点存储的是主键值?
保证数据一致性和节省存储空间。可以这么理解:商城系统订单表会存储一个用户ID作为关联外键,而不推荐存储完整的用户信息,因为当我们用户表中的信息(真实名称、手机号、收货地址···)修改后,不需要再次维护订单表的用户数据,同时也节省了存储空间。
为什么Mysql索引要用B+树不是B树?
用B+树不用B树考虑的是IO对性能的影响,B树的每个节点都存储数据,而B+树只有叶子节点才存储数据,所以查找相同数据量的情况下,B树的高度更高,IO更频繁。
数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘页(对应索引树的节点)。其中在MySQL底层对B+树进行进一步优化:在叶子节点中是双向链表,且在链表的头结点和尾节点也是循环指向的。
为何不采用Hash方式?
因为Hash索引底层是哈希表,哈希表是一种以key-value存储数据的结构,多个数据在存储关系上是完全没有任何顺序关系的;
所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。
哈希索引只适用于等值查询的场景。
而B+ Tree是一种多路平衡查询树,他的节点是天然有序的(左子节点小于父节点、父节点小于右子节点),所以对于范围查询的时候不需要做全表扫描。
哈希索引不支持多列联合索引的最左匹配规则,如果有大量重复键值得情况下,哈希索引的效率会很低,因为存在哈希碰撞问题。
哪些情况需要创建索引?
主键自动建立唯一索引;
频繁昨晚查询条件的字段;
查询中与其他表关联的字段,外键关系建立索引;
单键/组合索引的选择问题,高并发下倾向创建组合索引;
查询中排序的字段,排序字段通过索引访问大幅度提高排序速度;
查询中统计或分组字段;
哪些情况不需要创建索引?
表记录太少;
经常增删改的表;
数据重复且分布均匀的表字段;
频繁更新的字段不适合创建索引(会加重IO负担);
where 条件里用不到的字段不创建索引;
查询
SQL执行顺序
手写
机读
面试题
count(*) 、count(1) 和 count(列名) 的区别?
执行效果对比
count(*) 包括了所有列,相当于行数,在统计结果的时候,不会忽略列值为NULL;
count(1) 包括了所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL;
count(列名) 只包括列名那一列,在统计结果的时候,会忽略列值为NULL的计数;
执行效率对比
列名为主键,count(列名) 效率最优;
如果表只有一个字段(不为主键),则count(*) 效率最优;
列名不为主键,count(1) 比 count(列名)快;
如果表有多个列并且没有主键,则count(1) 比count(*)快;
MySQL中 in 和 exists 的区别?
exists:对外表用loop逐条查询,每次查询都会查看exists的条件语句,当exists里的条件语句能够返回记录时,条件就为真,返回当前loop到的这条记录;反之,如果exists里的条件语句不能返回记录,则当前loop到的这条记录被丢弃;exists的条件就像一个 bool 条件,当能返回结果集则 为true,不能返回结果集则为false。
in:in查询相当于多个or条件的叠加。
如果查询的两个表大小相当,那么用 in 和 exists 差别不大;
如果两个表一个较小,另一个是大表,则子查询表大的用exists,子查询表小的用 in;
UNION 和 UNION ALL的区别?
两者都是将结果集合并为一个,两个要联合的SQL语句,字段个数必须一样,而且字段类型要“相容”(一致);
UNION 在进行表连接后会去掉重复的数据记录(效率较低),而UNION ALL则不会去掉重复的数据记录;
UNION会按照字段的顺序进行排序,而UNION ALL只是简单的将两个结果合并就返回;
MySQL的两种连接:内连接、外连接?
内连接(inner join)
交叉连接(cross join)
交叉连接会把第一张表的每个值和第二张表的每个值进行匹配,结果如下:
相等连接
两个表有一共同列,且相等
不等连接
两个表有一共同列,且不相等
自然连接
自然连接只有在两张表中有相同的列(列的名称都相同)时才会有用,自然连接就是自动识别相同列的相等连接;
外连接(out join)
左连接(左外连接)
右连接(右外连接)
全外连接
语法如下
事务
事务的基本要素(ACID)
原子性(Atomicity)
一致性(Consistency)
隔离性(Isolation)
持久性(Durability)
并发事务代来的问题
更新丢失(Lost Update)
脏读(Dirty Reads)
不可重复读(Non-Repeatable Reads)
幻读(Phantom Reads)
事务隔离级别 读未 读已 可重 可串
Read-Uncommitted(读未提交)
Read-Committed(读已提交)
Repeatable-read(可重复读)
Serializable(可串行化)
查看当前数据库隔离级别
show variables like 'tx_isolation'; 或者 select @@tx_isolation;
show variables like 'transaction_isolation'; 或者 select @@transaction_isolation;
MVCC(多版本并发控制)
MVCC是行级锁的一个变种,但它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只是锁定必要的行。MVCC的实现是通过保存数据在某个时间的快照来实现的。不管需要执行多长时间,每个事务看到的数据都是一致的。
MVCC只在Committed Read 和 Repeatable Read 两种隔离级别下工作。
MVCC实现原理
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现。一列保存了行的创建时间,一列保存行的过期时间(或删除时间)。存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来查询到每行的版本号进行比较。
Repeatable Read 隔离级别下,MVCC工作方式
select
InnoDB只查找创建版本号小于或等于当前事务版本号的数据行,这样可以确保事务读取的行,要么是在开始事务之前已经存在,要么是事务自身插入或修改过的。
过期版本号要么未定义,要么大于当前事务版本号,这样可以确保事务读取到的行在事务开始之前未被删除。
insert
delete
update
优缺点
解决不可重复读和幻读的问题;大多数操作都不用加锁,使数据操作简单,性能好。
缺点:每行记录都需要额外的存储空间,需要做更多的行检查工作和一些额外的维护工作。
事务日志
InnoDB使用日志来减少提交事务时的开销。因为日志中已经记录了事务,就无需在每个事物提交时把缓冲池的脏块刷新到磁盘中。
事务修改的数据和索引通常会映射到表空间的随机位置,所以刷新这些变更到磁盘需要很多随机IO。随机IO比顺序IO昂贵得多,因为一个IO请求需要时间把磁头移到正确的位置,然后等待磁盘上读出需要的部分,再转到开始位置。
InnoDB用日志把随机IO变成顺序IO。一旦日志安全写到磁盘,事务就持久化了,即使断电了,InnoDB重启后可以通过redo log恢复以及提交的事务。
InnoDB使用一个后台线程智能地刷新这些变更到数据文件。这个线程可以批量组合写入,使得数据写入更顺序,以提高效率。
事务的实现
事务的实现是基于数据库的存储引擎。不同的存储引擎对事务的支持程度不一样。MySQL 中支持事务的存储引擎有 InnoDB 和 NDB。
事务的实现就是实现ACID特性。事务隔离性是通过锁实现,而事务的原子性、一致性和持久性则通过事务日志实现。
事务日志包括:redo log(重做日志)和undo log(回滚日志)
redo log(重做日志)实现持久化和原子性
在InnoDB存储引擎中,事务日志通过redo log 和日志缓存(InnoDB Log Buffer)实现。
事务开启时,事务中的操作,都会先写入存储引擎的日志缓存中,在事务提交之前,这些缓存的日志都需要提前刷新到磁盘上持久化,这就是“日志先行”(Write-Ahead logging).
当事务提交之后,在Buffer Pool中影射的数据文件才会慢慢刷新到磁盘。此时如果宕机,那么当系统重启进行恢复时,可以根据redo log中记录的日志,把数据库恢复到奔溃前的一个状态。未完成的事务,可以继续提交或者选择回滚,这基于恢复的策略而定。
在系统启动的时候,就已经为redo log分配了一块连续的存储空间,以顺序追加的方式记录redo log,通过顺序IO改善性能。所有事务共享redo log的存储空间,它们的redo log按语句的执行顺序,一次交替的记录在一起。
redo log 记录示例
undo log(回滚日志) 实现一致性
undo log主要为事务的回滚服务。
undo log记录了数据在某个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。
undo log记录的是已部分完成并写入磁盘的未完成(未提交)的事务。
单个事务的回滚,不会影响到其他事务做的操作。
redo log 与 undo log 的区别
两种日志都是为了恢复操作。
redo log是恢复提交事务修改的页操作。而undo log是回滚行记录到特定版本。
两者记录的内容也不同,redo log是物理日志,记录页的物理修改操作。而undo log是逻辑日志,根据每行记录继续记录。
redo + undo log简化过程
假设操作示例
在1-8的任意一步系统宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响。如果在8-9之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时redo log已经持久化。若在9之后系统宕机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据redo log把数据刷回磁盘。
面试题
事务的隔离级别有哪些?MySQL默认隔离级别是什么?
MySQL事务的四大特性及实现原理?
MVCC是什么,它的底层原理是?
什么是幻读、脏读、不可重复读?
不可重复读与幻读的区别?
不可重复读的重点是修改
幻读的重点在于新增或删除
并发事务处理带来的问题及解决办法
更新丢失
通常是应该完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此防止更新丢失应该是应用的责任。
脏读、不可重复读、幻读 都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决
一种是加锁
另一种是多版本并发控制
MySQL有多少种日志?
错误日志
查询日志
慢查询日志
二进制日志
中继日志
事务日志
锁机制
锁是计算机协调多个进程或线程并发访问某一资源的机制。
数据库锁机制简单说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则。
锁定分类
对数据操作的类型分类
读锁(共享锁)
写锁(排他锁)
对数据操作的粒度分类
表级锁
行级锁
页面锁
适用:从锁的角度来说,表锁适合已查询为主,只有少量按索引条件更新数据的应用。行锁适合有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用。
MyISAM表锁
表锁两种模式
表共享读锁
表独占写锁
MyISAM表的读操作与写操作之间,以及写操作之间是串行的。
默认情况下,写锁比读锁具有更高的优先级:当一个锁释放时,这个锁会优先给写锁队列中等候的获取锁请求,然后再给读锁队列的获取锁请求。
InnoDB行锁
InnoDB实现了2种行锁类型
共享锁(S)
排他锁(X)
为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(都是表锁)
意向共享锁(IS)
意向排它锁(IX)
索引失效hi导致行锁变表锁。比如:varchar 查询不写单引号的情况。
加锁机制
乐观锁与悲观锁
乐观锁
悲观锁
InnoDB的三种行锁
记录锁(Record Locks)
SELECT * FROM table WHERE id = 1 FOR UPDATE;
UPDATE SET age = 50 WHERE id = 1; -- id 列为主键或唯一索引列
间隙锁(Gap Locks)
间隙锁基于非唯一索引,使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。
SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;
Gap锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
临键锁(Next-key Locks)
可以理解为一种特殊的间隙锁,通过临键锁可以解决幻读的问题。每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据的临键锁时,会锁住一段左开右闭区间的数据。InnoDB中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。
死锁
死锁产生
死锁是指两个或多个事务在同一资源上互相占用,并请求锁定占用的资源,从而导致恶性循环。
当事务试图以不同的顺序锁定资源时,就可能产生死锁。多个事务同时锁定同一个资源时也可能回发生死锁。
锁的行为和顺序和存储引擎相关。以同样的顺序执行语句,有些存储引擎会产生死锁,有些不会。
死锁有两个原因:真正数据冲突;存储引擎的实现方式;
检测死锁
数据库系统实现了各种死锁检测和死锁超时的机制,InnoDB存储引擎能检测到死锁的循环依赖并立即返回一个错误。
死锁恢复
死锁发生后,只有部分或完全回滚其中一个事务,才能打破死锁。InnoDB目前处理死锁的方法是:将持有最少行级排它锁的事务进行回滚。
外部锁的死锁检测
发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并回退,另一个事务获得锁,继续完成事务。但在涉及外部锁或涉及表锁的情况下,InnoDB并不能完全自动检测到死锁,这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决。
死锁影响性能
死锁会影响性能而不是产生严重错误,因为InnoDB会自动检测并处理死锁。在高并发系统上,当许多线程等待同一个锁时,死锁检测可能导致速度变慢。有时当发生死锁时,禁用死锁检测(innodb_deadlock_detect配置选项)可能会更有效,这时可以依赖 innodb_lock_wait_timeout 设置进行事务回滚。
MyISAM避免死锁
在自动加锁的情况下,MyISAM总是一次获得SQL语句所需要的全部锁,所以MyISAM表不会出现死锁。
InnoDB避免死锁
使用 select ... for update 语句获取必要的锁;
直接申请足够级别的锁;
约定多个表的访问顺序;
改变事务隔离级别
面试题
数据库的乐观锁和悲观锁?
MySQL 中有哪几种锁,列举一下?
MySQL中InnoDB引擎的行锁是怎么实现的?
MySQL 间隙锁有没有了解,死锁有没有了解,写一段会造成死锁的 sql 语句,死锁发生了如何解决,MySQL 有没有提供什么机制去解决死锁?
select for update有什么含义,会锁表还是锁行还是其他?
for update 仅适用于InnoDB,且必须在事务块(begin/commit)中才能生效。
在进行事务操作时,通过 for update 语句,MySQL会对查询结果集中每行数据都添加排他锁(包含 行锁、表锁),其他线程对该记录的更新与删除操作都会阻塞。
只有通过索引条件检索数据,InnoDB才使用行级锁。否则InnoDB将使用表锁。
如何在MySQL分析死锁产生的原因?
如果出现死锁,可以用 show engine innodb status; 命令来确定最后一个死锁产生的原因。返回结果包括:死锁相关事务详细信息,如:引发死锁的SQL语句,事务已经获得的锁,正在等待什么锁,已及被回滚的事务等。
MySQL调优
影响MySQL性能的因素
业务需求(适合度)
存储定位
不适合放进MySQL的数据
二进制多媒体数据
流水队列数据
超大文本数据
需要放进缓存的数据
系统各种配置及规则
活跃用户的基本信息
活跃用户的个性化定制信息
准实时的统计信息
其他一些访问频繁但变更较少的数据
Schema设计对系统的性能影响
尽量减少对数据库访问的请求
尽量减少无用数据的查询请求
硬件环境对系统性能的影响
性能分析
MySQL常见瓶颈
CPU:CPU在饱和的时候,一般发生在数据装入内存或从磁盘读取数据。
IO:磁盘IO瓶颈发生在装入数据远大于内存容量的时候。
服务器硬件性能:top、free、iostat 和 vmstat 查看系统性能状态。
性能下降、SQL慢、执行时间长、等待时间长,原因分析
查询语句写的太烂;
索引失效(单值、复合);
关联查询太多join(设计缺陷或不得已的需求);
服务器调优及各个参数设置(缓冲、线程数等);
常见性能分析手段
慢查询日志
MySQL提供的一种日志记录,用于记录MySQL中响应时间超过阈值的语句,具体指运行时间超过 long_query_time 值的收起来,会被记录到慢查询日志中。
long_query_time 的默认值为10,运行10秒以上的语句被记录。
默认情况下,MySQL数据库没有开启慢查询日志,需要手动设置参数开启。
查看开启状态
开启慢查询日志
临时配置
永久配置
日志分析工具 mysqldumpslow
得到返回记录集最多的10个SQL
得到访问次数最多的10个SQL
得到按照实际排序的前10条里面含有左连接的查询语句
Explain(执行计划)
是什么?
使用Explain 关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或表结构的性能问题。
能干什么?
表的读取顺序
数据读取操作的操作类型
哪些索引可以使用
哪些索引被实际使用
表之间的引用
每张表有多少行被优化器查询
怎么用?
Explain + SQL语句, 得到以下信息
Explain返回的各字段解析
id
id相同,执行顺序从上往下;
id全不同,如果是子查询,id的序号会递增,id值越大优先级越高;
id部分相同,执行顺序是按照数字大的先执行,然后数字相同的安装从上往下的顺序执行;
select_type
SIMPLE:简单的select查询,不含子查询或union;
PRIMARY:查询中若包含复杂的子部分,最外层被标记为PRIMARY;
SUBQUERY:在select或where列表中包含了子查询;
DERIVED:在from列表中包含的资产性被标记为derived,mysql会递归执行这些子查询,把结果放在临时表里;
UNION:若第二个select出现在union之后,则被标记为union,若union包含在from子句的子查询中,外层select将被标记为derived;
UNION RESULT:从union表获取结果的select;
table
type
system:表只有一行记录(等于系统表),是const类型的特例,平时不会出现。
const:表示通过索引一次就找到了,const用于比较primary key或unique索引,因为只要匹配一行数据,所以很快,如将主键置于where列表中,MySQL就能将该查询转换为一个常量。
eq_ref:唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描。
ref:非唯一索引扫描,范围匹配某个单独值得所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行。
range:只检索给定返回的行,使用一个索引来选择行。key列显示使用了哪个索引。一般是在where语句中出现 between、<、>、in等的查询。
index:full index scan,index与ALL的区别为index类型值遍历索引树,通常比ALL快,因为索引文件通常比数据文件小。(index 和 all 都是全表扫描,但index是从索引中读取,all是从磁盘中读取。)
ALL:Full table scan,将遍历全表找到匹配的行。
possible_keys
key
key_len
表示索引中使用的字节数,可通过该列计算计算查询中使用的索引长度。在不损失精确性的情况下,长度越短越好。
key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。
ref
rows
Extra
using filesort: 说明mysql会对数据使用一个外部的索引排序,不是按照表内的索引顺序进行读取。mysql中无法利用索引完成的排序操作称为“文件排序”。常见于order by和group by语句中
Using temporary:使用了临时表保存中间结果,mysql在对查询结果排序时使用临时表。常见于排序order by和分组查询group by。
using index:表示相应的select操作中使用了覆盖索引,避免访问了表的数据行,效率不错,如果同时出现using where,表明索引被用来执行索引键值的查找;否则索引被用来读取数据而非执行查找操作
using where:使用了where过滤
using join buffer:使用了连接缓存
impossible where:where子句的值总是false,不能用来获取任何元祖
select tables optimized away:在没有group by子句的情况下,基于索引优化操作或对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化
distinct:优化distinct操作,在找到第一匹配的元祖后即停止找同样值的动作
示例
示例图
第一行(执行顺序4):id列为1,表示是union里的第一个select,select_type列的primary表示该查询为外层查询,table列被标记为,表示查询结果来自一个衍生表,其中derived3中3代表该查询衍生自第三个select查询,即id为3的select。【select d1.name......】
第二行(执行顺序2):id为3,是整个查询中第三个select的一部分。因查询包含在from中,所以为derived。【select id,name from t1 where other_column=''】
第三行(执行顺序3):select列表中的子查询select_type为subquery,为整个查询中的第二个select。【select id from t3】
第四行(执行顺序1):select_type为union,说明第四个select是union里的第二个select,最先执行【select name,id from t2】
第五行(执行顺序5):代表从union的临时表中读取行的阶段,table列的<union1,4>表示用第一个和第四个select的结果进行union操作。【两个结果union操作】
show profile分析查询
MySQL 提供可以用来分析当前会话中语句执行的资源消耗情况。
默认情况下,参数处于关闭状态,并保存最近15次的运行结果。
性能优化
索引优化
全值(等值)匹配 是最优选择;
最佳左前缀法则,如联合索引(a,b,c),可利用的索引就有(a), (a,b), (a,b,c);
不在索引列上做任何操作(计算、函数、类型转换),会导致索引失效而转向全表扫描;
存储引擎不能使用索引中范围条件右边的列;
尽量使用覆盖索引,减少select;
is null ,is not null 也无法使用索引;
like "xxx%" 是可以用到索引的,like以通配符开头('%abc'或'%abc%')索引失效会变成全表扫描的操作;
字符串不加单引号索引失效;
少用or,用它来连接时会索引失效;
<、<=、>、>=、BETWEEN、IN 可用到索引,<>、not in、!= 则不行,会导致全表扫描;
查询优化
永远小表驱动大表
in 和 exists 的选择
order by 关键字优化
MySQL支持两种方式的排序:Index和FileSort;index 效果高(扫描索引本身完成排序),FileSort效率较低。
order by 子句尽量使用 Index方式排序,避免使用FileSort方式排序;
order by 语句使用索引最左前列;
使用where子句与order by子句条件组合满足索引最左前列;
filesort方式排序有两种算法
双路排序:MySQL4.1之前是使用该方式,两次扫描磁盘,最终得到数据。
单路排序:从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出,效率高于双路排序。
优化策略
增大 sort_buffer_size 参数的设置;
增大 max_length_for_sort_data 参数的设置;
group by 关键字优化
group by 实际是先排序后进行分组,遵照索引建的最佳左前缀;
当无法使用索引列,增大 max_length_for_sort_data和sort_buffer_size参数的设置;
where 高于having,能在where限定的条件就不要去having限定了;
数据类型优化
更小的通常更好
简单就好
尽量避免NULL
面试题
日常工作中你是怎么优化SQL的?
SQL优化的一般步骤是什么,怎么看执行计划(explain),如何理解其中各个字段的含义?
如何写sql能够有效的使用到复合索引?
一条sql执行过长的时间,你如何优化,从哪些方面入手?
什么是最左前缀原则?什么是最左匹配原则?
查询中哪些情况不会使用索引?
分区、分表、分库
MySQL分区
是什么?
一般情况下,创建的表对应一组存储文件,使用MyISAM存储引擎时是一个 .MYI 和 .MYD 文件,使用InnoDB存储引擎时是一个 .ibd 和 .frm(表结构)文件。
当数据量较大时(千万条级别以上),MySQL性能会开始下降,这时就需要将数据分散到多组存储文件,保证单个文件的执行效率。
能做什么?
逻辑数据分割;
提高单一文件的写和读应用速度;
提高分区范围读查询的速度;
分割数据能够有多个不同的物理文件路径;
高效的保存历史数据;
怎么操作?
查看当前数据库是否支持分区
SHOW VARIABLES LIKE '%partition%'; -- 5.6及之前版本
show plugins; -- 5.6版本
分区类型
Range 分区
List 分区
Hash 分区
Key 分区
MySQL分表
垂直拆分
垂直分表,通常是按照业务功能的使用频次,把主要的、热门的字段放在一起作为主表。把不常用的,按照各自的业务属性进行聚集,拆分到不同的次要表中。主表和次要表的关系一般都是一对一的。
水平拆分(数据分片)
单表的容量不超过500W,否则建议水平拆分。是把一个表复制成同样表结构的不同表,然后把数据按照一定的规则划分,分别存储到这些表中,从而保证单表的容量不会太大,提升性能;当然这些结构一样的表,可以放在一个或多个数据库中。
水平分割的几种方法
使用MD5哈希
按时间划分
按热度划分
按ID值划分
MySQL分库
为什么要分库?
数据库集群环境后,都是多台slave,基本满足了读取操作;但是写入或大数据、频繁的写入操作,对master性能影响就比较大,这个时候单库并不能解决大规模并发写入的问题,需要考虑分库。
分库是什么?
一个库里表太多了,导致了海量数据,系统性能下降,把原本存储于一个库的表拆分存储到多个库上,通常是将表按照功能模块、关系密切程度划分出来,部署到不同的库上。
优点
减少增量数据写入时的锁,对查询的影响;
由于单表数量下降,常见的查询操作由于减少了需要扫描的记录,使得单表单词查询所需的检索行数变少,减少磁盘IO,延时变短;
分库分表后的难题?
分布式事务的问题,数据完整性和一致性的问题;
数据操作维度问题:用户、交易、订单各个不同的维度;
跨库联合查询的问题,可能需要两次查询跨节点的count、order by、 group by 以及聚合函数问题。可能需要分别在各个节点上得到结果后,在应用程序端进行合并处理,增加额外的数据管理负担;
面试题
为什么大部分互联网选择自己分库分表,而不选择分区表呢?
分区表,分区键设计不太灵活,如果不走分区键,很容易出现全表锁;
一旦数据并发量上来,如果在分区表实施关联,就是一个灾难;
自己分库分表,自己掌控业务场景和访问模式,可控。分区表不太可控;
随着业务的发展,数据量越来越多,高并发读写操作超过单个数据库服务器的处理能力怎么办?
采用数据分片,数据分片指按照某个维度,将存放在单一数据库中的数据分散地存放至多个数据库或表中。
数据分片的有效手段就是对关系型数据库进行分库和分表。
区别于分区的是,分区一般都是放在单机里,用的比较多的是时间范围分区,方便归档。分库分表需要代码实现,分区则是MySQL内部实现。分库分表和分区并不冲突,可以结合使用。
主从复制
复制的基本原理
slave 会从 master 读取binlog 来进行数据同步;
三个步骤
1. master 将改变记录到二进制日志(binary log)。这些记录过程叫做:二进制日志事件(binary log events);
salve 将 master 的binary log events 拷贝到它的中继器日志(relay log);
slave 重做中继日志中的事件,将改变应用到自己的数据库中。MySQL复制是异步且串行化的。
原理图
复制的基本原则
每个slave 只有一个master;
每个 slave 只能有一个唯一的服务器ID;
每个 master 可以有多个slave;
复制的最大问题
延时。
其他
数据库三大范式
第一范式(确保表中每列保持原子性)
数据库表中的字段都是单一属性的,不可再分;
这个单一属性有基本类型过程,包括:整型、字符型、逻辑型、日期等;
例如 “地址” 这个属性,如果业务上还可以细分,如拆分为省市区等,那就必须细分,否则不符合第一范式;
第二范式(确保表中每列都和主键列相关)
数据库表中不存在非关键字段对任一候选关键字的部分函数依赖,即所有非关键字段都完全依赖于任意一组候选关键字;
部分函数依赖指的是存在组合关键字中某些字段觉得非关键字的情况;
第三范式(确保表中每列都和主键列直接相关,而不是间接相关/传递依赖)
在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式;
传递函数依赖,指的是如果存在“A->B->C”的决定关系,则C传递函数依赖A。
满足第三范式的数据库表不应存在传递依赖关系:关键字段 -> 非关键字段X -> 非关键字段Y;
关系型数据库与非关系型数据库的区别?
关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织;
菲关系型数据库严格上不上一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等;
ClickHouse