Monthly Archives: November 2014

poi – 生成excel时使表头比较醒目、好看

CellStyle style = sheet.getWorkbook().createCellStyle(); //置成黄色 style.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); style.setFillPattern(CellStyle.SOLID_FOREGROUND); //表头的边界线清晰可见 style.setBorderBottom(HSSFCellStyle.BORDER_THIN); style.setBorderTop(HSSFCellStyle.BORDER_THIN); style.setBorderRight(HSSFCellStyle.BORDER_THIN); style.setBorderLeft(HSSFCellStyle.BORDER_THIN); Row header = sheet.createRow(0); int columnIndex = 0; for (…) { Cell cell = createCell(header, columnIndex); cell.setCellValue(headerText); cell.setCellStyle(style); //表头cell的宽度适应文字 sheet.autoSizeColumn(columnIndex); columnIndex++; }

其实用JMeter就可以对mysql做性能测试了

我用Super Smack做过几次MySQL性能测试。虽然可以用,但这种用c写的程序存在安装困难的问题:在阿里云的centos上可以装的上,在aws上未必就装的上;配置起来也很难受,那些个smack文件,太古怪。 后来我发现,其实用JMeter的jdbc request就可以对mysql性能测试。我用的模式是: 引用 1. 在开发机器上(mac/ubuntu/windows)以gui方式配置好Test Plan, 并试运行。 2. 将test plan文件上传到无干扰无界面的linux测试机上,以non-gui方式在测试机上执行测试,再收集结果。 JMeter支持server模式:用本地的jmeter控制测试机上的jmeter, 直接在本机上启动、停止、查看结果,用起来非常顺手。但它有个问题:在测试过程中,测试机上的jmeter要不停地从本地机器获取测试数据,并返回采样数据到本地机器。这些数据流动会增加测试机上JMeter的负担,使qps偏低。 所以我还是选择了在测试机上使用non-gui模式这种方案。下面给个完整的例子,例示jmeter jdbc request的使用。 例示:用JMeter对MySQL进行压力测试 测试目标 使用普通varchar作为主键,比起使用自增数字类型作为主键,到底会慢多少?这个性能损失是不是可以忽略不计? 测试数据准备 建两张差不多的表,一个使用数字user_id作主键,一个直接使用user_name drop table if exists user_id_pk; drop table if exists user_name_pk; create table user_id_pk ( user_id bigint unsigned not null auto_increment , user_name varchar(50) not null description varchar(200) default null created_when datetime not null […]

比较一个系统中两个模块的性能差别:不要同时压这两个模块

要比较一个系统中两个模块的性能差别(比如同一个库中的两个类似的表),有两种压测模式: 1. 一个test plan含两个senario, 执行一次,同时出两个模块的结果。 2. 搞两个test plan, 各带一个senario; 各执行一次,各出一个结果。 第一种模式很有吸引力,因为它可省下很多重复的配置工作;只执行一次,比较省时;像jmeter之类的还会直接给出直方图,以示比较。 然而,我的亲身经历表明,第1种模式由于公共资源占用问题会导致“大锅饭”效应: 同时压测两个模块,慢模块会占用过多公共资源(cpu, 内存,连接池等),导致快模块无法获得足够的资源因而也变慢。 这种模式下比较出来的结果并不准确。 所以,还是应该把两个测试互相隔离起来,搞两个 test plan,各带一个senario吧。 p.s. 如果你用jmeter,可以只写一个test plan,带两个sampler, 然后enable一个,disable另一个,这样每次执行时仍是隔离的。

分布式系统中的三种接口粒度

分布式系统中有三种接口粒度: 1. 内部用的domain service接口。这个接口最精致。   a. 出入参数跟数据库表有比较强的对应,基本上一个对象对应一张表。   b. 入参数据都假定已清洗,不必再校验。   c. 业务逻辑主要是CRUD+高度可重用的核心业务逻辑。   d. 返回数据一般是数据对象或者它们的类集。错误信息一般直接通过exception返回。 2. 供移动或web前端调用、或者供外部系统调用、面向use case的app service接口。这个接口最重。   a. 出入参数一般对应数据库中的多张表中的数据,还会有一些use case特有的字段,比如一个“帖子”对象中会给出“帖子作者的头像”、“当前用户能否删除本贴”、“帖子被评论次数”等。 这一层的数据对象是DTO(value object), 一般要从多个domain层对象的字段中组合而来。   b. 入参数据由用户直接输入或非可信系统输入,所以需要校验。   c.  业务逻辑一般需要通过组合多个domain service实现,比如 N个crud +  1个权限校验 + 1个展示层数据填充等,很难重用。   d. 返回数据一般是Result对象: 数据对象 + 错误码 + 错误信息, 不再抛exception. 3. 给内部可信系统调用的rpc service接口。这个接口的轻、重介于上面两种之间。   a. 出入参数包含的字段往往比domain层参数的稍多一点,比app层参数的少一些或少很多。   […]

集群环境下慎用本地缓存

集群环境下慎用本地缓存。 用户1在机器A上看到100条记录,用户2在机器B上看到的却是90条记录。 你会说你的业务允许两边看到不一样。 是的,两个用户看到的不一样不要紧。 但是同一个用户看到不一样的话,用户体验会非常差,差到要骂人。 例子是:用户1在机器A上提交表单删除100条记录,服务端处理完毕后让浏览器跳转(Redirect after Submission),负载均衡将这个请求跳转到机器B上,机器B上的本地缓存没变,所以仍然是删除前的记录数。 用户1看到这个结果,脑子里只有一个想法:删除没起作用。 所以,集群环境下使用本地缓存,一定要保证同一个用户先后访问的是同一台机器。

MySQL: 被索引的字段是否有空值对性能影响不大

‘High Performance MySQL’ 虽然说了  “Avoid NULL if possible”,但也说了 引用 “The performance improvement from changing NULL to NOT NULL is usually small”. 我针对这个做了下benchmarking. 性能差别确实很小。请看这里的末尾部分。 业务上说,有很多字段确实既要索引,又可能为空。对于这种情况,该NULLABLE就NULLABLE吧,没必要为了一点点性能上的收益而使用各种默认值给代码留坑。

typeahead.js + remote ajax完整例子

typeahead.js的样例文档很不详细,必须查API,而API也写得比较简略。 我这里给一个比较典型的样例,供参考。 典型用况:修改一条person记录。服务端要你上传personId参数, 但用户只记得personName; 这时你要让用户在一个输入框里输入personName, 搜索出person信息,用户选中一个记录,系统再在另一个只读的输入框里记录被选中的personId, 最后提交表单上传到服务端。 表单DOM: <form> <table> <tr> <td>Person Name</td> <td><input type="text" class="typeahead" name="personName" id="personNameInput" placeholder="输入Person Name搜索"/> </td> </tr> <tr> <td>Person ID</td> <td><input type="text" name="personId" id="personIdInput" readonly/> </td> </tr> … </table> </form>   其实也可以只提供一个输入框:用户输入Person Name并选择后,系统在输入框中显示Person ID.  但Person Name和Person ID一个是字符串类型,一个是数字类型。 遇到这种情况,typeahead.js会有个bug: 第一次搜索正常,清空输入框再次搜索就会报JS语法错误。 typeahead相关javascript: var persons = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(‘personName’), //服务端返回的json中, Person Name对应的字段叫personName […]