From 247e81bc61cb660043731075984bc019720e06cb Mon Sep 17 00:00:00 2001 From: stormboomer Date: Mon, 28 Aug 2023 08:03:43 +0200 Subject: [PATCH] Drasticly improve zoom tile calculation for larger maps when using MySQL storage engine. For Larger Tables doing Limit and Offset can have a big Impact on Statement execution because it is an IO heavy task. This fixes the issue by not doing any limit / offset on the SQL statement but instead query for all the data at once, and let the JDBC handler do the resultset handling. This can probably be adapted for MSSQL, PostgreSQL and SQLLite. --- .../dynmap/storage/mysql/MySQLMapStorage.java | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/DynmapCore/src/main/java/org/dynmap/storage/mysql/MySQLMapStorage.java b/DynmapCore/src/main/java/org/dynmap/storage/mysql/MySQLMapStorage.java index ec9c701b..c8c017b8 100644 --- a/DynmapCore/src/main/java/org/dynmap/storage/mysql/MySQLMapStorage.java +++ b/DynmapCore/src/main/java/org/dynmap/storage/mysql/MySQLMapStorage.java @@ -809,29 +809,21 @@ public class MySQLMapStorage extends MapStorage { } try { c = getConnection(); - boolean done = false; - int limit = 100; - int offset = 0; - while (!done) { - // Query tiles for given mapkey - Statement stmt = c.createStatement(); - ResultSet rs = stmt.executeQuery(String.format("SELECT x,y,zoom,Format FROM %s WHERE MapID=%d LIMIT %d OFFSET %d;", tableTiles, mapkey, limit, offset)); - int cnt = 0; - while (rs.next()) { - StorageTile st = new StorageTile(world, map, rs.getInt("x"), rs.getInt("y"), rs.getInt("zoom"), var); - final MapType.ImageEncoding encoding = MapType.ImageEncoding.fromOrd(rs.getInt("Format")); - if(cb != null) - cb.tileFound(st, encoding); - if(cbBase != null && st.zoom == 0) - cbBase.tileFound(st, encoding); - st.cleanup(); - cnt++; - } - rs.close(); - stmt.close(); - if (cnt < limit) done = true; - offset += cnt; + Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, //we want to stream our resultset one row at a time, we are not interessted in going back + java.sql.ResultSet.CONCUR_READ_ONLY); //since we do not handle the entire resultset in memory -> tell the statement that we are going to work read only + stmt.setFetchSize(100); //we can change the jdbc "retrieval chunk size". Basicly we limit how much rows are kept in memory. Bigger value = less network calls to DB, but more memory consumption + ResultSet rs = stmt.executeQuery(String.format("SELECT x,y,zoom,Format FROM %s WHERE MapID=%d;", tableTiles, mapkey)); //we do the query, but do not set any limit / offset. Since data is not kept in memory, just streamed from DB this should not be a problem, only the rows from setFetchSize are kept in memory. + while (rs.next()) { + StorageTile st = new StorageTile(world, map, rs.getInt("x"), rs.getInt("y"), rs.getInt("zoom"), var); + final MapType.ImageEncoding encoding = MapType.ImageEncoding.fromOrd(rs.getInt("Format")); + if(cb != null) + cb.tileFound(st, encoding); + if(cbBase != null && st.zoom == 0) + cbBase.tileFound(st, encoding); + st.cleanup(); } + rs.close(); + stmt.close(); if(cbEnd != null) cbEnd.searchEnded(); } catch (SQLException x) {