рджреЛ рдЧреИрд░-рдЕрд╡рд░реБрджреНрдз SQL рдбреЗрдЯрд╛рдмреЗрд╕ рдПрдХреНрд╕реЗрд╕ рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХреЗ рдЙрджрд╛рд╣рд░рдг Vert.X SQL рдФрд░ R2DBC рд╣реИрдВ ред рдЙрджрд╛рд╣рд░рдг PostgreSQL рдФрд░ рдЬрд╛рд╡рд╛ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рд╢реАрд▓ рдЖрд╡рд░рдг рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реЛрдВрдЧреЗред

рдЪрд▓реЛ рдореБрдЦреНрдп рдмрд╛рдд рд╕реЗ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ: рдЬреЗрдбрдбреАрдмреАрд╕реА рдПрдХ рдорд╣рд╛рди рдорд╛рдирдХ рд╣реИред рд╕реЗрд╡рд╛ рдХреА рдФрд░ рдИрдорд╛рдирджрд╛рд░реА рд╕реЗ рдХрд╛рдо рдХрд┐рдпрд╛ред
рд▓реЗрдХрд┐рди рдирдП рд░реБрдЭрд╛рди рдирдП рд╕рдорд╛рдзрд╛рди рдХреЗ рд▓рд┐рдП рдкреВрдЫ рд░рд╣реЗ рд╣реИрдВред рдФрд░ рдпрд╣ рдХрднреА-рдХрднреА рдЬреЗрдбреАрдмреАрд╕реА рдХреЛ рдЖрдЧреЗ рдмрдврд╝рд╛рдиреЗ рдХреЗ рд▓рд┐рдП рднреА рд╕рдордЭ рдореЗрдВ рдЖрддрд╛ рд╣реИред
рдореЗрд░рд╛ рд╕реБрдЭрд╛рд╡ рд╣реИ рдХрд┐ JVM рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдФрд░ рдЙрдирдХреЗ (рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди) рдкреНрд░рдпреЛрдЬреНрдпрддрд╛ рд╕реЗ SQL рдбреЗрдЯрд╛рдмреЗрд╕ рддрдХ рдкрд╣реБрдБрдЪ рдХреЗ рджреЛ рд╡реИрдХрд▓реНрдкрд┐рдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВред
, . тАФ , .
. , (PostgreSQL 12) . , , , , , .
, JDK тАФ OpenJDK 11, , Oracle JDK 8, .
pg_sleep(seconds) PostgreSQL.
тАФ (netty). patch-, . , netty , , HTTP-.
, . . , JDBC, . , , JDBC. тАФ , ThreadLocal , .
, , GitHub.
, . , -, , "" . , - - , "" .
. .
тАФ , . , . , .
, "", тАФ . - . , , . , тАФ . , тАФ ? , API . API JDBC, , тАФ JPA, JOOQ, Hibernate ORM .
- , - " " тАФ , . , . тАФ ( ). , , . " " , тАФ , , "" .
1: , - ( тАФ , ) . , ( ) , (, ).
2: , . , . - , , REST , . "" , .
, "" "", - . "" - . тАФ . -, "" , . , тАФ -, .
, тАФ . , , , тАФ " 2". , .
. .
: , / ( Spring Data R2DBC ). JDBC тАФ JPA, JOOQ, Hibernate ORM, . , , , . , тАФ , .
: . :
Vert.X SQL (PG) Client.
Eclipse, GitHub Postgres.
:
- PostgreSQL, MySQL, MSSQL, DB2
- callback, RxJava2. Kotlin, .
- vert.x , Quarkus.
- .
тЖТ
H2, . , , , .
, (rxjava2) :
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-pg-client</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-rx-java2</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version>4.1.15.Final</version>
<classifier>linux-x86_64</classifier>
</dependency>
API:
import io.vertx.pgclient.PgConnectOptions;
import io.vertx.reactivex.pgclient.PgPool;
import io.vertx.reactivex.sqlclient.Pool;
import io.vertx.reactivex.sqlclient.Tuple;
import io.vertx.sqlclient.PoolOptions;
. , prepared statement тАФ .
PgConnectOptions connectOptions = new PgConnectOptions()
.setPort(5432)
.setHost(host)
.setDatabase("postgres")
.setCachePreparedStatements(true)
.setPreparedStatementCacheMaxSize(1000)
.setSsl(false)
.setUser(user)
.setPassword(password);
pool (, 50).
PoolOptions poolOptions = new PoolOptions()
.setMaxSize(pool);
Pool client = PgPool.pool(connectOptions, poolOptions);
. :
- ,
- , Tuple
- RowSet, Iterable
- .
- , "" .
.
$1 $2 $3 . , rx тАФ RxJava2, vert.x. callback hell. RxJava2 , :
Flowable<String> titles = client.rxGetConnection()
.flatMapPublisher(connection ->
connection.rxPrepare("SELECT title FROM nicer_but_slower_film_list WHERE FID = $1")
.flatMap(statement -> statement.query().rxExecute(Tuple.of(Math.abs(random.nextInt(990)))))
.flattenAsFlowable(Functions.identity())
.map(row -> row.getString("title"))
.doOnError(Throwable::printStackTrace)
.subscribeOn(Schedulers.computation())
.doFinally(connection::close));
client.close();
R2DBC
тЖТ
:
- тАФ MariaDB, MySQL, PostgreSQL, MSSQL, H2
- Spring Data R2DBC, Spring Boot
- Reactive Streams, Project Reactor
:
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<version>0.8.2.RELEASE</version>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-pool</artifactId>
<version>0.8.2.RELEASE</version>
</dependency>
Project Reactor.
:
import io.r2dbc.pool.ConnectionPool;
import io.r2dbc.pool.ConnectionPoolConfiguration;
import io.r2dbc.spi.ConnectionFactories;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryOptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
. Prepared Statement , .
ConnectionFactory connectionFactory = ConnectionFactories.get(ConnectionFactoryOptions.builder()
.option(DRIVER, "postgresql")
.option(HOST, host)
.option(PORT, 5432)
.option(USER, user)
.option(PASSWORD, password)
.option(DATABASE, "postgres")
.option(SSL, false)
.build());
ConnectionPoolConfiguration configuration = ConnectionPoolConfiguration.builder(connectionFactory)
.maxIdleTime(Duration.ofMillis(1000))
.maxSize(poolSize)
.build();
ConnectionPool pool = new ConnectionPool(configuration);
. :
- ,
- Result
- mapper- .
- , "" .
тАФ close, . $1 $2 $3 . Project Reactor :
Flux<String> titles = Flux.usingWhen(pool.create(), connection ->
Flux.from(
connection.createStatement("SELECT title FROM nicer_but_slower_film_list WHERE FID = $1")
.bind("$1", Math.abs(random.nextInt(990)))
.execute()
).flatMap(result -> result.map(RdbcTest::getTitle))
, Connection::close);
private static String getTitle(Row row, RowMetadata meta) {
return row.get("title", String.class);
}
.
pool.close();
, "" , - - . R2DBC, VertX JDBC. Executions (, 50 000) , concurrency (, 1000) . backpressure.
, . . , (, ) .
VertX Flowable.range(1, executions)
.doOnNext(i -> { if (i % chunk == 0) LOGGER.info("Process {}", i);})
.flatMap(i -> client.preparedQuery(
"SELECT title FROM nicer_but_slower_film_list WHERE FID = $1")
.rxExecute(Tuple.of(Math.abs(random.nextInt(990))))
.doOnError(Throwable::printStackTrace)
.flattenAsFlowable(Functions.identity())
.map(row -> row.getString("title").length())
.doOnError(Throwable::printStackTrace)
.subscribeOn(Schedulers.computation()),
false, concurrency)
.doOnComplete(() -> LOGGER.info("Done with VertX"))
.blockingSubscribe(unused -> { }, Throwable::printStackTrace);
R2DBC Flux.range(1, executions)
.doOnNext(i -> { if (i % chunk == 0) LOGGER.info("Processing {}", i);})
.flatMap(i -> Flux.usingWhen(pool.create(),
connection -> Flux.from(
connection.createStatement("SELECT title FROM nicer_but_slower_film_list WHERE FID = $1")
.bind("$1", Math.abs(random.nextInt(990)))
.execute()
)
.flatMap(result -> Flux.from(result.map(RdbcTest::getTitle))),
Connection::close)
.subscribeOn(Schedulers.parallel())
.doOnError(Throwable::printStackTrace)
, concurrency)
.doOnError(Throwable::printStackTrace)
.doOnComplete(() -> LOGGER.info("Done with R2DBC"))
.blockLast();
JDBC + Hikari Pool + Reactor HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://" + host + "/postgres");
config.setMaximumPoolSize(poolSize);
config.setIsolateInternalQueries(false);
HikariDataSource ds = new HikariDataSource(config);
Flux.range(1, executions)
.flatMap(Mono::just)
.flatMap(index -> Mono.fromCallable(ds::getConnection)
.doOnNext(i -> { if (index % chunk == 0) LOGGER.info("Process {}", index);})
.map(this::request).subscribeOn(Schedulers.elastic())
, concurrency
)
.subscribeOn(Schedulers.elastic())
.doOnError(Throwable::printStackTrace)
.doOnComplete( ()->LOGGER.info("Done with JDBC"))
.blockLast();
private Integer request(Connection connection) {
try {
var s = connection.prepareStatement(
"SELECT title FROM nicer_but_slower_film_list WHERE FID = ?"
);
s.setInt(1, Math.abs(random.nextInt(990)));
var results = s.executeQuery();
int counter = 0;
while (results.next()) {
counter += results.getString("title").length();
}
results.close();
s.close();
connection.close();
return counter;
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new IllegalStateException(e);
}
}
c time:
/usr/bin/time --verbose java ...
Java Mission Control тАФ Java Flight Recorder.
java -XX:StartFlightRecording=disk=true,dumponexit=true,filename=/tmp/r2dbc.jfr,settings=profile,path-to-gc-roots=false,delay=1s,name=R2DBC ...
time userspace system, . VertX , R2DBC JDBC , . "" ( ). , R2DBC system , userspace. , vertx .
time R2DBC Command being timed: "java -jar r2dbc-1.0-SNAPSHOT-jar-with-dependencies.jar -iterations 50000 -concurrent 1000 -pool 50 -user anonymous -password 12345678 -host pg12.local"
User time (seconds): 34.28
System time (seconds): 5.55
Percent of CPU this job got: 10%
Maximum resident set size (kbytes): 307004
Minor (reclaiming a frame) page faults: 76835
Voluntary context switches: 121789
Involuntary context switches: 9670
time JDBC Command being timed: "java -jar jdbc-1.0-SNAPSHOT-jar-with-dependencies.jar -iterations 50000 -concurrent 1000 -pool 50 -user anonymous -password 12345678 -host pg12.local"
User time (seconds): 38.72
System time (seconds): 5.80
Percent of CPU this job got: 76%
Maximum resident set size (kbytes): 459688
Minor (reclaiming a frame) page faults: 125453
Voluntary context switches: 187752
Involuntary context switches: 14221
time VertX Command being timed: "java -jar vertx-1.0-SNAPSHOT-jar-with-dependencies.jar -iterations 50000 -concurrent 1000 -pool 50-user anonymous -password 12345678 -host pg12.local"
User time (seconds): 19.06
System time (seconds): 2.02
Percent of CPU this job got: 20%
Maximum resident set size (kbytes): 178516
Minor (reclaiming a frame) page faults: 43054
Voluntary context switches: 109914
Involuntary context switches: 4691
рдЬрд╛рд╡рд╛ рдорд┐рд╢рди рдХрдВрдЯреНрд░реЛрд▓ рдХреНрдпрд╛ рджрд┐рдЦрд╛рддрд╛ рд╣реИ? JDBC рдХреЛ рдЕрдзрд┐рдХ рдореЗрдореЛрд░реА рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ рдФрд░ рдЕрдзрд┐рдХ рдмрд╛рд░ GC рдХрд░рддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдереНрд░реЗрдбреНрд╕ рдЕрдиреБрднрд╛рдЧ рдореЗрдВ рдкреНрд░рд╕реНрддреБрддрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдкреНрд░рднрд╛рд╡рд╢рд╛рд▓реА рдЪрд┐рддреНрд░ рд╣реИрдВред
рдареАрдХ рдЧреИрд░ рдЕрд╡рд░реБрджреНрдз
R2DBC:

VertX:

рдФрд░ рдЦреМрдлрдирд╛рдХ JDBC:

рд╕рдм рдХреБрдЫ рд▓рд╛рд▓ рд╣реИ, рд╕рдм рдХреБрдЫ рдЕрд╡рд░реБрджреНрдз рд╣реИред рдбрд░ рд▓рдЧрддрд╛ рд╣реИред рдареАрдХ рд╣реИ, рдЖрдк рдкреВрдЫ рд╕рдХрддреЗ рд╣реИрдВ "рдХреНрдпрд╛?"ред рдЕрдкрдиреЗ рдЖрдк рд╕реЗ, рд▓рд╛рд▓ рд░реЗрдЦрд╛рдУрдВ рдХрд╛ рдХреЛрдИ рдорддрд▓рдм рдирд╣реАрдВ рд╣реИ рдФрд░ рдЕрдкрдиреЗ рдЖрдк рд╕реЗ рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ рд╣реИред рд╕рдорд╕реНрдпрд╛ рддрдм рд╣реИ рдЬрдм рд╡реЗ рдЕрдиреНрдп рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдЧрддрд┐рд╡рд┐рдзрд┐рдпреЛрдВ рдореЗрдВ рд╣рд╕реНрддрдХреНрд╖реЗрдк рдХрд░рддреЗ рд╣реИрдВред рдпрд╣ рдмрд╣реБрдд рд╕рдВрднрд╛рд╡рдирд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рдорд╛рдорд▓реЗ рдореЗрдВ рдЕрд▓рдЧ рд╕реЗ рд╕рд╛рдмрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред
рдЖрд╢рд╛ рд╣реИ рдХрд┐ рд▓реЗрдЦ рдЙрдкрдпреЛрдЧреА рдерд╛!