Mybatis批量Insert及水平分表


工作中需要讀取很多大數據量(1000w條)的文件並寫入到mysql表里,涉及到的技術點主要是數據庫的addbatch及水平分表。

數據庫的寫入場景包括:一條一條的寫入和批量寫入,這里數據庫的批量增加使用mybatis框架完成。

水平分表的意思是本來我們要將1000w的數據寫入到一張表里,但為了考慮未來表容量的擴展,及表的性能要求,將本來寫入一張表轉換成寫入多張表。

我在這里沒有使用一些框架(Cobar ClientShardbatismybatis-shards),而是采用Hash分表來實現的。

首先是Mybatis批量insert

說一下核心部分,整個工程參考我的github,運行的main方法在org/xiongmaotailang/mybatis/batchinsert/DbUtil.java中,涉及到的腳本是sql.txt

需要的數據表示例,包括4個字段。

CREATE TABLE `newspvuv` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `pv` bigint(11) DEFAULT NULL,
  `uv` bigint(11) DEFAULT NULL,
  `time` varchar(15) NOT NULL,
  PRIMARY KEY (`id`)
)

接下來看看批處理的mapper.xml文件(工程中org\xiongmaotailang\mybatis\batchinsert\mappers\DataMapper.xml),批量插入方法的定義

<mapper namespace="org.xiongmaotailang.mybatis.batchinsert.mappers.DataMapper">    
    <insert id="insertPVUV">
        insert into  ${table}(pv,uv,time) values(#{pv},#{uv},#{time})
    </insert>
    <insert id="batchInsertPVUV" parameterType="java.util.List">  
        insert into  ${table}(pv,uv,time) values  
        <foreach collection="list" item="item" index="index"  
            separator=",">  
            (#{item.pv,jdbcType=INTEGER},#{item.uv,jdbcType=INTEGER},#{item.time,jdbcType=CHAR}  
            )  
        </foreach>  
    </insert>  
</mapper>

id="insertPVUV"是一條一條的寫入的配置、id="batchInsertPVUV"是批量寫入的配置。

對上邊二個配置的main方法在DbUtil中。

    public static void main(String[] args) throws InterruptedException {
        testInsert();
        testBatchInsert();
    }
    private static void testInsert() {
        long start=System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            addPvUv(12,i,"123");
        }
        System.out.println("insert 1000 row :"+(System.currentTimeMillis()-start)+"ms");
    }
    private static void testBatchInsert() {
        long start=System.currentTimeMillis();
        List<NewsPvUv> list=new ArrayList<NewsPvUv>();
        for (int i = 0; i < 1000; i++) {
            NewsPvUv n = new NewsPvUv(12, i, "123");
            list.add(n);
        }
        addPvUv(list);
        System.out.println("batch insert 1000 row :"+(System.currentTimeMillis()-start)+"ms");
    }

上邊對比了對1000條數據的二種寫入方式,在我筆記本的測試結果如下圖,可見批量寫入的性能高效。

image

 

水平分表

整個工程參考我的github

方法一:使用MD5哈希

// 使用md5做hash水平分表
    public static String getTable(String mark, String prefix, int num) {
        if (num == 0)
            return prefix;
        String temp = md5(mark).substring(0, 2);
        int hexdec = Integer.parseInt(temp, 16);// 16轉成10進制
        int index = hexdec % num + 1;
        return prefix + "_" + index;
    }

    // 提供和php->md5一樣的功能
    private static String md5(String txt) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(txt.getBytes("GBK")); // 問題主要出在這里,Java的字符串是unicode編碼,不受源碼文件的編碼影響;而PHP的編碼是和源碼文件的編碼一致,受源碼編碼影響。
            StringBuffer buf = new StringBuffer();
            for (byte b : md.digest()) {
                buf.append(String.format("%02x", b & 0xff));
            }
            return buf.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

方法二:使用移位

// 使用移位分表
    /*
     * 如果我們預估我們系統的用戶是100億,單張表的最優數據量是100萬,
     * 那么我們就需要將UID移動20來確保每個表是100萬的數據,保留用戶表(user_xxxx)四位來擴展1萬張表
     */
    public static String getTable1(int uid, String prefix) {
        
        return prefix + "_" + String.format("%04d",(uid>>20));
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM