• 关于代码性能优化的总结


      今天同事发开中遇到了一个代码性能优化的问题,原本需求是:从一个数据库中查询某个表数据,存放到datatable中,然后遍历datatable,看这些数据在另一个数据库的表中是否存在,存在的话就要更新,不存在就要插入。

      就这个需求本身来说很简单,但是随着数据量的增大,之前通过循环遍历的方式就出现了性能问题。我在思索片刻后,给出的建议是分页查询和利用事务批量提交。

    1.利用数据库事务批量提交

    复制代码
     1 using (SqlTransaction transaction = targetConnection.BeginTransaction())
     2             {
     3                 foreach (DataRow row in dataTable.Rows)
     4                 {
     5                     if (CheckIfDataExists(targetConnection, transaction, row))
     6                     {
     7                         UpdateData(targetConnection, transaction, row);
     8                     }
     9                     else
    10                     {
    11                         InsertData(targetConnection, transaction, row);
    12                     }
    13                 }
    14 
    15                 transaction.Commit();
    16             }
    17         }
    18 
    19 //下面两个方法都还可以优化,需要接收批量sql语句,所以可以修改成list,然后遍历执行,此处能说明问题即可
    20   private void UpdateData(SqlConnection connection, SqlTransaction transaction, DataRow row)
    21     {
    22         using (SqlCommand command = new SqlCommand("UPDATE YourTable SET YourUpdateStatement WHERE YourCondition", connection, transaction))
    23         {
    24             // Add parameters to your command here, based on your update statement and condition
    25             // command.Parameters.AddWithValue("@ParameterName", row["ColumnName"]);
    26 
    27             command.ExecuteNonQuery();
    28         }
    29     }
    30 
    31     private void InsertData(SqlConnection connection, SqlTransaction transaction, DataRow row)
    32     {
    33         using (SqlCommand command = new SqlCommand("INSERT INTO YourTable (YourColumns) VALUES (YourValues)", connection, transaction))
    34         {
    35             // Add parameters to your command here, based on your columns and values
    36             // command.Parameters.AddWithValue("@ParameterName", row["ColumnName"]);
    37 
    38             command.ExecuteNonQuery();
    39         }
    40     }
    复制代码

      看到这里的时候,大家可以考虑下,以上方案还有什么优化的地方吗?

      当然是有的,如果数据量持续增大,datatable这样直接加载到内存的方式恐怕会成为性能问题点吧,我们得考虑怎么优化才能避免将大数据一次性加载到内存,大部分同学第一个想到的就是分页,这个方案当然是没有错,但是还不够高级,给大家提示一个关键字“yield”,或许从聪明的你已经悟到了,接着往下看。

    2.流式处理法

      什么是流式处理法呢

      流式处理是一种处理数据的方式,它允许你在数据到达时立即处理,而不是等待所有数据都到达后再处理。这种方式特别适合处理大量数据,因为它不需要一次性加载所有数据到内存中。 在C#中,你可以使用yield return关键字来创建一个返回IEnumerable的方法,这个方法可以在每次迭代时返回一个元素,而不是一次性返回所有元素。这就是一种流式处理的实现方式。
    举个例子:
    复制代码
     1 private IEnumerable GetDataFromSource()
     2     {
     3         using (SqlConnection sourceConnection = new SqlConnection(sourceConnectionString))
     4         {
     5             sourceConnection.Open();
     6 
     7             using (SqlCommand command = new SqlCommand("SELECT * FROM YourTable", sourceConnection))
     8             {
     9                 using (SqlDataReader reader = command.ExecuteReader())
    10                 {
    11                     DataTable dataTable = new DataTable();
    12 
    13                     while (reader.Read())
    14                     {
    15                         dataTable.LoadDataRow(reader.GetValues(), LoadOption.Upsert);
    16                         DataRow row = dataTable.Rows[dataTable.Rows.Count - 1];
    17                         yield return row;
    18                         dataTable.Clear();
    19                     }
    20                 }
    21             }
    22         }
    复制代码
      在这个示例中,GetDataFromSource方法每次迭代时返回一个DataRow,而不是一次性返回所有DataRow。这样,你就可以在每次迭代时处理一个DataRow
    ,而不需要一次性加载所有数据到内存中。但是如果你的数据处理需要跨行操作,你可能需要使用其他的方法了,这个就不适用。
      扫盲:yield一次只返回一个难道就不会多次访问数据库了吗?
      在这个示例中,yield return并不会导致多次访问数据库。实际上,数据库查询只执行一次,然后使用SqlDataReader逐行读取结果。yield return只是在每次迭代时返回一个DataRow,而不是一次性返回所有DataRow。 当你在foreach循环中迭代GetDataFromSource()方法时,每次迭代都会从上次停止的地方继续,直到SqlDataReader读取完所有行。这意味着,虽然你每次只处理一个DataRow,但数据库查询只执行一次。
     
      既然已经看到这里了,我们可以继续再考虑下关于性能优化,我们还能从哪些方面着手呢?
    1. 代码层面:
    - 使用更高效的数据结构和算法。
    - 使用缓存避免多次数据库交互
    - 减少不必要的计算和内存分配。
    - 利用并行和异步编程提高性能。
    - 使用性能分析工具定位和优化瓶颈。
     
    2. Web API方面:
    - 使用HTTP缓存减少不必要的请求。
    - 使用Gzip或Brotli压缩减少响应大小。
    - 使用分页、排序和过滤减少返回的数据量。
    - 使用GraphQL或OData让客户端可以指定需要的数据。
     
    3. 数据库方面:
    - 使用索引加速查询。
    - 使用批量操作减少数据库交互次数。
    - 使用读写分离和数据库分片提高并发性能。
    - 使用缓存减少数据库访问。
     
    4. Nginx方面:
    - 使用反向代理和负载均衡提高并发性能。
    - 使用缓存减少后端服务器的负载。
    - 使用Gzip压缩减少网络传输量。
     
    5. CDN方面:
    - 使用CDN加速静态资源的访问。
    - 使用边缘计算将计算任务靠近用户。
     
    6. 微服务方面:
    - 使用服务间的异步通信减少等待时间。
    - 使用服务的横向扩展提高并发性能。
    - 使用服务的分区设计提高可扩展性。
     
    - 根据业务需求,考虑使用redis、rabbitmq、mangoDB等等中间件
     
    7. 其他方面:
    - 使用自动扩缩容的云服务应对流量波动。
    - 使用性能监控和日志分析工具定位性能问题。
    - 使用容器和Kubernetes等技术提高部署和运行的效率。
    - 使用链路追踪SkyWorking具体查看哪条链路的性能瓶颈
     
      性能优化本身就是一个非常庞大的话题,需要具体问题具体分析,总的来说是,平常能用到的就是以上总结的这些点。技术永远是为业务服务的,根据不同的业务选择合适的技术是高级开发者必须要考虑的问题。今天分享就这些了,关于性能优化大家还有那些经验可以评论区分享!
  • 相关阅读:
    Android-Firebase快速解决合规问题第4篇,解决FirebaseAnalytics库违规获取应用列表问题
    vue3笔记1
    mysql悲观锁是行锁还是表锁?
    Codeforces Round #814 (Div. 2) A - F
    计算机毕业设计Java风情旅游网站(源码+系统+mysql数据库+lw文档)
    认识弹性盒子flex
    【云原生 | 60】Docker中通过docker-compose部署kafka集群
    JavaWeb概念视频笔记
    王道数据结构——树
    stm32h743驱动TFTLCD液晶屏显示图片+汉字(快速上手,只教怎么用,不讲原理!)
  • 原文地址:https://www.cnblogs.com/Mr-Worlf/p/18020445