最近用到了ehcache,根据从网上搜集的资料,调试了一个小例子,顺带做了性能测试。cache的效率的确很高,较sql直接查询性能上升近8000倍。
第一步:先在mysql环境中建好TEST,
CREATE TABLE TEST ( TEST_ID bigint(20) , TEST_NAME varchar(50), TEST_TIME timestamp default CURRENT_TIMESTAMP, TEST_VALUE decimal(10,3) )
表结构如下
mysql> describe TEST; +------------+---------------+------+-----+-------------------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +------------+---------------+------+-----+-------------------+-----------------------------+ | TEST_ID | bigint(20) | YES | | NULL | | | TEST_NAME | varchar(50) | YES | | NULL | | | TEST_TIME | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | | TEST_VALUE | decimal(10,3) | YES | | NULL | | +------------+---------------+------+-----+-------------------+-----------------------------+ 4 rows in set (0.11 sec)
第二步,建java工程,建立与TEST表对应bean,
import java.io.Serializable; import java.math.BigDecimal; import java.sql.Timestamp; import java.util.*; public class TEST implements Serializable { private static final long serialVersionUID = 1L; public Long TEST_ID; public String TEST_NAME; public Timestamp TEST_TIME; public BigDecimal TEST_VALUE; @Override public String toString() { return String.format("ID:%s,,,NAME:%s", TEST_ID, TEST_NAME); } }
Util类代码如下:
import java.util.Random; public class Util{ public static Random rand = new Random(); public static String atoz = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; public static String genString(int length) { StringBuilder re = new StringBuilder(length); re.append(atoz.charAt(rand.nextInt(52))); for (int i = 0; i < length; i++) { re.append(atoz.charAt(rand.nextInt(62))); } return re.toString(); } public static double genDouble() { double d1 = 5120 * rand.nextDouble(); double d2 = 1024000 * rand.nextDouble(); return d1 + d2; } }
主程序:
import java.math.BigDecimal; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.HashMap; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; public class Main { private static String dbURL="jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=GBK"; private static String user="root"; private static String pass="beautiful_mm"; private static String dbDriver="com.mysql.jdbc.Driver"; private static String FORMAT="yyyy-MM-dd HH:mm:ss"; private static SimpleDateFormat dateFormat=new SimpleDateFormat(FORMAT); /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub CacheManager manager = CacheManager.create("./src/main/resources/ehcache.xml"); Cache demo = manager.getCache("demoCache"); if(null==manager) {System.out.println("manager is null? can not get ehcache manager");} else {System.out.println("get ehcache manager succeed!");} try { // 插入10W数据,以备测试
traditionalInsert(100000); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } try { ehcache(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } Cache cid = manager.getCache("TEST_ID.TEST"); long begin = System.currentTimeMillis(); for(int i=1;i<10001;i++) { Element e5120 = cid.get(new Long(i)); } long end1 = System.currentTimeMillis(); System.out.println("从cache中查询10000条数据消耗时间: " + (end1-begin)); for(int i=1;i<10001;i++) { try { getRecord(i); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } long end2 = System.currentTimeMillis(); System.out.println("用sql查询10000条数据消耗时间: " + (end2-end1)); } public static HashMap<Long, TEST> simpleCache() throws Exception { HashMap<Long, TEST> cacheid = new HashMap<Long, TEST>(); Class.forName(dbDriver); Connection conn = DriverManager.getConnection(dbURL, user, pass); try { long begin = System.currentTimeMillis(); Statement s = conn.createStatement(); String sql = "SELECT TEST_ID,TEST_NAME,TEST_TIME,TEST_VALUE FROM TEST"; ResultSet querySet = s.executeQuery(sql); for (int i = 1; querySet.next(); i++) { TEST curr = new TEST(); curr.TEST_ID = querySet.getLong(1); curr.TEST_NAME = querySet.getString(2); curr.TEST_TIME = querySet.getTimestamp(3); curr.TEST_VALUE = querySet.getBigDecimal(4); cacheid.put(curr.TEST_ID, curr); } long end = System.currentTimeMillis(); System.out.printf("Time:%d\n", (end - begin)); } catch (Exception ex) { ex.printStackTrace(); } finally { conn.close(); } return cacheid; } public static void traditionalInsert(int total) throws Exception { Thread.sleep(3000); Timestamp current = new Timestamp(System.currentTimeMillis()); String currentStr = dateFormat.format(current); System.out.println(currentStr); Connection conn = DriverManager.getConnection(dbURL, user, pass); try { long begin = System.currentTimeMillis(); conn.setAutoCommit(false); String sql = "INSERT INTO TEST (TEST_ID,TEST_NAME,TEST_TIME,TEST_VALUE) VALUES (?, ?, ?, ?)"; PreparedStatement ps = conn.prepareStatement(sql); for (int i = 1; i <= total; i++) { ps.setLong(1, i); ps.setString(2, Util.genString(33)); ps.setTimestamp(3, current); ps.setBigDecimal(4, new BigDecimal(Util.genDouble())); ps.addBatch(); if ((i % 500) == 0) { ps.executeBatch(); } } ps.executeBatch(); System.out.println("insert !! done"); conn.commit(); long end = System.currentTimeMillis(); System.out.printf("Count:%d Time:%d\n", total, (end - begin)); } catch (Exception ex) { ex.printStackTrace(); conn.rollback(); } finally { conn.close(); } } public static void ehcache() throws Exception { CacheManager manager = CacheManager.create("./src/main/resources/ehcache.xml"); manager.addCache("TEST_ID.TEST"); Cache cid = manager.getCache("TEST_ID.TEST"); Class.forName(dbDriver); Connection conn = DriverManager.getConnection(dbURL, user, pass); try { long begin = System.currentTimeMillis(); Statement s = conn.createStatement(); String sql = "SELECT TEST_ID,TEST_NAME,TEST_TIME,TEST_VALUE FROM TEST"; ResultSet querySet = s.executeQuery(sql); for (int i = 1; querySet.next(); i++) { TEST curr = new TEST(); curr.TEST_ID = querySet.getLong(1); curr.TEST_NAME = querySet.getString(2); curr.TEST_TIME = querySet.getTimestamp(3); curr.TEST_VALUE = querySet.getBigDecimal(4); cid.put(new Element(curr.TEST_ID, curr)); } long end = System.currentTimeMillis(); System.out.printf("ehcache cost Time:%d\n", (end - begin)); } catch (Exception ex) { ex.printStackTrace(); } finally { conn.close(); } } public static TEST getRecord(int id) throws Exception { TEST ret = null; Class.forName(dbDriver); Connection conn = DriverManager.getConnection(dbURL, user, pass); try { long begin = System.currentTimeMillis(); Statement s = conn.createStatement(); String sql = "SELECT TEST_ID,TEST_NAME,TEST_TIME,TEST_VALUE from TEST where TEST_ID="+id; ResultSet querySet = s.executeQuery(sql); for (int i = 1; querySet.next(); i++) { TEST curr = new TEST(); curr.TEST_ID = querySet.getLong(1);; curr.TEST_NAME = querySet.getString(2); curr.TEST_TIME = querySet.getTimestamp(3); curr.TEST_VALUE = querySet.getBigDecimal(4); return curr; // cid.put(new Element(curr.TEST_ID, curr)); } long end = System.currentTimeMillis(); System.out.printf("ehcache cost Time:%d\n", (end - begin)); } catch (Exception ex) { ex.printStackTrace(); } finally { conn.close(); } return ret; } }
最终运行结果:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. get ehcache manager succeed! 2012-11-25 22:28:41 insert !! done Count:100000 Time:14980 ehcache cost Time:5881 从cache中查询10000条数据消耗时间: 71 用sql查询10000条数据消耗时间: 500620
可以看出,从cache中取数据的效率较直接从DB中查高出7000倍,即使加上构建cache的时间,也胜过查库约80倍。
目前可以看出,cache的构建时间将会是大范围应用的一个瓶颈,一个应用的启动时间会因为cache构建而变的过长,这在应用中想办法处理。待后续研究。