程序员修仙之路--高性能排序多个文件

640?wx_fmt=gif

点击上方蓝色字体,关注我们

640?wx_fmt=gif

菜菜呀,昨天晚上班级空间崩溃了

640?wx_fmt=png程序员主力 Y总640?wx_fmt=gif

what?

640?wx_fmt=png菜菜640?wx_fmt=jpeg

我看服务器上写了很多个日志文件,我看着太费劲了,能不能按照日期排序整合成一个文件呀?

640?wx_fmt=png程序员主力 Y总640?wx_fmt=gif

Y总要查日志呀?

640?wx_fmt=png菜菜640?wx_fmt=jpeg

我就是喜欢编程,编程就是我的全部,给你半个小时搞一下

640?wx_fmt=png程序员主力 Y总640?wx_fmt=gif

天天这么短时间搞这么多烂七八糟的需求,能不能给我涨点工资呀?

640?wx_fmt=png菜菜640?wx_fmt=jpeg

你去和X总说,我不管这事,我只管编程!!

640?wx_fmt=png程序员主力 Y总640?wx_fmt=gif

............

640?wx_fmt=png菜菜640?wx_fmt=jpeg640?wx_fmt=png

菜菜的涨工资申请还在待审批中....

640?wx_fmt=png

        作为一个技术人员,技术的问题还是要解决。经过线上日志的分析,日志采用小时机制,一个小时一个日志文件,同一个小时的日志文件有多个,也就是说同一时间内的日志有可能分散在多个日志文件中,这也是Y总要合并的主要原因。每个日志文件大约有500M,大约有100个。此时,如果你阅读到此文章,该怎么做呢?不如先静心想2分钟!!

640?wx_fmt=gif问题分析

要想实现Y总的需求其实还是有几个难点的:

1.  如何能把所有的日志文件按照时间排序

2.  日志文件的总大小为500M*100 ,大约50G,所以全部加载到内存是不可能的

3.  程序执行过程中,要频繁排序并查找最小元素。


那我们该怎么做呢?其中一个解决方案就是它:

640?wx_fmt=gif解决方案堆定义

堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。


堆总是满足下列性质:

1. 堆中某个节点的值总是不大于或不小于其父节点的值

2. 堆总是一棵完全二叉树(完全二叉树要求,除了最后一层,其他层的节点个数都是满的,最后一层的节点都靠左排列)

        对于每个节点的值都大于等于子树中每个节点值的堆,我们叫作“大顶堆”。对于每个节点的值都小于等于子树中每个节点值的堆,我们叫作“小顶堆”。

640?wx_fmt=png


堆实现

        完全二叉树比较适合用数组来存储(链表也可以实现)。为什么这么说呢?用数组来存储完全二叉树是非常节省存储空间的。因为我们不需要存储左右子节点的指针,单纯地通过数组的下标,就可以找到一个节点的左右子节点和父节点。

640?wx_fmt=jpeg

        经过上图可以发现,数组位置0为空,虽然浪费了一个存储空间,但是当计算元素在数组位置的时候确非常方便:数组下标为X的元素的左子树的下标为2x,右子树的下标为2x+1。

        其实实现一个堆非常简单,就是顺着元素所在的路径,向上或者向下对比然后交换位置


1. 添加元素

    添加元素的时候我们习惯采用自下而上的调整方式来调整堆,我们在数组的最后一个空闲位置插入新元素,按照堆的下标上标原则查找到父元素对比,如果小于父元素的值(大顶堆),则互相交换。如图:

640?wx_fmt=jpeg


2. 删除最大(最小元素)

    对于大顶堆,堆顶的元素就是最大元素。删除该元素之后,我们需要把第二大元素提到堆顶位置。依次类推,直到把路径上的所有元素都调整完毕。

640?wx_fmt=jpeg


640?wx_fmt=gif扩展阅读

1.  小顶堆的顶部元素其实就是整个堆最小的元素,大顶堆顶部元素是整个堆的最大元素。这也是堆排序的最大优点,取最小元素或者最大元素时间复杂度为O(1)

2.  删除元素的时候我们要注意一点,如果采用自顶向下交换元素的方式,在很多情况下造成堆严重的不平衡(左右子树深度相差较大)的情况,为了防止类似情况,我们可以把最后一个元素提到堆顶,然后调整的策略,因为最后一个元素总是在最后一级,不会造成左右子树相差很大的情况。

3.  对于有重复元素的堆,一种解决方法是认为是谁先谁大,后进入堆的元素小于先进入堆的元素,这样在查找的时候一定要查彻底才行。另外一种方式是在堆的每个元素中存储一个链表,用来存放相同的元素,原理类似于散列表。不过这样在删除这个元素的时候需要特殊处理一下。

4.  删除堆顶数据和往堆中插入数据的时间复杂度都是 O(logn)。

5.  不断调整堆的过程其实就是排序过程,在某些场景下,我们可以利用堆来实现排序。

640?wx_fmt=gif640?wx_fmt=gif


640?wx_fmt=gifasp.net core 模拟代码以下代码经过少许修改甚至不修改的情况下可直接在生产环境应用


小顶堆实现代码
/// <summary>
    /// 小顶堆,T类型需要实现 IComparable 接口
    /// </summary>
    class MinHeap<T> where T : IComparable
    {
        private T[] container; // 存放堆元素的容器
        private int capacity;  // 堆的容量,最大可以放多少个元素
        private int count; // 堆中已经存储的数据个数

        public MinHeap(int _capacity)
        
{
            container = new T[_capacity + 1];
            capacity = _capacity;
            count = 0;
        }
        //插入一个元素
        public bool AddItem(T item)
        
{
            if (count >= capacity)
            {
                return false;
            }
            ++count;
            container[count] = item;
            int i = count;
            while (i / 2 > 0 && container[i].CompareTo(container[i / 2]) < 0)
            {
                // 自下往上堆化,交换 i 和i/2 元素
                T temp = container[i];
                container[i] = container[i / 2];
                container[i / 2] = temp;
                i = i / 2;
            }
            return true;
        }
        //获取最小的元素
        public T GetMinItem()
        
{
            if (count == 0)
            {
                return default(T);
            }
            T result = container[1];
            return result;
        }
        //删除最小的元素,即堆顶元素
        public bool DeteleMinItem()
        
{
            if (count == 0)
            {
                return false;
            }
            container[1] = container[count];
            container[count] = default(T);
            --count;
            UpdateHeap(container, count, 1);
            return true;
        }
        //从某个节点开始从上向下 堆化
        private void UpdateHeap(T[] a, int n, int i)
        
{
            while (true)
            {
                int maxPos = i;
                //遍历左右子树,确定那个是最小的元素
                if (i * 2 <= n && a[i].CompareTo(a[i * 2]) > 0)
                {
                    maxPos = i * 2;
                }
                if (i * 2 + 1 <= n && a[maxPos].CompareTo(a[i * 2 + 1]) > 0)
                {
                    maxPos = i * 2 + 1;
                }
                if (maxPos == i)
                {
                    break;
                }
                T temp = container[i];
                container[i] = container[maxPos];
                container[maxPos] = temp;
                i = maxPos;
            }
        }
    }
模拟日志文件内容
//因为需要不停的从log文件读取内容,所以需要一个和log文件保持连接的包装
    class LogInfoIndex : IComparable
    {
        //标志内容来自于哪个文件
        public int FileIndex { getset; }
        //具体的日志文件内容
        public LogInfo Data { getset; }

        public int CompareTo(object obj)
        {
            var tempInfo = obj as LogInfoIndex;
            if (this.Data.Index > tempInfo.Data.Index)
            {
                return 1;
            }
            else if (this.Data.Index < tempInfo.Data.Index)
            {
                return -1;
            }
            return 0;
        }
    }
    class LogInfo
    {       
        //用int来模拟datetime 类型,因为用int 看的最直观
        public int Index { getset; }
        public string UserName { getset; }
    }
生成模拟日志程序
 static void WriteFile()
        
{
            int fileCount = 0;
            while (fileCount < 10)
            {
                string filePath = $@"D:\log\{fileCount}.txt";
                int index = 0;
                while (index < 100000)
                {
                    LogInfo info = new LogInfo() { Index = index, UserName = Guid.NewGuid().ToString() };
                    File.AppendAllText(filePath, JsonConvert.SerializeObject(info)+ "\r\n");
                    index++;
                }
                fileCount++;
            }

        }

文件内容如下:

640?wx_fmt=jpeg


测试程序
static void Main(string[] args)
        
{
            int heapItemCount = 10;
            int startIndex = 0;
            StreamReader[] allReader = new StreamReader[10];
            MinHeap<LogInfoIndex> container = new MinHeap<LogInfoIndex>(heapItemCount);

            //首先每个文件读取一条信息          
            while(startIndex< heapItemCount)
            {
                string filePath = $@"D:\log\{startIndex}.txt";
                System.IO.StreamReader reader = new System.IO.StreamReader(filePath);
                allReader[startIndex] = reader;
                string content= reader.ReadLine();
                var contentObj = JsonConvert.DeserializeObject<LogInfo>(content);
                LogInfoIndex item = new LogInfoIndex() {  FileIndex= startIndex , Data= contentObj };
                container.AddItem(item);
                startIndex++;
            }
            //然后开始循环出堆,入堆
            while (true)
            {
                var heapFirstItem = container.GetMinItem();
                if (heapFirstItem == null)
                {
                    break;
                }
                container.DeteleMinItem();

                File.AppendAllText($@"D:\log\total.txt", JsonConvert.SerializeObject(heapFirstItem.Data) + "\r\n");
                var nextContent = allReader[heapFirstItem.FileIndex].ReadLine();
                if (string.IsNullOrWhiteSpace( nextContent))
                {
                    //如果其中一个文件已经读取完毕 则跳过
                    continue;
                }
                var contentObj = JsonConvert.DeserializeObject<LogInfo>(nextContent);
                LogInfoIndex item = new LogInfoIndex() { FileIndex = heapFirstItem.FileIndex, Data = contentObj };
                container.AddItem(item);
            }
            //释放StreamReader
            foreach (var reader in allReader)
            {
                reader.Dispose();
            }
            Console.WriteLine("完成");        
            Console.Read();
        }


最终排序结果如下图:


640?wx_fmt=jpeg


机器使用cpu内存完全没有达到所有排序文件的总大小:


640?wx_fmt=png


640?wx_fmt=png
640?wx_fmt=gif程序员修仙之路--把用户访问记录优化到极致程序员修仙之路--设计一个实用的线程池●程序员修仙之路--数据结构之CXO让我做一个计算器●程序猿修仙之路--数据结构之设计高性能访客记录系统●程序猿修仙之路--算法之快速排序到底有多快程序猿修仙之路--数据结构之你是否真的懂数组?

程序猿修仙之路--算法之希尔排序

程序员修仙之路--算法之插入排序

程序员修仙之路--算法之选择排序

互联网之路,菜菜与君一同成长

长按识别二维码关注

640?wx_fmt=jpeg640?wx_fmt=gif

听说转发文章

会给你带来好运

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

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

相关文章

【.NET Core项目实战-统一认证平台】第十四章 授权篇-自定义授权方式

上篇文章我介绍了如何强制令牌过期的实现&#xff0c;相信大家对IdentityServer4的验证流程有了更深的了解&#xff0c;本篇我将介绍如何使用自定义的授权方式集成老的业务系统验证&#xff0c;然后根据不同的客户端使用不同的认证方式来集成到统一认证平台。.netcore项目实战交…

D - Covering HDU - 6185(未解决完)

D - Covering HDU - 6185 题意&#xff1a; 4 * n的地板&#xff0c;有无数个1 * 2 和2 * 1 的砖块&#xff0c;问有多少方式填满&#xff1f; 1≤n≤10^18 题解&#xff1a; 矩阵快速幂 代码&#xff1a;

.NET-记一次架构优化实战与方案-前端优化

前言上一篇《.NET-记一次架构优化实战与方案-梳理篇》整理了基本的业务知识&#xff0c;同时也罗列了存在的问题&#xff0c;本篇主要是针对任务列表的页面进行性能优化。该篇主要涉及的是代码实现上的优化&#xff0c;实现上的问题是战术债务&#xff0c;也就是我们平常出现的…

GDOI2022游记

文章目录Day -1Day 0Day 1Day 2Day 3()Day ?~?Day ?Day -1 考前好像写题状态不太好&#xff08;可能是纯粹的懒&#xff09;。 开始写板子&#xff0c;很多算法都很久没碰了&#xff0c;有的调了很久才过。树剖都调了一个多小时&#xff0c;身败名裂。不过想想省选应该不怎…

.NET-记一次架构优化实战与方案-梳理篇

前言程序员输出是他敲写的代码&#xff0c;那么输入就是他思考好的设计。因此不做设计是不存在&#xff0c;设计只分优秀的设计和糟糕的设计。为了避免过度设计浪费成本&#xff0c;需要针对现有业务与问题进行展开。业务梳理是不可避免的。优化是无止尽&#xff0c;为了更有成…

.NET Core 3.0 中的数据库驱动框架 System.Data

虽然没有得到很多关注&#xff0c;但System.Data对于.NET 中任何关系型数据库的访问都至关重要。因为其前身是 ActiveX Data Objects&#xff0c;所以它也被称为 ADO.NET。System.Data 提供了一个通用框架&#xff0c;是构建.NET 数据库驱动程序的基础。该框架提供了数据库驱动…

Matrix Problem

Matrix Problem 题意&#xff1a; 给你一个n * m的二维数据c&#xff0c;c的每个元素值为0或1 现在要求你构造同样大小的数组a和b&#xff0c;要求c[i][j] 1’的话&#xff0c;a[i][j] b[i][j] ‘1’&#xff0c;如果c[i][j] ‘0’.a[i][j]!b[i][j]&#xff0c;且a和b中的1…

ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露

一、前言在涉及到后端项目的开发中&#xff0c;如何实现对于用户权限的管控是需要我们首先考虑的&#xff0c;在实际开发过程中&#xff0c;我们可能会运用一些已经成熟的解决方案帮助我们实现这一功能&#xff0c;而在 Grapefruit.VuCore 这个项目中&#xff0c;我将使用 Jwt …

[2021-06-19] 提高组新手副本Ⅱ(联网,欧几里得,分解树,开关灯)

文章目录考试心路历程联网titlesolutioncode欧几里得titlesolutioncode分解树titlesolutioncode开关灯titlesolutioncode考试心路历程 佛了佛了&#xff0c;caocaocaocaocaocao 人直接炸嗨升天 并查集直接送走200200200分&#xff01;&#xff01;&#xff01;我屮艸芔茻 T1二…

在.NET Core中设计自己的服务宿主(Service Hosting)框架

很多时候我们都会有设计一个后台服务的需求&#xff0c;比如&#xff0c;传统的Windows Service&#xff0c;或者Linux下的守护进程。这类应用的一个共同特点就是后台运行&#xff0c;并且不占用控制台界面。通常情况下&#xff0c;后台服务在提供服务时&#xff0c;会通过日志…

CF1131 G. Most Dangerous Shark(DP+单调栈优化)

文章目录problemsolutioncodeproblem solution dpi:dp_i:dpi​: 前iii个多米诺骨牌全都倒下的最小花费 li,ril_i,r_ili​,ri​分别表示第iii个多米诺骨牌倒下时所能波及到的最左/右位置 往左倒&#xff0c;则[li,i)[l_i,i)[li​,i)内的牌都可以选择性地先推倒 dpimin⁡{dpjcos…

Cat Virus

Cat Virus 题意&#xff1a; 让你构造一颗树&#xff0c;要求如果一个点为黑&#xff0c;其子树全为黑&#xff0c;白点任意&#xff0c;现在让你构造一棵树&#xff0c;使其染色方案数为K&#xff0c;节点尽可能少 题解&#xff1a; 首先画出k<9的全部情况&#xff0c;并…

微软发布XAML Studio工具:快速构建UWP XAML原型

IT之家1月30日消息 微软车库的最新项目XAML Studio已经在Windows 10应用商店上架&#xff0c;将帮助开发人员快速构建UWP XAML原型&#xff0c;以后可以轻松地将其复制到Visual Studio中。它将允许开发人员实时预览他们的XAML代码&#xff0c;并与结果进行交互&#xff0c;就像…

.NET Core 3 Preview 2发布,C#8更强大的模式匹配

.NET Core 3 Preview 2 发布了&#xff0c;此版本主要带来了 C# 8 相关的新功能&#xff0c;C# 8 Preview 2 是 .NET Core 3 SDK 的一部分。C# 8 中使用模式进行更多操作&#xff0c;主要特性包括&#xff1a;using 声明改变需要缩进代码的方式&#xff0c;现在可以编写以下代码…

CF407 E. k-d-sequence(线段树+单调栈)

文章目录CF407 E. k-d-sequenceproblemsolutioncodeCF407 E. k-d-sequence problem solution special case&#xff0c;d0d0d0&#xff0c;相当于寻找最长的一段数字相同的区间 other case&#xff0c;如果要满足公差为ddd等差序列 区间内每个数在模ddd意义下同余每个数互不…

D. Binary Literature

D. Binary Literature 题意&#xff1a; 给三个长度为2 * n的01串&#xff0c;让你构造一个长度小于3 * n的字符串&#xff0c;使得这个串至少包含两个01串 题解&#xff1a; 很巧妙的构造题 三个指针分别指向三个串&#xff0c;因为是01串&#xff0c;所以一定存在两个字符…

安逸:鼠绘《诗与远方》

【作品名称】《诗与远方》【作者介绍】徐安&#xff08;笔名安逸&#xff0c;常州&#xff09;&#xff0c;PPT专家&#xff0c;鼠绘专家。平面设计专业&#xff0c;6年PPT设计经验&#xff1b;历届江苏省PPT制作大赛一等奖获得者&#xff0c;PA口袋动画重要合作人。PPT动画制作…

[HNOI2016]网络(树链剖分+线段树+大根堆)

[HNOI2016]网络 problem solution 另辟蹊径&#xff0c;不把交互请求赋在新增路径上&#xff0c;反而把交互请求赋在树上除去该请求路径覆盖点的其它点上 显然&#xff0c;路径问题树剖是非常可以的、 那么一个点上的信息就表示所有不经过该点的交互请求&#xff0c;用堆…

IdentityServer4实战 - JWT Token Issuer 详解

一.前言本文为系列补坑之作&#xff0c;拖了许久决定先把坑填完。下文演示所用代码采用的 IdentityServer4 版本为 2.3.0&#xff0c;由于时间推移可能以后的版本会有一些改动&#xff0c;请参考查看&#xff0c;文末附上Demo代码。本文所诉Token如无特殊说明皆为 JWT Token。众…

P3834 【模板】可持久化线段树 2(整体二分做法)

P3834 【模板】可持久化线段树 2&#xff08;主席树&#xff09; 我们详细讲讲这个整体二分如何求区间第k小 我们都知道二分可以求出区间里某个想要的值&#xff0c;如果有很多询问&#xff0c;我们对每个询问都进行二分&#xff0c;复杂度就是O(QNlog(1e9))铁超&#xff0c;那…