7月 052012
 

固定链接:http://www.gamtin.info/archives/291

最近项目中遇到的一个比较恶心的问题,放狗(Google)找了好久也没有什么结果,暂时记到这里,算是备忘一下。

发生环境:.NET Framework 2.0/3.5(真TM古老的环境啊)

现象:在对DataTable进行Select()或者DataView.RowFilter的时候,条件中 Column = ‘A’ 和 Column = ‘A<空格>’ 的结果是一样的,甚至,最后有N个空格也不会对结果产生任何影响。但是,Column = ‘<空格>A’ 则可以得到期待的结果。似乎微软会无视掉最后的所有空格……

临时的解决案:Column = ‘A<空格>’ AND LEN(Column) = 2

如果有更好的方案,叩首拜谢。

6月 202012
 

固定链接:http://www.gamtin.info/archives/272

最近,面试了不少求职者,简单梳理下我喜欢问的题目。

首先说明,我是一个.NET(C#)程序员,我所面对的求职者的目标职位也仅仅是PG到SE,个别的是PL。下面的问题也主要是技术类问题。

1、.NET基础

  • 声明变量时 int?、double?的含义
  • using关键字的用法
  • DataTable中Row的RowState属性有哪几种

2、思维方式/实现方法

  • 怎么将一个DataTable的数据按照某列重新排序
  • 怎么去除一个DataTable中的重复数据

3、DB/SQL相关

  • 内连接和外连接的区别
  • union和union all的区别

其实都是非常简单的问题,不过能够准确的回答上来的求职者也不是很多。

大连的软件开发行业看似繁荣,实际上大家的能力非常弱。

5月 212012
 

固定连接:http://www.gamtin.info/archives/264

某项目,规模很大,去年/今年公司的风云项目。

今天早晨突然得到消息,说要让我和几个人紧急去帮忙。据说是性能不好,目标是改善代码逻辑,提高性能。

虽然说现在自己的项目已经让我快要呼吸困难了,但是大老板亲自来请了,还是要出现的。

然后和项目组的人碰了下,那边介绍了下项目情况和要求我们做的事情,之后分配了下任务便各自散开了。

拿到代码,看了看,于是得到了标题那样的感觉。

为什么呢?

首先,其项目的数据量规模可以达到百万、千万级别。

其次,因为某种原因,数据不能通过条件从DB取得,而是全部数据都取到内存中。

最后,代码中用来保存数据的对象完全没有考虑到性能问题。想要检索数据只能使用循环全部检索……。

到这里了,相信你已经有和我一样的看法了吧。

3月 252012
 

固定链接:http://www.gamtin.info/archives/235

这是最近以来非常忙的根源之一。

到现在为止,似乎即将有一个结果了,于是简单整理一下。

项目背景

简单介绍一下现在这个项目。

这是一个基于.NET Framework 2.0的SmartClient项目,当然这样说是为了好听。实际上就是一个C/S的东西,通讯靠的是WebService协议,将传输数据序列化为XML格式传来传去。客户端程序靠微软的ClickOnce来发布,还算方便。后面保存数据的是Oracle 10g。顺带一提,对DB的CRUD操作,基本都是Stored Procedure完成的。

问题

某客户积累了N年的数据,业务表达到了100万条,关联的Master表数据差不多30万条左右吧。某机能的任务就是按照客户要求收集这些数据,并且进行一些出报表或者整体更新的任务。

实际操作中,数据收集(结果量50万左右)会消耗2小时以上的时间,因为设置了2小时的Timeout,所以实际多少时间不详。改到结果20万左右的时候,2小时以内倒是可以结束,但是不时的在Client端发生OutOfMemoryException。为此,客户一直在抱怨,我们的压力很大。

分析/解决

表面现象是,大量数据的情况下性能非常差劲。分析下来,突破点有三:

  1. Orcale方面:优化SQL
  2. 程序方面:优化业务逻辑算法
  3. 传输方面:提高数据传输效率

逐条分别来说。

Oracle方面

在DB表结构已经存在,并且已经运行多年的情况下,如果想改变设计是非常不现实的。而且,现在来看,问题似乎并不是表结构设计有多么的不合理。业务数据表主要就是一个,虽然需要连接的Master表不少,且数据量也不小,但是都是主键之间的连接,这种小事对Oracle来说应该不会有太大的问题。

然后分析SQL文。这时候,做外包的短板就出现了,虽然接触了好多年的Oracle,但是对于性能上的考虑完全没有过。被逼到没办法,赶快上网学习怎样研究分析Oracle的执行计划。传统观念认为(包括网上的不少类似“Oracle性能优化经验XX条”),有子查询会降低性能,但是实际的计划看来,子查询对性能的影响其实是挺小的(注意,这里是有条件的,某些子查询的写法似乎对查询效率影响不大),Oracle引进的CBO优化器的效果果然很牛逼。折腾了2、3天,发现SQL完全没有优化的可能。

实际的执行来看,用本地的有5万条结果的DB,用不到5分钟便可以查询完成并且返回到客户端了,这种性能其实已经不错了。

程序方面

分析业务逻辑算法,这其实是相对最简单的了。

检查发现,客户端表示这些数据的性能非常差。还是上面的5万条数据,处理后,主画面实际上只需要表示1500条左右(其他数据会在几个子画面表示)。但就是这个处理,竟然消耗了20分钟!!

一看代码,才发现,这是个“金矿”啊。当时的程序员,将这数据Copy了2份,然后用3层循环来不停的遍历这些可怜的数据。整个处理下来,每条数据被访问了n^3次。于是果断的改掉,成功的将20分钟的处理降低到1分钟左右。

这个问题,正好可以用来以后做面试用:

假设DataTable中有5列(Columun_1~Column_5),现在想得到这样的结果,你怎么来实现?如果数据量非常大,怎么考虑性能?

在这个DataTable后面增加一列,Count(int),用来保存Column_1,Column_2结果相同的数据的个数。

例:

Column_1 Column_2 Column_3 Column_4 Column_5
1 1 1 1 1
1 1 2 2 2
1 1 3 2 2
1 2 1 1 1
1 2 2 2 2
1 1 4 4 4
2 1 1 1 1
2 1 2 2 2

结果:

Column_1 Column_2 Column_3 Column_4 Column_5 Count
1 1 1 1 1 4
1 1 2 2 2 4
1 1 3 2 2 4
1 2 1 1 1 2
1 2 2 2 2 2
1 1 4 4 4 4
2 1 1 1 1 2
2 1 2 2 2 2

其实是挺简单的算法,但是似乎对一些外包的程序员来说,有点难度的。

实际上,在DataTable中,用循环是性能很差的。如果只要取得Count之类的,还是用Select().Length/View.Count效率来得更好。

传输方面

大量的数据,不仅仅是运算的成本高,传输的成本也不低。实际测试来看,5万条数据的大小近50MB,这些数据在网络上传来传去的效率是不能被无视的。而且还发生过客户端接受数据的时候出现OutOfMemoryException。

于是有了这几种方案:

  1. 多次传送方案之一:多次从DB取得数据,类似分页。
    这个问题,头两天在Twitter上咨询过,因为结果是需要Orderby的,且排序的列包括了没有索引的列,所以性能会更差。一次整体取得4分钟不到,分开取得(每次1000条)的话,会直奔20分钟去了。此方案否决!
  2. 多次传送方案之二:单次DB取得,多次Client和WebService通讯。
    因为是基于WebSercie,就是单纯的请求(Request)/响应(Response)模式,一次请求结束后,该线程便被回收了,无法保存数据。且项目的框架已经固定,没法实现类似轮询功能(让WebService继续保持),再加上时间紧,没法(也不敢)大规模的改造项目框架。便想到,将数据以文件形式保存到Server侧,直到全部发送完毕再删除。虽然增加了些IO操作,但是就现在的硬件性能而言,这不到100MB的文件实在是小菜一碟。
    这个方案的问题也是明显的,这么多次的操作,如果某次出现错误,或者客户端异常了,搞不好这个文件就作为垃圾永远的留在Server上了,这是个无法接受的现象。所以,这个方案也基本上被否决了。
  3. 数据压缩
    现在的序列化方法是都转成Base64格式,经验来看,转成Base64本身就会让数据量增加。本来数据量就不小,还加,压力更大了。于是增加了压缩和解压缩的逻辑。实际测试来看,压缩后的数据量大概是原来的14%,可以达到预期,似乎是个比较完美的解决方案。
    但是 ,问题也是有的,虽然减轻了网络压力,但是增加了服务器/客户端的内存和CPU压力。这时候就是个平衡的问题了,相对于增加网络带宽,似乎换个性能更好的机器的成本更低吧。
    到这里,想到了西乔的那个漫画:猛击这里

最后

到这里,基本就基本结束了。本来想简单介绍了,但也写下了不少。

折腾这段时间下来,感觉做外包的,视野确实窄,接触的东西也太少。