# Spring boot 2 real combat: using redis’s geo function to find nearby locations

Time：2021-7-7

## 1. Preface

The boss suddenly wants to go online for a demand to obtain the business agent point with the current location of one kilometer. Online tomorrow! When I received this demand, I almost vomited blood, and the time was too tight. Hurry to check the relevant technology selection. After a lot of twists and turns, the demand was finally completed at 10 pm. Now, let’s summarize the idea of general implementation.

## 2. MySQL is not suitable

When meeting the demand, we should first think about whether the existing things can be satisfied and how the cost is.

MySQLIt’s the first thing I can think of. After all, most of the data needs to be persistent until theMySQL。 But usingMySQLIt needs to be calculated by yourselfGeohash。 It needs to use a lot of mathematical and geometric calculations, and needs to learn geography related knowledge. The threshold is high, and it is impossible to complete the requirements in a short time, and it is not easy to do so in the long runMySQLGood at the field, so did not consider it.

Geohashreference resources https://www.cnblogs.com/LBSer…

## 2. Geo in redis

RedisIt’s the one we’re most familiar withK-VDatabase, which is often used as a high-performance cache database to use, most projects will use it. from3.2Version starts, it starts to provideGEOAbility, which is used to realize the functions such as nearby location, calculating distance and so on.GEOThe related commands are as follows:

Redis command describe
GEOHASH Returns the location of one or more location elementsGeohashexpress
GEOPOS Returns the position (longitude and latitude) of all the given positioning elements from the key
GEODIST Returns the distance between two given positions
GEORADIUS Taking the given latitude and longitude as the center, find out the elements within a certain radius
GEOADD Adds the specified geospatial location (latitude, longitude, name) to the specified key
GEORADIUSBYMEMBER Find out the elements in the specified range, the center point is determined by the given location element

Redis will assume that the earth is a perfect sphere, so there may be some deviation in position calculation, which is said to be < = 0.5%. For the demand with strict geographical location requirements, it needs to go through some scenario tests to check whether it can meet the demand.

### 2.1 writing geographic information

So how to achieve all the elements within the target unit radius? We can pass the longitude and latitude of all positions through the above table`GEOADD`Convert these geographic information into 52 bitGeohashwrite inRedis

The command format is as follows:

``geoadd key longitude latitude member [longitude latitude member ...]``

Corresponding examples:

``````redis> geoadd cities:locs 117.12 39.08 tianjin 114.29 38.02  shijiazhuang
(integer) 2``````

It means that the longitude is`117.12`Latitude is`39.08`The location of`tianjin`And longitude`114.29`Latitude is`38.02`The location of`shijiazhuang`joinkeyby`cities:locs`Ofsorted setIn the collection. You can add one to more than one location. Then we can calculate the geographical position with the help of other commands.

The effective longitude is from – 180 degrees to 180 degrees. The effective latitude ranges from – 85.05112878 degrees to 85.05112878 degrees. When the coordinate position exceeds the specified range, the command will return an error.

### 2.2 areas within the radius of statistical units

We can rely on`GEORADIUS`To find all the elements within a certain radius with given latitude and longitude.

The command format is as follows:

``georadius key longtitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] ``

This command is better than that`GEOADD`More complicated:

• radiusRadius length, required. hinder`m``km``ft``mi`, is the length unit option, choose one of four.
• WITHCOORDThe longitude and dimension of the location element are also returned, which is not required.
• WITHDISTWhen the position element is returned, the distance between the position element and the center point is also returned. The unit of distance is consistent with the query unit, which is not required.
• WITHHASHThe 52 bit precision of the return positionGeohashValue, not required. I seldom use this one anyway. Maybe others are lower levelLBSApplication services need this.
• COUNTReturns the number of qualified position elements, which is not required. For example, return to the top 10 to avoid performance problems caused by too many matching results.
• ASC|DESCSorting method is optional. Unsorted is returned by default, but most of us need to sort. Refer to the center position and use it from near to farASCFrom far to nearDESC

For example, we are`cities:locs`(115.03, 38.44) as the center`200km`The results include the name of the city, the corresponding coordinates and the distance from the center (km), and are arranged from near to far. The order is as follows:

``````redis> georadius cities:locs 115.03 38.44 200 km WITHCOORD WITHDIST ASC
1) 1) "shijiazhuang"
2) "79.7653"
3) 1) "114.29000169038772583"
2) "38.01999994251037407"
2) 1) "tianjin"
2) "186.6937"
3) 1) "117.02000230550765991"
2) "39.0800000535766543"``````

You can add`COUNT 1`To find the nearest location.

## 3. Based on redis Geo

Now that the general principles and ideas have been finished, the next step is practical operation. combinationSpring BootWhat should we do?

### 3.1 development environment

Need to haveGEOCharacteristicRedisVersion, here I use theRedis 4。 In addition, we use the client`spring-boot-starter-data-redis`。 We’ll use it here`RedisTemplate`Object.

### 3.2 batch adding location information

In the first step, we need to initialize the location data to theRedisIn the middle. staySpring Data RedisA position coordinate in`(lng,lat)`Can be encapsulated into`org.springframework.data.geo.Point`Object. Then specify a name to form a locationGeoInformation.`RedisTemplate`The method of batch adding location information is provided. We can make itSection 2.1The add command in is converted to the following code:

``````Map<String, Point> points = new HashMap<>();
points.put("tianjin", new Point(117.12, 39.08));
points.put("shijiazhuang", new Point(114.29, 38.02));

It can be initialized by combining with the applicationrunner interface provided by spring boot.

``````@Bean
public ApplicationRunner cacheActiveAppRunner(RedisTemplate<String, String> redisTemplate) {

return args -> {
final String GEO_KEY = "cities:locs";

//Clean up cache
redisTemplate.delete(GEO_KEY);

Map<String, Point> points = new HashMap<>();
points.put("tianjin", new Point(117.12, 39.08));
points.put("shijiazhuang", new Point(114.29, 38.02));
BoundGeoOperations<String, String> geoOps = redisTemplate.boundGeoOps(GEO_KEY);
};
}``````

The geographic data is persisted to MySQL and then synchronized to redis.

### 3.3 query specific location nearby

`RedisTemplate`in the light of`GEORADIUS`The command also has encapsulation:

``GeoResults<GeoLocation<M>> radius(K key, Circle within, GeoRadiusCommandArgs args)``

`Circle`The object is the area covered by the package (Figure 1), and the required elements are the coordinates of the center point`Point`Object, radius, and metric, for example:

``````Point point = new Point(115.03, 38.44);

Metric metric = RedisGeoCommands.DistanceUnit.KILOMETERS;
Distance distance = new Distance(200, metric);

Circle circle = new Circle(point, distance);``````

`GeoRadiusCommandArgs`Used to encapsulate`GEORADIUS`For some optional command parameters, seeChapter 2.2In`WITHCOORD``COUNT``ASC`For example, we need to include the first five data of coordinates, center distance and sorting from near to far in the returned results

``````RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands
.includeDistance()
.includeCoordinates()
.sortAscending()
.limit(limit);``````

Then execute`radius`The method will be available`GeoResults<RedisGeoCommands.GeoLocation<String>>`As a result of encapsulation, we can get the data we want by parsing the iterative object

``````GeoResults<RedisGeoCommands.GeoLocation<String>> radius = redisTemplate.opsForGeo()

List<StageDTO> stageDTOS = new ArrayList<>();
RedisGeoCommands.GeoLocation<String> content = geoLocationGeoResult.getContent();
//Member name, such as Tianjin
String name = content.getName();
//Corresponding longitude and latitude coordinates
Point pos = content.getPoint();
//Distance from center point
Distance dis = geoLocationGeoResult.getDistance();
});
}``````

### 3.4 deleting elements

Sometimes we may need to delete a location element, butRedisOfGeoThere is no command to delete members. But because the bottom layer is`zset`We can use`zrem`Command to delete, the correspondingJavaThe code is:

``redisTemplate.boundZSetOps(GEO_STAGE).remove("tianjin");``

## 4. Summary

Today we useRedisOfGeoFeatures to achieve the common nearby geographic information query requirements, simple and easy to use. Actually, use another oneNosqldatabaseMongoDBIt can also be achieved. In the case of small amount of dataRedisHas been able to meet the needs of a good. If the amount of data is large, it can be usedMongoDBTo achieve. The problems involved in this paper are as followsDEMOFocus on:Manong little fat brotherReply to official accountredisgeoobtain.

`Pay attention to the official account: Felordcn for more information`

Personal blog: https://felord.cn

## Implementation example of go operation etcd

etcdIt is an open-source, distributed key value pair data storage system, which provides shared configuration, service registration and discovery. This paper mainly introduces the installation and use of etcd. Etcdetcd introduction etcdIt is an open source and highly available distributed key value storage system developed with go language, which can be used to configure sharing […]