neo4j优化(新)
1.查询时指定具体的标签名(表名)
需要创建索引
CREATE INDEX idx_user_name FOR (p:User) ON (p.name);
MATCH (p {name: 'User_20005'}) RETURN p; ##整个图里查找,性能低
MATCH (p:User {name: 'User_20005'}) RETURN p; ##具体标签里查找,性能好
EXPLAIN MATCH (p {name: 'User_20005'}) RETURN p;
EXPLAIN MATCH (p:User {name: 'User_20005'}) RETURN p;
2.通过使用WITH子句,可以将复杂的Cypher查询拆分为多个步骤,每一步聚焦于特定的处理逻辑,从而提升可读性和优化空间
##查找朋友数量超过10的人
MATCH (p:User)-[:FRIEND_WITH]->(friend)
WITH p, count(friend) AS friendCount
WHERE friendCount > 10
RETURN p.name, friendCount;
3.限制返回数据:只返回需要的属性,而不是整个节点或关系
##返回整个节点(可能包含大量属性)
MATCH (p:User {name: 'User_20005'}) RETURN p;
##只返回需要的属性
MATCH (p:User {name: 'User_20005'}) RETURN p.name,p.age;
4.使用OPTIONAL MATCH替代OUTER JOIN模式
##查找用户及所在的公司(如果有)
MATCH (u:User {name: 'User_20005'})
OPTIONAL MATCH (u)-[:WORKS_AT]->(o:Company)
RETURN u.name, o.id
5.避免笛卡尔积:确保MATCH子句中的模式是连接的
##潜在笛卡尔积
MATCH (a:User), (b:Company)
WHERE a.name = 'User_20005' AND b.name = 'Company_4104'
RETURN a, b
##优化:如果有关联,明确关联
MATCH (a:User {name: 'User_20005'})-[:WORKS_AT]->(b:Company {name: 'Company_4104'})
RETURN a, b
6.优化IN操作:对于大型列表,IN操作可能较慢
##大型列表
WITH [1,2,3,4,5] AS ids
MATCH (p:User)
WHERE p.id IN ids
RETURN p
##优化:使用UNWIND和MERGE/MATCH
WITH [1,2,3,4,5] AS ids
UNWIND ids AS targetId
MATCH (p:User {id: targetId})
RETURN p
7.优化可变长度路径
在优化可变长度路径查询时,应注意控制路径的搜索范围和复杂度。
首先,建议通过限定路径的最小和最大深度(如[:REL*1..5])来减少遍历的节点和关系数量,避免无界搜索导致性能下降;
##1到2跳的路径
MATCH p = (a:User{name:'User_20005'})-[:WORKS_AT*1..2]->(b:Company)
RETURN p
8.参数化查询
##硬编码查询
MATCH (p:Person {name: 'Alice'}) RETURN p
MATCH (p:Person {name: 'Bob'}) RETURN p
##参数化查询(效果好)
MATCH (p:Person {name: $name}) RETURN p
当使用不同的参数值(如 {name: 'Alice'} 或 {name: 'Bob'})执行参数化查询时,Neo4j 会将其视为同一个查询,只是参数不同。这样可以重用已经缓存的查询计划,显著减少每次查询时的解析和优化开销,从而提升整体性能.
9.查询提示(Hints)
##强制使用索引
MATCH (p:Person)
USING INDEX p:Person(name)
WHERE p.name = 'Alice'
RETURN p
##强制扫描(避免使用索引)
MATCH (p:Person)
USING SCAN p:Person
WHERE p.name = 'Alice'
RETURN p
10.造数据例子
Neo4jBulkImporter.java
package org.hxl.neo4j;
import org.neo4j.driver.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
public class Neo4jBulkImporter {
private static final String URI = "bolt://192.168.1.135:7687";
private static final String USER = "neo4j";
private static final String PASSWORD = "neo4j123";
private static final int BATCH_SIZE = 5000;
private static final int TOTAL_USERS = 100_000;
private static final int TOTAL_COMPANIES = 10_000;
private static final int RELATIONS_PER_USER = 3;
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
try (Driver driver = GraphDatabase.driver(URI, AuthTokens.basic(USER, PASSWORD))) {
// 创建唯一约束确保数据完整性
createConstraints(driver);
// 导入公司数据
System.out.println("导入公司数据...");
importCompanies(driver);
// 导入用户数据
System.out.println("导入用户数据...");
importUsers(driver);
// 创建用户关系
System.out.println("创建用户关系...");
createFriendships(driver);
// 创建用户-公司关系
System.out.println("创建用户-公司关系...");
createEmploymentRelationships(driver);
// 创建公司关系
System.out.println("创建公司关系...");
createCompanyRelationships(driver);
}
long endTime = System.currentTimeMillis();
System.out.printf("\n数据导入完成! 总耗时: %.2f 秒%n", (endTime - startTime) / 1000.0);
}
// 创建唯一约束
private static void createConstraints(Driver driver) {
try (Session session = driver.session()) {
session.run("CREATE CONSTRAINT IF NOT EXISTS FOR (u:User) REQUIRE u.id IS UNIQUE");
session.run("CREATE CONSTRAINT IF NOT EXISTS FOR (c:Company) REQUIRE c.id IS UNIQUE");
System.out.println("唯一约束创建成功");
}
}
// 导入公司数据
private static void importCompanies(Driver driver) {
ExecutorService executor = Executors.newFixedThreadPool(4);
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int offset = 0; offset < TOTAL_COMPANIES; offset += BATCH_SIZE) {
int currentOffset = offset;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
int currentBatchSize = Math.min(BATCH_SIZE, TOTAL_COMPANIES - currentOffset);
try (Session session = driver.session()) {
String cypher = "UNWIND $batch AS row " +
"CREATE (c:Company {id: row.id, name: row.name, " +
"industry: row.industry, founded: row.founded})";
session.run(cypher, Values.parameters("batch",
generateCompanyData(currentBatchSize, currentOffset)));
}
}, executor);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
executor.shutdown();
System.out.printf("成功导入 %d 家公司%n", TOTAL_COMPANIES);
}
// 生成公司数据
private static List<Map<String, Object>> generateCompanyData(int size, int offset) {
String[] industries = {"科技", "金融", "医疗", "教育", "制造", "零售", "娱乐", "能源"};
List<Map<String, Object>> batch = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Map<String, Object> record = new HashMap<>();
int id = offset + i;
record.put("id", id);
record.put("name", "Company_" + id);
record.put("industry", industries[id % industries.length]);
record.put("founded", 1950 + (id % 70));
batch.add(record);
}
return batch;
}
// 导入用户数据
private static void importUsers(Driver driver) {
ExecutorService executor = Executors.newFixedThreadPool(8);
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int offset = 0; offset < TOTAL_USERS; offset += BATCH_SIZE) {
int currentOffset = offset;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
int currentBatchSize = Math.min(BATCH_SIZE, TOTAL_USERS - currentOffset);
try (Session session = driver.session()) {
String cypher = "UNWIND $batch AS row " +
"CREATE (u:User {id: row.id, name: row.name, " +
"email: row.email, age: row.age, city: row.city})";
session.run(cypher, Values.parameters("batch",
generateUserData(currentBatchSize, currentOffset)));
}
}, executor);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
executor.shutdown();
System.out.printf("成功导入 %d 位用户%n", TOTAL_USERS);
}
// 生成用户数据
private static List<Map<String, Object>> generateUserData(int size, int offset) {
String[] cities = {"北京", "上海", "广州", "深圳", "杭州", "成都", "武汉", "南京"};
List<Map<String, Object>> batch = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Map<String, Object> record = new HashMap<>();
int id = offset + i;
record.put("id", id);
record.put("name", "User_" + id);
record.put("email", "user" + id + "@example.com");
record.put("age", 18 + (id % 60));
record.put("city", cities[id % cities.length]);
batch.add(record);
}
return batch;
}
// 创建用户之间的朋友关系
private static void createFriendships(Driver driver) {
ExecutorService executor = Executors.newFixedThreadPool(6);
List<CompletableFuture<Void>> futures = new ArrayList<>();
int totalRelations = TOTAL_USERS * RELATIONS_PER_USER;
int batches = (int) Math.ceil((double) totalRelations / BATCH_SIZE);
for (int i = 0; i < batches; i++) {
int batchIndex = i;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try (Session session = driver.session()) {
String cypher = "UNWIND $batch AS row " +
"MATCH (u1:User {id: row.userId1}), (u2:User {id: row.userId2}) " +
"CREATE (u1)-[:FRIEND_WITH {since: row.since}]->(u2)";
session.run(cypher, Values.parameters("batch",
generateFriendshipData(BATCH_SIZE, batchIndex * BATCH_SIZE)));
}
}, executor);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
executor.shutdown();
System.out.printf("成功创建 %d 个朋友关系%n", totalRelations);
}
// 生成朋友关系数据
private static List<Map<String, Object>> generateFriendshipData(int size, int offset) {
List<Map<String, Object>> batch = new ArrayList<>(size);
Random random = new Random();
for (int i = 0; i < size; i++) {
Map<String, Object> relation = new HashMap<>();
int user1 = random.nextInt(TOTAL_USERS);
int user2;
do {
user2 = random.nextInt(TOTAL_USERS);
} while (user1 == user2);
relation.put("userId1", user1);
relation.put("userId2", user2);
relation.put("since", 2010 + random.nextInt(14));
batch.add(relation);
}
return batch;
}
// 创建用户-公司雇佣关系
private static void createEmploymentRelationships(Driver driver) {
ExecutorService executor = Executors.newFixedThreadPool(4);
List<CompletableFuture<Void>> futures = new ArrayList<>();
int batches = (int) Math.ceil((double) TOTAL_USERS / BATCH_SIZE);
for (int i = 0; i < batches; i++) {
int batchIndex = i;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try (Session session = driver.session()) {
String cypher = "UNWIND $batch AS row " +
"MATCH (u:User {id: row.userId}), (c:Company {id: row.companyId}) " +
"CREATE (u)-[:WORKS_AT {position: row.position, " +
"startYear: row.startYear}]->(c)";
session.run(cypher, Values.parameters("batch",
generateEmploymentData(BATCH_SIZE, batchIndex * BATCH_SIZE)));
}
}, executor);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
executor.shutdown();
System.out.printf("成功创建 %d 个雇佣关系%n", TOTAL_USERS);
}
// 生成雇佣关系数据
private static List<Map<String, Object>> generateEmploymentData(int size, int offset) {
String[] positions = {"工程师", "经理", "分析师", "设计师", "销售", "总监", "顾问"};
Random random = new Random();
List<Map<String, Object>> batch = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
int userId = offset + i;
if (userId >= TOTAL_USERS) break;
Map<String, Object> relation = new HashMap<>();
relation.put("userId", userId);
relation.put("companyId", random.nextInt(TOTAL_COMPANIES));
relation.put("position", positions[random.nextInt(positions.length)]);
relation.put("startYear", 2000 + random.nextInt(24));
batch.add(relation);
}
return batch;
}
// 创建公司之间的合作关系
private static void createCompanyRelationships(Driver driver) {
int totalRelations = TOTAL_COMPANIES / 2;
ExecutorService executor = Executors.newFixedThreadPool(4);
List<CompletableFuture<Void>> futures = new ArrayList<>();
int batches = (int) Math.ceil((double) totalRelations / BATCH_SIZE);
for (int i = 0; i < batches; i++) {
int batchIndex = i;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try (Session session = driver.session()) {
String cypher = "UNWIND $batch AS row " +
"MATCH (c1:Company {id: row.companyId1}), (c2:Company {id: row.companyId2}) " +
"CREATE (c1)-[:PARTNER_WITH {since: row.since, " +
"type: row.type}]->(c2)";
session.run(cypher, Values.parameters("batch",
generateCompanyRelationsData(BATCH_SIZE, batchIndex * BATCH_SIZE)));
}
}, executor);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
executor.shutdown();
System.out.printf("成功创建 %d 个公司合作关系%n", totalRelations);
}
// 生成公司关系数据
private static List<Map<String, Object>> generateCompanyRelationsData(int size, int offset) {
String[] types = {"技术合作", "市场合作", "供应链合作", "研发合作"};
Random random = new Random();
List<Map<String, Object>> batch = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Map<String, Object> relation = new HashMap<>();
int company1 = random.nextInt(TOTAL_COMPANIES);
int company2;
do {
company2 = random.nextInt(TOTAL_COMPANIES);
} while (company1 == company2);
relation.put("companyId1", company1);
relation.put("companyId2", company2);
relation.put("since", 2000 + random.nextInt(24));
relation.put("type", types[random.nextInt(types.length)]);
batch.add(relation);
}
return batch;
}
}