高性能数据库连接池的内幕
每个schema里面所有的连接会按照autocommit进行分组。 分为自动提交(autocommit=true) 和非自动提交(autocommit=false)。获取连接时优先获取相同autocommit的分组里的连接,如果没有可用连接则从另外一个分组中获取连接,业务操作执行完后,再归还到对应的分组里面。该种机制避免了开启事务多执行的两条事务语句。 锁性能优化连接池的通用功能: ![]() 连接池主要包含五部分:获取连接,归还连接,定时任务,维护组件及资源池 获取连接: 1:获取超时:如果超过规定时间未获取到连接,则会抛出异常 2:有效性检查:当从资源池里面获取到资源,需要检查该资源的有效性,如果失效,再次获取连接。避免执行业务的时候报错。 3:创建连接:可以同步创建,也可以异步创建。 归还连接: 1:归还连接:比如需要检查最大空闲数,确定是物理关闭还是归还到连接池 2:销毁连接: 可同步销毁也可异步销毁 定时任务: 1:空闲检查:主要是检查空闲连接,连接空闲超过一定时间,则会关闭连接。 2:最小连接数控制:一般会设置最小连接数。保证当前系统里面最小的连接数。如果不够,则会新建连接。 组件维护: 1:连接状态控制:空闲,使用,删除等状态控制 2:异常处理:对JDBC访问的异常统一处理,如果异常与连接相关,则会将该连接销毁掉。 3:缓存:避免对SQL重复解析,PrepareStatement机制下,会对SQL解析的对象进行缓存。 4:JDBC封装:对JDBC进行了实现,真正的实现是底层的driver,比如MySQL-connector-java 。 资源池: 1:资源池是存放连接的地方,也是连接池最核心的地方。 2:所有的组件基本上都与资源池进行交互,对连接资源的竞争非常激烈。该处的性能将决定了整个连接池的性能。 3:一般资源池的实现是使用JDK提供的BlockingQueue。那么是否有方案可以进行无锁的设计,来避免竞争。 资源池无锁设计![]() 获取连接大概流程: 1:从ThreadLocal里面获取连接,如果没有空闲连接,则从全局连接池(CopyOnWriteArrayList)中获取。 2:如果全局连接池中没有空闲连接,则会异步新建连接。 3:判定超时时间是否大于阈值,如果小于阈值,则进行自旋。否则进行park休眠。 4:连接建立成功后,会对park的线程进行唤醒 主要从四个方面实现了无锁的设计:ThreadLocal,CopyOnWriteArrayList,异步建立连接及自旋。 ThreadLocal1:每个线程均有一个连接队列。该队列是全局队列的引用。 2:获取连接时先从ThreadLocal里面拿连接,如果连接是空闲状态,则使用。否则移除掉,再拿下一个,直到拿不到连接为止。 3:归还连接时,只需要归还到Threadlocal的队列里面,同时设置连接为空闲状态 4:如果使用BlockQueue,获取连接时调用poll,归还连接时调用offer,存在两次锁的竞争。优化后通过CAS避免了两次锁的开销(获取连接时,使用CAS置连接为非空闲状态;归还时,使用CAS置连接为空闲状态) CopyOnWriteArrayList1:该队列使用场景是:大量读,少量写的操作,并且存储的数据比较有限。而连接池的场景非常适合采用CopyOnWriteArrayList。 2:在获取连接或者归还连接时,只会通过CAS更改连接的状态,不会对连接池进行添加或者删除的操作。 3:一般连接池连接的个数比较可控,CopyOnWriteArrayList在写操作时会对所有连接进行拷贝,对内存影响不大。 异步建立连接获取到连接后,判断一下是否有并发正在等待获取连接,如果有,则异步建立连接。避免下一个连接的等待。如果CopyOnWriteArrayList没有空闲连接,则异步建立连接。 自旋该自旋比较类似于JDK对synchronized的自旋机制。如果发现超时时间大于设定的阈值(比如10微秒),则会进行线程挂起。如果小于设定的阈值,则重新获取连接,进行自选,避免线程的上下文切换带来的性能开销。。 优化小技巧方法内联优化 1:每调用一次方法,线程便会新建一个栈帧,新建栈帧开销相对比较大 2:JIT在运行时会进行内联优化,多个方法使用一个栈帧,避免栈帧新建过多 3:JIT方法内联优化默认的字节码个数阈值是35个字节,低于35个字节,才会进行优化。(可通过-XX:MaxInlineSize=35进行设置) ![]() 通过修改上述代码,编译后字节码修改到34个字节,则可以满足内联的条件。 心跳语句选择![]() PrepareStatement模式选择![]() MySQL driver默认是client模式,如果需要开启server模式,需要设置 useServerPrepStmts=true 。PrepareStatement默认的client模式和Statement对于DB端没有区别。大家普遍理解PrepareStatement和Statement的区别是PrepareStatement可以避免SQL注入。但是避免SQL注入是如何做到的? 使用PrepareStatement设置参数的时候,比如调用setString(int parameterIndex, String x),本地会对设置的参数进行转义来避免SQL注入。 (编辑:威海站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |