数据库性能最佳实践
JDBC
性能对于数据库应用程序框架的影响
JDBC驱动
是影响数据库应用程序的最重要的因素,替代方案是因为具有更好的性能
- 工作在哪里执行
- 瘦驱动
应用程序的内存占用更小,依赖数据库服务器完成更多的处理工作 - 胖驱动
将工作从数据库服务器转移至应用程序,客户端需要处理更多的任务
驱动工作环境反应了驱动的性能
JDBC链接池
服务器维护JDBC的连接池,池化对象占用的内存和池化将触发额外GC数量之间取得平衡。
实际链接的对象可能不是很大,但是语句缓存(基于每个链接而存在)可能很大。
如果数据库是瓶颈,向忙碌的系统施加负载只会降低性能
改善方法:
用连接池的限制发送最小规模的数据库的工作量。
应用程序线程可能需要等待一个空闲链接,但如果数据库没有压到,系统总吞吐量会最大化。
预处理语句和语句池
PreparedStatement预处理调用允许数据库重用正在执行SQL信息
为后续数据库执行语句节省了工作
预处理语句还具有安全性和编程的优势,特别是调用指定参数方面
PrepareStatement预编译重用:
第一次执行SQL语句,将SQL发送给数据库服务器进行解析,编译和优化,然后将结果缓存起来便于后面执行。
- 建立语句池
预处理语句池的运转以每个链接为基础。
如果程序中的一个线程从池中取出一个链接并在该链接上使用预处理语句,那么这个语句关联只对该链接有效。
每个链接实例都会关联自己的语句池,最后语句池中会包含所有的语句池。
当第一个链接没有用到特定预处理语句时,第一个请求会慢一点。
连接池关联的语句池会占用堆内存,大量的堆空间,看看是否对GC时间产生负面影响。 - 管理语句池
设置语句池是否开启和是否处理。
事务处理
应用程序对正确性要求最终决定了事务的处理方式。
数据库事务有两方面的性能损失:
- 事务的创建和提交需要时间——不确定的时间
- 数据库处理事务的过程中会获得一组特定数据的锁,如果两个事务竞争一个数据锁,应用程序的可扩展将收到影响。
如何对事务编程和如何在事务中持有数据库锁的问题思考
-
JDBC事务控制
事务的开始和结束都基于Connection对象的使用方式
用法:链接一个自动提交模式(setAutoCommit)不需要自动提交,如果自动提交会有性能影响,如果关闭自动提交,链接一个对象上的第一次调用会默认创建一个隐式的事务,这个事务持续到显示执行提交或者回滚方法。使用该链接的下一个数据库调用会开启一个新的事务。
基于事务提交成本高的原因平衡一下事务执行的工作和事务锁的设计之间的平衡。
批处理对于数据库调用和事务的平衡。 -
事务隔离和锁
数据库的可扩展性——因为数据在事务中被锁定,锁可以保证数据的完整性-让事务处理隔离态。
1.TRANSACTION_SERIALIZABLE 可串行化
在事务执行期间,事务访问的所有数据都被锁定,序列化事务每次查询是看到的数据都是一样。防止脏读,幻读,不可重复读2.TRANSACTION_REPEATABLE_READ 幻读
事务运行期间,被访问数据都锁定,其他事务可在任何时候写入数据到表,
导致幻读的原因是执行查询语句之后另一个事务写入数据后再次查询发现数据变了。- TRANSACTION_READ_COMMITTED 不可重复读
只锁定事务进行期间被写入的数据,当前事务作用域,导致不可重复读原因是事务运行期间某一个时刻读取数据和另一个时间不同,因为当前事务提交了数据。
允许多个事务读取提交的数据,如果有修改的事务执行则必须等待该事务的提交。
4.TRANSACTION_READ_UNCOMMITTED 脏读
无锁,一个事务可以读取在另一事务中未提交的数据,可造成脏读是因为其他事务可能并没有实际发生写操作而导致获取错误的数据。 - TRANSACTION_READ_COMMITTED 不可重复读
结果集处理
setFetchSize方法设置驱动一次加载数据行数——平衡JDBC内部缓冲区和应用程序的使用