Apakah kolam hikari?
Soalan ringkas ini dalam siaran di BlueSky membawa saya kepada penjelasan yang saya fikir sangat hebat. Saya datang ke sini untuk menyelesaikannya.
Dalam konteks khusus, Hikari Connection Pool sedang diperkatakan. Tetapi, jika Hikari ialah Kolam Sambungan, apakah "Kolam" itu?
Sebelum menerangkan apa itu HikariCP, kami perlu menerangkan apa itu kolam sambungan. Dan untuk menerangkan kolam sambungan, kami perlu menerangkan kolam.
Adakah kita akan menggunakan analogi ekonomi untuk ini? Analogi ekonomi sejarah yang penuh dengan kelemahan dan ketidaktepatan dengan dunia sebenar, tetapi ayuh, tangguhkan ketidakpercayaan anda dengan cepat hanya untuk penjelasan! Ia serba lengkap.
Bayangkan anda adalah tuan/puan pada zaman pertengahan. Anda memegang alat untuk menjalankan kerja petani. Dan anda mahu mereka bekerja. Jadi bagaimana anda menjamin ini? Jika alat itu milik anda? Anda perlu menghantar alatan kepada petani, mudah.
Jadi bayangkan keadaannya: petani anda memerlukan cangkul untuk merumput tanah, jadi dia pergi ke sana dan meminta cangkul kepada anda. Anda akan memberinya cangkul dan terus hidup. Tetapi bagaimana jika dia tidak mengembalikannya, bagaimana dengan stok cangkulnya? Suatu hari nanti ia akan berakhir...
Alternatif untuk menyerahkan cangkul adalah dengan membuat cangkul. Anda adalah tuan/puan tanah itu, jadi anda mempunyai akses kepada tukang besi untuk meleburkan logam ke dalam bentuk cangkul dan memasukkannya ke dalam pemegang. Tetapi ini bukan sesuatu yang anda boleh hasilkan dengan segera tanpa petani duduk di ruang menunggu. Untuk membuat ciri baharu ini, anda memerlukan sejumlah besar masa dan tenaga.
Sekarang, jika petani memulangkan cangkul pada penghujung hari, ia boleh digunakan oleh petani lain pada hari berikutnya.
Di sini anda mengawal kolam cangkul. kolam ialah corak reka bentuk yang menunjukkan sesuatu yang anda boleh lakukan tindakan berikut:
Perkara biasa lain yang perlu ada dalam kolam objek:
Baiklah, mari kita lebih dekat dengan HikariCP. Mari kita bincangkan di sini tentang sambungan pangkalan data dalam Java.
Di java, kami meminta untuk mewujudkan sambungan ke pangkalan data. Terdapat pilihan sambungan langsung, yang anda perlu fahami secara langsung tentang kelas mana yang hendak dihubungi dan beberapa butiran, atau hanya nikmati pilihan penemuan perkhidmatan.
A priori, untuk menggunakan penemuan perkhidmatan, penyedia perkhidmatan membuat cara untuk mendaftarkan apa yang dia sediakan dan kemudian "penemuan perkhidmatan" mengejarnya untuk melihat siapa yang boleh menyampaikan permintaan itu.
Saya mempunyai kes di mana saya perlu membuat sambungan JDBC untuk bercakap dengan pangkalan data. Walau bagaimanapun, pemandu JDBC saya tidak menerima penggunaan nulls sebagai nilai, hanya nulls secara langsung dalam pertanyaan. Jadi apa yang saya buat? Seorang pemandu di atas seorang pemandu!
Idea umum adalah seperti berikut. Bayangkan saya mempunyai pertanyaan ini yang saya ingin masukkan nilai ke dalam:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
Sekarang bayangkan bahawa saya sedang berurusan dengan sisipan pertama nilai ini ke dalam bank. Untuk melakukan ini, saya perlu meninggalkannya dengan ID=1, CONTENT=first dan PARENT=null kerana, lagipun, tiada rekod induk seperti itu (ia adalah yang pertama, selepas semua).
Apa yang akan dilakukan secara semula jadi:
try (final var pstmt = conn.prepareStatement( """ INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) """)) { pstmt.setInt(1, 1); pstmt.setString(2, "first"); pstmt.setNull(3, Types.INTGEGER); // java.sql.Types pstmt.executeUpdate(); // de fato altere o valor }
Saya mahu terus menggunakannya dengan cara ini, lagipun ia adalah cara idiomatik untuk menggunakannya. Dan menurut CUPID, saya berasal dari "idiomatik." Idea mempunyai kod idiomatik adalah tepat untuk "mengurangkan beban mental yang tidak perlu".
Untuk menyelesaikan masalah ini, pilihan saya ialah: biarkan prepareStatement sehingga saat terakhir sebelum executeUpdate. Jadi saya menyimpan semua null untuk digunakan dan, apabila saya menyedari bahawa saya sebenarnya memerlukan null, saya menjalankan penggantian rentetan dan menjana pertanyaan baharu, dan pertanyaan baharu ini sebenarnya akan dilaksanakan.
Dalam kes ini, saya mulakan dengan:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
Jadi, saya perlu memasukkan nilai ini:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) -- 1, 'first', NULL
Tetapi saya sebenarnya tidak boleh menggunakan null, jadi saya mencipta kunci untuk mengenal pasti bahawa tempat ketiga adalah null:
-- (value, value, NULL) INSERT INTO some_table (id, content, parent) VALUES (?, ?, NULL) -- 1, 'first'
Dan dalam kes ini saya menyediakan rentetan baharu ini dan meletakkan hujah mengikut apa yang diminta.
Baiklah, itu berkata, bagaimana saya boleh menunjukkan kepada aplikasi saya bahawa saya perlu menggunakan pemandu JDBC saya? Bagaimana saya mendaftar?
Projek yang dimaksudkan ialah Pstmt Null Safe. Pada asasnya, terdapat keajaiban dalam pemuat kelas Java yang, apabila memuatkan balang, ia mencari folder metadata yang dipanggil META-INF. Dan dalam kes pemandu JDBC, META-INF/services/java.sql.Driver, dan saya mencatatkannya dengan kelas yang melaksanakan java.sql.Driver: br.com.softsite.pstmtnullsafe.jdbc.PstmtNullSafeDriver.
Menurut dokumentasi java.sql.Driver, setiap pemandu harus mencipta contoh sendiri dan mendaftar dengan DriverManager. Saya melaksanakannya seperti ini:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
Blok statik dimuatkan dengan sendirinya. Dan bagaimana kita tahu sambungan mana yang harus diuruskan oleh pemandu saya? Panggilan dibuat melalui DriverManager#getConnection(String url). Kami mempunyai URL untuk bertanya kepada pemandu jika ia menerima sambungan. Konvensyen (di sini sekali lagi, cara idiomatik untuk menggunakannya) ialah dengan awalan pada skema URL. Memandangkan saya mahu pemandu saya menyambung di atas pemandu lain, saya melakukannya menggunakan skema ini:
try (final var pstmt = conn.prepareStatement( """ INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) """)) { pstmt.setInt(1, 1); pstmt.setString(2, "first"); pstmt.setNull(3, Types.INTGEGER); // java.sql.Types pstmt.executeUpdate(); // de fato altere o valor }
Jadi, untuk melaksanakan ujian, saya menyambung dengan SQLite, dan menggunakan penunjuk Xerial untuk meminta sambungan dalam memori melalui URI sambungan:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
Untuk "menutup" sambungan, konvensyen saya menunjukkan bahawa saya tidak mengulang jdbc:, jadi:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) -- 1, 'first', NULL
Membedah URI di atas:
-- (value, value, NULL) INSERT INTO some_table (id, content, parent) VALUES (?, ?, NULL) -- 1, 'first'
Baiklah, dan bagaimana anda menyatakan ini? Pemacu#acceptsURL mesti kembali benar jika saya boleh membuka sambungan. Saya hanya boleh melakukan ini:
public static final PstmtNullSafeDriver instance; static { instance = new PstmtNullSafeDriver(); try { DriverManager.registerDriver(instance); } catch (SQLException e) { e.printStackTrace(); } }
Tetapi apakah ini menunjukkan jika saya cuba memuatkan pemandu yang tidak wujud? Tiada apa-apa, ia akan menyebabkan masalah pada masa lain. Dan itu tidak baik, yang ideal adalah untuk ranap dari awal. Jadi untuk ini, saya akan cuba memuatkan pemandu dari bawah, dan jika saya tidak boleh, saya akan mengembalikan palsu:
jdbc:pstmt-nullsafe:<url de conexão sem jdbc:> \__/ \____________/ | | | Nome do meu driver Padrão para indicar JDBC
Kod pemacu sebenar mempunyai beberapa lagi perkara yang tidak berkaitan dengan perbincangan di sini tentang HikariCP, mahupun mengenai Sumber Data, JDBC atau topik yang diliputi dalam siaran ini.
Jadi, apabila meminta sambungan "null safe" kepada DriverManager, mula-mula ia menemui pemandu saya dan pemandu saya secara rekursif cuba menyemak sama ada terdapat kemungkinan sambungan di bawah hud. Disahkan bahawa ada pemandu yang mampu menangani perkara ini, saya katakan ya, ia boleh.
Antara muka Sambungan melaksanakan antara muka AutoCloseable. Ini bermakna anda mengambil sambungan, menggunakan sambungan seperti yang anda mahu, dan kemudian anda menutup sambungan. Adalah agak standard untuk anda menggunakan beberapa arahan dengan ini atau, jika anda menggunakan sambungan secara langsung, gunakannya di dalam blok cuba-dengan-sumber:
jdbc:sqlite::memory:
Kini, proses mencipta sambungan adalah proses yang mahal. Dan juga proses penemuan perkhidmatan tidak betul-betul percuma. Jadi yang ideal ialah menyimpan pemandu untuk kemudian menjana sambungan. Mari kembangkan ini sedikit demi sedikit.
Pertama, kita perlu mempunyai objek yang boleh kita lancarkan bersama pemandu. Ia boleh menjadi objek global dengan mudah, komponen Spring yang disuntik, atau apa-apa seperti itu. Mari kita panggil ia JdbcConnector:
jdbc:pstmt-nullsafe:sqlite::memory:
Satu pelaksanaan yang mungkin untuk getJdbcConnection() adalah bergantung pada keadaan yang diliputi oleh fungsi ini:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
Semuanya berjalan lancar setakat ini. Tetapi... ingatkah contoh awal di mana petani meminta cangkul dalam kolam alatan? Jadi... Bolehkah kita mengambil kira perkara ini? Daripada benar-benar menutup sambungan, kami boleh mengembalikan sambungan ke kolam. Demi ketepatan, saya akan melindungi daripada berbilang akses serentak, tetapi saya tidak akan bimbang tentang kecekapan di sini.
Mari kita anggap di sini bahawa saya mempunyai kelas yang dipanggil ConnectionDelegator. Ia melaksanakan semua kaedah Sambungan, tetapi ia tidak melakukan apa-apa dengan sendirinya, ia hanya mewakilkan kepada sambungan yang dihantar kepadanya sebagai pembina. Contohnya, untuk kaedah isClosed():
try (final var pstmt = conn.prepareStatement( """ INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) """)) { pstmt.setInt(1, 1); pstmt.setString(2, "first"); pstmt.setNull(3, Types.INTGEGER); // java.sql.Types pstmt.executeUpdate(); // de fato altere o valor }
Dan seterusnya untuk kaedah lain. Ia adalah abstrak untuk fakta mudah bahawa saya ingin memaksa diri saya melakukan sesuatu selain daripada perwakilan mudah apabila saya menggunakannya.
Baiklah, mari kita pergi. Ideanya ialah sambungan akan diminta, yang mungkin wujud atau tidak. Jika wujud, saya membungkusnya dalam kelas baharu ini supaya saya boleh mengembalikannya ke pool apabila saya menutup sambungan. Hmmm, jadi saya akan lakukan sesuatu dalam kaedah close()... Okay, mari kita bungkus dahulu. Mari kita biarkan getConnection() sebagai disegerakkan untuk mengelakkan masalah konkurensi:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
Ok, jika saya mempunyai elemen dalam kolam sambungan, saya menggunakannya sehingga ia kosong. Tetapi ia tidak pernah diisi! Jadi adakah kita akan menyelesaikan masalah ini? Apabila ia ditutup, kita boleh mengembalikannya ke kolam!
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) -- 1, 'first', NULL
Ok, sekarang apabila anda selesai menggunakan sambungan, ia dihantar semula ke
kolam. Ini tidak memenuhi dokumentasi untuk kaedah Connection#close() kerana dalam dokumentasi ia menyebut bahawa ia mengeluarkan semua sumber JDBC yang berkaitan dengan sambungan ini. Ini bermakna saya perlu menyimpan rekod semua Penyata, ResultSets, PreparedStatements, dsb. Kita boleh mengendalikannya dengan mencipta kaedah yang dilindungi pada ConnectionDelegator yang dipanggil closeAllInnerResources(). Dan panggilnya dekat():
-- (value, value, NULL) INSERT INTO some_table (id, content, parent) VALUES (?, ?, NULL) -- 1, 'first'
Dan dengan itu kami mempunyai sesuatu yang mengembalikan sambungan kepada saya atas permintaan dan yang mempunyai keupayaan untuk membentuk kumpulan sumber.
Adakah anda tahu nama apa yang diberikan Java kepada objek yang menyediakan sambungan? Sumber Data. Dan adakah anda tahu apa lagi yang Java katakan tentang DataSources? Bahawa terdapat beberapa jenis, secara konseptual. Dan daripada jenis ini, 2 yang paling relevan ialah:
Dan di sini kita melalui proses sentiasa mencipta sambungan (jenis asas) serta berkembang menjadi Sumber Data
berkumpulan.
HikariCP ialah Sumber Data. Khususnya, dikumpul Sumber Data. Tetapi dia mempunyai satu ciri: dia adalah yang terpantas daripada semua. Untuk menjamin kelajuan ini, dalam kumpulan sambungannya untuk digunakan semasa kitaran hayat aplikasi, HikariCP membuat rahsia: ia sudah mencipta semua sambungan yang tersedia. Oleh itu, apabila getConnection tiba, HikariCP hanya perlu menyemak kolam sambungan.
Jika anda ingin mendalami subjek ini, anda boleh menyemak artikel ini di Baeldung mengenai subjek tersebut, dan juga menyemak repositori di github.
Atas ialah kandungan terperinci Apakah kolam hikari?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!