瞌睡龙

技术杂货铺

0%

引言

在微服务架构中,服务治理是一个关键的组成部分。Ribbon是Spring Cloud中的一个重要组件,用于实现客户端负载均衡。有时候,我们需要自定义Ribbon的负载均衡规则,但在这个过程中,可能会遇到一些问题。本文将讨论在自定义Ribbon规则时可能遇到的问题,并提供解决方案。

一. 现象

当我们尝试单独配置Ribbon,例如自定义负载均衡规则,并将配置类标记为@Configuration,然后使用@Bean注解进行配置时,可能会遇到以下错误提示:

1
No qualifying bean of type ‘com.netflix.client.config.IClientConfig’ available

同时,在调用多个provider服务时,可能会出现服务调用错误,如:

1
feign.FeignException$NotFound: status 404

二. 原因

这些问题的根本原因是Ribbon配置类不能包含在主程序的@ComponentScan范围内。每一个ServiceId都应该有它自己专属的SpringContext,但错误使用@Bean注解会导致负载均衡规则被多个服务共享,实际上只存在一个配置对象。

三. 修改方式

要解决这些问题,可以通过以下方式之一进行修改:

1. 使用YAML配置

在配置文件中增加对服务provider的rule配置,示例:

1
2
3
base-service:
ribbon:
NFLoadBalancerRuleClassName: com.xxxx.common.Rule

2. 使用System.setProperty()

在应用启动时,可以使用System.setProperty()方法来设置负载均衡规则类,示例:

1
System.setProperty("base-service.ribbon.NFLoadBalancerRuleClassName", Rule.class.getName());

四. 参考资料

补充

官网描述

在[Ribbon]官网中对于自定义指定Rule有如下描述:

官网描述

这个自定义配置类不能放在@ComponentScan扫描的当前包以及子包中,否则这个自定义的IRule会被所有的Ribbon客户端共享,会导致一系列的问题。

常规处理方式

根据官网的描述,不能放在当前包及其子包中被扫描到,因此可以采取以下常规处理方式:

  1. 新建一个独立的package,将自定义的Rule规则类放入其中。
  2. 在主启动类中使用@RibbonClient注解,并指定configuration为自定义规则类的.class文件。

这种方式相对繁琐,但是是一种有效的解决方案。

推荐的方式

为了更简单地解决这个问题,可以考虑使用Spring中bean的不同作用域(scope)。默认情况下,Spring bean是singleton单例模式的,这意味着所有Ribbon客户端在初始化时都会获取到同一个对象。但实际上,还有prototype这一scope可供选择配置,可以将自定义的IRule配置为prototype,示例伪代码如下:

1
2
3
4
5
@Bean
@Scope(value = "prototype")
public IRule myRule() {
return new MyRule();
}

这样,每个Ribbon客户端都会得到一个独立的规则实例,解决了共享规则的问题。

结论

通过采用上述方式,可以顺利解决在自定义Ribbon规则时可能遇到的问题,让您可以继续愉快地编写代码。

引言

在处理Etcd数据镜像时,您可能会遇到以下错误:Error: etcdserver: mvcc: database space exceeded。这是因为Etcd数据库的空间使用超出了限制。本文将介绍如何通过压缩Etcd数据空间和整理碎片来解决这个问题。

阅读全文 »

问题提出

流量有损是在应用发布时的常见问题,其现象通常会反馈到流量监控上,如下图所示,发布过程中服务RT突然升高,造成部分业务响应变慢,给用户的最直观体验就是卡顿;或是请求的500错误数突增,在用户侧可能感受到服务降级或服务不可用,从而影响用户体验。
因为应用发布会伴随流量有损,所以我们往往需要将发布计划移到业务低谷期,并严格限制应用发布的持续时间,尽管如此,还是不能完全避免发布带来的风险,有时甚至不得不选择停机发布。EDAS作为一个通用应用管理系统,应用发布是其最基本的功能之一,而K8s 应用是EDAS中最普遍的应用的形态,下文将通过对EDAS客户真实场景的归纳,从K8s的流量路径入手,分析有损发布产生的原因,并提供实用的解决方案。

阅读全文 »

全链路灰度的做法

1.链路上各个组件和服务能够根据请求流量特征进⾏动态路由。
2.需要对服务下的所有节点进⾏分组,能够区分版本。
3.需要对流量进⾏灰度标识、版本标识。
4.需要识别出不同版本的灰度流量。

思路:
使用 Spring Cloud LoadBalancer 进行流量路由。
服务注册时在 Metadata 中 lane 为泳道标记,空位基线版本。
规定 Http Header 中 laneTag 为泳道标识,由上游带入。
使用 Filter 识别出不同版本的灰度流量并调用 LoadBalancer。

阅读全文 »

引言:在数据库管理和优化中,索引是提高查询效率的关键工具。MongoDB作为一个广泛使用的NoSQL数据库,其索引设计和实现机制为数据检索提供了强大支持。本文深入探讨MongoDB索引的类型、管理、分析以及底层实现原理,旨在为开发者和数据库管理员提供实用指南,以优化数据库性能。

什么是索引

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。索引目标是提高数据库的查询效率,没有索引的话,查询会进行全表扫描(scan every document in a collection),数据量大时严重降低了查询效率。默认情况下Mongo在一个集合(collection)创建时,自动地对集合的_id创建了唯一索引。

索引类型

单键索引 (Single Field)

MongoDB支持所有数据类型中的单个字段索引,并且可以在文档的任何字段上定义。
对于单个字段索引,索引键的排序顺序无关紧要,因为MongoDB可以在任一方向读取索引。
单个例上创建索引:

1
db.集合名.createIndex({"字段名":排序方式}) 

特殊的单键索引 过期索引 TTL ( Time To Live)
TTL索引是MongoDB中一种特殊的索引, 可以支持文档在一定时间之后自动过期删除,目前TTL索引只能在单字段上建立,并且字段类型必须是日期类型。

1
db.集合名.createIndex({"日期字段":排序方式}, {expireAfterSeconds: 秒数})

多键索引(Multikey indexes)

针对属性包含数组数据的情况,MongoDB支持针对数组中每一个element创建索引,Multikey indexes支持 strings,numbers和nested documents。

地理空间索引(Geospatial Index)

针对地理空间坐标数据创建索引。 2dsphere索引,用于存储和查找球面上的点 2d索引,用于存储和查找平面上的点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
db.company.insert(
{
loc : { type: "Point", coordinates: [ 116.482451, 39.914176 ] },
name: "大望路地铁",
category : "Parks"
}
)
db.company.ensureIndex( { loc : "2dsphere" } )
db.company.find({
"loc" : {
"$geoWithin" : {
"$center":[[116.482451,39.914176],0.05]
}
}
})

全文索引

MongoDB提供了针对string内容的文本查询,Text Index支持任意属性值为string或string数组元素的索引查询。 注意:一个集合仅支持最多一个Text Index,中文分词不理想 推荐ES。

1
2
db.集合.createIndex({"字段": "text"})
db.集合.find({"$text": {"$search": "coffee"}})

哈希索引 Hashed Index

针对属性的哈希值进行索引查询,当要使用Hashed index时,MongoDB能够自动的计算hash值,无需程序计算 hash值。注:hash index仅支持

等于查询,不支持范围查询。

1
db.集合.createIndex({"字段": "hashed"})

索引和explain 分析

索引管理

创建索引并在后台运行

1
db.COLLECTION_NAME.createIndex({"字段":排序方式},{background:true});

获取针对某个集合的索引

1
db.COLLECTION_NAME.getIndexes()

索引的大小

1
db.COLLECTION_NAME.totalIndexSize()

索引的重建

1
db.COLLECTION_NAME.reIndex()

索引的删除

1
2
3
db.COLLECTION_NAME.dropIndex("INDEX-NAME")
db.COLLECTION_NAME.dropIndexes()
// 注意: _id 对应的索引是删除不了的

explain 分析

使⽤js循环 插⼊100万条数据 不使⽤索引字段 查询查看执⾏计划 ,然后给某个字段建⽴索引,使⽤索引字段作为查 询条件 再查看执⾏计划进⾏分析 explain()也接收不同的参数,通过设置不同参数我们可以查看更详细的查询计划。

  • queryPlanner:queryPlanner是默认参数,具体执⾏计划信息参考下⾯的表格。
  • executionStats:executionStats会返回执⾏计划的⼀些统计信息(有些版本中和allPlansExecution等同)。
  • allPlansExecution:allPlansExecution⽤来获取所有执⾏计划,结果参数基本与上⽂相同

queryPlanner 默认参数

参数 含义
plannerVersion 查询计划版本
namespace 要查询的集合(该值返回的是该query所查询的表)数据库.集合
indexFilterSet 针对该query是否有indexFilter
parsedQuery 查询条件
winningPlan 被选中的执⾏计划
winningPlan.stage 被选中执⾏计划的stage(查询⽅式),常⻅的有:COLLSCAN/全表扫描:(应该知道就是CollectionScan,就是所谓的“集合扫描”,和mysql中table
scan/heap scan类似,这个就是所谓的性能最烂最⽆奈的由来)、IXSCAN/索引扫描:(是IndexScan,这就说明我们已经命中索引了)、FETCH/根据索引去检索⽂档、SHARD_MERGE/合并分⽚结果、IDHACK/针对_id进⾏查询等
winningPlan.inputStage ⽤来描述⼦stage,并且为其⽗stage提供⽂档和索引关键字。
winningPlan.stage的childstage 如果此处是IXSCAN,表示进⾏的是index scanning。
winningPlan.keyPattern 所扫描的index内容
winningPlan.indexName winning plan所选⽤的index。
winningPlan.isMultiKey 是否是Multikey,此处返回是false,如果索引建⽴在array上,此处将是true。
winningPlan.direction 此query的查询顺序,此处是forward,如果⽤了.sort({字段:-1})将显示backward。
filter 过滤条件
winningPlan.indexBounds winningplan所扫描的索引范围,如果没有制定范围就是[MaxKey, MinKey],这主要是直接定位到mongodb的chunck中去查找数据,加快数据读取。
rejectedPlans 被拒绝的执⾏计划的详细返回,其中具体信息与winningPlan的返回中意义相同,故不在此赘述)
serverInfo MongoDB服务器信息

executionStats参数

参数 含义
executionSuccess 是否执⾏成功
nReturned 返回的⽂档数
executionTimeMillis 执⾏耗时
totalKeysExamined 索引扫描次数
totalDocsExamined ⽂档扫描次数
executionStages 这个分类下描述执⾏的状态
stage 扫描⽅式,具体可选值与上⽂的相同
nReturned 查询结果数量
executionTimeMillisEstimate 检索document获得数据的时间
inputStage.executionTimeMillisEstimate 该查询扫描⽂档 index所⽤时间
works ⼯作单元数,⼀个查询会分解成⼩的⼯作单元
advanced 优先返回的结果数
docsExamined ⽂档检查数⽬,与totalDocsExamined⼀致。检查了总共的document 个数,⽽从返回上⾯的nReturned数量

executionStats返回逐层分析

第⼀层,executionTimeMillis最为直观explain返回值是executionTimeMillis值,指的是这条语句的执⾏时间,这个值当然是希望越少越好。

其中有3个executionTimeMillis,分别是:

executionStats.executionTimeMillis 该query的整体查询时间。
executionStats.executionStages.executionTimeMillisEstimate 该查询检索document获得数据的时间。
executionStats.executionStages.inputStage.executionTimeMillisEstimate 该查询扫描⽂档 index所⽤时间。

第⼆层,index与document扫描数与查询返回条⽬数 这个主要讨论3个返回项 nReturnedtotalKeysExaminedtotalDocsExamined,分别代表该条查询返回的条⽬、索引扫描条⽬、⽂档扫描条⽬。 这些都是直观地影响到executionTimeMillis,我们需要扫描的越少速度越快。 对于⼀个查询,我们最理想的状态是: nReturned=totalKeysExamined=totalDocsExamined

第三层,stage状态分析 那么⼜是什么影响到了totalKeysExaminedtotalDocsExamined?是stage的类型。类型列举如下:

  • COLLSCAN: 全表扫描
  • IXSCAN: 索引扫描
  • FETCH:根据索引去检索指定document
  • SHARD_MERGE:将各个分⽚返回数据进⾏merge
  • SORT:表明在内存中进⾏了排序
  • LIMIT:使⽤limit限制返回数
  • SKIP:使⽤skip进⾏跳过
  • IDHACK:针对_id进⾏查询
  • SHARDING_FILTER:通过mongos对分⽚数据进⾏查询
  • COUNT:利⽤db.coll.explain().count()之类进⾏count运算
  • TEXT:使⽤全⽂索引进⾏查询时候的stage返回
  • PROJECTION:限定返回字段时候stage的返回

对于普通查询,我希望看到stage的组合(查询的时候尽可能⽤上索引):

Fetch+IDHACK
Fetch+IXSCAN
Limit+(Fetch+IXSCAN)
PROJECTION+IXSCAN
SHARDING_FITER+IXSCAN

不希望看到包含如下的stage:

COLLSCAN(全表扫描)
SORT(使⽤sort但是⽆index)
COUNT 不使⽤index进⾏count)

allPlansExecution参数

1
queryPlanner 参数和executionStats的拼接

MongoDB 索引底层实现原理分析

MongoDB 是文档型的数据库,它使用BSON 格式保存数据,比关系型数据库存储更方便。比如之前关系型数据库中处理用户、订单等数据要建立对应的表,还要建立它们之间的关联关系。但是BSON就不一样了,我们可以把一条数据和这条数据对应的数据都存入一个BSON对象中,这种形式更简单,通俗易懂。

MySql是关系型数据库,数据的关联性是非常强的,区间访问是常见的一种情况,底层索引组织数据使用B+树,B+树由于数据全部存储在叶子节点,并且通过指针串在一起,这样就很容易的进行区间遍历甚至全部遍历。

MongoDB使用B-树,所有节点都有Data域,只要找到指定索引就可以进行访问,单次查询从结构上来看要快于MySql。

B- 树

B-树是一种自平衡的搜索树,形式很简单:

B-树的特点:

  1. 多路 非二叉树
  2. 每个节点 既保存数据 又保存索引
  3. 搜索时 相当于二分查找

B+ 树

B+树是B-树的变种

B+ 树的特点:

  1. 多路非二叉
  2. 只有叶子节点保存数据
  3. 搜索时 也相当于二分查找
  4. 增加了 相邻节点指针

从上⾯我们可以看出最核⼼的区别主要有俩,⼀个是数据的保存位置,⼀个是相邻节点的指向。就是这俩造成了

MongoDB和MySql的差别。

  1. B+树相邻接点的指针可以⼤⼤增加区间访问性,可使⽤在范围查询等,⽽B-树每个节点 key 和 data 在⼀起适合随机读写 ,⽽区间查找效率很差。
  2. B+树更适合外部存储,也就是磁盘存储,使⽤B-结构的话,每次磁盘预读中的很多数据是⽤不上的数据。因 此,它没能利⽤好磁盘预读的提供的数据。由于节点内⽆ data 域,每个节点能索引的范围更⼤更精确。
  3. 注意这个区别相当重要,是基于(1)(2)(3)的,B-树每个节点即保存数据⼜保存索引 树的深度⼤,所以磁盘IO的次数多,B+树只有叶⼦节点保存,较B树⽽⾔深度⼩,磁盘IO少,有利于区间访问。

引言:在当今的快速发展的互联网及大数据时代,选择合适的数据库对于确保应用的性能、可扩展性和灵活性至关重要。MongoDB,作为一种领先的NoSQL数据库,因其出色的性能、灵活的数据模型以及易于扩展的特性,在各种场景下被广泛应用。本文深入探讨MongoDB的适用场景,以及它在不同行业中的具体应用,帮助开发者和架构师了解如何在他们的项目中有效利用MongoDB。

MongoDB的适用场景

网站数据

MongoDB非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。这使其成为动态网站和应用后端存储的理想选择。

缓存

因其高性能特性,MongoDB也适合作为信息基础设施的缓存层。在系统重启后,MongoDB搭建的持久化缓存层可以有效避免下层数据源过载。

大尺寸、低价值的数据

使用传统关系型数据库存储某些大尺寸低价值数据时可能会显得浪费。MongoDB以其高效的存储能力,成为这类数据的理想存储解决方案。

高伸缩性场景

对于需要数十甚至数百台服务器组成的数据库,MongoDB的设计提供了内置的MapReduce引擎支持和集群高可用解决方案,非常适合高伸缩性需求。

对象及JSON数据的存储

MongoDB的BSON数据格式非常适合文档化格式的存储及查询,使其成为存储JSON数据和对象的理想选择。

MongoDB的行业具体应用场景

游戏领域

使用MongoDB存储用户信息、装备、积分等,直接以内嵌文档的形式存储,便于查询、更新,满足游戏行业对数据操作的高性能需求。

物流行业

MongoDB能够存储订单信息,并通过内嵌数组形式存储订单状态更新,一次查询即可获取订单的所有变更,适应物流行业动态变化的需求。

社交网络

在社交应用中,MongoDB可用于存储用户信息及动态内容,利用地理位置索引实现附近的人、地点等功能,增强社交互动体验。

物联网 (IoT)

MongoDB适合存储智能设备信息及设备日志,支持对海量数据进行多维度分析,满足物联网应用的数据处理需求。

直播平台

直播平台使用MongoDB存储用户信息、礼物信息等,满足实时数据处理的需求,提升用户体验。

如何抉择是否使用MongoDB

在决定是否采用MongoDB作为项目数据库时,可以考虑以下应用特征:

  • 应用不需要事务及复杂join支持
  • 新应用,需求频繁变动,数据模型无法确定,希望快速迭代开发
  • 应用需要2000-3000以上的读写QPS
  • 应用需处理TB甚至PB级别数据存储
  • 应用发展迅速,需要能快速水平扩展
  • 应用要求存储的数据不丢失
  • 应用需要99.999%高可用
  • 应用需要大量的地理位置查询、文本查询

通过对上述特征的评估,可以更好地决定MongoDB是否适合你的项目需求。


MongoDB以其灵活的数据模型、高性能和易于扩展的特点,在多个行业和场景下展现出了巨大的潜力。了解和评估这些适用场景和行业应用,将帮助你充分利用MongoDB提升应用的性能和用户体验。

Docker简介

百科说:Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。

阅读全文 »

zsh相较于其他shell,在命令补全,历史记录,跳转,别名方面有很多优势。zsh本身配置可能复杂一些,所以有人在github上创建了一个可以快速上手的zsh项目,叫做oh-my-zsh,zsh + oh-my-zsh可以极大的方便我们的命令操作。

阅读全文 »