MyException - 我的异常网
当前位置:我的异常网» SQL » 加倍提升ORM系统SQL性能的一个方法

加倍提升ORM系统SQL性能的一个方法

www.MyException.Cn  网友分享于:2013-10-27  浏览:0次
成倍提升ORM系统SQL性能的一个方法

ORM确实事很方便让人不需要大量的写SQL,但是很多人诟病造成SQL性能不好

举个例子,一个功能是修改get用户数据的金币清零然后update

非常简单的一个业务:

 

User user=dao.getUser(uid);
user.setGold(0);
dao.updateUser(user);

 就这样一个简单的功能,实际会向数据库发送一长串的SQL update语句

 

如:update user set a=?,b=?,c=?............gold=? where uid = ?

看到这里有人会明白问题出在哪里了,想想一下每个表都有一堆字段, 而实际上我们在各个业务里面只会修改一两个字段,导致每次调用业务都会生产这样一长串的SQL,给数据库来带巨大而且没有必要的压力。

 

有的人会说:我编程习惯很好,平时都会把调用频率高的方法单独写sql。例如上面修改金币会单独写一个updateGold方法。当然,这是一种很好的习惯并且也能解决这个问题。但是 大部分开发者、大部分业务实现并不会这样做。不是吗?

 

现在可以通过另外一种方法来实现对原有的ORM系统优化兼顾开发效率和系统性能提升:

 

	/** update之前先比较,只update修改过的列 */
	public <T> void compareAndUpdate(T oldT, T newT);

 简单的介绍一下就是,通过对比新旧实体,根据修改过的列来生成指定的列到达针对性更新的目的。

 

 

//以前:
User user=dao.getUser(uid);
user.setGold(0);
dao.updateUser(user);
//产生的sql:
update user set a=?,b=?,c=?............gold=? where uid = ?

//新的做法:
User user=dao.getUser(uid);
User oldUser=BeanUtils.cloneBean(user);
user.setGold(0);
dao.compareAndUpdate(oldUser,user);
//产生的sql:
update user set gold = ? where uid = ?

sql语句的缩短成倍的降低了与数据库通讯的开销,并且大大的降低了数据库压力。

如果上面的写法可能会稍微麻烦点,我们可以再偷点懒:
User user=dao.getUser(uid);
Object oldBean=BeanUtils.copyBean(user);//这样的区别是copy这段代码即可,不需要经常修改oldBean的类型。因为我们不关心oldBean是什么类型。
user.setGold(0);
dao.compareAndUpdate(oldBean,user);

 有人会说实体copy和拼接sql也会产生开销,这个是不错。但是这种开销对于数据库的压力来说根本就是九牛一毛不值一提。

 

 

compareAndUpdate这个方法是我自己写的ORM框架:freyja-jdbc 里面改写update方法产生的。如果是使用的其他ORM框架 有预留接口的话可以改写下即可。没有接口的话可能需要自己去实现了,方法就是映射实体和字段 生成sql语句

hibernate 好久没碰了,怎么改写hibernate也能达到一样的效果不清楚,有兴趣的可以自己研究下。

 

最后还是放下相关代码吧

	public static Parameter compareAndUpdate(Object oldEntity, Object newEntity) {
		FreyjaEntity entity = ShardingUtil.getEntity(newEntity.getClass());
		List<Object> args = new ArrayList<Object>();
		Object idValue = null;

		BeanMap oldBeanMap = BeanMap.create(oldEntity);

		BeanMap beanMap = BeanMap.create(newEntity);

		List<String> columnNameList = new ArrayList<String>();
		for (Property p : entity.getProperties().values()) {
			ShardingProperty s = (ShardingProperty) p;
			Object newPropertyValue = beanMap.get(p.getName());
			if (s.isId()) {
				idValue = newPropertyValue;
				continue;
			} else {
				Object oldPropertyValue = oldBeanMap.get(p.getName());
				if ((newPropertyValue == null && oldBeanMap == null)
						|| newPropertyValue.equals(oldPropertyValue)) {// 值未改变过,不更新
					continue;
				}
			}
			columnNameList.add(p.getName());
			args.add(newPropertyValue);
		}
		args.add(idValue);

		Parameter parameter = null;

		if (entity.isSubTable()) {
			DbResult result = ShardingUtil.engine.getShardingStrategy()
					.getShardingTableNameById(entity.getTableName(), idValue);
			parameter = entity.updateByColumn(columnNameList, idValue);

			parameter.setDbNo(result.getDbNo());
		} else {
			parameter = entity.updateByColumn(columnNameList, null);
		}
		parameter.setArgs(args.toArray());
		return parameter;
	}

	/** 根据指定列生产相关信息 */
	public Parameter updateByColumn(List<String> columnNameList, Object idValue) {
		String set = "";
		List<Integer> types = new ArrayList<Integer>();
		for (String cn : columnNameList) {
			ShardingProperty s = (ShardingProperty) getProperties().get(cn);
			set += s.getDbColumnName() + " = ? ,";
			types.add(s.getTypes());
		}

		set = set.substring(0, set.length() - 1);
		String setSql = " set " + set + " where " + getId().getDbColumnName()
				+ " = ?";
		types.add(getId().getTypes());
		String sql = null;
		if (idValue == null) {
			sql = "update " + getTableName() + setSql;
		} else {// 分库
			DbResult result = ShardingUtil.engine.getShardingStrategy()
					.getShardingTableNameById(getTableName(), idValue);
			sql = "update " + result.getTableName() + setSql;
		}
		Parameter p = new Parameter();
		p.setSql(sql);
		p.setSqlTypes(ListUtil.toPrimitive(types));
		return p;
	}

 

 ----------------------------------------------------------------------------

实际操作了下后发现虽然确实是不错,但是对现有系统和写法有出入。有没有兼容现有系统的写法更智能一些的办法呢。

然后想到了另外一种做法:

public class PersistObj implements Serializable {

	/** 用于更新compareAndUpdate,并且改属性不会被序列化 */
	private transient  Object oldBean;
get();set();
}

//然后让你的实体继承这个对象,这样默认都会有了一个oldBean属性来存放oldBean
//再写个通用方法

	@Override
	public <T> T getAndClone(Class<T> clazz, Object id) {

		T t = super.get(clazz, id);

		if (t instanceof PersistObj) {
			PersistObj obj = (PersistObj) t;
			Object oldBean = BeanUtils.cloneBean(t);
			obj.setOldBean(oldBean);
		}

		return t;
	}

//这样查询的时候就把oldBean存储了。

//之前的update 方法改写下:
		if (oldT == null) {
			if (newT instanceof PersistObj) {
				PersistObj obj = (PersistObj) newT;
				oldT = (T) obj.getOldBean();
			}
		}

让oldBean从 oldBean属性里面去取。不需要再传递了

 

这样做后的效果是:

		User user = userDao.getAndClone(uid);
		user.setGold(0);
		userDao.compareAndUpdate(user);

//这里这样写是为了让大家明白如何工作的。
//实际上这些方法是可以替换底层封装的,业务代码不需要修改。
//也就是说可以达到这种写法效果:

User user = userDao.getUser(uid);
user.setGold(0);
userDao.updateUser(user);

//是不是和最初一样了,在不影响现有代码的基础上提升性能

这样做后对原有的业务不需要再修改, 因为这些dao方法都是底层的。替换的就可以了。service业务方法不需要变动。

 

当然,这样写有2个缺点:

1、无法对缓存有效。 

 2、每次查询都会克隆一遍。

 

 

针对第一个问题:当有用到缓存的地方,只能用之前的方法,手动的cloneBean。

针对第二个问题:首先对于不需要这种功能的实体不用getAndClone方法就没有任何影响

对于其他的一般我们查询肯定是为了修改的。大部分逻辑都是这样。所以默认情况影响不大,如果明确不涉及到修改,不使用getAndClone方法就可以了使用其他默认的get()方法就可以了。也没有太大影响,只是编写的时候多了一种选择

 

文章评论

 程序员的样子
程序员的样子
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
为什么程序员都是夜猫子
为什么程序员都是夜猫子
总结2014中国互联网十大段子
总结2014中国互联网十大段子
我的丈夫是个程序员
我的丈夫是个程序员
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
10个调试和排错的小建议
10个调试和排错的小建议
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
老程序员的下场
老程序员的下场
程序员必看的十大电影
程序员必看的十大电影
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
旅行,写作,编程
旅行,写作,编程
编程语言是女人
编程语言是女人
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员应该关注的一些事儿
程序员应该关注的一些事儿
我是如何打败拖延症的
我是如何打败拖延症的
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
Java程序员必看电影
Java程序员必看电影
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
那些争议最大的编程观点
那些争议最大的编程观点
程序员的鄙视链
程序员的鄙视链
程序员都该阅读的书
程序员都该阅读的书
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
一个程序员的时间管理
一个程序员的时间管理
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
如何成为一名黑客
如何成为一名黑客
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
代码女神横空出世
代码女神横空出世
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序员和编码员之间的区别
程序员和编码员之间的区别
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有