owned this note
owned this note
Published
Linked with GitHub
# 應付大量新增情況:批量新增、預存程式、多執行緒
**因為客戶說要大量新增簡訊和郵件,一次至少要5萬筆,為了提高效能,開始一連串的改進。**
**1.使用批量新增**
```java=\
@Transactional(propagation = Propagation.SUPPORTS)
public void saveBatchSms(List<Sms> smsList)
{
String sqlSms = "INSERT INTO Sms (SmsSeq, SmsSysId,Content,OfficeHourSend,Status,LastUpdateTime,MaintainHost,MaintainProgramId,MainUserId,CreateDate,CreateProgramId,CreateTime,CreateUserId,DeleteFlag,Mobile,Reserved,BatchMark,InstantFlag,ExpectDeliverTime) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
Session session = this.getSession();
Work work=new Work(){
public void execute(Connection connection)throws SQLException{
PreparedStatement stmtSms = null;
try {
if(smsList != null && !smsList.isEmpty()) {
long start = System.currentTimeMillis();
connection.setAutoCommit(false);
//通過JDBC API執行用於批量更新的SQL語句
stmtSms = connection.prepareStatement(sqlSms);
for(Sms bean:smsList) {
stmtSms.setString(1, bean.getSmsSeq());
stmtSms.setString(2, bean.getSmsSysId());
//用NString可以用Unicode存多國語言:泰文、越南文等等
stmtSms.setNString(3, bean.getContent());
stmtSms.setString(4, bean.getOfficeHourSend());
stmtSms.setString(5, bean.getStatus());
stmtSms.setTimestamp(6, new Timestamp(bean.getLastUpdateTime().getTime()));
stmtSms.setString(7, bean.getMaintainHost());
stmtSms.setString(8, bean.getMaintainProgramId());
stmtSms.setString(9, bean.getMainUserId());
stmtSms.setString(10, bean.getCreateDate());
stmtSms.setString(11, bean.getCreateProgramId());
stmtSms.setTimestamp(12, new Timestamp(bean.getCreateTime().getTime()));
stmtSms.setString(13, bean.getCreateUserId());
stmtSms.setString(14, bean.getDeleteFlag());
stmtSms.setString(15, bean.getMobile());
stmtSms.setString(16, bean.getReserved());
stmtSms.setString(17, bean.getBatchMark());
stmtSms.setString(18, bean.getInstantFlag());
stmtSms.setTimestamp(19, new Timestamp(bean.getExpectDeliverTime().getTime()));
stmtSms.addBatch();
}
stmtSms.executeBatch();
long end = System.currentTimeMillis();
System.out.println(String.format("Total time: %d 毫秒", (end - start)));
logger.info(String.format("Total time: %d 毫秒", (end - start)));
connection.commit();
}
}catch(SQLException e) {
try {
connection.rollback();
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
}finally {
if (stmtSms != null) {
try {
stmtSms.close();
} catch (SQLException e) {
logger.error("preparedStatement.close() fail.", e);
System.out.println("preparedStatement.close() fail."+e);
}
}
if (connection != null) {
try {
connection.setAutoCommit(true);
connection.close();
} catch (SQLException e) {
logger.error("connection.close() fail.", e);
System.out.println("connection.close() fail."+ e);
}
}
}
}
};
session.doWork(work);
}
```
**2.使用Store Procedure預存程序**
後來效能還是不夠快,所以再來加上tore Procedure
```java=\
//用Store Procedure來做
@Transactional(propagation = Propagation.SUPPORTS)
public void saveSmsBatchBySP(List<Sms> smsList)
{
Session session = this.getSession();
Work work=new Work(){
public void execute(Connection connection)throws SQLException{
CallableStatement stmtSms = null;
try {
if(smsList != null && !smsList.isEmpty()) {
long start = System.currentTimeMillis();
connection.setAutoCommit(false);
//通過JDBC API執行用於批量更新的SQL語句
stmtSms = connection.prepareCall("{call dbo.BatchInsertSms(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}");
for(Sms bean:smsList) {
stmtSms.setString(1, bean.getSmsSysId());
//用NString可以用Unicode存多國語言:泰文、越南文等等
stmtSms.setNString(2, bean.getContent());
stmtSms.setString(3, bean.getOfficeHourSend());
stmtSms.setString(4, bean.getStatus());
stmtSms.setTimestamp(5, new Timestamp(bean.getLastUpdateTime().getTime()));
stmtSms.setString(6, bean.getMaintainHost());
stmtSms.setString(7, bean.getMaintainProgramId());
stmtSms.setString(8, bean.getMainUserId());
stmtSms.setString(9, bean.getCreateDate());
stmtSms.setString(10, bean.getCreateProgramId());
stmtSms.setTimestamp(11, new Timestamp(bean.getCreateTime().getTime()));
stmtSms.setString(12, bean.getCreateUserId());
stmtSms.setString(13, bean.getDeleteFlag());
stmtSms.setString(14, bean.getMobile());
stmtSms.setString(15, bean.getReserved());
stmtSms.setString(16, bean.getBatchMark());
stmtSms.setString(17, bean.getInstantFlag());
stmtSms.setTimestamp(18, new Timestamp(bean.getExpectDeliverTime().getTime()));
stmtSms.addBatch();
}
stmtSms.executeBatch();
long end = System.currentTimeMillis();
System.out.println(String.format("Total time: %d 毫秒", (end - start)));
logger.info(String.format("Total time: %d 毫秒", (end - start)));
connection.commit();
}
}catch(SQLException e) {
try {
connection.rollback();
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
}finally {
if (stmtSms != null) {
try {
stmtSms.close();
} catch (SQLException e) {
logger.error("preparedStatement.close() fail.", e);
System.out.println("preparedStatement.close() fail."+e);
}
}
if (connection != null) {
try {
connection.setAutoCommit(true);
connection.close();
} catch (SQLException e) {
logger.error("connection.close() fail.", e);
System.out.println("connection.close() fail."+ e);
}
}
}
}
};
session.doWork(work);
}
```
```sql=
CREATE PROCEDURE BatchInsertSms
@SmsSysId varchar(10),
@Content nvarchar(1000),
@OfficeHourSend varchar(1),
@Status varchar(1),
@LastUpdateTime datetime,
@MaintainHost varchar(2),
@MaintainProgramId varchar(20),
@MainUserId varchar(10),
@CreateDate varchar(8),
@CreateProgramId varchar(20),
@CreateTime datetime,
@CreateUserId varchar(10) ,
@DeleteFlag varchar(1),
@Mobile nvarchar(10),
@Reserved varchar(32),
@BatchMark varchar(1) ,
@InstantFlag varchar(1) ,
@ExpectDeliverTime datetime
AS
BEGIN
INSERT INTO Sms (
SmsSeq,
SmsSysId,
Content,
OfficeHourSend,
Status,
LastUpdateTime,
MaintainHost,
MaintainProgramId,
MainUserId,
CreateDate,
CreateProgramId,
CreateTime,
CreateUserId,
DeleteFlag,
Mobile,
Reserved,
BatchMark,
InstantFlag,
ExpectDeliverTime
)
VALUES (
(CONVERT(VARCHAR(8), GETDATE(), 112) + RIGHT('000000' + CAST((NEXT VALUE FOR SmsMail) AS VARCHAR),6)),
@SmsSysId,
@Content,
@OfficeHourSend,
@Status,
@LastUpdateTime,
@MaintainHost,
@MaintainProgramId,
@MainUserId,
@CreateDate,
@CreateProgramId,
@CreateTime,
@CreateUserId,
@DeleteFlag,
@Mobile,
@Reserved,
@BatchMark,
@InstantFlag,
@ExpectDeliverTime
)
END
```
**3.加上多執行緒**
速度有快了一點,但客戶想要效能再好一點,於是又加入了多執行緒。
```java=\
if(StringUtils.isNotBlank(multiMobilePart1)) {
new Thread(new Runnable() {
public void run() {
insertSms(multiMobilePart1.split(","),u,userId,maintainPgId);
}
}).start();
}
```
```java=
public void insertSms(String[] mobileTempArray,UserProfile u,String userId,String maintainPgId) {
List<Sms> smsList = new ArrayList<>();
try {
Date dateTime = new Date();
Date deliverTime = new Date();
if(StringUtils.isNotBlank(showExpectDeliverTime)) {
deliverTime = sdFormat.parse(showExpectDeliverTime);
System.out.println("deliverTime: "+deliverTime);
}
System.out.println("@@@ showExpectDeliverTime:"+showExpectDeliverTime);
for(String str:mobileTempArray)
{
Sms sms = new Sms();
BeanClone.clone(smsMaintainAddVo, sms);
// String seq = smsDao.getSeqNo();
// logger.info("getMaxSeq:{}",seq);
// sms.setSmsSeq(String.valueOf(seq));
sms.setSmsSysId("EIP");
sms.setContent(smsMaintainAddVo.getContent());
sms.setOfficeHourSend(convertHoliday(smsMaintainAddVo.getHolidayList()));
sms.setStatus("0");//修正 20160920
sms.setLastUpdateTime(new Date());
sms.setMaintainHost("C2");
sms.setMaintainProgramId(maintainPgId);
sms.setMainUserId(userId);
sms.setCreateDate(DateUtils.getSysDate());
sms.setCreateProgramId(maintainPgId);
sms.setCreateTime(new Date());
sms.setCreateUserId(userId);
sms.setDeleteFlag("N");
sms.setMobile(str);
sms.setReserved(smsMaintainAddVo.getReserved());
sms.setBatchMark(smsMaintainAddVo.getBatchMark());
if(StringUtils.equals(smsMaintainAddVo.getSendTimeType(), "0")) {
sms.setInstantFlag("Y");
sms.setExpectDeliverTime(dateTime);
}else if(StringUtils.equals(smsMaintainAddVo.getSendTimeType(), "1")) {
sms.setInstantFlag("N");
sms.setExpectDeliverTime(deliverTime);
}
logger.info("insret sms:"+sms);
smsList.add(sms);
}
//用Store Procedur做
smsDao.saveSmsBatchBySP(smsList);
logger.info("##### End time:"+ new Date());
System.out.println("##### End time:"+ new Date());
}catch(Exception e){
logger.error("modifyDate error:", e);
}
}
```
前端頁面
```javascript=
$("#bt_save").click(function(){
if(checkInput()){
var mobile = $("#mobile").val();
var temp = mobile.Split("\n");
var max = temp.length;
$("#maxCount").val(max);
var border = Math.floor(max/10);
jQuery("#actionType").val('A');
var start = 0;
var count = 1;
/*
if(border > 0){
for(var i=0;i<max;i++){
if(i % (border) == (border - 1)){
if(start == (border*9)){
$("#multiMobilePart"+count).val(temp.slice(start,max));
break;
}else{
$("#multiMobilePart"+count).val(temp.slice(start,(i+1)));
start = (i+1);
}
count++;
}
}
}else{
*/
$("#multiMobilePart1").val(temp);
//}
//jQuery("#multiMobile").val(temp);
$("#smsMaintainAddform").attr("action",
"smsMaintain").submit();
$("input").attr("disabled",true);
$(".btnCalendar").attr("disabled",true);
}
});
```
**經過了三重加持,效能大幅躍進**
**insert 5萬筆資料,從原本的5小時進步到了57秒**
###### tags: `Java`