1

关于代码性能优化的总结 - 程序员不帅哥

 7 months ago
source link: https://www.cnblogs.com/Mr-Worlf/p/18020445
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

关于代码性能优化的总结

  今天同事发开中遇到了一个代码性能优化的问题,原本需求是:从一个数据库中查询某个表数据,存放到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<SqlCommand>,然后遍历执行,此处能说明问题即可
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<T>的方法,这个方法可以在每次迭代时返回一个元素,而不是一次性返回所有元素。这就是一种流式处理的实现方式。
举个例子:
 1 private IEnumerable<DataRow> 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具体查看哪条链路的性能瓶颈
  性能优化本身就是一个非常庞大的话题,需要具体问题具体分析,总的来说是,平常能用到的就是以上总结的这些点。技术永远是为业务服务的,根据不同的业务选择合适的技术是高级开发者必须要考虑的问题。今天分享就这些了,关于性能优化大家还有那些经验可以评论区分享!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK