文章目录
一、什么是数据库
对大量信息进行管理的高效解决方案,按照数据结构来组织、存储和管理数据的仓库。说白了就是一些关联表的集合。
二、什么是关系型数据库
众所周知,MYSQL分为关系型数据库和非关系型数据库,关系型数据库是一个结构化的数据库,创建在关系模型(二维表格模型)基础上,一般面向记录,SQL语句就是一种基于关系型数据库的语言,用于执行对关系型数据库中数据的检索和操作。主流的关系型数据库包括orancle、mysql、sqlserver、microsoft access、DB2等
(1)数据以表格的形式出现
(2)每行为各种记录名称
(3)每列为记录名称所对应的数据域
(4)许多的行和列组成一张表单
(5)若干的表单组成database
三、非关系型数据库
NOSQL(NOTSQL=NOTONLTSQL),意思是‘不仅仅是sql’,是非关系型数据库的总称,除了主流的关系型数据库外的数据库,都认为是非关系型。主流的 NoSQL 数据库有Redis、 MongoDB、 Hbase、 Memcached、Postgresql等。redis 正常的学memecached、mangodb 、postgresql。
四、关系型数据库和非关系型数据库的区别
(1)、数据存储方式不同关系型和非关系型数据库的主要差异是数据存储的方式。关系型数据天然就是表格式的,因此存储在数据表的行和列中。数据表可以彼此关联协作存储,也很容易提取数据。与其相反,非关系型数据不适合存储在数据表的行和列中,而是大块组合在一起。非关系型数据通常存储在数据集中,就像文档、键值对或者图结构。你的数据及其特性是选择数据存储和提取方式的首要影响因素。
① 关系型:依赖于关系模型E-R图,同时以二维表格式的方式存储数据
② 非关系型:除了以表格形式存储之外,通常会以大块的形式组合在一起进行存储数据
(2)、扩展方式不同SQL和NoSQL数据库最大的差别可能是在扩展方式上,要支持日益增长的需求当然要扩展。要支持更多并发量,SQL数据库是纵向扩展,也就是说提高处理能力,使用速度更快速的计算机,这样处理相同的数据集就更快了。因为数据存储在关系表中,操作的性能瓶颈可能涉及很多个表,这都需要通过提高计算机性能来克服。虽然SQL数据库有很大扩展空间,但最终肯定会达到纵向扩展的上限。而NoSQL数据库是横向扩展的。因为非关系型数据存储天然就是分布式的,NoSQL数据库的扩展可以通过给资源池添加更多普通的数据库服务器 (节点) 来分担负载。
① 关系:纵向(天然表格式)
② 非关:横向(天然分布式)
(3)、对事务性的支持不同如果数据操作需要高事务性或者复杂数据查询需要控制执行计划,那么传统的SQL数据库从性能和稳定性方面考虑是最佳选择。SQL数据库支持对事务(ACID)原子性细粒度控制,并且易于回滚事务。虽然NoSQL数据库也可以使用事务操作,但稳定性方面没法和关系型数据库比较,所以它们真正闪亮的价值是在操作的扩展性和大数据量处理方面。
① 关系型:特别适合高事务性要求和需要控制执行计划的任务
② 非关系:此处会稍显弱势,其价值点在于高扩展性和大数据量处理方面
五、redis简介
1、什么是redis
Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server(远程数据服务),该软件使用C语言编写,Redis是一个key-value存储系统,它支持丰富的数据类型,如:string、list、set、zset(sorted set)、hash。
2、redis的特点
①Redis以内存作为数据存储介质,所以读写数据的效率极高,远远超过数据库。以设置和获取一个256字节字符串为例,它的读取速度可高达110000次/s,写速度高达81000次/s。
②Redis跟memcache不同的是,储存在Redis中的数据是持久化的,断电或重启后,数据也不会丢失。因为Redis的存储分为内存存储、磁盘存储和log文件三部分,重启后,Redis可以从磁盘重新将数据加载到内存中,这些可以通过配置文件对其进行配置,正因为这样,Redis才能实现持久化。
③Redis支持主从模式,可以配置集群,这样更利于支撑起大型的项目,这也是Redis的一大亮点。
3、redis应用场景,它能做什么
众多语言都支持Redis,因为Redis交换数据快,所以在服务器中常用来存储一些需要频繁调取的数据,这样可以大大节省系统直接读取磁盘来获得数据的I/O开销,更重要的是可以极大提升速度。 拿大型网站来举个例子,比如a网站首页一天有100万人访问,其中有一个板块为推荐新闻。要是直接从数据库查询,那么一天就要多消耗100万次数据库请求。上面已经说过,Redis支持丰富的数据类型,所以这完全可以用Redis来完成,将这种热点数据存到Redis(内存)中,要用的时候,直接从内存取,极大的提高了速度和节约了服务器的开销。
4、REDIS服务器程序是单进程模型
Redis服务在一台服务器上可以同时启动多个Redis进程,Redis的实际处理速度则是完全依靠于主进程的执行效率。若在服务器上只运行一个Redis进程,当多个客户端同时访问时,服务器的处理能力是会有一定程度的下降;若在同一台服务器上开启多个Redis进程,Redis在提高并发处理能力的同时会给服务器的CPU造成很大压力。即:在实际生产环境中,需要根据实际的需求来决定开启多少个Redis进程。一般来说,我们建议可以开启2个进程。原因是第一步,备份,第二步,抗高并发的同时尽量不给CPU造成太大的压力。若对高并发要求高一些,可能会考虑在同一台服务器开启多个进程,若CPU资源比较紧张,那么采取单进程即可。
5、单进程为什么快速
首先无论是NGINX还是redis,优化时都会采用EPOLL+I/O多路复用机制。那么redis是跑在单进程中的,所有的操作都是按照顺序线性执行的,但是由于读写等待用户输入或输出都是阻塞的,所以I/O操作在一般情况下往往不能直接返回,这会导致某一文件的I/O阻塞导致整个进程无法对其他客户提供服务,而I/O多路复用就是为了解决这个问题而出现的。
其次,epoll机制的优势在于,首先epoll没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于2048,一般来说这个数目和系统内存关系很大,具体数目可以考虑使用“cat /proc/sys/fs/file-max”查看。
第三,效率提升,epoll最大的优点就是在于它只管你活跃的状态或活跃的连接,而跟连接总数无法,因此在实际的网络环境中,EPOLL的效率就会远远高于select和poll,
在此,解释一下select 和poll,首先select的机制是以数组的形式,我们将其称为数模式,查找活跃点时需要先遍历一遍,根据数组的规律,在排队过程中,采取冒泡排序的方式将最活跃的点排在最前面,则整体速度较慢。
第二,poll是select的进阶版,poll其实大体跟select差不多, 但是poll没有最大连接数的限制,因为是用链表实现的.那么什么是链表模式,链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表:顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
区别:因为前者每次调用时都要传递你所要监控的所有socket给select/poll系统调用,这意味着需要将用户态的socket列表copy到内核态,如果以万计的句柄会导致每次都要copy几十几百KB的内存到内核态,非常低效。而我们调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表。所以,实际上在你调用epoll_create后,内核就已经在内核态开始准备帮你存储要监控的句柄了,每次调用epoll_ctl只是在往内核的数据结构里塞入新的socket句柄。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的操作。多个socket 会定义为一个fd文件描述符(每有一个新建、打开、修改等“事件”内核就会返回一个fd(可理解为索引)),在每个fd激活时,会进行内核中的回调函数
6、回调函数
什么是回调函数:也被称为高阶函数,高阶函数指函数作为参数被传递或者作为返回值输出。回调函数的特点:1、不会立即执行2、闭包3、this指向回调函数的优点:1、避免代码重复2、拥有更多多功能函数的地方实现更好的抽象3、让代码具有更多的可维护性4、使得代码更容易阅读5、编写更多特定功能的函数
7、I/O多路复用
I/O多路复用程序会监听socket 活跃的链接,然后调用该socket (而此socket 文件句柄主要实现的是网络上的“ip+port+协议所标识的网络中的‘进程’”)可以简单理解为虚拟接口(抽象层)本质而言就是使用一个线程来追踪多个socket(I/O流)的状态,来管理多个I/O I/O复用:一个线程同时追踪、管理多个连接(原本默认情况下是一个线程对应一个连接)回调:预先定义具体的执行过程(定义一个方法) ,等待调用,在一个线程给执行时,可以根据“触发器”“钩子” 完成回调,回调的目的是提高处理性能 和节省资源
8、redis优点
(1)、具有极高的数据读写速度:数据读取的速度最高可达到 110000 次/s,数据写入速度最高可达到 81000 次/s。
(2)、⭐⭐⭐支持丰富的数据类型:支持key-value、 Strings、Lists、 Hashes(散列值)、 Sets 及Ordered Sets 等数据类型操作。string 字符串(可以为整形、浮点和字符型,统称为元素)list 列表:(实现队列,元素不唯一,先入先出原则)set 集合:(各不相同的元素)hash hash散列值:(hash的key必须是唯一的)set /ordered sets 集合/有序集合
(3)、⭐⭐⭐支持数据的持久化:可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
(4)原子性:Redis所有操作都是原子性的。
(5)支持数据备份:即master-salve 模式的数据备份。丰富的特性 – Redis还支持 publish(消息发布)/subscribe(订阅), 通知, 设置key有效期等等特性。Redis作为基于内存运行的数据库,缓存是其最常应用的场景之一。除此之外,Redis常见应用场景还包括获取最新N个数据的操作、排行榜类应用、计数器应用、存储关系、实时分析系统、日志记录。支持key-value、 Strings、Lists、 Hashes(散列值)、 Sets 及Ordered Sets 等数据类型操作。
9、项目中是如何使用缓存的?为什么要使用缓存?缓存使用不当会有什么后果?
项目中将监控、性能等指标数据通过复杂计算后,存入缓存中。 使用缓存是为了高性能和高并发。 高性能: 如果不存入缓存,每次收到请求后需要花费几分钟计算相关指标,如果能提前将数据计算好存入缓存,后续收到请求后可以直接从缓存内取出数据并返回,只需要花费几毫秒的时间。 高并发: 直接使用数据库(如Mysql)一般只能承受2000QPS,而内存天然支持高并发,承受几万QPS都不在话下。 缓存使用不当可能会出现: 缓存雪崩、缓存穿透、缓存与数据库的双写一致性问题以及缓存并发竞争问题等。
10、Redis 和 Memcached 有什么区别?Redis 的线程模型是什么?为什么单线程的 Redis 比多线程的 Memcached 效率要高得多?
Reid与Memcached的区别: Redis支持复杂的数据结构和更丰富的数据操作。memcahced只支持简单的key-value存储(散列表),不支持枚举,不支持持久化(一个相当于是快照,一个相当于是事务写入日志)。Redis支持list、set、sorted set、hash等众多数据结构,同时还支持持久化和复制的功能。redis提供了大量api来支持复杂的数据操作,而memcached则没有提供,必须将数据从缓存中取出,接着在客户端进行一系列的运算,再重新塞回缓存。Redis原生支持集群模式。在Redis的3.X版本开始便能提供Redis Cluster集群模式,而memcached没有原生的集群模式,必须依靠客户端来实现向集群内不同的分片中写数据。性能上有区别。由于Redis只使用单核,而memcached可以使用多核,所以平均每个核上,Redis在存储小数据时比memcached性能更高,而在100K以上的数据中,memcached的性能要高于Redis。能更高。
11、Redis 的过期策略都有哪些?
Redis的过期策略 Redis的过期策略是定期删除+惰性删除。
定期删除: Redis每隔一段时间(默认100ms)会随机抽取一部分设置了过期时间的key,检查其是否过期,如果过期则物理删除。请注意Redis绝不会每隔一段时间就会扫描所有设置了过期时间的key,因为这样做会给Redis带来极大的负担。由于部分扫描的特性,导致了Redis中会存在许多已经到期、且被标记成逻辑删除,但实际上并没有被物理删除的key占据了宝贵的内存空间,所以就需要用到惰性删除了。
惰性删除: 在请求获取某个key时,Redis主动查询它是否设置了过期时间,如果到期了则将其物理删除。
内存淘汰机制 即便使用了定期删除+惰性删除,Redis内存中仍然会存在一些已过期,但没有被扫描到,也没有被主动请求获取的key,它们的数量可能会越来越多,甚至导致Redis内存耗尽的严重后果。此外,没有设置过期时间,却被人为删除的key也可能导致Redis内存耗尽。所以内存淘汰机制就派上用场了! 看看redis.conf当中针对内存的配置:
maxmemory 用来设置redis能够存放数据的最大内存大小。一旦超出大小后,Redis将使用指定的清除策略,清理掉部分数据。 如果不设置这个变量,或者设置为0,对于64位的服务器来说,就是默认不限制内存的使用,直到消耗完服务器中所有的内存。对于32位的服务器来说,默认限制只能使用3GB的内存。maxmemory-policy 当Redis使用的内存达到最大限制后,Redis采用什么策略来清除内存中的数据。
1)noeviction: 新写入操作会报错。(没人用)
2)all-keys-lru: 在键空间中,移除最近最少使用的key。(最长使用)
3)allkeys-random: 在键空间中,随机移除某个key。(较少使用,无法控制移除哪个key)
4)volatile-lru: 在设置了过期时间的键空间中,移除最近最少使用的key。
5)volatile-random: 在设置了过期时间的键空间中,随机移除某个key。
6)volatile-ttl: 在设置了过期时间的键空间中,优先移除有更早到期时间的key。
12、REDIS的高并发与高可用
12.1、Redis与系统高并发之间的关系,想要设计一套能承载数十万甚至上百万QPS的系统,光凭Redis是远远不够的,Redis只不过是支撑高并发的大型缓存架构中的一个非常重要的环节。比如系统底层的缓存中间件使用Redis,再加上良好的缓存架构设计(多级缓存架构,热点缓存等)的共同作用下,才能支撑数十万甚至上百万的高并发。
12.2、redis单机的瓶颈
根据业务操作的复杂度,单机版本的Redis最大能够承载的QPS在数万左右。(如果我们只是做简单的set get操作,那么承载的QPS会较高,但如果使用Redis的复杂api,比如运行lua脚本,会导致Redis承载QPS变低)。此时,如果让上千万、甚至上亿的用户直接接入Redis,就会导致Redis出现处理不及时、卡死等情况。 单机部署的Redis几乎不可能支撑超过10万QPS,除非机器性能特别好,并且所做的操作也不复杂。
12.3、REDIS如何支撑超过10万QPS的并发量:读写分离缓存承载的并发操作大都是"读操作",也就是支撑"读高并发"的,而反观"写操作"的频率相对会小很多。所有的写请求只会发往一台Redis,这台Redis被称作master。master只负责向其它Redis同步数据至slave节点和处理写请求,slave redis对外提供读操作服务。图中这种架构方案又被称作"主从架构"。 打个比方,假设一台slave redis的能承载的读QPS为5W,在使用上图中的主从架构后,整套缓存中间件能承载的读QPS为5W * 2 = 10W。此外,主从架构支持水平扩容,如果读请求QPS遇到瓶颈了,只需要额外增Redis(slave)即可。
12.4、redis replication核心机制Redis采用异步的方式复制数据到slave节点,不过从redis2.8开始,slave node会周期性的向master node发送请求,确认自己每次复制的数据量,用于检测master同步给slave的数据与slave确认的数据的数据一致性。一个master node可以配置多个slave node。比如选择水平扩容后,除了在整套主从架构中增加一台slave node外,还需要在master node中增加slave node的配置信息。slave node可以连接其他slave node。slave node在做复制操作时,不会阻塞master node的正常工作。slave node在处理、同步从master node发送而来的数据时,它会用旧数据对外提供查询操作,不会导致查询阻塞。但当复制完成后,删除旧数据集并切换、加载新数据集的过程中,会暂停对外提供查询操作。slave node主要就是用来进行横向扩容的,通过读写分离,我们不断地向主从架构中添加slave node,以达到整体架构对外提供读操作的吞吐量。
12.5、master节点开启持久化功能对于主从架构安全保障的意义master节点通过异步的方式,已经把数据同步到了slave节点上了,那么为什么还要为master节点开启本地持久化功能呢?难道不能直接把slave节点看作是master节点的热备吗?
答: 不能。如果将master节点的RDB和AOF持久化方案全部关闭,那么master上的数据只会在内存中保留,一旦遇到服务器断电、宕机或者重启,master没有任何的可供恢复的本地数据,此时master会将空数据同步给所有的slave节点,导致整套主从架构中,所有slave节点中的数据也被清空。最终导致100%的数据丢失。
12.6、两种数据丢失的情况和缓解方案 异步复制导致的数据丢失问题 master node接收到客户端发送而来的写请求后,首先会在本地执行写命令,接着会创建线程,通过异步的方式向slave node同步数据。如果此时master node不幸宕机,那么存储在内存的复制缓冲区内的写命令就永久性的会丢失了。
集群脑裂导致的数据丢失问题 master node因为网络问题,与sentinel cluster和slave node产生了网络隔离,sentinel误以为master node宕机,通过故障转移,将slave node提升成了master node,但事实上旧的master node并没有宕机,这就造成了整个Redis集群中出现了两个master node。假如在出现上述问题时,客户端恰好能与旧的master node通信,那么在网络恢复之前,客户端新发出的写入请求全部堆积在了旧的master node上,哨兵在监测到有两个master node后,会将旧的master node降级为slave node,并让新的master node同步数据至这个slave node上。显然,在恢复网络的过程中客户端发出的写请求会永久性的丢失。
解决方案
只需要通过配置以下两个参数,就可以减少异步复制或集群脑裂导致的数据丢失问题: min-slaves-to-write 1min-slaves-max-lag 10 要求至少有1个slave node,与master之间进行数据同步复制所耗费的时间不能超过10秒。如果发现所有slave node数据同步的时间都超过了10秒,那么master node将不再接收任何请求。 减少异步复制的数据丢失 min-slaves-max-lag这个配置可以保证,一旦检测到slave node复制数据、返回ack所耗费的时间太长,为了避免master宕机后丢失的数据过多,master node自身会拒绝客户端发出的写请求(不再将新的写请求写入到复制缓冲区),将损失降低到可控范围。
还有一种可能性,比如因为网络传输或者IO读写速度不对等,导致master中的数据永远比slave多,并且差距越来越大。当检测到slave node落后了自己超过10秒的数据后,master node会停止接收写请求(停顿一段时间),等待slave同步master中的数据,接着再恢复工作。(master node停止接收请求后,我们一般会对客户端做处理,比如对客户端做降级,新产生的写请求暂时不发送给Redis,而是优先写入到本地磁盘中,接着针对外部的请求,客户端再做一些限流措施,减缓外部请求涌入系统内部的速度。或者先将数据写入Kafka等消息队列中,接着每隔10分钟从Kafak中取出数据,重新向Redis的master发送写请求)减少脑裂导致的数据丢失 如果Redis集群出现了脑裂,旧master发现自己slave node返回ack的时间不满足上述两个参数的配置时,会拒绝接收客户端发出的写请求。
注意,这么做只能减缓而不能完全解决数据丢失的问题,比如min-slaves-max-lag=10,那么在最坏的场景下,还是会有10秒的写请求永久性的丢失。如果我们把min-slaves-max-lag配置的过于严苛,又会导致客户端经常性的服务降级,因此只能寻找一个折中的配置方案。