我正在编写一个java应用程序,它将一个数据库的信息(db2)复制到另一个数据库(sql server)。操作顺序非常简单:
我的一切工作都很顺利,但在一天的某些时候,需要进行的更新量会大幅增加(可以达到数十万)。
下面你可以看到我的代码的通用版本。它遵循应用程序的基本算法。对象是通用的,实际应用程序有5种不同类型的指定对象,每种对象都有自己的更新程序线程类。但是下面的通用函数正是它们的样子。在updateDatabase()
方法中,它们都会添加到threads
并且所有内容都会同时运行。
private void updateDatabase()
{
List<Thread> threads = new ArrayList<>();
addObjectThreads( threads );
startThreads( threads );
joinAllThreads( threads );
}
private void addObjectThreads( List<Thread> threads )
{
List<Object> objects = getTransformService().getObjects();
logger.info( "Found " + objects.size() + " Objects" );
createThreads( threads, objects, ObjectUpdaterThread.class );
}
private void createThreads( List<Thread> threads, List<?> objects, Class threadClass )
{
final int BASE_OBJECT_LOAD = 1;
int objectLoad = objects.size() / Database.getMaxThreads() > 0 ? objects.size() / Database.getMaxThreads() + BASE_OBJECT_LOAD : BASE_OBJECT_LOAD;
for (int i = 0; i < (objects.size() / objectLoad); ++i)
{
int startIndex = i * objectLoad;
int endIndex = (i + 1) * objectLoad;
try
{
List<?> objectSubList = objects.subList( startIndex, endIndex > objects.size() ? objects.size() : endIndex );
threads.add( new Thread( (Thread) threadClass.getConstructor( List.class ).newInstance( objectSubList ) ) );
}
catch (Exception exception)
{
logger.error( exception.getMessage() );
}
}
}
public class ObjectUpdaterThread extends BaseUpdaterThread
{
private List<Object> objects;
final private Logger logger = Logger.getLogger( ObjectUpdaterThread.class );
public ObjectUpdaterThread( List<Object> objects)
{
this.objects = objects;
}
public void run()
{
for (Object object : objects)
{
logger.info( "Now Updating Object: " + object.getId() );
getTransformService().updateObject( object );
}
}
}
所有这些都转到了弹出服务,看起来像下面的代码。它的泛型,但每种类型的对象都具有完全相同的逻辑类型。上面代码中的getObjects()只是传递给DAO的一行,所以不需要真正发布它。
@Service
@Scope(value = "prototype")
public class TransformServiceImpl implements TransformService
{
final private Logger logger = Logger.getLogger( TransformServiceImpl.class );
@Autowired
private TransformDao transformDao;
@Override
public void updateObject( Object object )
{
String sql;
if ( object.exists() )
{
sql = Object.Mapper.UPDATE;
}
else
{
sql = Object.Mapper.INSERT;
}
boolean isCompleted = false;
while ( !isCompleted )
{
try
{
transformDao.updateObject( object, sql );
isCompleted = true;
}
catch (Exception exception)
{
logger.error( exception.getMessage() );
threadSleep();
logger.info( "Now retrying update for Object: " + object.getId() );
}
}
logger.info( "Updated Object: " + object.getId() );
}
}
最后这些全部转到看起来像这样的DAO:
@Repository
@Scope(value = "prototype")
public class TransformDaoImpl implements TransformDao
{
//@Resource is like @Autowired but with the added option of being able to specify the name
//Good for autowiring two different instances of the same class [NamedParameterJdbcTemplate]
//Another alternative = @Autowired @Qualifier(BEAN_NAME)
@Resource(name = "db2")
private NamedParameterJdbcTemplate db2;
@Resource(name = "sqlServer")
private NamedParameterJdbcTemplate sqlServer;
final private Logger logger = Logger.getLogger( TransformerImpl.class );
@Override
public void updateObject( Objet object, String sql )
{
MapSqlParameterSource source = new MapSqlParameterSource();
source.addValue( "column1_value", object.getColumn1Value() );
//put all source values from the POJO in just like above
sqlServer.update( sql, source );
}
}
我的插入语句如下所示:
"INSERT INTO dbo.OBJECT_TABLE " +
"(COLUMN1, COLUMN2...) " +
"VALUES(:column1_value, :column2_value... "
我的更新语句如下所示:
"UPDATE dbo.OBJECT_TABLE SET " +
"COLUMN1 = :column1_value, COLUMN2 = :column2_value, " +
"WHERE PRIMARY_KEY_COLUMN = :primary_key_value"
我知道很多代码和东西,但我只想布置我所拥有的一切,希望我可以获得帮助,使其更快或更高效。更新如此多的行需要几个小时的时间,如果它只需要几个小时或几个小时而不是几个小时,那就太好了。谢谢你的帮助。我欢迎所有关于弹簧,线程和数据库的学习经历。
答案 0 :(得分:0)
- 检查某个时间段内是否有任何更新
- 从指定时间范围内的第一个数据库中获取所有内容
醇>
源表中的LAST_UPDATED_DATE列(或者您正在使用的任何内容)上是否有索引?而不是将负担放在您的应用程序上,如果它在您的控制范围内,为什么不在源数据库中编写一些在“更新日志”表中创建条目的触发器?这样,您的应用程序需要做的就是使用并执行这些条目。
您如何管理交易?如果你为每个操作创建一个新的交易,它将会非常缓慢。
关于线程代码,您是否考虑过使用更标准的内容而不是编写自己的内容?你拥有的是一个非常典型的producer/consumer,Java对{(3}}和ThreadPoolExecutor这类事物有很好的支持,可以在执行不同任务的线程之间移动数据。
使用现成品的好处是:1)经过充分测试2)有许多调整选项和尺寸调整策略,您可以调整以提高性能。
此外,您是否考虑将每种类型的处理逻辑封装到单独的策略类中,而不是为每种需要处理的对象类型使用5种不同的线程类型?这样,您可以使用单个工作线程池(这将更容易调整大小和调整)。
答案 1 :(得分:0)
如果您要向服务器发送大量SQL,则应考虑使用Statement.addBatch
和Statement.executeBatch
方法对其进行批处理。这些批次的大小是有限的(我总是限制为64K的SQL),但它们大大降低了到数据库的往返次数。
当我在迭代并创建SQL时,我会跟踪已经批量的数量,当SQL越过64K边界时,我会触发executeBatch
并开始一个新的。
您可能想要尝试64K号码,这可能是我当时使用的Oracle限制。
我无法与Spring交谈,但批处理是JDBC Statement
的一部分。我确信这很简单。