.Net/C#分库分表高性能O(1)瀑布流分页

框架介绍

依照惯例首先介绍本期主角:ShardingCore 一款ef-core下高性能、轻量级针对分表分库读写分离的解决方案,具有零依赖、零学习成本、零业务代码入侵

dotnet下唯一一款全自动分表,多字段分表框架,拥有高性能,零依赖、零学习成本、零业务代码入侵,并且支持读写分离动态分表分库,同一种路由可以完全自定义的新星组件框架

你的star和点赞是我坚持下去的最大动力,一起为.net生态提供更好的解决方案

项目地址

  • github地址 https://github.com/dotnetcore/sharding-core

  • gitee地址 https://gitee.com/dotnetchina/sharding-core

背景

在大数据量下针对app端的瀑布流页面分页的优化实战,有大量的数据,前端需要以瀑布流的形式展示出来,我们最简单的就是以用户发布的文章为例,假设我们有大量的文章帖子被,需求需要按帖子的发布时间倒序展示给用户看,那么在手机端我们一般都是以下拉刷新,上拉加载的形式去展示,那么我们一般会有以下集中写法。

常规分页操作

select count(*) from article
select * from article order by publish_time desc limit 1,20

这个操作是一般我们的常规分页操作,先进行total然后进行分页获取,这种做法的好处是支持任意规则的分页,缺点就是需要查询两次,一次count一次limit当然后期数据量实在太大可以只需要第一次count,但是也有一个问题就是如果数据量一直在变化会出现下一次分页中还会有上一次的部分数据,因为数据在不断地新增,你的分页没跟上发布的速度那么就会有这个情况发送.

瀑布流分页

除了上述常规分页操作外,我们针对特定顺序的分页也可以进行特定的分页方式来实现高性能,因为基于大前提我们是大数量下的瀑布流,我们的文章假设是以雪花id作为主键,那么我们的分页可以这么写

select * from article where id<last_id order by publish_time desc limit 0,20

首先我们来分析一下,这个语句是利用了插入的数据分布是顺序和你需要查询的排序一直来实现的,又因为id不会重复并且雪花id的顺序和时间是一致的都是同向的所以可以利用这种方式来进行排序,limit每次不需要跳过任何数目,直接获取需要的数目即可,只需要传递上一次的查询结果的id即可,这个方式弥补了上述常规分页带来的问题,并且拥有非常高的性能,但是缺点也显而易见,不支持跳页,不支持任意排序,所以这个方式目前来说非常适合前端app的瀑布流排序。

分片下的实现

首先分片下需要实现这个功能我们需要有id支持分片,并且publish_time按时间分表,两者缺一不可。

原理

假设文章表article我们是以publish_time作为分片字段,假设按天分表,那么我们会拥有如下的表

article_20220101、article_20220102、article_20220103、article_20220104、article_20220105、article_20220106......

雪花id辅助分片

因为雪花id可以反解析出时间,所以我们对雪花id的=,>=,>,<=,<,contains的操作都是可以进行辅助分片进行缩小分片范围
假设我们的雪花id解析出来是2021-01-05 11:11:11,那么针对这个雪花id<小于操作我们可以等价于x < 2021-01-05 11:11:11,那么如果我问你这下我们需要查询的表有哪些,很明显 [article_20220101、article_20220102、article_20220103、article_20220104、article_20220105],除了20220106外我们都需要查询。

union all分片模式

如果你使用union all的分片模式那么通常会将20220101-20220105的所有的表进行union all然后机械能过滤,那么优点可想而知:简单,连接数消耗仅1个,sql语句支持的多,缺点也显而易见,优化起来后期是个很大的问题,并且跨库下的使用有问题

select * from 
(select * from article_20220101 union all select * from article_20220102 union all select * from article_20220103....) twhere id<last_id order by publish_time desc limit 0,20

流式分片,顺序查询

如果你是流式分片模式进行聚合通常我们会将20220101-20220105的所有的表进行并行的分别查询,然后针对每个查询的结果集进行优先级队列的排序后获取,优点:语句简单便于优化,性能可控,支持分库,缺点:实现复杂,连接数消耗多

select * from article_20220101 where id<last_id order by publish_time desc limit 0,20
select * from article_20220102where id<last_id order by publish_time desc limit 0,20
select * from article_20220103 where id<last_id order by publish_time desc limit 0,20
......

流式分片下的优化

目前 ShardingCore采用的是流式聚合+union all,当且仅当用户手动3调用UseUnionAllMerge时会将分片sql转成union all 聚合。

针对上述瀑布流的分页ShardingCore是这么操作的

  • 确定分片表的顺序,也就是因为分片字段是publish_time,又因为排序字段是publish_time所以分片表其实是有顺序的,也就是[article_20220105、article_20220104、article_20220103、article_20220102、article_20220101],
    因为我们是开启n个并发线程所以这个排序可能没有意义,但是如果我们是仅开启设置单个连接并发的时候,程序将现在通过id<last_id进行表筛选,之后依次从大到小进行获取直到满足skip+take也就是0+20=20条数据后,进行直接抛弃剩余查询返回结果,那么本次查询基本上就是和单表查询一样,因为基本上最多跨两张表基本可以满足要求(具体场景不一定)

  • 说明:假设last_id反解析出来的结果是2022-01-04 05:05:05那么可以基本上排除article_20220105,判断并发连接数如果是1那么直接查询article_20220104,如果不满足继续查询article_20220103,直到查询结果为20条如果并发连接数是2那么查询[article_20220104、article_20220103]如果不满足继续下面两张表直到获取到结果为20条数据,所以我们可以很清晰的了解其工作原理并且来优化

说明

  • 通过上述优化可以保证流式聚合查询在顺序查询下的高性能O(1)

  • 通过上述优化可以保证客户端分片拥有最小化连接数控制

  • 设置合理的主键可以有效的解决我们在大数据分片下的性能优化

实践

ShardingCore目前针对分片查询进行了不断地优化和尽可能的无业务代码入侵来实现高性能分片查询聚合。

接下来我将为大家展示一款dotnet下唯一一款全自动路由、多字段分片、无代码入侵、高性能顺序查询的框架在传统数据库领域下的分片功能,如果你使用过我相信你一定会爱上他。

第一步:安装依赖

# ShardingCore核心框架 版本6.4.2.4+
PM> Install-Package ShardingCore
# 数据库驱动这边选择的是mysql的社区驱动 efcore6最新版本即可
PM> Install-Package Pomelo.EntityFrameworkCore.MySql

第二步添加对象和上下文

有很多朋友问我一定需要使用fluentapi来使用ShardingCore吗,只是个人喜好,这边我才用dbset+attribute来实现

//文章表[Table(nameof(Article))]public class Article{[MaxLength(128)][Key]public string Id { get; set; }[MaxLength(128)][Required]public string Title { get; set; }[MaxLength(256)][Required]public string Content { get; set; }public DateTime PublishTime { get; set; }}
//dbcontextpublic class MyDbContext:AbstractShardingDbContext,IShardingTableDbContext{public MyDbContext(DbContextOptions<MyDbContext> options) : base(options){
//请勿添加会导致efcore 的model提前加载的方法如Database.xxxx}public IRouteTail RouteTail { get; set; }public DbSet<Article> Articles { get; set; }}

第三步:添加文章路由

public class ArticleRoute:AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute<Article>{public override void Configure(EntityMetadataTableBuilder<Article> builder){builder.ShardingProperty(o => o.PublishTime);}public override bool AutoCreateTableByTime(){return true;}public override DateTime GetBeginTime(){return new DateTime(2022, 3, 1);}}

到目前为止基本上Article已经支持了按天分表

第四步:添加查询配置,让框架知道我们是顺序分表且定义分表的顺序

public class TailDayReverseComparer : IComparer<string>{public int Compare(string? x, string? y){//程序默认使用的是正序也就是按时间正序排序我们需要使用倒序所以直接调用原生的比较器然后乘以负一即可return Comparer<string>.Default.Compare(x, y) * -1;}}//当前查询满足的复核条件必须是单个分片对象的查询,可以join普通非分片表public class ArticleEntityQueryConfiguration:IEntityQueryConfiguration<Article>{public void Configure(EntityQueryBuilder<Article> builder){//设置默认的框架针对Article的排序顺序,这边设置的是倒序builder.ShardingTailComparer(new TailDayReverseComparer());如下设置和上述是一样的效果让框架真对Article的后缀排序使用倒序//builder.ShardingTailComparer(Comparer<string>.Default, false);//简单解释一下下面这个配置的意思//第一个参数表名Article的哪个属性是顺序排序和Tail按天排序是一样的这边使用了PublishTime//第二个参数表示对属性PublishTime asc时是否和上述配置的ShardingTailComparer一致,true表示一致,很明显这边是相反的因为默认已经设置了tail排序是倒序//第三个参数表示是否是Article属性才可以,这边设置的是名称一样也可以,因为考虑到匿名对象的selectbuilder.AddOrder(o => o.PublishTime, false,SeqOrderMatchEnum.Owner|SeqOrderMatchEnum.Named);//这边为了演示使用的id是简单的时间格式化所以和时间的配置一样builder.AddOrder(o => o.Id, false,SeqOrderMatchEnum.Owner|SeqOrderMatchEnum.Named);//这边设置如果本次查询默认没有带上述配置的order的时候才用何种排序手段//第一个参数表示是否和ShardingTailComparer配置的一样,目前配置的是倒序,也就是从最近时间开始查询,如果是false就是从最早的时间开始查询//后面配置的是熔断器,也就是复核熔断条件的比如FirstOrDefault只需要满足一个就可以熔断builder.AddDefaultSequenceQueryTrip(true, CircuitBreakerMethodNameEnum.Enumerator, CircuitBreakerMethodNameEnum.FirstOrDefault);//这边配置的是当使用顺序查询配置的时候默认开启的连接数限制是多少,startup一开始可以设置一个默认是当前cpu的线程数,这边优化到只需要一个线程即可,当然如果跨表那么就是串行执行builder.AddConnectionsLimit(1, LimitMethodNameEnum.Enumerator, LimitMethodNameEnum.FirstOrDefault);}}

第五步:添加配置到路由

public class ArticleRoute:AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute<Article>{//省略.....public override IEntityQueryConfiguration<Article> CreateEntityQueryConfiguration(){return new ArticleEntityQueryConfiguration();}}

第六步:startup配置

var builder = WebApplication.CreateBuilder(args);// Add services to the container.
ILoggerFactory efLogger = LoggerFactory.Create(builder =>
{builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
});
builder.Services.AddControllers();
builder.Services.AddShardingDbContext<MyDbContext>().AddEntityConfig(o =>{o.CreateShardingTableOnStart = true;o.EnsureCreatedWithOutShardingTable = true;o.AddShardingTableRoute<ArticleRoute>();}).AddConfig(o =>{o.ConfigId = "c1";o.UseShardingQuery((conStr, b) =>{b.UseMySql(conStr, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});o.UseShardingTransaction((conn, b) =>{b.UseMySql(conn, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});o.AddDefaultDataSource("ds0", "server=127.0.0.1;port=3306;database=ShardingWaterfallDB;userid=root;password=root;");o.ReplaceTableEnsureManager(sp => new MySqlTableEnsureManager<MyDbContext>());}).EnsureConfig();var app = builder.Build();app.Services.GetRequiredService<IShardingBootstrapper>().Start();
using (var scope = app.Services.CreateScope())
{var myDbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();if (!myDbContext.Articles.Any()){List<Article> articles = new List<Article>();var beginTime = new DateTime(2022, 3, 1, 1, 1,1);for (int i = 0; i < 70; i++){var article = new Article();article.Id = beginTime.ToString("yyyyMMddHHmmss");article.Title = "标题" + i;article.Content = "内容" + i;article.PublishTime = beginTime;articles.Add(article);beginTime= beginTime.AddHours(2).AddMinutes(3).AddSeconds(4);}myDbContext.AddRange(articles);myDbContext.SaveChanges();}
}
app.MapControllers();app.Run();

第七步编写查询表达式

public async Task<IActionResult> Waterfall([FromQuery] string lastId,[FromQuery]int take){Console.WriteLine($"-----------开始查询,lastId:[{lastId}],take:[{take}]-----------");var list = await _myDbContext.Articles.WhereIf(o => String.Compare(o.Id, lastId) < 0,!string.IsNullOrWhiteSpace(lastId)).Take(take)..OrderByDescending(o => o.PublishTime)ToListAsync();return Ok(list);}

运行程序
5e34c9b057dea337e1115b2d6c2dc6ad.png

因为07表是没有的所以这次查询会查询07和06表,之后我们进行下一次分页传入上次id

5c27d8ff75b9e55273544e84afb733f6.png

因为没有对Article.Id进行分片路由的规则编写所以没办法进行对id的过滤,那么接下来我们配置Id的分片规则

首先针对ArticleRoute进行代码编写

public class ArticleRoute:AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute<Article>{public override void Configure(EntityMetadataTableBuilder<Article> builder){builder.ShardingProperty(o => o.PublishTime);builder.ShardingExtraProperty(o => o.Id);}public override bool AutoCreateTableByTime(){return true;}public override DateTime GetBeginTime(){return new DateTime(2022, 3, 1);}public override IEntityQueryConfiguration<Article> CreateEntityQueryConfiguration(){return new ArticleEntityQueryConfiguration();}public override Expression<Func<string, bool>> GetExtraRouteFilter(object shardingKey, ShardingOperatorEnum shardingOperator, string shardingPropertyName){switch (shardingPropertyName){case nameof(Article.Id): return GetArticleIdRouteFilter(shardingKey, shardingOperator);}return base.GetExtraRouteFilter(shardingKey, shardingOperator, shardingPropertyName);}/// <summary>/// 文章id的路由/// </summary>/// <param name="shardingKey"></param>/// <param name="shardingOperator"></param>/// <returns></returns>private Expression<Func<string, bool>> GetArticleIdRouteFilter(object shardingKey,ShardingOperatorEnum shardingOperator){//将分表字段转成订单编号var id = shardingKey?.ToString() ?? string.Empty;//判断订单编号是否是我们符合的格式if (!CheckArticleId(id, out var orderTime)){//如果格式不一样就直接返回false那么本次查询因为是and链接的所以本次查询不会经过任何路由,可以有效的防止恶意攻击return tail => false;}//当前时间的tailvar currentTail = TimeFormatToTail(orderTime);//因为是按月分表所以获取下个月的时间判断id是否是在临界点创建的//var nextMonthFirstDay = ShardingCoreHelper.GetNextMonthFirstDay(DateTime.Now);//这个是错误的var nextMonthFirstDay = ShardingCoreHelper.GetNextMonthFirstDay(orderTime);if (orderTime.AddSeconds(10) > nextMonthFirstDay){var nextTail = TimeFormatToTail(nextMonthFirstDay);return DoArticleIdFilter(shardingOperator, orderTime, currentTail, nextTail);}//因为是按月分表所以获取这个月月初的时间判断id是否是在临界点创建的//if (orderTime.AddSeconds(-10) < ShardingCoreHelper.GetCurrentMonthFirstDay(DateTime.Now))//这个是错误的if (orderTime.AddSeconds(-10) < ShardingCoreHelper.GetCurrentMonthFirstDay(orderTime)){//上个月tailvar previewTail = TimeFormatToTail(orderTime.AddSeconds(-10));return DoArticleIdFilter(shardingOperator, orderTime, previewTail, currentTail);}return DoArticleIdFilter(shardingOperator, orderTime, currentTail, currentTail);}private Expression<Func<string, bool>> DoArticleIdFilter(ShardingOperatorEnum shardingOperator, DateTime shardingKey, string minTail, string maxTail){switch (shardingOperator){case ShardingOperatorEnum.GreaterThan:case ShardingOperatorEnum.GreaterThanOrEqual:{return tail => String.Compare(tail, minTail, StringComparison.Ordinal) >= 0;}case ShardingOperatorEnum.LessThan:{var currentMonth = ShardingCoreHelper.GetCurrentMonthFirstDay(shardingKey);//处于临界值 o=>o.time < [2021-01-01 00:00:00] 尾巴20210101不应该被返回if (currentMonth == shardingKey)return tail => String.Compare(tail, maxTail, StringComparison.Ordinal) < 0;return tail => String.Compare(tail, maxTail, StringComparison.Ordinal) <= 0;}case ShardingOperatorEnum.LessThanOrEqual:return tail => String.Compare(tail, maxTail, StringComparison.Ordinal) <= 0;case ShardingOperatorEnum.Equal:{var isSame = minTail == maxTail;if (isSame){return tail => tail == minTail;}else{return tail => tail == minTail || tail == maxTail;}}default:{return tail => true;}}}private bool CheckArticleId(string orderNo, out DateTime orderTime){//yyyyMMddHHmmssif (orderNo.Length == 14){if (DateTime.TryParseExact(orderNo, "yyyyMMddHHmmss", CultureInfo.InvariantCulture,DateTimeStyles.None, out var parseDateTime)){orderTime = parseDateTime;return true;}}orderTime = DateTime.MinValue;return false;}}

完整路由:针对Id进行多字段分片并且支持大于小于排序

以上是多字段分片的优化,详情博客可以点击这边 .Net下你不得不看的分表分库解决方案-多字段分片

然后我们继续查询看看结果

7d65b9a5c7435b6832d5bef4068c2910.png
第三页也是如此
eeeb56463a4957ff858d40ed914108a5.png

总结

当前框架虽然是一个很年轻的框架,但是我相信我对其在分片领域的性能优化应该在.net现有的所有框架下找不出第二个,并且框架整个也支持union all聚合,可以满足列入group+first的特殊语句的查询,又有很高的性能,一个不但是全自动分片而且还是高性能框架拥有非常多的特性性能,目标是榨干客户端分片的最后一点性能。

MAKE DOTNET GREAT AGAIN

最后的最后

身位一个dotnet程序员我相信在之前我们的分片选择方案除了mycatshardingsphere-proxy外没有一个很好的分片选择,但是我相信通过ShardingCore 的原理解析,你不但可以了解到大数据下分片的知识点,更加可以参与到其中或者自行实现一个,我相信只有了解了分片的原理dotnet才会有更好的人才和未来,我们不但需要优雅的封装,更需要原理的是对原理了解。

我相信未来dotnet的生态会慢慢起来配上这近乎完美的语法

您的支持是开源作者能坚持下去的最大动力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/290691.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos静默安装oracle关于报错UnsatisfiedLinkError exception loading native library:njni10

静默安装oracle时&#xff0c;日志文件中打印出如下语句 提示Oracle NetConfiguration Assistant failed&#xff0c;原因是找不到libaio.so.1 这是缺少依赖 执行指令 yum -y install libaio* libaio-devel* 删除home文件夹&#xff0c;再次执行 ./runInstaller -silent…

Ant—使用Ant构建一个简单的Java工程(两)

博客《Ant—使用Ant构建一个简单的Java项目&#xff08;一&#xff09;》演示了使用Ant工具构建简单的Java项目&#xff0c;接着这个样例来进一步学习Ant&#xff1a;上面样例须要运行多条ant命令才干运行Test类中的main函数&#xff0c;能不能简化须要运行命令呢&#xff1f;答…

Java Thread Status(转)

public static enum Thread.State extends Enum<Thread.State>线程状态。线程可以处于下列状态之一&#xff1a; 1.NEW 至今尚未启动的线程的状态。 2.RUNNABLE 可运行线程的线程状态。 处于可运行状态的某一线程正在 Java 虚拟机中运行&#xff0c;但它可能正在…

秀!微软《550页图解.Net+WPF完整版》.pdf 附下载!

赶紧看看微软公司基于最新的.netwpf编写文档。对于零基础可以作为.net的快速入门教材&#xff0c;对于高级程序员而言&#xff0c;这也是你的充电之路&#xff01;NO.1资料介绍该手册,全面的介绍.net的新特性&#xff0c;看完这个资料&#xff0c;你能够对.net/c#WPF的新功能&a…

《移动App测试的22条军规》—第1章1.2节移动App的生命周期

本节书摘来自异步社区《移动App测试的22条军规》一书中的第1章&#xff0c;第1.2节移动App的生命周期&#xff0c;作者黄勇&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.2 移动App的生命周期移动App测试的22条军规&#xff08;1&#xff09;对于还处于…

电子商务应用课程知识整理 第一章-电子商务概述与类型

一、电子商务定义 电子商务&#xff08;Electronic Commerce&#xff0c;简称EC&#xff09;&#xff0c;是指在全球各地广泛的商业贸易活动中&#xff0c;在因特网开放的网络环境下&#xff0c;基于客户端&#xff08;浏览器、移动端&#xff09;/服务器&#xff0c;买卖双方…

电子商务应用课程知识整理 第二章-电子商务相关知识与技术

一、万维网 万维网&#xff08;亦作“网络”、www、Web或World Wide Web&#xff09;&#xff0c;是一个资源空间。在这个空间中包含一些有用的事务&#xff0c;有一个”统一资源标识符“&#xff08;URL&#xff09;表示。这些资源通过超文本传输协议&#xff08;HTTP&#x…

Avalonia跨平台入门第十三篇之Expander控件

在前面分享的几篇中咱已经玩耍了Popup、ListBox多选、Grid动态分、RadioButton模板、控件的拖放效果、控件的置顶和置底、控件的锁定、自定义Window样式、动画效果;今天趁着空闲时间接着去摸索基于Expander控件实现的效果,最终实现的效果如下图:来看看具体的布局:Expander控件样…

《版式设计——日本平面设计师参考手册》—第1章应用对象样式

本节书摘来自异步社区《版式设计——日本平面设计师参考手册》一书中的第1章应用对象样式&#xff0c;作者Designing编辑部,更多章节内容可以访问云栖社区“异步社区”公众号查看。 应用对象样式版式设计——日本平面设计师参考手册对象样式可以帮助我们更有效率地进行工作。 h…

软件概要设计做什么,怎么做

2019独角兽企业重金招聘Python工程师标准>>> 作者&#xff1a;赵磊 博客&#xff1a;http://elf8848.iteye.com 软件概要设计做什么&#xff0c;怎么做 一、软件设计一般流程&#xff1a; 1、先前的软件需求分析阶段&#xff0c;已经搞清楚了 “要解决什么问题”…

开源社区的激励模型

| 作者&#xff1a;庄表伟| 编辑&#xff1a;刘雪洁| 设计&#xff1a;宋传琪| 责编&#xff1a;王玥敏缘起开源社区里的激励模型&#xff0c;其实是一个经久不衰的老话题。4年多以前&#xff0c;我还写过一篇《区块链技术是否会终结开源时代&#xff1f;》&#xff0c;也是在讨…

电子商务应用课程知识整理 第四章-搜索引擎

文章目录一、搜索引擎定义分类1.全文搜索引擎2.目录索引3.元搜索引擎4.垂直搜索引擎工作原理1.抓取网页2.处理网页3.提供检索服务核心算法组成部分发展趋势二、网络蜘蛛三、中文分词基于词典的分词方法基于统计的分词方法基于理解的分词方法分词难点1.歧义识别2.新词识别四、链…

《Network Warrior中文版(第2版)——思科网络工程师必备手册》一3.3 自动协商故障...

本节书摘来自异步社区《Network Warrior中文版&#xff08;第2版&#xff09;——思科网络工程师必备手册》一书中的第3章&#xff0c;第3.3节&#xff0c;作者【美】Gary A. Donahue&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看 3.3 自动协商故障 Network…

Linux 命令(三)--用户管理

2019独角兽企业重金招聘Python工程师标准>>> 查看用户: cat /etc/passwd --------------------- 添加用户: useradd 选项 用户名 -d 目录 指定用户主目录,如果不存在使用-m 创建 -g 指定用户所属的用户组 -G 用户组 ,用户组指定用户所属的附加组 -e <有效期> …

1.单机部署hadoop测试环境

之前看了很多理论上的知识&#xff0c;感觉云里雾里的&#xff0c;所以赶紧着手搭建个单机版的hadoop跑一跑&#xff0c;开启自学大数据技术的第一步~~ 1.在开源的世界里&#xff0c;我就是个土豪&#xff0c;要啥有啥&#xff0c;所以首先你得有个jdk&#xff0c;有钱所以用最…

《互联网+流通——F2R助力传统产业创新与转型》一一第1章 “互联网+”的新时代...

第1章 “互联网&#xff0b;”的新时代 回顾过去20多年&#xff0c;互联网在中国不断发展壮大&#xff0c;从根本上重构了中国人的生活方式。伴随“互联网”在2015年正式被写入国家战略&#xff0c;“互联网”将助推国民经济三驾马车的再造新生&#xff0c;以创新驱动中国经济的…

Oracle数据库体系结构

文章目录Oracle系统体系结构由三个部分组成&#xff1a;**实例、物理结构和逻辑结构**实例和物理结构&#xff08;数据库&#xff09;组成了Oracle服务器。一、实例1.1 内存结构1.1.1 系统全局区&#xff08;SGA&#xff09;1.1.1.1 共享池&#xff08;共享储存区&#xff09;1…

Distributed transactions with multiple databases, Spring Boot, Spring Data JPA and Atomikos

2019独角兽企业重金招聘Python工程师标准>>> A couple of weeks ago I was evaluating the possibility to use Spring Boot, Spring Data JPA and Atomikos for distributed transactions involving multiple databases. After looking at the Spring blog articl…

Avalonia跨平台入门第十四篇之ListBox折叠列表

在前面分享的几篇中咱已经玩耍了Popup、ListBox多选、Grid动态分、RadioButton模板、控件的拖放效果、控件的置顶和置底、控件的锁定、自定义Window样式、动画效果、Expander控件;今天趁着空闲时间接着去摸索基于ListBox的折叠列表的效果,最终实现的效果如下图:先来看看布局吧:…

iptables配置详解

-A参数是将规则写到现有链规则的最后面-I 参数默认是将一条规则添加到现有规则链的最前面&#xff0c;当然也可以指定插入到第几行 行数可以用数字来指定 比如说将一条规则添加到某一条链的第三行 那么原来在第三行的规则就会降到下一行第四行。例如&#xff1a; iptables -I …