Elasticsearch Data Migration

ES数据迁移方案:基于JavaAPI的高效索引重建
本文介绍了如何利用JavaAPI进行Elasticsearch的数据迁移,包括获取客户端连接、迁移方法实现以及测试过程。重点在于通过合理设置参数,确保在迁移大量数据时集群性能稳定,同时优化索引速度。

本文介绍ES的数据迁移方案:

 

由于ES更新速度比较快,很大程度上, 我们需要更新版本、插件、甚至更新分词器, 单纯的upgrade很有可能不能满足业务需求, 更坏的情况下, 可能需要重建索引。本文从Java API 的角度来介绍ES的数据迁移(或数据重新索引)。基于以下逻辑实现,个人已测试过2亿数据的迁移,可以放心使用。

 

1. 获取clientl连接。本文选择transportClient。

public class ClientUtil
{

    static Settings defaultSettings = ImmutableSettings.settingsBuilder().put("client.transport.sniff", false).put("client.transport.ping_timeout","10s").build(); //如果你的集群node数量是稳定的,那么最好关闭sniff。 同时, 将ping时间设置高于默认5s, 很大程序上可以解决No Node available exception.
 
    // 创建私有对象
    private static TransportClient targetClient;
    
    private static TransportClient sourceClient;
 
    static {
        try {
            Class<?> clazz = Class.forName(TransportClient.class.getName());
            Constructor<?> constructor = clazz.getDeclaredConstructor(Settings.class);
            constructor.setAccessible(true);
		    Settings finalSettings = ImmutableSettings.settingsBuilder()
		                .put(defaultSettings)
		                .build();
		    targetClient = (TransportClient) constructor.newInstance(finalSettings);
		    targetClient.addTransportAddress(new InetSocketTransportAddress("192.168.1.100", 9300))
		    			.addTransportAddress(new InetSocketTransportAddress("192.168.1.101", 9300));
		    sourceClient = (TransportClient) constructor.newInstance(finalSettings);
		    sourceClient.addTransportAddress(new InetSocketTransportAddress("192.168.1.110", 9300))
		                .addTransportAddress(new InetSocketTransportAddress("192.168.1.111", 9300));
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
 
    // 取得源实例
    public static synchronized Client getSourceTransportClient() {
        return sourceClient;
    }
    
    // 取得目标实例
    public static synchronized Client getTargetTransportClient() {
        return targetClient;
    }

}

 

以上代码用于获取源cluster和目标cluster的client.

 

2.迁移主方法:

private void doMigrate(Client sourceclient, Client targetclient, String sourceIndexName, String targetIndexName, String indexDocType, int pageSize)
	{
		int total = 0;
		SearchResponse searchResponse = sourceclient.prepareSearch(sourceIndexName).setSearchType(SearchType.SCAN)
				.setQuery(matchAllQuery()).setSize(pageSize).setScroll(TimeValue.timeValueSeconds(20)).execute()
				.actionGet(); //scroll 的time不能太大, 以免对集群造成负载
		boolean exists = targetclient.admin().indices().prepareExists(targetIndexName).execute().actionGet().isExists();
		if (!exists)
			targetclient
					.admin()
					.indices()
					.prepareCreate(targetIndexName)
					.setSettings(
							settingsBuilder().put("index.number_of_replicas", 0).put("index.refresh_interval", "-1"))
					.execute().actionGet(); //设置replica为0, 不refresh, 为了提高索引速度。
		try
		{
			Thread.sleep(200);
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		BulkProcessor bulkProcessor = BulkProcessor.builder(targetclient, new BulkProcessor.Listener()
		{

			@Override
			public void beforeBulk(long executionId, BulkRequest request)
			{

			}

			@Override
			public void afterBulk(long executionId, BulkRequest request, BulkResponse response)
			{
				if (response.hasFailures())
				{
					throw new RuntimeException("BulkResponse show failures: " + response.buildFailureMessage());
				}
			}

			@Override
			public void afterBulk(long executionId, BulkRequest request, Throwable failure)
			{
				throw new RuntimeException("Caught exception in bulk: " + request + ", failure: " + failure, failure);
			}
		}).setConcurrentRequests(10).build(); //设置线程数量, 大小可以根据自己机器调配。

		while (true)
		{
			searchResponse = sourceclient.prepareSearchScroll(searchResponse.getScrollId())
					.setScroll(TimeValue.timeValueSeconds(20)).execute().actionGet();
			for (SearchHit hit : searchResponse.getHits())
			{

				IndexRequestBuilder indexRequestBuilder = targetclient.prepareIndex(targetIndexName, indexDocType);
				indexRequestBuilder.setSource(hit.getSource());
				indexRequestBuilder.setId(hit.getId());
				indexRequestBuilder.setOpType(IndexRequest.OpType.INDEX);
				bulkProcessor.add(indexRequestBuilder.request());
				total++;
			}
			System.out.println("Already migrated : " + total + " records!");
			if (searchResponse.getHits().hits().length == 0)
			{
				break;
			}
		}
		try
		{
			Thread.sleep(10000);//Sleep 10s waiting the cluster.
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		bulkProcessor.close();
		targetclient
		.admin()
		.indices().prepareUpdateSettings(targetIndexName).setSettings(
				settingsBuilder().put("index.number_of_replicas", 1).put("index.refresh_interval", "1s"))
		.execute().actionGet();
	}

 

 3.测试:

public static void main(String[] args) throws ElasticSearchException, IOException, InterruptedException
	{
		int pageSize = 40; //分页大小, 不能过大, 太大影响集群性能, 可能引起no node 异常。
		Client sourceclient = ClientUtil.getSourceTransportClient();
		Client targetclient = ClientUtil.getTargetTransportClient();
	        //调用doMigrate方法。 doMigrate(sourceclient, targetclient, "test", "testnew", "test", pageSize);}
2025.09.01 17:01:05 INFO app[][o.s.a.AppFileSystem] Cleaning or creating temp directory D:\sonarqube-25.8.0.112029\temp 2025.09.01 17:01:05 INFO app[][o.s.a.es.EsSettings] Elasticsearch listening on [HTTP: 127.0.0.1:9001, TCP: 127.0.0.1:{}] 2025.09.01 17:01:05 INFO app[][o.s.a.ProcessLauncherImpl] Launch process[ELASTICSEARCH] from [D:\sonarqube-25.8.0.112029\elasticsearch]: C:\Program Files\Java\jdk-17\bin\java -Xms4m -Xmx64m -XX:+UseSerialGC -Dcli.name=server -Dcli.script=./bin/elasticsearch -Dcli.libs=lib/tools/server-cli -Des.path.home=D:\sonarqube-25.8.0.112029\elasticsearch -Des.path.conf=D:\sonarqube-25.8.0.112029\temp\conf\es -Des.distribution.type=tar -cp D:\sonarqube-25.8.0.112029\elasticsearch\lib\*;D:\sonarqube-25.8.0.112029\elasticsearch\lib\cli-launcher\* org.elasticsearch.launcher.CliToolLauncher 2025.09.01 17:01:05 INFO app[][o.s.a.SchedulerImpl] Waiting for Elasticsearch to be up and running 2025.09.01 17:01:16 INFO app[][o.s.a.SchedulerImpl] Process[es] is up 2025.09.01 17:01:16 INFO app[][o.s.a.ProcessLauncherImpl] Launch process[WEB_SERVER] from [D:\sonarqube-25.8.0.112029]: C:\Program Files\Java\jdk-17\bin\java -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djava.io.tmpdir=D:\sonarqube-25.8.0.112029\temp -XX:-OmitStackTraceInFastThrow --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED --add-exports=java.base/jdk.internal.ref=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.management/sun.management=ALL-UNNAMED --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED -Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -Dhttp.nonProxyHosts=localhost|127.*|[::1] -cp ./lib/sonar-application-25.8.0.112029.jar;D:\sonarqube-25.8.0.112029\lib\jdbc\postgresql\postgresql-42.7.7.jar org.sonar.server.app.WebServer D:\sonarqube-25.8.0.112029\temp\sq-process7484978067730404954properties 2025.09.01 17:01:19 INFO app[][o.s.a.SchedulerImpl] Process[Web Server] is stopped 2025.09.01 17:01:20 INFO app[][o.s.a.SchedulerImpl] Process[ElasticSearch] is stopped 2025.09.01 17:01:20 INFO app[][o.s.a.SchedulerImpl] SonarQube is stopped 已设配置sonar.jdbc.url=jdbc:postgresql://localhost/sonar?currentSchema=public #sonar表示数据库名称 sonar.host.url=http://localhost:9000 /sonarqube sonar.jdbc.username=sonar #数据库用户名 sonar.jdbc.password=123456 #数据库密码 sonar.login=admin #登陆sonarqube的账号 sonar.password=Sun.12345678 #登陆sonarqube的密码 sonar.search.javaOpts=-Xms1g -Xmx1g -XX:+UseG1GC -Des.bootstrap.mlockall=false sonar.web.javaOpts=-Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError sonar.ce.javaOpts=-Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError sonar.search.javaAdditionalOpts=-XX:MaxGCPauseMillis=200 -XX:+UseG1GC -XX:MaxDirectMemorySize=1g
最新发布
09-02
<think> 我们之前已经讨论过SonarQube启动时Web Server停止的问题,并给出了解决方案。现在用户再次询问,并提供了配置信息(包括数据库连接配置)。根据用户描述,他们已经设置了正确的JDBC URL(如`sonar.jdbc.url=jdbc:postgresql://localhost/sonar?currentSchema=public`),但仍然遇到Web Server停止的问题。 重新分析:Web Server进程停止通常是因为它依赖的服务(如Elasticsearch或数据库)未能正确启动或连接。虽然之前我们重点排查了Elasticsearch的权限和内存问题,但根据用户当前提供的上下文,现在需要增加对数据库连接的检查。 ### 可能的原因及解决方案 #### 1. **数据库连接问题**(新排查点) - 尽管配置了JDBC URL,但可能出现以下情况: - PostgreSQL服务未运行 - 用户名/密码错误 - 数据库`sonar`不存在 - 连接超时 - **解决方案**: - 验证PostgreSQL服务状态(Windows): ```powershell Get-Service -Name postgresql* ``` 如果未运行,启动服务: ```powershell Start-Service -Name "postgresql-x64-14" ``` - 使用`psql`测试连接: ```powershell psql -U sonar -d sonar -h localhost -W ``` 输入密码,执行简单SQL(如`SELECT 1;`)验证权限。 #### 2. **数据库表结构不兼容**(版本升级遗留问题) - SonarQube 25.8要求数据库schema版本必须兼容,若从旧版本升级而来可能存在冲突。 - **解决方案**: - 备份数据库后执行SonarQube提供的升级脚本(需参考官方升级指南)。 - 或创建新数据库并重新初始化(适合全新安装): ```sql CREATE DATABASE sonar_new WITH ENCODING 'UTF8'; GRANT ALL PRIVILEGES ON DATABASE sonar_new TO sonar; ``` 修改`sonar.properties`中的数据库名为`sonar_new`。 #### 3. **Web Server内存不足** - 原始日志中Web Server配置:`-Xms1g -Xmx1g`,对于大型项目可能不足。 - **解决方案**:增加内存(不超过物理内存的50%): ```properties # conf/sonar.properties sonar.web.javaOpts=-Xms2g -Xmx4g ``` #### 4. **Elasticsearch未就绪**(仍需验证) - 虽然之前已处理,但日志中仍有`Process[Web Server] is stopped`,需确认ES是否真正启动成功。 - **排查方法**:检查`logs/es.log`是否有错误日志。 --- ### 完整解决流程(按优先级) 1. **检查数据库连接**(最高优先级) - 使用管理员账号登录PostgreSQL,验证`sonar`用户权限: ```sql \du sonar ``` 确保有`sonar`数据库的完全权限。 2. **查看详细错误日志** - 定位`logs/web.log`,搜索`ERROR`或`org.sonar.server.platform.ServerImpl`。 - 常见错误示例: - `org.postgresql.util.PSQLException: FATAL: database "sonar" does not exist` - `Authentication failed` 3. **清除临时文件并重启** ```powershell Stop-Service -Name "SonarQube" Remove-Item "D:\sonarqube-25.8.0.112029\temp\*" -Recurse -Force Start-Service -Name "SonarQube" ``` 4. **升级数据库驱动**(若日志提示不兼容) - 下载最新PostgreSQL JDBC驱动(如`postgresql-42.7.7.jar`)放入`lib/jdbc/postgresql/`。 --- ### 验证步骤 ```powershell # 查看启动日志(等待30秒) Get-Content "D:\sonarqube-25.8.0.112029\logs\sonar.log" -Wait # 预期成功日志: # "SonarQube is up" ``` --- ### 📌 关键配置模板 `conf/sonar.properties` 核心参数示例: ```properties sonar.jdbc.url=jdbc:postgresql://localhost/sonar?currentSchema=public sonar.jdbc.username=sonar sonar.jdbc.password=your_password # Elasticsearch内存(独立配置) sonar.search.javaOpts=-Xms1g -Xmx2g # Web服务内存 sonar.web.javaOpts=-Xms1g -Xmx2g ``` --- §§相关问题§§ 1. 如何查看SonarQube日志中的具体错误原因? 2. PostgreSQL数据库如何创建用户并授权? 3. SonarQube升级后如何处理数据库迁移? 4. 如何配置SonarQube使用外部Elasticsearch集群? </think> ### 🔍 SonarQube启动时Web Server进程停止的解决方案 根据日志分析(Web Server在启动4秒后停止),结合您的配置(Java 17 + PostgreSQL),核心问题通常集中在**数据库连接、文件权限或内存配置**。以下是逐步解决方案: --- ### ✅ 步骤1:验证数据库连接配置 修改 `D:\sonarqube-25.8.0.112029\conf\sonar.properties` 确保数据库配置正确: ```properties # 验证以下关键配置(参考引用[1][2]): sonar.jdbc.url=jdbc:postgresql://localhost/sonar?currentSchema=public sonar.jdbc.username=sonar # 确保与PG创建的用户名一致 sonar.jdbc.password=your_password # 实际密码 ``` **必须检查项**: - PostgreSQL服务是否运行:`psql -U sonar -d sonar`(测试登录) - 用户权限:`GRANT ALL PRIVILEGES ON DATABASE sonar TO sonar;` - Schema存在性:`SELECT schema_name FROM information_schema.schemata;` --- ### ✅ 步骤2:修复文件系统权限(Windows专属) SonarQube需要**完全控制**以下目录: ```powershell # 授予NETWORK SERVICE权限 icacls "D:\sonarqube-25.8.0.112029" /grant "NETWORK SERVICE:(OI)(CI)F" /T icacls "D:\sonarqube-25.8.0.112029\data" /grant "NETWORK SERVICE:(OI)(CI)F" /T icacls "D:\sonarqube-25.8.0.112029\temp" /grant "NETWORK SERVICE:(OI)(CI)F" /T # 删除临时文件锁定 Remove-Item "D:\sonarqube-25.8.0.112029\temp\*" -Recurse -Force ``` --- ### ✅ 步骤3:调整内存配置 修改 `sonar.properties` 防止内存不足导致崩溃: ```properties # ElasticSearch内存(默认值过小) sonar.search.javaOpts=-Xms1g -Xmx2g -XX:MaxDirectMemorySize=512m # Web服务内存(Java 17需更高开销) sonar.web.javaOpts=-Xms1g -Xmx2g ``` --- ### ✅ 步骤4:日志诊断关键错误 检查 `D:\sonarqube-25.8.0.112029\logs\web.log` 中的致命错误: - **数据库连接失败**:`org.postgresql.util.PSQLException` - **表结构不兼容**:`Schema migration failed`(需执行DB升级脚本) - **端口冲突**:`Address already in use`(修改 `sonar.web.port=9001`) --- ### ✅ 步骤5:强制数据库迁移(升级场景) 如果是从旧版本升级: 1. 备份PostgreSQL数据库 2. 执行清理命令: ```bash # 进入SonarQube目录 cd D:\sonarqube-25.8.0.112029 # 强制重建索引(会清空分析数据) java -jar lib\sonar-application-25.8.0.112029.jar start ``` --- ### 📌 验证启动流程 ```powershell # 停止服务 Stop-Service -Name "SonarQube" # 清理环境 Remove-Item "D:\sonarqube-25.8.0.112029\temp\*" -Recurse -Force # 启动并追踪日志 Start-Service -Name "SonarQube" Get-Content "D:\sonarqube-25.8.0.112029\logs\sonar.log" -Wait ``` **成功标志**: ```log 2025.09.01 HH:MM:SS INFO app[][o.s.a.SchedulerImpl] SonarQube is up ``` --- ### 💡 预防措施 1. **定期清理数据库**(参考引用[3]原理): ```sql -- 删除90天前旧数据 DELETE FROM projects WHERE created_at < NOW() - INTERVAL '90 days'; ``` 2. **监控内存公式**: $内存总量 > (Xmx_{ES} + Xmx_{Web} + 512MB)$ 例如:物理内存8GB时,建议 $Xmx_{ES}=3g$, $Xmx_{Web}=2g$ ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值