MariaDB spatial indexes let you query geographic data incredibly fast, but they work by turning your latitude/longitude points into a grid of integers, which feels like a weird, indirect way to do it at first.

Let’s say you have a table of businesses, and you want to find all businesses within 5 miles of a specific point. Without a spatial index, you’d be doing a full table scan, calculating the distance for every single row. With a spatial index, MariaDB can drastically narrow down the possibilities.

Here’s a quick setup to see it in action.

CREATE TABLE `locations` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `name` VARCHAR(255),
  `point` POINT NOT NULL,
  SPATIAL INDEX(`point`)
) ENGINE=InnoDB;

INSERT INTO `locations` (`name`, `point`) VALUES
('Central Park', ST_GeomFromText('POINT( -73.9683 40.7851)')),
('Times Square', ST_GeomFromText('POINT( -73.9857 40.7580)')),
('Empire State Building', ST_GeomFromText('POINT( -73.9857 40.7484)')),
('Statue of Liberty', ST_GeomFromText('POINT( -74.0445 40.6892)'));

Now, let’s find places near a point. We’ll use ST_DWithin which is the magic function for this. Imagine our target point is near the Empire State Building.

SET @target_point = ST_GeomFromText('POINT( -73.9857 40.7484)');
SET @distance_in_degrees = 0.01; -- This is approximate, we'll refine it.

SELECT id, name
FROM locations
WHERE ST_DWithin(point, @target_point, @distance_in_degrees);

When you run this, you’ll get 'Times Square' and 'Empire State Building'. ST_DWithin checks if two geometries are within a specified distance of each other. But what is @distance_in_degrees? That’s the tricky part. Spatial indexes in MariaDB (and MySQL) use a technique called R-trees. They don’t store raw lat/lon. Instead, they convert your geographic points into a linear ordering based on a space-filling curve (like a Hilbert curve or Z-order curve). This allows them to use B-tree-like indexing.

The index partitions the 2D space into a grid of cells. Each point is assigned a value based on which cell it falls into, and the index is built on these cell values. When you query with ST_DWithin, MariaDB first uses the index to find all points that might be within the search radius. This is a quick, approximate filter. Then, for the points that pass this initial filter, it performs a precise geometric calculation to confirm if they are truly within the distance. The distance_in_degrees parameter is used to define the bounding box of cells that could contain points within your desired radius.

The exact distance calculation depends on the SRID (Spatial Reference System Identifier) of your geometries. If you’re using SRID 4326 (WGS 84, standard lat/lon), distances are in degrees, which is not a constant measure across the globe. A degree of longitude is much shorter at the poles than at the equator. For accurate measurements in meters or kilometers, you must use a projected coordinate system (like UTM) and specify the SRID accordingly.

So, for precise results in meters, you’d typically convert your points to a projected SRID. For SRID 4326, ST_DWithin with a distance in degrees is an approximation. To get accurate distances in meters for SRID 4326, you’d use ST_Distance_Sphere:

SET @target_point = ST_GeomFromText('POINT( -73.9857 40.7484)', 4326);
SET @distance_in_meters = 1000; -- 1 kilometer

SELECT id, name
FROM locations
WHERE ST_Distance_Sphere(point, @target_point) <= @distance_in_meters;

However, ST_Distance_Sphere cannot use the spatial index directly because it performs a precise spherical calculation. The spatial index is designed for ST_DWithin where the distance parameter can be related to cell boundaries.

Here’s the mental model:

  1. Data Storage: Your POINT data is stored.
  2. Spatial Index: A separate R-tree index is built. It doesn’t store the raw POINT WKT. Instead, it stores a representation of the point’s location within a multi-level grid system. This grid is generated using a space-filling curve algorithm.
  3. Querying (ST_DWithin): When you call ST_DWithin(geometry_column, search_point, distance), MariaDB first translates search_point and distance into a bounding box of grid cells. It then uses the R-tree index to quickly find all indexed entries (points) whose cells fall within this bounding box. This is the candidate set.
  4. Refinement: For each candidate point retrieved from the index, MariaDB performs a precise geometric calculation (e.g., ST_Distance or ST_Distance_Sphere) to verify if it’s actually within the distance.
  5. SRID Matters: The interpretation of distance in ST_DWithin is SRID-dependent. For SRID 4326, it’s in degrees, which leads to inaccuracies. For projected SRIDs, it’s in the units of that projection (e.g., meters).

The most counterintuitive part of MariaDB’s spatial indexing is that the distance parameter in ST_DWithin for SRID 4326 isn’t a direct Euclidean or spherical distance. It’s a parameter that helps define the range of grid cells to check. This means that a fixed distance value in degrees might include points much farther away than you expect, or exclude points that are closer, especially when dealing with large areas or near the poles. To get accurate, meter-based results, you often need to combine ST_DWithin (for the index-based filtering) with ST_Distance_Sphere (for precise verification) or, more efficiently, use a projected SRID from the start and set ST_DWithin’s distance in the units of that SRID.

The next hurdle is understanding how to efficiently handle polygons and lines with spatial indexes, and how to choose the right SRID for your geospatial problem.

Want structured learning?

Take the full Mariadb course →