日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mybatis mysql 分表_Mybatis的分表实战

發布時間:2024/9/19 数据库 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mybatis mysql 分表_Mybatis的分表实战 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言:

以前寫代碼, 關于mysql的分庫分表已被中間件服務所支持, 業務代碼涉及的sql已規避了這塊. 它對擴展友好, 你也不知道到底他分為多少庫, 多少表, 一切都是透明的.

不過對于小的團隊/工作室而言, 可能就沒有那么強大的分布式中間件的基礎設施支持了, 而當數據庫上去的時候, 分庫分表就需要客戶端client這邊去支持維護了. 如何優雅地使用mybatis支持分表, 這就是本文的主題.

mybatis插件機制:

mybatis支持插件(plugin), 講得通俗一點就是攔截器(interceptor). 它支持ParameterHandler/StatementHandler/Executor/ResultSetHandler這四個級別進行攔截.

總體概況為:

攔截參數的處理(ParameterHandler)

攔截Sql語法構建的處理(StatementHandler)

攔截執行器的方法(Executor)

攔截結果集的處理(ResultSetHandler)

比如sql rewrite, 它屬于StatementHandler的階段. 以分表實踐為例, 它可以簡單理解為把table名稱替換為分表table名稱的過程.

模擬實戰:

讓我們模擬實戰一回, 假定我們有個需求, 就是把重要的業務日志數據, 導入到表tb_record中.

CREATE TABLE `tb_record` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`logs` varchar(128) NOT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

但是現在隨著業務數據暴增, 單表支撐不了這么多數據. 因此決定把tb_record做水平切分, 按天來做切分tb_record_{yyyyMMdd}, 比如2018/07/26這天的數據, 就導入到表tb_record_20180726中.

之前的mapper接口類如下:

public interface RecordMapper {

@Insert("INSERT INTO tb_record(logs) VALUES(#{logs})")

int addRecord(@Param("logs") String logs);

}

在不改變代碼的前提下, 如何支持分表的無感知實現.

代碼編寫:

由于mybatis的攔截器是全局的, 因此這邊引入特定的注解用于區分目標/非目標對象(數據庫表).

定義分表策略接口和具體的實現類:

// 分表的策略類

public interface ITableShardStrategy {

String tableShard(String tableName);

}

// 按天切分的分表策略類

public class DateTableShardStrategy implements ITableShardStrategy {

private static final String DATE_PATTERN = "yyyyMMdd";

@Override

public String tableShard(String tableName) {

SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN);

return tableName + "_" + sdf.format(new Date());

}

}

定義注解:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface TableShard {

// 要替換的表名

String tableName();

// 對應的分表策略類

Class extends ITableShardStrategy> shardStrategy();

}

編寫具體的mybatis攔截器實現:

@Intercepts({

@Signature(

type = StatementHandler.class,

method = "prepare",

args = { Connection.class, Integer.class }

)

})

public class TableShardInterceptor implements Interceptor {

private static final ReflectorFactory defaultReflectorFactory = new DefaultReflectorFactory();

@Override

public Object intercept(Invocation invocation) throws Throwable {

StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

MetaObject metaObject = MetaObject.forObject(statementHandler,

SystemMetaObject.DEFAULT_OBJECT_FACTORY,

SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,

defaultReflectorFactory

);

MappedStatement mappedStatement = (MappedStatement)

metaObject.getValue("delegate.mappedStatement");

String id = mappedStatement.getId();

id = id.substring(0, id.lastIndexOf('.'));

Class clazz = Class.forName(id);

// 獲取TableShard注解

TableShard tableShard = (TableShard)clazz.getAnnotation(TableShard.class);

if ( tableShard != null ) {

String tableName = tableShard.tableName();

Class extends ITableShardStrategy> strategyClazz = tableShard.shardStrategy();

ITableShardStrategy strategy = strategyClazz.newInstance();

String newTableName = strategy.tableShard(tableName);

// 獲取源sql

String sql = (String)metaObject.getValue("delegate.boundSql.sql");

// 用新sql代替舊sql, 完成所謂的sql rewrite

metaObject.setValue("delegate.boundSql.sql", sql.replaceAll(tableName, newTableName));

}

// 傳遞給下一個攔截器處理

return invocation.proceed();

}

@Override

public Object plugin(Object target) {

// 當目標類是StatementHandler類型時,才包裝目標類,否者直接返回目標本身, 減少目標被代理的次數

if (target instanceof StatementHandler) {

return Plugin.wrap(target, this);

} else {

return target;

}

}

@Override

public void setProperties(Properties properties) {

}

}

注: 不同mybatis的版本, 具體的api略有出入, 當前mybatis版本為(3.4.6).

配置plugin標簽, 注意要在mybatis-config.xml(mybatis全局屬性配置文件)中進行配置

測試:

對原來的RecordMapper添加@TableShard注解:

@TableShard(tableName = "tb_record", shardStrategy = DateTableShardStrategy.class)

public interface RecordMapper {

@Insert("INSERT INTO tb_record(logs) VALUES(#{logs})")

int addRecord(@Param("logs") String logs);

}

編寫簡單的測試代碼:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration({"classpath:application-context.xml"})

public class RecordMapperTest {

@Resource

private RecordMapper recordMapper;

@Test

public void testAddRecord() {

String logs = "hello lilei";

recordMapper.addRecord(logs);

}

}

查看數據庫進行數據驗證:

后記:

總的來說, mybatis的攔截器給開發者很大的自由度, 像這邊的分表實踐是很好的例子. 但分表的策略有很多, 很多都是基于特定的維度進行散列, 總覺得在攔截器中實現, 多少有些侵入性, 要做到無感透明, 其實還是挺難的.

總結

以上是生活随笔為你收集整理的mybatis mysql 分表_Mybatis的分表实战的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。