Web应用单点压力测试调优-第5季
各项配置:
my.cnf
[client] port = 3306 socket = /tmp/mysql.sock [mysqld] port = 3306 socket = /tmp/mysql.sock skip-external-locking key_buffer_size = 16M max_allowed_packet = 1M myisam_sort_buffer_size = 8M log-bin=mysql-bin binlog_format=mixed server-id = 1 #update start max_connections=1500 query_cache_size=16M default-storage-engine=INNODB #table_cache=256 tmp_table_size=8M thread_cache_size=8 read_buffer_size=64K read_rnd_buffer_size = 256K net_buffer_length = 8K table_open_cache = 32 sort_buffer_size = 256K innodb_additional_mem_pool_size=2M innodb_flush_log_at_trx_commit=0 innodb_log_buffer_size=4M innodb_buffer_pool_size=32M innodb_log_file_size=128M innodb_thread_concurrency=1 #update over [mysqldump] quick max_allowed_packet = 16M [mysql] no-auto-rehash [myisamchk] key_buffer_size = 20M sort_buffer_size = 20M read_buffer = 2M write_buffer = 2M [mysqlhotcopy] interactive-timeout
Tomcat配置
catalina.sh头
JAVA_OPTS="-server -XX:PermSize=82M -XX:MaxPermSize=82M -Xss256k -Xms450m -Xmx450m -Xmn300m -XX:SurvivorRatio=6 -Xverify:none -XX:MaxTenuringThreshold=15 -XX:+UseFastAccessorMethods -XX:+UseAdaptiveSizePolicy -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.137.233"
server.xml连接器(因为瓶颈不在连接器,所以暂未使用apr连接器)
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="45000" redirectPort="8443" maxHttpHeaderSize="4096" disableUploadTimeout="true" enableLookups="false" bufferSize="4096" />
程序中的连接池配置
jdbc.driverClass=com.mysql.jdbc.Driver #jdbc.jdbcUrl=jdbc:mysql://192.168.137.200:3306/story?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=128&prepStmtCacheSqlLimit=512&autoReconnect=true jdbc.jdbcUrl=jdbc:mysql://localhost:3306/story?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=32&prepStmtCacheSqlLimit=256&autoReconnect=false&metadataCacheSize=16&maintainTimeStats=false&cacheResultSetMetadata=true&useNewIO=true&emptyStringsConvertToZero=false&maxRows=30 jdbc.user =root jdbc.password=111111 jdbc.miniPoolSize=300 jdbc.maxPoolSize=1500 jdbc.initialPoolSize=300 jdbc.maxIdleTime = 120000 jdbc.acquireIncrement=30 jdbc.acquireRetryAttempts = 40 jdbc.acquireRetryDelay=120000 jdbc.testConnectionOnCheckin = true jdbc.automaticTestTable = test jdbc.idleConnectionTestPeriod = 30000 jdbc.checkoutTimeout=65000
调整8-将热点数据news的数据集list缓存到HashMap中
这是最后的办法了,没有办法的办法,简单缓存数据集到本地内存。这里假设热点数据是news数据集,关键代码如下
String sqlString = sql.toString()+rowStartIdxAndCount[0]+rowStartIdxAndCount[1]; readLock.lock(); if(cache.containsKey(sqlString)){ readLock.unlock(); return cache.get(sqlString); }else{ readLock.unlock(); writeLock.lock(); List<NewsBean> list = (List<NewsBean>) quaryGridListPrepare(sql.toString(), NewsBean.class, rowStartIdxAndCount[0], rowStartIdxAndCount[1]); cache.put(sqlString, list); writeLock.unlock(); return list; } /* return (List<NewsBean>) quaryGridListPrepare(sql.toString(), NewsBean.class, rowStartIdxAndCount[0], rowStartIdxAndCount[1]); */ } private Map<String,List<NewsBean>> cache = new HashMap<String,List<NewsBean>>(); private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private Lock readLock = rwl.readLock(); private Lock writeLock = rwl.writeLock();
之后再将mysql的配置修改,将资源调小一点(其实最大连接数也可以调小)
#update start max_connections=1500 query_cache_size=16M default-storage-engine=INNODB #table_cache=256 tmp_table_size=8M thread_cache_size=8 read_buffer_size=64K read_rnd_buffer_size = 256K net_buffer_length = 8K table_open_cache = 16 sort_buffer_size = 256K innodb_additional_mem_pool_size=2M innodb_flush_log_at_trx_commit=0 innodb_log_buffer_size=4M innodb_buffer_pool_size=16M innodb_log_file_size=128M innodb_thread_concurrency=1 #update over
给JVM再多一点点内存空间
JAVA_OPTS="-server -XX:PermSize=82M -XX:MaxPermSize=82M -Xss256k -Xms480m -Xmx480m -Xmn320m -XX:SurvivorRatio=4 -Xverify:none -XX:MaxTenuringThreshold=20 -XX:+UseFastAccessorMethods -XX:+UseAdaptiveSizePolicy -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.137.233"
1500并发,准备9s循环6次。
走势图
报告
平稳后,TPS上升到了267/S。平均在250~270之间
这里是一个理想的场景,实际情况是:根据日志,进行离线分析(hadoop)可以分析出哪些表是热点表,哪些sql是热点sql,哪些数据是热点数据。之后利用缓存策略,再将其缓存。再利用读写锁,进行缓存操作,TPS上升上去。此刻场景仅仅将news表作为热点表,其实不科学也不严谨,不过表明一点,此方案在内存十分有限的情况下,可行。只不过得权衡缓存数据的大小,以便让缓存的命中率(hit count)高一些,别浪费哪些内存资源。
JVM堆内存走势(因新生代空间比例较大,所以波浪较为明显)
到底哪些数据应该放入缓存,这是一个长期的监控数据分析后得出的结论。根据经验估算,此单点机器所有功能都用上后,缓存再稍微改造成普适性接口,基本TPS可以平均维持在100/s左右的数值。
调优顺序(根据场景,制定不同时期的2/8原则):http请求数>数据库连接>数据库优化>日志IO节省>Java业务代码优化>JVM优化>热点数据缓存>操作系统内核参数优化,之后在根据此顺序迭代进行(如果你是个吞吐量达人)。其中Mysql、Sql语句、缓存机制、操作系统内核参数还有进一步优化的余地。
(未完,待续)
标记:23页,阶段性总结之前