分布式会话和单点登录SSO系统CAS,实现完整的示例代码
【7W使用字长文】LVS Keepalived实现Nginx高可用性,一文理解Nginx
复制高可用主从Redis集群
介绍分布式架构
什么是分布式架构?
随着越来越多的人参与互联网的浪潮,单一的应用架构越来越不能满足需求。因此,出现了分布式集群架构。因此,分布式建筑的发展已经成为Web开发者必须掌握的技能之一。什么是分布式?如何实现分布式,如何处理分布式带来的问题?本系列文章来源于对分布式组件系统的学习总结,包括但不限于Zookeeper、Dubbo、消息队列(ActiveMQ、Kafka、RabbitMQ)、NoSql(Redis、MongoDB)、Nginx、分库分表MyCat、Netty等内容。
简单来说,分工协作,专人做专事是分布式概念。不同的功能模块分散在不同的服务器中,每个子系统负责一个或多个不同的业务模块,服务可以相互交互和通信。
分布式系统设计对用户是透明的。用户只关心请求是否能返回相应的结果,类似于集群对用户的透明概念。同时,分布式架构中的每个服务也可以作为集群,可以发展为集群分布式系统架构,服务集群对其他服务也是透明的。微服务架构是一种分布式架构,微服务架构是分布式架构的子集。
- 集群:贵公司的业务增长非常快。老板发现你的一个后端太忙了,所以他招募了几个后端开发来帮助你。这是后端集群;后来,我发现前端太忙了,配备了几个前端,即前端集群。因此,不难看出,在拆分应用程序后,您可以扩展单个服务并将其组成一个集群,这是分布式的好处之一。
- 节点:这也很容易理解。服务是节点。例如,您是后端集群中的节点,集群本身也可以被视为整个应用程序的集群节点。
- 副本:副本是为服务和数据提供的冗余,以确保高可用性。
- 中间件:为开发者提供便利,屏蔽复杂的底层的一类框架组件。如服务管理通信、序列化、负载均衡等组件。
单体架构和分布式架构的描述
例如,当一个工人制造汽车时,他从左到右处理每一个环节,最终得到一辆整车。此时,由于一个人的能力有限,服务的性能顶点非常低。
当然,我们也可以邀请更多的工人,多线,形成相当于服务集群的效果,当然,这是整个服务集群的所有功能。但是还有一个问题,比如安装底盘比较简单,几分钟就完成了,但是安装轮子比较困难,几天都完成不了。所以影响汽车建设完成的瓶颈在于车轮。我复制了整个过程,邀请了一个工人来做。成本增加了,但效率没有提高那么大。因为安装底盘很简单,我可以独自跟上生产速度,我不需要两个人都安装底盘。
为了解决这个问题,我们首先分配人员,每个人只做他们擅长的事情,就像装配线一样。这样,学习时间将缩短,熟练后效率将迅速提高,形成基本的分布式结构。
很难安装轮子。一个人安装需要几天时间。我可以分配更多的人一起安装,这可以提高这一步的安装时间。如果其他步骤相对简单,只需保持一个人。定向优化服务,提高其性能瓶颈,尽量缩短各服务处理的时差,大大降低成本,形成分布式集群结构。
分布式架构的优点
- 解耦:解耦后方便性能优化。
- 系统模块化,可重用化:发布时不需要将所有模块一起发布,只需发布更新的模块即可。
- 提高系统并发量:单体架构并发量有限,分布式架构可以集群一个可承载并发量较低的模块,从而提高整体架构并发量的上限。
- 优化运维部署效率:迭代发布时,只需按模块发布,包装较轻,单体结构包装往往较大。
分布式架构的缺点
- 架构复杂:功能多,模块多,服务器多,架构复杂。
- 多个子系统的部署很复杂:当同时修改多个模块时,需要同时更新多个服务器上的应用程序
- 系统之间的通信耗时:服务之间的调用会有时间损失,这是不可避免的。
- 新人慢慢融入团队:架构复杂时,新人上手难度会变高
- 复杂调试:如果调用链路较长,debug通常需要同时启动多个服务,打印日志也需要在多个服务中查看。
基本设计原则
- 异步解构:模块拆分后,模块与模块之间的通信可分为异步和同步。,不要使用同步,因为异步的效率往往远高于同步,而异步解耦需要涉及消息队列。
- 无论是查询还是增删,用户的请求都可能需要通过多个子系统,数据的一致性需要保证。主要是为了增加和修改操作,因为查询结果是一致的,删除数据不是,删除几次不会删除其他数据,如果添加不处理会增加大量的重复数据,会使应用程序出现问题。
- 拆分原则:可根据业务拆分,如订单系统、文件系统、门户系统等。;或根据系统功能,如上传文件子系统、下载文件子系统等。
- 融合分布式中间件:比如Redis可作为缓存,Zookeeper可作为协调,MQ可作为消息队列等,我们可以将其整合到我们的系统中。
- 容错高可用:单独部署每一项服务都容易产生单点故障,这是我们可以用集群方案来处理单点故障,。
分布式架构所需的技术
- 分布式缓存中间件Redis
- 分布式会话、单点登录
- 分布式搜索引擎Elasticsearch
- 分布式文件系统
- 分布式新闻队列
- 分布式锁
- 数据库读写分离,分库分表
- 全局唯一关键的数据库表id设计
- 分布式事务和数据一致性
- 接口幂等设计及分布式限流
分布式缓存和技术选择
什么是Redis
Redis(Remote Dictionary Server ),也就是说,远程字典服务是用ANSI C语言编写、支持网络、基于内存或可持续的日志,Key-Value提供多种语言的数据库API。自2010年3月15日起,Redis开发工作原因VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。
Redis很大程度上补偿了出现Memcached这类key/value在某些情况下,存储不足可以补充关系数据库。
它提供了Java,C/C ,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等待客户端,使用非常方便。
和Memcached类似地,它支持存储value包括在内的类型相对较多string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。支持这些数据类型push/pop、add/remove并取并集、差集和更丰富的操作,这些操作都是原子性的。在此基础上,Redis支持各种排序方式。与Memcached同样,为了保证效率,数据在内存中缓存。
区别的是Redis将更新的数据定期写入磁盘或将修改操作写入额外的记录文件,并在此基础上实现master-slave(主从)同步。
数据可以从主服务器同步到任何数量的服务器,从服务器到其他与服务器相关的主服务器。这使得Redis可复制单层树。存盘可以有意无意地写数据。由于发布/订阅机制的完全实现,当数据库在任何地方同步树时,可以订阅一个频道,接收主服务器的完整信息发布记录。同步有助于读取操作的可扩展性和数据冗余。
Redis官网地址,很好记,是的redis.io。(域名后缀io属于国家域名,是的british Indian Ocean territory,即英属印度洋领地)
特性和特点
Redis现在最受欢迎NoSQL数据库之一,Redis是一个使用ANSI C存储数据库具有以下特点:
- 基于内存运行,性能高效
- 理论上可以无限扩展支持分布式
- key-value存储系统
- 开源的使用ANSI C语言编写,遵守BSD协议、支持网络、基于内存或可持续的日志,Key-Value提供多种语言的数据库API
与其他数据库类型相比,Redis特点是:
- C/S通讯模型
- 单进程单线程模型
- 数据类型丰富
- 操作具有原子性
- 持久化
- 高并发读写
- 支持lua脚本
参考链接:
Redis是什么?看这一篇就够了
为什么要引入Redis
在Web应用发展的初期,那时关系型数据库受到了较为广泛的关注和应用,原因是因为那时候Web站点基本上访问和并发不高、交互也较少。而在后来,随着访问量的提升,使用关系型数据库的Web站点多多少少都开始在性能上出现了一些瓶颈,而瓶颈的源头一般是在磁盘的I/O上。而随着互联网技术的进一步发展,各种类型的应用层出不穷,这导致在当今云计算、大数据盛行的时代,对性能有了更多的需求,主要体现在以下四个方面:
- 低延迟的读写速度:应用快速地反应能极大地提升用户的满意度
- 支撑海量的数据和流量:对于搜索这样大型应用而言,需要利用PB级别的数据和能应对百万级的流量
- 大规模集群的管理:系统管理员希望分布式应用能更简单的部署和管理
- 庞大运营成本的考量:IT部门希望在硬件成本、软件成本和人力成本能够有大幅度地降低
为了克服这一问题,NoSQL应运而生,它同时具备了高性能、可扩展性强、高可用等优点,受到广泛开发人员和仓库管理人员的青睐。
现有架构分析
- 浏览器访问时,请求先到LVS,使用Keepalived让两台LVS达成一个主备关系或者双主热备关系
- LVS的之后是Nginx集群,LVS是四层负载均衡,使用DR模式使请求通过LVS,而响应直接由应用响应到浏览器
- 每个Nginx集群的每一个节点都使用了upstream模块,配置了tomcat集群
- 因为每个应用服务是完全相同的,所以他们的都会连接到同一个数据库,现在应用的性能瓶颈就在应用到数据库之间
- 在应用的时,对于数据库来说当中有一个,请求中百分之八十都是读请求,写请求远远少于读请求
举个例子,假如系统中有2千万用户信息,用户信息基本固定,一旦录入很少变动,那么你每次加载所有用户信息时,如果都要请求数据库,数据库编译并执行你的查询语句,这样效率就会低下很多,针对这种并且数据量较大的情况,通常做法,就是把他加入缓存,每次取数前先去判断,如果缓存不为空,那么就从缓存取值,如果为空,再去请求数据库,并将数据加入缓存,这样大大提高系统访问效率。
其次,假如有一个明星的负面新闻被曝光在微博上了,这时会有几千万上亿的请求会访问到它。对于这种高流量的热点新闻,我们也可以加入缓存,减缓数据库压力,是数据库不会被上亿的流量冲垮。
所以,我们可以使用Redis缓存把应用服务和数据库分隔开,把一些热点数据和不经常变动的数据放到缓存中,让上亿的请求访问到Redis,而不会直接接触数据库,从而达到保护数据库的效果。相当于提高了数据库的读取这类型数据的性能,也就是提高了应用服务的性能和吞吐量,也可以承载更多的并发。
Redis的作用
动物园中有各种动物,当游客来到动物园中,她想要知道大熊猫有多少只。她去询问管理员时,管理员进去动物园中数了数大熊猫的数量,返回到原来的地方告诉游客她想要知道的信息。这时若客流量突然变大时,管理员对于每一个人的问题都要去动物园中寻找,他就会非常累。
管理员灵机一动想到了一个方法,他每次去动物园中寻找时,把问题和问题对应的结果写到一个账本上。久而久之,账本上记录的问题和数据越来越多。
当他遇到有人问到之前记录过的一个问题时,他就会直接把账本上的问题答案回答给游客,就不用再一次进去动物园中了。这时,他能接待的客流量就增多了。
这时又会出现一个问题,当动物园中动物的数据发生变化之后,账本和真实数据就对应不上了。这就要靠动物园中的维护人员来保证数据的一致性了,当动物园中动物数据发生变化之后,维护人员要及时修改账本的数据,使其数据和真实数据保持一致。
而账本就相当于我们架构中的Redis,管理员和动物园相当于数据库,这样我们应用的并发量和吞吐量也就提高了,当原有数据发生变化时(新增或修改),要及时更新Redis中缓存的数据,保证数据一致性。
什么是NoSql
NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL"。
- 平时我们使用的最多的数据库MySql、postgreSql等等都是关系型数据库,NoSql则是非关系型数据库
- 对于关系型数据库来说,是需要把数据存储到库、表、行、字段里,查询的时候根据条件一行一行地去匹配,当量非常大的时候就很耗费时间和资源,尤其是数据是需要从磁盘里去检索
- NoSQL数据库存储原理非常简单(典型的数据类型为k-v),不存在繁杂的关系链,比如mysql查询的时候,需要找到对应的库、表(通常是多个表)以及字段
- NoSQL数据可以存储在内存里,查询速度非常快
- NoSQL在性能表现上虽然能优于关系型数据库,但是它并不能完全替代关系型数据库
- NoSQL因为没有复杂的数据结构,扩展非常容易,支持分布式
传统项目都是使用关系型数据库,访问量也不是很大,任何请求过来直接查询数据库也不会造成什么影响。当今互联网中数据量越来越大,请求数和并发要求越来越高,而传统的纯数据库架构设计慢慢的不能满足我们的需求。普通的关系型数据库不再适合把超大量的数据提供出去做并发型的查询(用户信息,地理信息,购物信息等)。数据库单表的性能是有限的,数据库做的好优化的好的话单表数据量能到达400w-600w的样子,而一般一点的最多只能到300w-400w,就会对应用的性能造成很大的影响。
一旦数据量达到300w时,数据库管理员(DBA)就要及时准备数据库的优化方案,从而的对数据库进行优化。数据库的数据量庞大之后,关系型数据库也不适合对数据进行深挖(数据挖掘、大数据分析等)。这时使用NoSql就是顺势而为,NoSql水平(横向)扩展非常方便高效。Redis的数据结构是key-value的这种结构,存储形式是非常简单的,所以扩展起来就非常容易,而且由于是键值对这种形式,增加或者删除缓存中的数据会非常高效。数据库中数据量到达上百万的话,数据库中增加和删除数据性能会非常差,若是针对字段的新增或者删除其对性能的影响更是灾难性的,对于整体的数据会有非常大的影响。
对于Redis来说,它的读取性能是非常高的,每秒能达到十万次,远远大于传统关系型数据库。还能搭建集群,而且非常容易。可以存储数据,也可以对于缓存的数据进行持久化(不同的NoSql,对于持久化的支持情况不同)。
NoSql的分类
键值对数据库
相关产品:Redis、Riak、SimpleDB、Chordless、Scalaris、Memcached
应用:内容缓存
优点:扩展性好、灵活性好、大量写操作时性能高
缺点:无法存储结构化信息、条件查询效率较低
使用者:百度云(Redis)、GitHub(Riak)、BestBuy(Riak)、Twitter(Ridis和Memcached)
列存储数据库
相关产品:BigTable、HBase、Cassandra、HadoopDB、GreenPlum、PNUTS
应用:分布式数据存储与管理
优点:查找速度快、可扩展性强、容易进行分布式扩展、复杂性低
使用者:Ebay(Cassandra)、Instagram(Cassandra)、NASA(Cassandra)、Facebook(HBase)
文档型数据库
相关产品:MongoDB、CouchDB、ThruDB、CloudKit、Perservere、Jackrabbit
应用:存储、索引并管理面向文档的数据或者类似的半结构化数据
优点:性能好、灵活性高、复杂性低、数据结构灵活
缺点:缺乏统一的查询语言
使用者:百度云数据库(MongoDB)、SAP(MongoDB)
图形数据库
相关产品:Neo4J、OrientDB、InfoGrid、GraphDB
应用:大量复杂、互连接、低结构化的图结构场合,如社交网络、推荐系统等
优点:灵活性高、支持复杂的图形算法、可用于构建复杂的关系图谱
缺点:复杂性高、只能支持一定的数据规模
使用者:Adobe(Neo4J)、Cisco(Neo4J)、T-Mobile(Neo4J)
参考链接:
NoSQL的四大类型
:Redis、Memcached、MongoDB是国内使用的最多的数据库,假如一个项目没有使用到任何一个NoSql则可以断定这是一个非常传统的项目
什么是分布式缓存
在高并发的分布式的系统中,缓存是必不可少的一部分。没有缓存对系统的加速和阻挡大量的请求直接落到系统的底层,系统是很难撑住高并发的冲击,所以分布式系统中缓存的设计是很重要的一环。
在高并发的场景下,通过分布式缓存可以提高数据的读取数据,相应的应用的性能和吞吐量就提高了。这里只针对读操作,读取数据时尽量在缓存中读取,可以降低数据库对服务器性能的开支,降低服务器的压力,而写操作时才把数据直接写到数据库,这里遵循(百分之八十都是读操作)。Redis是基于内存的,读写性能远远高于磁盘存储的数据库。一般情况下,尽量让大部分查询请求去命中缓存,少量的请求根据需要直接操作数据库。在分布式系统中,分布式缓存中的数据可以被所有服务访问到,可以很轻松的实现服务之间数据的共享。
:
-
加速读写。因为缓存通常是全内存的,比如Redis、Memcache。对内存的直接读写会比传统的存储层如MySQL,性能好很多。举个例子:同等配置单机Redis QPS可轻松上万,MySQL则只有几千。加速读写之后,响应时间加快,相比之下系统的用户体验能得到更好的提升。
-
降低后端的负载。缓存一些复杂计算或者耗时得出的结果可以降低后端系统对CPU、IO、线程这些资源的需求,让系统运行在一个相对资源健康的环境。
:
- 数据不一致性:缓存层与存储层的数据存在着一定时间窗口一致,时间窗口与缓存的过期时间更新策略有关。
- 代码维护成本:加入缓存后,需要同时处理缓存层和存储层的逻辑,增加了开发者维护代码的成本。
- 运维成本:引入缓存层,比如Redis。为保证高可用,需要做主从,高并发需要做集群。
参考链接:
深入理解分布式缓存设计
缓存方案的对比
Ehcache
Ehcache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储、缓存加载器、缓存扩展、缓存异常处理程序、一个gzip缓存servlet过滤器、支持REST和SOAP API等特点。
Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Ehcache),但本身不直接提供缓存功能的实现。它支持注解方式使用缓存,非常方便。
特性
- 快速、简单
- 多种缓存策略
- 缓存数据有两级:内存和磁盘,因此无需担心容量问题
- 缓存数据会在虚拟机
重启
的过程中写入磁盘 - 可以通过RMI、可插入API等方式进行分布式缓存
- 具有缓存和缓存管理器的侦听接口
- 支持
多
缓存管理器实例
,以及一个实例的多个缓存区域
- 提供Hibernate的缓存实现
优点
- 基于Java开发:对于Java项目的整合,其代码的健壮性比较好
- 基于JVM缓存:在JVM中使用的话,速度比较快,性能也会更高
- 简单、轻巧、方便:整合起来非常容易,很多框架都整合了Ehchache,例如:hibernate
缺点
- 集群不支持:不支持缓存共享,对于集群的实现非常复杂,维护起来也非常不方便
- 分布式不支持:Ehchache更加适合于单应用以及快速开发
集成
可以单独使用,一般在第三方库中被用到的比较多(如mybatis、shiro等)Ehcache 对分布式支持不够好,多个节点不能同步,通常和redis一块使用
灵活性
Ehcache具备对象api接口和可序列化api接口
不能序列化的对象可以使用出磁盘存储外Ehcache的所有功能
支持基于Cache和基于Element的过期策略,每个Cache的存活时间都是可以设置和控制的。
提供了LRU、LFU和FIFO缓存淘汰算法,Ehcache 1.2引入了最少使用和先进先出缓存淘汰算法,构成了完整的缓存淘汰算法。
提供内存和磁盘存储,Ehcache和大多数缓存解决方案一样,提供高性能的内存和磁盘存储。
动态、运行时缓存配置,存活时间、空闲时间、内存和磁盘存放缓存的最大数目都是可以在运行时修改的。
应用持久化
在jvm重启后,持久化到磁盘的存储可以复原数据
Ehache是第一个引入缓存数据持久化存储的开源java缓存框架,缓存的数据可以在机器重启后从磁盘上重新获得
根据需要将缓存刷到磁盘。将缓存条目刷到磁盘
的操作可以通过cache.fiush方法执行,这大大方便了Ehcache的使用
Ehcache和Redis 比较
- Ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。
- Redis是通过socket访问到缓存服务,效率比Ecache低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案。如果是单个应用或者对缓存访问要求很高的应用,用Ehcache。如果是大型系统,存在缓存共享、分布式部署、缓存内容很大的,建议用Redis。
参考链接:
EhCache
ehcache memcache redis 三大缓存男高音
Memcache
Memcache是一个自由、源码开放、高性能、分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度。Memcache是一个存储键值对的HashMap,在内存中对任意的数据(比如字符串、对象等)所使用的key-value存储,数据可以来自数据库调用、API调用,或者页面渲染的结果。Memcache设计理念就是小而强大,它简单的设计促进了快速部署、易于开发并解决面对大规模的数据缓存的许多难题,而所开放的API使得Memcache能用于Java、C/C++/C#、Perl、Python、PHP、Ruby等大部分流行的程序语言。
Memcache通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。简单的说就是将数据调用到内存中,然后从内存中读取,从而大大提高读取速度。Memcached是以守护程序(监听)方式运行于一个或多个服务器中,随时会接收客户端的连接和操作。
另外,说一下Memcache和Memcached的区别:
-
Memcache是项目的名称
-
Memcached是Memcache服务器端可以执行文件的名称
特性和限制
- 在 Memcached中可以保存的item数据量是没有限制的,只要内存足够 。
- Memcached单进程在32位系统中最大使用内存为2G,若在64位系统则没有限制,这是由于32位系统限制单进程最多可使用2G内存,要使用更多内存,可以分多个端口开启多个Memcached进程 。
- 最大30天的数据过期时间,设置为永久的也会在这个时间过期,常量REALTIME_MAXDELTA 60 * 60 * 24 * 30控制
- 最大键长为250字节,大于该长度无法存储,常量KEY_MAX_LENGTH 250控制
- 单个item最大数据是1MB,超过1MB数据不予存储,常量POWER_BLOCK 1048576进行控制,它是默认的slab大小
- Memcache服务端是不安全的,比如已知某个Memcache节点,可以直接telnet过去,并通过flush_all让已经存在的键值对立即失效
- Memcache的高性能源自于两阶段哈希结构:第一阶段在客户端,通过Hash算法根据Key值算出一个节点;第二阶段在服务端,通过一个内部的Hash算法,查找真正的item并返回给客户端。从实现的角度看,Memcache是一个非阻塞的、基于事件的服务器程序
- 最大同时连接数是200,通过 conn_init()中的freetotal进行控制,最大软连接数是1024,通过 settings.maxconns=1024 进行控制
- 跟空间占用相关的参数:settings.factor=1.25, settings.chunk_size=48, 影响slab的数据占用和步进方式
- memcached是一种无阻塞的socket通信方式服务,基于libevent库,由于无阻塞通信,对内存读写速度非常之快。
- memcached分服务器端和客户端,可以配置多个服务器端和客户端,应用于分布式的服务非常广泛。
- memcached作为小规模的数据分布式平台是十分有效果的。
- memcached是键值一一对应,key默认最大不能超过128个字 节,value默认大小是1M,也就是一个slabs,如果要存2M的值(连续的),不能用两个slabs,因为两个slabs不是连续的,无法在内存中 存储,故需要修改slabs的大小,多个key和value进行存储时,即使这个slabs没有利用完,那么也不会存放别的数据。
- memcached已经可以支持C/C++、Perl、PHP、Python、Ruby、Java、C#、Postgres、Chicken Scheme、Lua、MySQL和Protocol等语言客户端
参考链接:
Memcache的 简介
MemCache详细解读
Memcached简介
优点
- 部分容灾:假设只用一台Memcache,如果这台Memcache服务器挂掉了,那么请求将不断的冲击数据库,这样有可能搞死数据库,从而引发”雪崩“。如果使用多台Memcache服务器,由于Memcache使用一致性哈希算法,万一其中一台挂掉了,部分请求还是可以在Memcache中命中,为修复系统赢得一些时间。
- 横向扩展简单:一台Memcache服务器的容量毕竟有限,可以使用多台Memcache服务器,增加缓存容量。
- 均衡请求:使用多台Memcache服务器,可以均衡请求,避免所有请求都冲进一台Memcache服务器,导致服务器挂掉。
- 多核运行:Memcache 可以利用多核优势,单实例吞吐量极高,可以达到几十万 QPS(取决于 key、value 的字节大小以及服务器硬件性能,日常环境中 QPS 高峰大约在 4-6w 左右)。适用于最大程度扛量。
- 支持直接配置为 session handle。
缺点
-
只支持简单的 key/value 数据结构,不像 Redis 可以支持丰富的数据类型。
-
无法进行持久化,数据不能备份,只能用于缓存使用,且重启后数据全部丢失。
-
无法进行数据同步,不能将Memcache中的数据迁移到其他Memcache实例中。
-
Memcached 内存分配采用 Slab Allocation 机制管理内存,value 大小分布差异较大时会造成内存利用率降低,并引发低利用率时依然出现踢出等问题。需要用户注重 value 设计。
Memcache和Redis比较
- 持久化能力:Redis支持持久化,Memcache也支持但一般不做持久化(重启丢失数据)
- 数据类型支持:Redis类型较多(5种数据类型,string、list、hash、set、sorted set),Memcache只能是字符串
- 线程模型:Redis是单线程+多路IO复用,虽然没有锁冲突,但很难利用多核特性提升整体吞吐量。Memcache是多线程+锁的方式,主线程监听,work子线程接收请求,执行读写,有锁冲突。;
- 数据库特征:Redis不是所有的数据都存储在内存,在很多方面具备数据库的特征,Memcache只是简单的kv缓存;相当于Memcache更像是redis在功能上的一个子集。
- 高可用支持:高可用(redis原生支持高可用功能,可以实现主从复制,哨兵模式,redis集群模式,而Memcache要实现高可用,需要进行二次开发,例如客户端的双读双写,或者服务端的集群同步)(延伸:虽然数据类型单一,但是Memcache的内存管理机制导致无碎片,这让Memcache工作更加稳定,而redis本身也考虑到自己功能复杂,会产生碎片,并且容易崩溃,所以支持高可用)
- 内容大小比较:Redis存储的内容比较大(Memcache的value存储最大是1M,如果存储value很大,只能选择redis)
- 内存分配:memchache使用预分配内存池的方式管理内存,能够省区内存分配的时间,这个节省的时间在数据量很大的时候还是很可观的。而redis则是临时申请空间,可能导致碎片。Redis和Memcache在写入性能上面差别不大的,读取性能上面尤其是批量读取性能上面Memcache更强的。
参考链接:
Redis 和 Memcache 相比较优缺点的不同区别是什么
Redis与Memcache区别最全整理
Memcache 持久性分布式数据库MemcacheDB
Redis,Memcache该如何选择?
Redis和Memcache区别,优缺点对比
redis和memcached的区别(总结)
Redis
简单来说 Redis 就是一个用C语言写的数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。另外,Redis 也经常用来做分布式锁。Redis 提供了多种数据类型来支持不同的业务场景。除此之外,Redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。
Redis是一个开源的,先进的key-value持久化产品。它通常被称为数据结构服务器,它的值可以是字符串(String)、哈希(Map)、列表(List)、集合(Sets)和有序集合(Sorted sets)等类型。可以在这些类型上面做一些原子操作,如:字符串追加、增加Hash里面的值、添加元素到列表、计算集合的交集,并集和差集;或者区有序集合中排名最高的成员。为了取得好的性能,Redis是一个内存型数据库。不限于此,Redis也可以把数据持久化到磁盘中,或者把数据操作指令追加了一个日志文件,把它用于持久化。也可以用Redis容易的搭建master-slave架构用于数据复制。其它让它像缓存的特性包括,简单的check-and-set机制,pub/sub和配置设置。Redis可以用大部分程序语言来操作:C、C++、C#、Java、Node.js、php、ruby等等。
特性
-
速度快
正常情况下,Redis执行命令的速度非常快,官方给出的数字是读写性能可以达到10万/秒,当然这也取决于机器的性能,但这里先不讨论机器性能上的差异,只分析一下是什么造就了Redis除此之快的速度,可以大致归纳为以下三点:
- Redis的所有数据都是存放在内存中的,所以把数据放在内存中是Redis速度快的最主要原因。
- Redis是用C语言实现的,一般来说C语言实现的程序“距离”操作系统更近,执行速度相对会更快。
- Redis使用了单线程架构,预防了多线程可能产生的竞争问题。
-
基于键值对的数据结构服务器
几乎所有的编程语言都提供了类似字典的功能,例如Java里的map、Python里的dict,类似于这种组织数据的方式叫作基于键值的方式,与很多键值对数据库不同的是,Redis中的值不仅可以是字符串,而且还可以是具体的数据结构,这样不仅能便于在许多应用场景的开发,同时也能够提高开发效率。Redis的全称是REmote Dictionary Server,它主要提供了5种数据结构:字符串、哈希、列表、集合、有序集合。
-
丰富的功能
除了5种数据结构,Redis还提供了许多额外的功能:
- 提供了键过期功能,可以用来实现缓存。
- 提供了发布订阅功能,可以用来实现消息系统。
- 支持Lua脚本功能,可以利用Lua创造出新的Redis命令。
- 提供了简单的事务功能,能在一定程度上保证事务特性。
- 提供了流水线(Pipeline)功能,这样客户端能将一批命令一次性传到Redis,减少了网络的开销。
-
简单稳定
Redis的简单主要表现在三个方面。
- Redis的源码很少。
- Redis使用单线程模型,这样不仅使得Redis服务端处理模型变得简单,而且也使得客户端开发变得简单。
- Redis不需要依赖于操作系统中的类库(例如Memcache需要依赖libevent这样的系统类库),Redis自己实现了事件处理的相关功能。
- Redis虽然很简单,但是不代表它不稳定。维护的上千个Redis为例,没有出现过因为Redis自身bug而宕掉的情况。
-
客户端语言多
Redis提供了简单的TCP通信协议,很多编程语言可以很方便地接入到Redis,并且由于Redis受到社区和各大公司的广泛认可,所以支持Redis的客户端语言也非常多,几乎涵盖了主流的编程语言,例如Java、PHP、Python、C、C++、Nodejs等。
-
持久化
通常看,将数据放在内存中是不安全的,一旦发生断电或者机器故障,重要的数据可能就会丢失,因此Redis提供了两种持久化方式:RDB和AOF,即可以用两种策略将内存的数据保存到硬盘中(如图所示)这样就保证了数据的可持久性。
-
主从复制
Redis提供了复制功能,实现了多个相同数据的Redis副本(如图所示),复制功能是分布式Redis的基础。
-
高可用和分布式
Redis从2.8版本正式提供了高可用实现Redis Sentinel,它能够保证Redis节点的故障发现和故障自动转移。Redis从3.0版本正式提供了分布式实现Redis Cluster,它是Redis真正的分布式实现,提供了高可用、读写和容量的扩展性。
参考链接:
redis的八大特性
优点
- 支持多种数据类型:包括set,zset,list,hash,string这五种数据类型,操作非常方便。比如,如果你在做好友系统,查看自己的好友关系,如果采用其他的key-value系统,则必须把对应的好友拼接成字符串,然后在提取好友时,再把value进行解析,而Redis则相对简单,直接支持list的存储(采用双向链表或者压缩链表的存储方式)。
- 持久化存储:作为一个内存数据库,最担心的,就是万一机器死机,数据会消失掉。redi使用rdb和aof做数据的持久化存储。主从数据同时,生成rdb文件,并利用缓冲区添加新的数据更新操作做对应的同步。
- 丰富的特性:pub/sub,key过期策略,事务,支持多个DB等。
- 性能很好:由于是全内存操作,所以读写性能很好,可以达到10w/s的频率。公司有项目使用Redis,目前的访问频率是80w/s,通过适当的部署,线上运行一切ok。
缺点
- 由于是内存数据库,所以,单台机器,存储的数据量,跟机器本身的内存大小。虽然Redis本身有key过期策略,但是还是需要提前预估和节约内存。如果内存增长过快,需要定期删除数据。
- 如果进行完整重同步,由于需要生成rdb文件,并进行传输,会占用主机的CPU,并会消耗现网的带宽。不过Redis2.8版本,已经有部分重同步的功能,但是还是有可能有完整重同步的。比如,新上线的备机。
- 修改配置文件,进行重启,将硬盘中的数据加载进内存,时间比较久。在这个过程中,Redis不能提供服务。
相关问题
-
数据库和缓存双写一致性问题:一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。
-
缓存穿透:即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。解决方案:
- 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
- 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
- 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
-
缓存雪崩:即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。解决方案:
- 给缓存的失效时间,加上一个随机值,避免集体失效。
- 使用互斥锁,但是该方案吞吐量明显下降了。
- 双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点
- 从缓存A读数据库,有则直接返回
- A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
- 更新线程同时更新缓存A和缓存B。
-
缓存的并发竞争:同时有多个子系统去set一个key。
-
如果对这个key操作,不要求顺序:这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。
-
如果对这个key操作,要求顺序:(假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC)期望按照key1的value值按照 valueA–>valueB–>valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。假设时间戳如下:
- 系统A key 1 {valueA 3:00}
- 系统B key 1 {valueB 3:05}
- 系统C key 1 {valueC 3:10}
那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。
其他方法,比如利用队列,将set方法变成串行访问也可以。总之,灵活变通。
-
-
Redis的过期策略以及内存淘汰机制:Redis采用的是定期删除+惰性删除策略。
-
为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略。
-
定期删除+惰性删除是如何工作的呢?
定期删除,Redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,Redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,Redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。于是,惰性删除派上用场。也就是说在你获取某个key的时候,Redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
-
采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,Redis的内存会越来越高。那么就应该采用内存淘汰机制。
在redis.conf中有一行配置:
# maxmemory-policy volatile-lru
该配置就是配内存淘汰策略的
- noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把Redis既当缓存,又做持久化存储的时候才用。不推荐
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐
- ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。
-
参考链接:
Redis 优缺点
redis的优点和缺点
Redis的使用
Redis的安装和配置
-
打开官网:https://redis.io (中文官网:http://www.redis.cn)点击下载最新版本的Redis
-
上传到Linux服务器
-
解压安装包
tar -zxvf redis.xxxx.tar.gz
-
进入解压缩之后的文件夹,可以发现其中有一个Makefile文件,因此安装方式和Nginx类似
-
因为是C语言编写的,所以安装gcc的编译环境
yum install gcc-c++
-
编译
make
当最后看到:Hint: It’s a good idea to run ‘make test’ 😉
说明编译成功了
-
安装
make install
-
拷贝Redis解压目录中的utils文件夹下的启动脚本到系统目录中,以备后用
cp ./utils/redis_init_script /etc/init.d/
-
创建配置文件所在的文件夹以及Redis的工作空间
mkdir /usr/local/redis -p
mkdir /usr/local/redis/db -p
-
拷贝redis.conf文件夹到创建的目录中
cp ./redis.conf /usr/local/redis/redis.conf
-
修改配置文件
vim /usr/local/redis/redis.conf
修改以下配置项
- 设置后台启动:daemonize yes
- 设置工作空间:dir /usr/local/redis/db
- 设置允许访问Redis服务的ip:bind 0.0.0.0
- 修改访问密码(此项配置默认是关闭的):requirepass imooc
部分配置项:
daemonize yes #是否以后台进程运行 pidfile /var/run/redis/redis-server.pid #pid文件位置 port 6379#监听端口 bind 127.0.0.1 #绑定地址,如外网需要连接,设置0.0.0.0 timeout 300 #连接超时时间,单位秒 loglevel notice #日志级别,分别有: # debug :适用于开发和测试 # verbose :更详细信息 # notice :适用于生产环境 # warning :只记录警告或错误信息 logfile /var/log/redis/redis-server.log #日志文件位置 syslog-enabled no #是否将日志输出到系统日志 databases 16#设置数据库数量,默认数据库为0 ############### 快照方式 ############### save 900 1 #在900s(15m)之后,至少有1个key发生变化,则快照 save 300 10 #在300s(5m)之后,至少有10个key发生变化,则快照 save 60 10000 #在60s(1m)之后,至少有1000个key发生变化,则快照 rdbcompression yes #dump时是否压缩数据 dir /var/lib/redis #数据库(dump.rdb)文件存放目录 ############### 主从复制 ############### slaveof <masterip> <masterport> #主从复制使用,用于本机redis作为slave去连接主redis masterauth <master-password> #当master设置密码认证,slave用此选项指定master认证密码 slave-serve-stale-data yes #当slave与master之间的连接断开或slave正在与master进行数据同步时,如果有slave请求,当设置为yes时,slave仍然响应请求,此时可能有问题,如果设置no时,slave会返回"SYNC with master in progress"错误信息。但INFO和SLAVEOF命令除外。 ############### 安全 ############### requirepass foobared #配置redis连接认证密码 ############### 限制 ############### maxclients 128#设置最大连接数,0为不限制 maxmemory <bytes>#内存清理策略,如果达到此值,将采取以下动作: maxmemory-policy volatile-lru#如果达到maxmemory值,采用此策略 # volatile-lru :默认策略,只对设置过期时间的key进行LRU算法删除 # allkeys-lru :删除不经常使用的key # volatile-random :随机删除即将过期的key # allkeys-random :随机删除一个key # volatile-ttl :删除即将过期的key # noeviction :不过期,写操作返回报错 maxmemory-samples 3 #默认随机选择3个key,从中淘汰最不经常用的 ############### 附加模式 ############### appendonly no #AOF持久化,是否记录更新操作日志,默认redis是异步(快照)把数据写入本地磁盘 appendfilename appendonly.aof #指定更新日志文件名 # AOF持久化三种同步策略: # appendfsync always #每次有数据发生变化时都会写入appendonly.aof # appendfsync everysec #默认方式,每秒同步一次到appendonly.aof # appendfsync no #不同步,数据不会持久化 no-appendfsync-on-rewrite no #当AOF日志文件即将增长到指定百分比时,redis通过调用BGREWRITEAOF是否自动重写AOF日志文件。 ############### 虚拟内存 ############### vm-enabled no #是否启用虚拟内存机制,虚拟内存机将数据分页存放,把很少访问的页放到swap上,内存占用多,最好关闭虚拟内存 vm-swap-file /var/lib/redis/redis.swap #虚拟内存文件位置 vm-max-memory 0 #redis使用的最大内存上限,保护redis不会因过多使用物理内存影响性能 vm-page-size 32 #每个页面的大小为32字节 vm-pages 134217728 #设置swap文件中页面数量 vm-max-threads 4 #访问swap文件的线程数 ############### 高级配置 ############### hash-max-zipmap-entries 512 #哈希表中元素(条目)总个数不超过设定数量时,采用线性紧凑格式存储来节省空间 hash-max-zipmap-value 64 #哈希表中每个value的长度不超过多少字节时,采用线性紧凑格式存储来节省空间 list-max-ziplist-entries 512 #list数据类型多少节点以下会采用去指针的紧凑存储格式 list-max-ziplist-value 64 #list数据类型节点值大小小于多少字节会采用紧凑存储格式 set-max-intset-entries 512 #set数据类型内部数据如果全部是数值型,且包含多少节点以下会采用紧凑格式存储 activerehashing yes #是否激活重置哈希
参考链接:
Redis配置文件redis.conf详细配置说明
redis配置文件中常用配置详解
-
修改Redis启动脚本
vim /etc/init.d/redis_init_script
修改脚本的配置文件地址为:
CONF=“/usr/local/redis/redis.conf”
-
给脚本新增执行权限
chmod 777 /etc/init.d/redis_init_script
-
使用脚本启动Redis Server
/etc/init.d/redis_init_script start
-
查看Redis进程是否启动成功
ps -ef | grep redis
-
在脚本中配置启动级别和描述
在脚本文件中新增以下内容:
# chkconfig: 22345 10 90
# description: Start and Stop redis
-
将脚本注册到启动项中
chkconfig redis_init_script on
参考链接:
Linux 添加开机启动方法(服务/脚本)
例子:
# 1.将(脚本)启动文件移动到 /etc/init.d/或者/etc/rc.d/init.d/目录下。(前者是后者的软连接) mv /www/wwwroot/test.sh /etc/rc.d/init.d # 2.启动文件前面务必添加如下三行代码,否侧会提示chkconfig不支持。 #!/bin/sh 告诉系统使用的shell,所以的shell脚本都是这样 #chkconfig: 35 20 80 分别代表运行级别,启动优先权,关闭优先权,此行代码必须 #description: http server 自己随便发挥!!!,此行代码必须 /bin/echo $(/bin/date +%F_%T) >> /tmp/test.log # 3.增加脚本的可执行权限 chmod +x /etc/rc.d/init.d/test.sh # 4.添加脚本到开机自动启动项目中。添加到chkconfig,开机自启动。 [root@localhost ~]# cd /etc/rc.d/init.d [root@localhost ~]# chkconfig --add test.sh [root@localhost ~]# chkconfig test.sh on # 5.关闭开机启动 [root@localhost ~]# chkconfig test.sh off # 6.从chkconfig管理中删除test.sh [root@localhost ~]# chkconfig --del test.sh # 7.查看chkconfig管理 [root@localhost ~]# chkconfig --list test.sh
使用redis-cli工具操作Redis
-
打开Redis自启动脚本文件,查看redis-cli工具的安装位置
vim /etc/init.d/redis_init_script
-
使用此工具连接Redis
可以直接输入redis-cli或者输入绝对路径进入使用状态
新开一个连接可以查看工具的此工具的进程,输入
ps -ef | grep redis
-
当我们使用时发现Redis报错,提示没有授权
是我们连接Redis时没有输入密码的原因
-
使用时设置密码,使用set命令和get命令操作key-value
使用auth命令后面跟密码进行认证
使用set命令设置key-value键值对
使用get命令获取key对应的value值