C#百万数据处理

news2024/11/15 8:43:00

C#百万数据处理

在我们经验的不断增长中不可避免的会遇到一些数据量很大操作也复杂的业务
这种情况我们如何取优化如何去处理呢?一般都要根据业务逻辑和背景去进行合理的改进。


文章目录

  • C#百万数据处理
  • 前言
  • 一、项目业务需求和开发背景
    • 项目开发背景
    • 数据量计算
    • 业务需求
    • 业务分析
  • 二、老代码分析
    • 运行逻辑
    • 代码展示
    • 伪代码展示
    • 分析性能低原因
  • 三、优化后的代码分析
    • 运行逻辑
    • 代码展示
    • 伪代码展示
    • 优化了那些问题
  • 总结


前言

这里我给大家带来一个我自己所经历的百万数据处理案例,该案例中会拿优化前和优化后的代码进行对比,让大家更直观的感受优化给项目带来的效率提升。该项目优化用到了:线程同步,多线程,sqlSugar,异步,委托等知识。


一、项目业务需求和开发背景

在这里插入图片描述

项目开发背景

随着连锁门店数量的不断增加,门店商品的库存数据和商品信息的维护业务压力不断增加。数据量越来越大。

数据量计算

假如数据如下:

  • 门店数量:160家
  • 门店商品数量:6000+
  • 还需要更新商品对码信息:6000+
  • 数据接口最多每页查询1000条
  • 那么按以上数据要处理的数据最少就为:966000条

业务需求

  1. 新的商品要做插入操作
  2. 旧的商品要做更新操作(更新库存信息和商品编码厂家等信息)
  3. 门店库存数据变更要在半小时内完成

业务分析

按以上条件我们可以分析出:需要有Insert和Update操作。其中经验丰富的程序员就会发现,其实像这种业务一般 Insert操作最多只占所有业务的20%,Update操作占80%。
注意:
很多小白程序员都是不进行业务分析就直接撸代码,这种方式是不可取的,等你撸完代码之后你就会发现效率根本就不行。后面又花大量时间去优化,还如不体现分析了再进行开发。

二、老代码分析

运行逻辑

老代码的操作流程如下

  1. 查询出所有门店
  2. 循环门店,将门店数据传入操作函数中
  3. 操作函数中再根据传入的门店信息循环去调用接口
  4. 当传入的这个门店的数据拉取完成之后再去操作数据库

代码展示

代码如下:

//查询所有门店商品信息
 List<StoreCheckCodeDO> storeInfos = b_Stores.GetStoreCheckCodes();
 //这里是用于更新对码的信息
 List<ProductInfoDO> infoDOs = new List<ProductInfoDO>();
 foreach (var storeInfo in storeInfos )
   {
   //GetProductInfo的作用是调用总部接口获取并更新商品数据
	InventoryHelper.GetProductInfo(storeInfo , ref infoDOs );
   }
 if(infoDOs.count>0)
 {
 //更新对码信息
 InventoryHelper.ChangeProductCheckCode(infoDOs);
 }

GetProductInfo 的具体实现:

public static bool GetProductInfo(string orgid, ref List<ProductInfoDO> infoDOs, string goodsNo = "", string goodsName = "")
        {
            bool rel = false;
            int pageSize = 1000;
            int pageNum = 1;
            List<DslStoreStockDO> list =根据门店ID查询它的商品库存信息
            Console.WriteLine("开始更新门店:{0}", orgid);
            //定义个集合来存储拉取到的数据
            List<StoreStockDO> Stores = new List<StoreStockDO>();
            while (true)
            {
                try
                {
                  
                    DslERPRes<QueryStockRes> res3 =调用总部数据接口每页查询1000条,返回的数据
                    if (res3 != null && res3.code == 0)
                    {
                        if (res3.data != null && res3.data.list != null)
                        {
                            if (res3.data.total > 0)
                            {
                                #region 模型转换
								//循环总部返回的数据集合,并转成我们需要的DO是实体
                                foreach (var product in res3.data.list)
                                {
                                    DslStoreStockDO dsl = new DslStoreStockDO();
                                    把总部数据转为我们需要的实体存储我们的集合中
                                    dslStores.Add(dsl);
                                    ProductInfoDO infoDO = 根据总部商品ID查询本地对码表返回商品对码信息实体
                                    if (infoDO == null)
                                    {
                                    //如果没有则new一个对象
                                        infoDO = new ProductInfoDO();
                                        infoDO.LocalSku = product.goodsNo.ToString();
                                    }
                                    //如果有这个商品就对其数据更新,没有就用新的对象存储后续做新增操作
                                    infoDO.Name = product.goodsName;
                                    infoDO.DslSku = product.goodsNo.ToString();
                                    infoDO.goodsType = product.goodsType;
                                    infoDO.proarea = product.prodArea;
                                    infoDO.barcode = product.barcode;
                                    infoDO.CreateTime = DateTime.Now;
                                    infoDO.UpdateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                    infoDO.Operator = "系统自动更新";
                                    infoDO.IsDelete = false;
                                    //查询集合中已存在的该商品
                                    ProductInfoDO b_there = infoDOs.Where(t => t.DslSku == product.goodsNo.ToString()).FirstOrDefault();
                                    if (b_there != null)
                                    {
                                    //如果有则更新为现在从总部拉取到的商品信息
                                        if (infoDO.goodsType != b_there.goodsType)
                                        {
                                            b_there.goodsType = infoDO.goodsType;
                                        }
                                        if (infoDO.proarea != b_there.proarea)
                                        {
                                            b_there.proarea = infoDO.proarea;
                                        }
                                        if (infoDO.barcode != b_there.barcode)
                                        {
                                            b_there.barcode = infoDO.barcode;
                                        }
                                    }
                                    else
                                    {
                                    //插入实体集合中
                                        infoDOs.Add(infoDO);
                                    }
                                }
                                #endregion
								//判断该门店商品信息是否拉完
                                if (res3.data.pages == pageNum)
                                {
                                //利用list的 Except 方法进行数据筛选
                                    var different = list.Except(dslStores, new StoreStockDOComparer()).ToList();//找到没拉到库存的
                                    different.ForEach(goodsInfo =>
                                    {
                                    //把没有从总部拉到商品的库存数据变更为0
                                        goodsInfo.goodsQty = 0;
                                        goodsInfo.warehouseGoodsQty = 0;

                                    });
                                    //清除容器内数据从新插入信息变更数据
                                    //list 这个list就是我们开头根据门店id查询该门店所有商品库存信息的数据,
                                    //所以要清空重新写入我们调整好的数据
                                    list = new List<DslStoreStockDO>();
                                    list.AddRange(dslStores);
                                    list.AddRange(different);
                                    //获取完毕,正常结束
                                    Console.WriteLine("请稍等正在提交事务...");
                                    //_BulkCopyModel方法是做insertOrUpadate操作的。
                                    //根据后面传入的主键【goodsNo,placePointNo 】去操作数据库的数据
                                    int count = _sqlsugar_DSL._BulkCopyModel(list, "门店库存表", t => new { t.goodsNo, t.placePointNo });
                                    rel = count > 0;
                                    Console.WriteLine("门店:{0}数据更新成功,操作数据条数:{1}", orgid, count);
                                    break;
                                }
                                pageNum++;
                            }
                            else
                            {
                                Console.WriteLine("未查询到更新数据");
                                break;
                            }
                        }
                        else
                        {
                            ///获取完毕,正常结束
                            Console.WriteLine("门店:{0}第{1}页数据拉取失败", orgid, pageNum.ToString());
                            break;
                        }
                    }
                    else
                    {
                        //获取异常,强制结束
                        Console.WriteLine("门店:{0}第{1}页数据发起请求失败:{2}", orgid, pageNum.ToString(), res3.msg);
                        break;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("门店:{0}第{1}页数据处理失败:{2}", orgid, pageNum.ToString(), ex.Message);
                    break;
                }

            }
            return rel;
        }

伪代码展示

上述代码还是比较复杂很长,不太能直观的一眼看到问题所在下面我就写一段伪造代码来给大家展示一下逻辑

代码如下(示例):

门店集合=查询到所有门店

商品对码信息收集集合 =new List<T>();
 
foreach (var 单个门店实体 in 门店集合 )
   {
   //GetProductInfo的作用是调用总部接口获取并更新商品数据
	InventoryHelper.GetProductInfo(门店ID, ref 商品对码信息收集集合 );
   }
 
 GetProductInfo(门店ID,ref 门店对码信息收集集合)
 {
 List<库存数据实体> 旧库存数据=根据门店ID获取库存信息(门店ID);
 List<库存数据实体> 新库存数据=new  List<库存数据实体>();
   while(true)
   {
   		页码=1;
   		每页查询数量=1000;
   		总部数据返回数据集合=请求总部数据接口(页码,每页查询数量)
   		foreach (var 数据返回数据实体 in 总部数据返回数据集合 )
   		{
   		
   		数据返回数据实体 转为 库存数据实体 
   		 
   		新库存数据.add(库存数据实体)
		
		数据返回数据实体数据 取出所需数据组成 商品对码实体
		
		门店对码信息收集集合.add(商品对码实体)
		
   		}
   		if(页码=总部返回数据集合.总页数)
   		{
   		新库存数据 做Insert OR Update 操作
   		break;
		}
		页码++;
   }
 }

分析性能低原因

  • 单线程,需要一个门店一个门店的处理
  • 每处理一个门店都需要去操作一次数据库,做Insert OR Update 操作
  • 单线程请求总部接口线程要等待接口返回才继续往下处理没有达到效率最大化
  • 每次都要去查询门店的所有商品库存,导致不必要的查询开销。

三、优化后的代码分析

运行逻辑

  • 查询出所有门店,根据传入线程数做分组
  • 多线程异步获取总部接口库存数据
  • 当分组的总部数据获取完成后以组为单位更新数据库数据

代码展示

代码如下:

 public static void GetProductInfo(List<DslStoreCheckCodeDO> orgids, int ThreadNumber = 1)
        {
            if (ThreadNumber > orgids.Count)
            {
                ThreadNumber = 1;
            }
            string startTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            List<List<DslStoreCheckCodeDO>> groupedLists = orgids.Select((value, index) => new { Index = index, Value = value }).GroupBy(x => x.Index / (orgids.Count() / ThreadNumber)).Select(x => x.Select(v => v.Value).ToList()).ToList();
            List<ProductInfoDO> outval = new List<ProductInfoDO>();
            int _ThreadCount = groupedLists.Count();
            int finishcount = 0;
            object locker = new object();
            foreach (var dslStores in groupedLists)
            {
                new Thread(async () =>
                {
                    string TID = Thread.CurrentThread.ManagedThreadId.ToString("00");
                    int count = 0;
                    List<ProductInfoDO> productInfos = new List<ProductInfoDO>();
                    List<DslStoreStockDO> dslStock = new List<DslStoreStockDO>();
                    foreach (var StoreInfo in dslStores)
                    {
                        count++;
                        Console.WriteLine("线程ID[{0}]剩余【{1}/{2}】", TID, dslStores.Count, count);
                        string orgid = StoreInfo.DslStoreNo;
                        List<ProductS> products = await GetProductInfo(orgid);
                        #region 模型转换
                        foreach (var product in products)
                        {
                            DslStoreStockDO dsl = new DslStoreStockDO();
                            dsl.placePointNo = orgid;
                            dsl.goodsNo = product.goodsNo;
                            dsl.goodsId = product.goodsId;
                            dsl.goodsName = product.goodsName;
                            dsl.goodsType = string.IsNullOrWhiteSpace(product.goodsType) ? "无" : product.goodsType;
                            dsl.prodArea = product.prodArea ?? "无";
                            dsl.barcode = string.IsNullOrWhiteSpace(product.barcode) ? "无" : product.barcode;
                            dsl.goodsQty = product.goodsQty;
                            dsl.warehouseGoodsQty = product.warehouseGoodsQty ?? 0;
                            dsl.purPrice = product.purPrice ?? 0;
                            dsl.retailPrice = product.retailPrice;
                            dsl.salesTaxRate = product.salesTaxRate;
                            dsl.cretime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                            dsl.isChange = true;
                            dslStock.Add(dsl);
                            ProductInfoDO infoDO = dSL_ERP.GetCodeCahe(product.goodsNo.ToString());
                            if (infoDO == null)
                            {
                                infoDO = new ProductInfoDO();
                                infoDO.LocalSku = product.goodsNo.ToString();
                            }
                            infoDO.Name = product.goodsName;
                            infoDO.DslSku = product.goodsNo.ToString();
                            infoDO.goodsType = product.goodsType;
                            infoDO.proarea = product.prodArea;
                            infoDO.barcode = product.barcode;
                            infoDO.CreateTime = DateTime.Now;
                            infoDO.UpdateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                            infoDO.Operator = "系统自动更新";
                            infoDO.IsDelete = false;
                            productInfos.Add(infoDO);
                        }
                        #endregion

                    }
                    if (count == dslStores.Count)
                    {   //分配的门店处理完成后再同一操作数据库
                        List<string> SotoreIds = dslStores.Select(t => t.DslStoreNo).ToList();
                        List<DslStoreStockDO> Oldstocks = _sqlsugar_DSL.GetModelList<DslStoreStockDO>("StoreStock", t => SotoreIds.Contains(t.placePointNo)); 
                        List<DslStoreStockDO> newStocks = dslStock.Except(Oldstocks, new StoreStockDOComparer()).ToList();
                        List<DslStoreStockDO> commonStocks = dslStock.Except(newStocks, new StoreStockDOComparer()).ToList();
                        try
                        {

                            if (Oldstocks.Count > 0)
                            {
                                var diffent = Oldstocks.Except(dslStock, new StoreStockDOComparer()).ToList();
                                diffent.ForEach(goodsInfo =>
                                {
                                    goodsInfo.goodsQty = 0;
                                    goodsInfo.warehouseGoodsQty = 0;
                                });
                                commonStocks.AddRange(diffent);
                            }
                            Console.WriteLine("请稍等正在提交...");
                            int InsNumber = 0;
                            if (newStocks.Count > 0)
                            {
                                InsNumber = await _sqlsugar_DSL.AsynBulkCopyModel(newStocks, "StoreStock", t => new { t.goodsNo, t.placePointNo });
                            }
                            int UpdNumber = await _sqlsugar_DSL.AsynBulkUpdateModel(commonStocks, "StoreStock", new string[] { "goodsNo", "placePointNo" }, new string[] { "placePointNo", "goodsNo", "goodsId", "goodsName", "goodsType", "prodArea", "barcode", "goodsQty", "warehouseGoodsQty", "purPrice", "retailPrice", "salesTaxRate", "cretime", "isChange" });
                            Console.WriteLine("提交成功,更新{0}条数据,插入{1}条数据", UpdNumber, InsNumber);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("线程{0}内部报错:{1}", TID, ex.Message);
                        }

                    }
                    lock (locker)
                    {
                        outval.AddRange(productInfos);
                        finishcount++;
                        Monitor.Pulse(locker); //完成,通知等待队列,告知已完,执行下一个。
                    }
                }).Start();
            }
            lock (locker)
            {
                while (finishcount != _ThreadCount)
                {
                    Monitor.Wait(locker);//等待
                }

            }
            IEnumerable<ProductInfoDO> infoDOs = outval.OrderByDescending(x => x.UpdateTime).GroupBy(x => new { x.DslSku, x.barcode }).Select(y => y.First());
            Console.WriteLine("开始更新商品对码表,待更新数:{0}", infoDOs.Count());
            if (infoDOs.Count() > 0)
            {
                Console.WriteLine("正在提交...");
                ChangeProductCheckCode(infoDOs.ToList());
                Dictionary<string, string> pairs = new Dictionary<string, string>();
                pairs.Add("JsonStr", CommonFun.Base64Encode(JsonConvert.SerializeObject(infoDOs)));
                string rel = HttpHelper.HttpPost("...", pairs);
                Console.WriteLine("商品对码表跟新成功,商品信息表更新:{0}", rel);
            }
            Console.WriteLine("【全量更新所有门店】执行完毕线程挂起24小时...[Start:{0}|End:{1}]", startTime, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));



            #region 异步获取商品信息
            async Task<List<ProductS>> GetProductInfo(string orgid)
            {
                List<ProductS> products = new List<ProductS>();
                string res = await Task.Run(() =>
                {
                    int pageNum = 1;
                    int pageSize = 1000;
                    while (true)
                    {
                        QueryStockReq req3 = new QueryStockReq();
                        req3.goodsno = "";
                        req3.goodsName = "";
                        req3.pageNum = pageNum;
                        req3.pageSize = pageSize;
                        req3.placePointNo = orgid;
                        Console.WriteLine("门店【{1}】开始获取第{0}页数据-线程ID【{2}】", req3.pageNum.ToString(), orgid, Thread.CurrentThread.ManagedThreadId.ToString("00"));
                        DslERPReq dslERP23 = new DslERPReq(req3);
                        DslERPRes<QueryStockRes> res3 = dSL_ERP.queryStock(dslERP23);

                        if (res3 != null && res3.code == 0)
                        {
                            if (res3.data != null && res3.data.list != null)
                            {
                                if (res3.data.total > 0)
                                {
                                    products.AddRange(res3.data.list);
                                    if (res3.data.pages == pageNum)
                                    {
                                        break;
                                    }
                                    pageNum++;
                                }
                                else
                                {
                                    Console.WriteLine("未查询到更新数据,返回数据{0},数据JSON:【{1}】", res3.msg, JsonConvert.SerializeObject(res3));
                                    break;
                                }
                            }
                            else
                            {
                                Console.WriteLine("门店:{0}第{1}页数据拉取失败返回信息{2}", orgid, req3.pageNum.ToString(), res3.msg);
                                break;
                            }
                        }
                        else
                        {
                            Console.WriteLine("门店:{0}第{1}页数据发起请求失败:{2}", orgid, req3.pageNum.ToString(), res3.msg);
                        }
                    }
                    return "";
                });
                return products;
            }
            #endregion
        }

伪代码展示

 public static void GetProductInfo(List<门店信息实体> 门店信息实体集合, int 处理线程数量= 1)
        {
            if (处理线程数量 > 门店信息实体集合.Count)
            {
                处理线程数量 = 1;
            }
            string startTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            List<List<门店信息实体>> 门店分组集合= 门店信息实体集合.Select((value, index) => new { Index = index, Value = value }).GroupBy(x => x.Index / (门店信息实体集合.Count() / 处理线程数量)).Select(x => x.Select(v => v.Value).ToList()).ToList();
            List<商品对码实体> outval = new List<商品对码实体>();
            int _ThreadCount = 门店分组集合.Count();
            int finishcount = 0;
            object locker = new object();//线程锁
            foreach (var 门店实体集合 in 门店分组集合)
            {
                new Thread(async () =>
                {
                    string TID = Thread.CurrentThread.ManagedThreadId.ToString("00");
                    int count = 0;
                    List<ProductInfoDO> productInfos = new List<ProductInfoDO>();
                    List<库存数据实体> 总部库存数据集合= new List<库存数据实体>();
                    foreach (var StoreInfo in 门店实体集合)
                    {
                        count++;
                        Console.WriteLine("线程ID[{0}]剩余【{1}/{2}】", TID, 门店实体集合.Count, count);
                        string orgid = StoreInfo.门店ID;
                        //这里是异步从总部库存信息接口获取商品库存信息
                        List<ProductS> products = await GetProductInfo(orgid);
                        #region 模型转换
                        foreach (var product in products)
                        {
                            库存数据实体dsl = new 库存数据实体();
                            dsl.placePointNo = orgid;
                            dsl.goodsNo = product.goodsNo;
                            dsl.goodsId = product.goodsId;
                            dsl.goodsName = product.goodsName;
                            dsl.goodsType = string.IsNullOrWhiteSpace(product.goodsType) ? "无" : product.goodsType;
                            dsl.prodArea = product.prodArea ?? "无";
                            dsl.barcode = string.IsNullOrWhiteSpace(product.barcode) ? "无" : product.barcode;
                            dsl.goodsQty = product.goodsQty;
                            dsl.warehouseGoodsQty = product.warehouseGoodsQty ?? 0;
                            dsl.purPrice = product.purPrice ?? 0;
                            dsl.retailPrice = product.retailPrice;
                            dsl.salesTaxRate = product.salesTaxRate;
                            dsl.cretime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                            dsl.isChange = true;
                            总部库存数据集合.Add(dsl);
                            ProductInfoDO infoDO = dSL_ERP.GetCodeCahe(product.goodsNo.ToString());
                            if (infoDO == null)
                            {
                                infoDO = new ProductInfoDO();
                                infoDO.LocalSku = product.goodsNo.ToString();
                            }
                            infoDO.Name = product.goodsName;
                            infoDO.DslSku = product.goodsNo.ToString();
                            infoDO.goodsType = product.goodsType;
                            infoDO.proarea = product.prodArea;
                            infoDO.barcode = product.barcode;
                            infoDO.CreateTime = DateTime.Now;
                            infoDO.UpdateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                            infoDO.Operator = "系统自动更新";
                            infoDO.IsDelete = false;
                            productInfos.Add(infoDO);
                        }
                        #endregion

                    }
                    //注意这里我为什么要用count来判断这组是否执行完成,因为我们操作数据库的语句优化为了异步的
                    //而Look语句内是不支持await 语句的,所有利用组合里的门店数和已处理数来判断
                    if (count == 门店实体集合.Count)
                    {   //分配的门店处理完成后再同一操作数据库
                        List<string> SotoreIds = 门店实体集合.Select(t => t.门店ID).ToList();
                        // 查询出这个批次里所有门店的老的库存数据
                        List<库存数据实体> Oldstocks = _sqlsugar_DSL.GetModelList<库存数据实体>("StoreStock", t => SotoreIds.Contains(t.门店ID)); 
                        //
                        List<库存数据实体> newStocks = 总部库存数据集合.Except(Oldstocks, new StoreStockDOComparer()).ToList();
                        List<库存数据实体> commonStocks = 总部库存数据集合.Except(newStocks, new StoreStockDOComparer()).ToList();
                        try
                        {

                            if (Oldstocks.Count > 0)
                            {
                                var diffent = Oldstocks.Except(总部库存数据集合, new StoreStockDOComparer()).ToList();
                                diffent.ForEach(goodsInfo =>
                                {
                                    goodsInfo.goodsQty = 0;
                                    goodsInfo.warehouseGoodsQty = 0;
                                });
                                commonStocks.AddRange(diffent);
                            }
                            Console.WriteLine("请稍等正在提交...");
                            int InsNumber = 0;
                            if (newStocks.Count > 0)
                            {
                                InsNumber = await _sqlsugar_DSL.AsynBulkCopyModel(newStocks, "StoreStock", t => new { t.goodsNo, t.placePointNo });
                            }
                            int UpdNumber = await _sqlsugar_DSL.AsynBulkUpdateModel(commonStocks, "StoreStock", new string[] { "goodsNo", "placePointNo" }, new string[] { "placePointNo", "goodsNo", "goodsId", "goodsName", "goodsType", "prodArea", "barcode", "goodsQty", "warehouseGoodsQty", "purPrice", "retailPrice", "salesTaxRate", "cretime", "isChange" });
                            Console.WriteLine("提交成功,更新{0}条数据,插入{1}条数据", UpdNumber, InsNumber);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("线程{0}内部报错:{1}", TID, ex.Message);
                        }

                    }
                    lock (locker)
                    {
                        outval.AddRange(productInfos);
                        finishcount++;
                        Monitor.Pulse(locker); //完成,通知等待队列,告知已完,执行下一个。
                    }
                }).Start();
            }
            lock (locker)
            {
                while (finishcount != _ThreadCount)
                {
                    Monitor.Wait(locker);//等待
                }

            }
            IEnumerable<ProductInfoDO> infoDOs = outval.OrderByDescending(x => x.UpdateTime).GroupBy(x => new { x.DslSku, x.barcode }).Select(y => y.First());
            Console.WriteLine("开始更新商品对码表,待更新数:{0}", infoDOs.Count());
            if (infoDOs.Count() > 0)
            {
                Console.WriteLine("正在提交...");
                ChangeProductCheckCode(infoDOs.ToList());
                Dictionary<string, string> pairs = new Dictionary<string, string>();
                pairs.Add("JsonStr", CommonFun.Base64Encode(JsonConvert.SerializeObject(infoDOs)));
                string rel = HttpHelper.HttpPost("...", pairs);
                Console.WriteLine("商品对码表跟新成功,商品信息表更新:{0}", rel);
            }
            Console.WriteLine("【全量更新所有门店】执行完毕线程挂起24小时...[Start:{0}|End:{1}]", startTime, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));



            #region 异步获取商品信息
            async Task<List<ProductS>> GetProductInfo(string orgid)
            {
                List<ProductS> products = new List<ProductS>();
                string res = await Task.Run(() =>
                {
                    int pageNum = 1;
                    int pageSize = 1000;
                    while (true)
                    {
                        QueryStockReq req3 = new QueryStockReq();
                        req3.goodsno = "";
                        req3.goodsName = "";
                        req3.pageNum = pageNum;
                        req3.pageSize = pageSize;
                        req3.placePointNo = orgid;
                        Console.WriteLine("门店【{1}】开始获取第{0}页数据-线程ID【{2}】", req3.pageNum.ToString(), orgid, Thread.CurrentThread.ManagedThreadId.ToString("00"));
                        DslERPReq dslERP23 = new DslERPReq(req3);
                        DslERPRes<QueryStockRes> res3 = dSL_ERP.queryStock(dslERP23);

                        if (res3 != null && res3.code == 0)
                        {
                            if (res3.data != null && res3.data.list != null)
                            {
                                if (res3.data.total > 0)
                                {
                                    products.AddRange(res3.data.list);
                                    if (res3.data.pages == pageNum)
                                    {
                                        break;
                                    }
                                    pageNum++;
                                }
                                else
                                {
                                    Console.WriteLine("未查询到更新数据,返回数据{0},数据JSON:【{1}】", res3.msg, JsonConvert.SerializeObject(res3));
                                    break;
                                }
                            }
                            else
                            {
                                Console.WriteLine("门店:{0}第{1}页数据拉取失败返回信息{2}", orgid, req3.pageNum.ToString(), res3.msg);
                                break;
                            }
                        }
                        else
                        {
                            Console.WriteLine("门店:{0}第{1}页数据发起请求失败:{2}", orgid, req3.pageNum.ToString(), res3.msg);
                        }
                    }
                    return "";
                });
                return products;
            }
            #endregion
        }

优化了那些问题

  • 从总部取数据变为了异步多线程避免了线程阻塞
  • 门店按组合处理减小了数据库操作次数
  • 操作数据库改为异步避免阻塞

总结

以上就是我个人所经历的大数据处理,虽然优化的并不算完美但是还是总结出了不少经验,也从中学习到了很多,比如,单线程和多线程的运用以及委托和线程同步等知识。

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

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

相关文章

OpenFeign原理浅析

OpenFeign原理我个人觉得是非常简单的&#xff0c;如果你对Spring非常了解&#xff0c;知道FactoryBean&#xff0c;以及注入bean的方式&#xff0c;并掌握动态代理&#xff0c;那么自己实现一个类似这样的Http代理客户端是一点问题也没有的&#xff01; 使用流程 首先我们先过…

BLE连接、配对和绑定

参考&#xff1a;一篇文章带你解读蓝牙配对绑定 参考&#xff1a;BLE安全之SM剖析(1) 参考&#xff1a;BLE安全之SM剖析&#xff08;2&#xff09; 参考&#xff1a;BLE安全之SM剖析(3) 参考&#xff1a;https://blog.csdn.net/chengbaojin/article/details/103691046 参考&…

【MQTT5】原生PHP对接Uni H5、APP、微信小程序实时通讯消息服务

文章目录 视频演示效果前言一、分析二、全局注入MQTT连接1.引入库2.写入全局连接代码 二、PHP环境建立总结 视频演示效果 【uniapp】实现买定离手小游戏 前言 Mqtt不同环境问题太多&#xff0c;新手可以看下 《【MQTT】Esp32数据上传采集&#xff1a;最新mqtt插件&#xff08;支…

Flowable-服务-骆驼任务

目录 定义图形标记XML内容Flowable与Camel集成使用示例设计Came路由代码 定义 Camel 任务不是 BPMN 2.0 规范定义的官方任务&#xff0c;在 Flowable 中&#xff0c;Camel 任务是作为一种特殊的服务 任务来实现的。主要做路由工作的。 图形标记 由于 Camel 任务不是 BPMN 2.…

BMI指数计算小工具Java

现在越来越多的人关注健康&#xff0c;关注身材管理&#xff0c;不妨做个小工具&#xff0c;计算自己的BMI&#xff0c;给自己制定合理的健身或减肥计划&#xff0c;享受健康生活&#xff01;&#xff01;&#xff01;BMI的计算标准从网上找的&#xff0c;不知道是否准确&#…

❤ yarn 和npm 的使用

❤ yarn 和npm 的使用 yarn 版本1的使用 yarn 简介 Yarn是facebook发布的一款取代npm的包管理工具。 yarn特点&#xff1a; 1&#xff0c;速度超快。 Yarn 缓存了每个下载过的包&#xff0c;所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率&#xff0c;因…

TransGPT 开源交通大模型开源

TransGPT 是开源交通大模型&#xff0c;主要致力于在真实交通行业中发挥实际价值。 它能够实现交通情况预测、智能咨询助手、公共交通服务、交通规划设计、交通安全教育、协助管理、交通事故报告和分析、自动驾驶辅助系统等功能。 TransGPT 作为一个通用常识交通大模型&#…

cmd相关操作命令

1.根据端口号查询对应进程的PID netstat -ano | findstr 端口号 例如&#xff1a;netstat -ano | findstr 9080&#xff1b;该端口所属进程的PID为6684 2.根据PID查询对应进程 tasklist | findstr PID 例如&#xff1a;tasklist | findstr 6684&#xff1b;该PID所属进程名为…

啥都收费,不仅智能电视没人买了,连电视盒子也卖不出了

分析机构给出的数据指今年上半年国内的电视盒子销量跌破百万至92.9万台&#xff0c;同比下滑29%&#xff0c;享受额更是大跌32%&#xff0c;显示出电视盒子即使大幅降价也没人买了&#xff0c;导致消费者远离电视在于收费太离谱了。 一、啥都收费 如今的智能电视其实并不智能&a…

使用贝叶斯滤波器通过运动模型和嘈杂的墙壁传感器定位机器人研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Day48 算法记录|动态规划15 (子序列)

子序列 392. 判断子序列115.不同的子序列 392. 判断子序列 这道题和1143最长公共字串相同 dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的字符串t&#xff0c;相同子序列的长度为dp[i][j]。 class Solution {public boolean isSubsequence(String s,…

【C语言初阶篇】自定义类型结构体我不允许还有人不会!

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言1 . 什么是结构体1.1 结构的定义1.2 结构的声明 2.结构体初始化2.1 用标签名定义和初始化2.2…

【Vue】Vue-Cli整合Echart

Vue-Cli整合Echart 文章目录 Vue-Cli整合Echart一、创建Vue-Cli项目1、创建并运行Vue Cli hello-world项目2、运行vue-element-admin项目 二、Vue-Cli整合Echart1、使用echarts CDN源实现K线图绘制2、使用VueCli echarts实现K线图绘制3、echart更多使用问题整理 一、创建Vue-C…

LeetCode222. 完全二叉树的节点个数

222. 完全二叉树的节点个数 文章目录 [222. 完全二叉树的节点个数](https://leetcode.cn/problems/count-complete-tree-nodes/)一、题目二、题解方法一&#xff1a;递归遍历所有结点方法二&#xff1a;根据完全二叉树的特性递归方法三&#xff1a;迭代 一、题目 给你一棵 完全…

[C语言] 数组

1. 一维数组的创建和初始化 2. 一维数组的使用 3. 一维数组在内存中的存储 4. 二维数组的创建和初始化 5. 二维数组的使用 6. 二维数组在内存中的存储 7. 数组越界 8. 数组作为函数参数 9. 数组的应用实例 1 &#xff1a;三子棋 10. 数组的应用实例 2 &#…

初阶数据结构——二叉树题目

文章目录 一、单值二叉树二、检查两颗树是否相同三、另一棵树的子树四、二叉树的前序遍历五、对称二叉树 一、单值二叉树 单值二叉树 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff…

AD原理图检查ERC(编译检查)

原理图的编译&#xff1a; 原理图界面选择菜单栏的&#xff1a;工程---->Compile PCB Project 也可以项目管理栏右击对应项目文件 在右下角&#xff0c;点击panels---->Message&#xff0c;就可以打开Message界面 原理图的常用检测&#xff1a; 原理图界面选择菜单…

kv键值对快速转换为json串(字典类型) | 批量添加双引号

从浏览器中复制的以下数据, 并不能直接使用 refresh:0 start:0 count:20 selected_categories:%7B%7D uncollect:false playable:true tags:在python中需转为字典类型 {refresh: 0,start: 0,count: 20,selected_categories: %7B%7D,uncollect: false,playable: true,tags: , …

SIMD系列-GATHER/SCATTER操作

SIMD系列-GATHER/SCATTER操作 众所周知&#xff0c;SIMD寄存器可以使用LOAD/STORE操作与标量域&#xff08;或者更准确的说是内存&#xff09;进行通信。这些操作的缺点是&#xff1a;只允许移动内存中连续的数据元素。然而&#xff0c;我们代码中&#xff0c;经常需要访问非连…

浅谈大数据软件的功能性分析

在当今时代的潮流中&#xff0c;工作中遇到大数据处理的时候非常多&#xff0c;因此需要一些大数据分析软件帮助人们进行工作。由于这些软件针对的对象不同&#xff0c;因此使用方法也不同&#xff0c;那么为了帮助更多的人了解大数据分析软件&#xff0c;我们就对这些软件的功…