從前有個大師,率領一群徒弟,為客戶做了一個軟件系統。某天,客戶提出了一個新的需求,向系統中的所有用戶發送系統消息。由於當時系統剛上線不久,系統中的用戶也就幾十個。大師為了考驗自己的徒弟,便將該需求分配給他的徒弟,要求每個人都做一套方案出來,於是便有了下面的故事。
徒弟們接收到該項任務後,每個人都想到了先建一張系統消息表,每次發送系統消息時,將數據保存在詞表中,用戶就能從該表中讀取他個人的系統消息。用戶信息表的模型如下:
基於上面的數據庫模型,徒弟們分別作了不同的實現方案,如下:
實現方案一:
小A是個急性子,領到任務後。立即開始了他的編程思路:將系統中的所有用戶都取出來,然後遍歷所有的用戶,每次迭代時插入一條系統消息。偽代碼如下:
List<FavUser> userList = favUserService.getAllUser(); for (FavUser favUser : userList){ SysMessage sysMessage = new SysMessage(); ... sysMessage.setReceiveUserId(favUser.getUserId()); sysMessageService.addSysMessage(sysMessage); }
由於系統中的用戶較少,小A幾遍測試,發現系統中運行良好,便將該方案提交了上去。
實現方案二:
小B接到任務後,想到應該先把系統中所有的用戶Ids取出來,然後遍歷這些ids,每次迭代時都插入一條系統消息。基於此,小B的偽代碼如下:
List<Integer> userIdsList = favUserService.getAllUserIds(); for (Integer userId : userIdsList ){ SysMessage sysMessage = new SysMessage(); ... sysMessage.setReceiveUserId(userId); sysMessageService.addSysMessage(sysMessage); }
由於系統中的用戶較少,小B幾遍測試,發現系統中運行良好,也將該方案提交了上去。
實現方案三:
小C接到任務後,考慮到每次插入的系統消息,除了用戶id不同外,其余的數據項都相同,便想到了批量插入數據。由於MySQL數據庫支持批量插入數據,小C設計出了執行的SQL語句與偽代碼:
執行的SQL語句如下:
< insert id= "addBatchSysMessage" parameterType= "com.favccxx.favsoft.SysMessage" > insert into sys_message (MESSAGE_TITLE, MESSAGE_CONTENT, MESSAGE_STATUS, RECEIVE_USER_ID, RECEIVE_TIME, CREATE_TIME ) values <foreach collection= "list" item= "item" index = "index" separator= "," > ( #{item.messageTitle,jdbcType= VARCHAR },#{item.messageContent,jdbcType= VARCHAR }, #{item.messageStatus,jdbcType= CHAR }, #{item.receiveUserId,jdbcType= INTEGER },#{item.receiveTime,jdbcType= TIMESTAMP }, #{item.createTime,jdbcType= TIMESTAMP } ) </foreach> </ insert >
偽代碼如下:
List<FavUser> userList = favUserService.getAllUser(); List<SysMessage> dataList = new ArrayList<SysMessage>(); for (FavUser favUser : userList){ sysMessage.setReceiveUserId(favUser.getUserId()); dataList.add(sysMessage); } List<SysMessage> subList = dataList.subList( , ); sysMessageService.addBatchSysMessage(subList);
小C向系統中添加了幾千個模擬用戶,測試系統運行良好。但發現將系統中的用戶增加至幾萬時,發送系統消息速度明顯變慢。於是,小C采用了分組的方式進行插入,每10,000條插入一次,系統運行良好。
實現方案四
小D接到任務後,考慮的也是批量插入數據,但與小C不同的是,他想通過執行一次SQL完成批量插入數據。即先將待發送的消息存入數據庫中,然後通過MySQL查詢並同時將數據插入系統消息。小D的MySQL設計如下:
< insert id= "addAllSysMessage" parameterType= "com.favccxx.favsoft.SysMessage" > insert into sys_message ( MESSAGE_TITLE, MESSAGE_CONTENT, MESSAGE_STATUS, RECEIVE_USER_ID, RECEIVE_TIME, CREATE_USER_ID, CREATE_TIME ) select a.MESSAGE_TITLE as MESSAGE_TITLE, a.MESSAGE_CONTENT as MESSAGE_CONTENT, as MESSAGE_STATUS, b.user_id AS RECEIVE_USER_ID, now() as RECEIVE_TIME, now() as CREATE_TIME from sys_message_send_info a, ( select user_id FROM auth_user ) b where sendInfoId=#{sendInfoId} </ insert > 1 int insertCount = sysMessageService.addAllSysMessage(sendInfoId);
小D向系統中插入了10萬個模擬用戶,經測試,系統運行良好。以下是向113508個用戶發送消息的花費時間。