Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities.
preface
- The current SpringBoot default database connection pool is Hikari, as opposed to Druid connection pool:
- Hikari is characterized by speed, and it uses many optimization mechanisms and operations internally, mainly for higher performance.
- Druid connection pooling is not fast, it is monitoring and analyzing data and SQL.
- Each has its own characteristics, and neither is best. Choose the appropriate connection pool based on your project and requirements.
FastList
- Inside the Hikari connection pool, the ArrayList collection is abandoned for better performance, and instead a custom FastList collection is created
- Internally, it is similar to the ArrayList implementation, with different tuning optimizations for the GET and remove methods
- The circled section above shows FastList’s optimization of the method for retrieving elements, removing the validation of subscripts.
- The location indicated in the figure above is an optimization of FastList’s method of deleting elements. ArrayList scans elements from the beginning for deletion, while FastList scans elements from the end for deletion
- In Hikari it is common to remove (close) the last element put in, so FastList avoids some invalid lookups.
ConcurrentBag
- The Hikari connection pool internally defines the FastList collection as well as the ConcurrentBag concurrent access collection
- ConcurrentBag can be accessed concurrently by multiple threads, but if you look at its internal source code, you will find that there is no resource locking operation
- Concurrency is supported by three attributes: threadList, sharedList, and handoffQueue
- ThreadList: Holds local link resources for the current thread
- SharedList: Saves all linked resources
- HandoffQueue: blocking one – in, one – out queue
/** * The method will borrow a BagEntry from the bag, blocking for the * specified timeout if none are available. * * @param timeout how long to wait before giving up, in units of unit * @param timeUnit a <code>TimeUnit</code> determining how to interpret the timeout parameter * @return a borrowed instance from the bag or null if a timeout occurs * @throws InterruptedException if interrupted while waiting */ public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException { // Try the thread-local list first final List<Object> list = threadList.get(); for (int i = list.size() - 1; i >= 0; i--) { final Object entry = list.remove(i); @SuppressWarnings("unchecked") final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry; if (bagEntry ! = null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { return bagEntry; } } // Otherwise, scan the shared list ... then poll the handoff queue final int waiting = waiters.incrementAndGet(); try { for (T bagEntry : sharedList) { if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { // If we may have stolen another waiter's connection, request another bag add. if (waiting > 1) { listener.addBagItem(waiting - 1); } return bagEntry; } } listener.addBagItem(waiting); timeout = timeUnit.toNanos(timeout); do { final long start = currentTime(); final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS); if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { return bagEntry; } timeout -= elapsedNanos(start); } while (timeout > 10_000); return null; } finally { waiters.decrementAndGet(); }}Copy the code
- Borrow () method to obtain link resources execution process:
- The link resource saved by the current thread is retrieved from a threadList object. If it succeeds, the state is set to STATE_IN_USE and the link resource is returned.
- If threadList fails to obtain the link resource, it obtains the free link resource from the sharedList collection. If it succeeds, set the state to STATE_IN_USE and return the link resource.
- If sharedList fails to obtain link resources, handoffQueue will wait for idle connection resources to enter.
Note: The linked resource returned by sharedList still exists in the sharedList collection. Other threads can access the linked resource, but its state is STATE_IN_USE and cannot be used. SharedList is a collection of CopyOnWriteArrayList objects that are compatible with the current Hikari connection pool scenario where there are more reads than writes (the number of linked resources is fixed and resources are reused).Copy the code
/** * This method will return a borrowed object to the bag. Objects * that are borrowed from the bag but never "requited" will result * in a memory leak. * * @param bagEntry the value to return to the bag * @throws NullPointerException if value is null * @throws IllegalStateException if the bagEntry was not borrowed from the bag */ public void requite(final T bagEntry) { bagEntry.setState(STATE_NOT_IN_USE); for (int i = 0; waiters.get() > 0; i++) { if (bagEntry.getState() ! = STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) { return; } else if ((i & 0xff) == 0xff) { parkNanos(MICROSECONDS.toNanos(10)); } else { yield(); } } final List<Object> threadLocalList = threadList.get(); threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry); }Copy the code
- Requite () puts back linked resource method execution flow:
- First set the state of the current linked resource object to STATE_NOT_IN_USE,
- Then it judges whether the current link resource has been used by other threads. If the current link resource is not put into the queue to wake up the blocked thread, the sleep will be 10 nanoseconds after the cycle of 256 times.
- Finally, put the link resource object into the local thread resource.
The last
- Hikari connection pooling uses ConcurrentBag collection concurrency lock-free mechanism and FastList collection to improve performance
- Of course, the speed in Hikari is not only because of these two sets, but also many other optimization points to the extreme
- Reference: This wave of performance optimization, too fracking!
- Learn with an open mind and make progress together