序文
データベーススキーマとデータベースの最適化について話してデータシステムの成長量、で、我々は必然的に、多くの場合、サブライブラリーのサブテーブルにこの言葉を聞くことができます。
もちろん、このような垂直分割、水平分割ように、多くのサブライブラリーサブテーブルの方法がある。多くの中間生成物、例えばMyCat、ShardingJDBCがあります。
適した解決方法のビジネスシーン選択し、データを分割するためのプロジェクト関与作業を完了するために私たちを助けることができる身近なオープンソースのフレームワークを選択します。
この記事では、これらの方法論とオープンソースのフレームワークに関する詳細な議論を行うことを意図していない、私は別のシナリオを議論したいと思います:
システムは、我々はいくつかの比較的複雑なミドルウェア製品の導入に値するかどうか、テーブル、ただ1つまたは少数の小さな数を分割するために多くの必要がない場合には、我々は彼らの原則を理解していない場合は特に、管理するための自信を持っています彼ら?
お使いのシステムは、テーブルの数が少ない分割する必要があり、オープンソースコンポーネントを研究する専用のリソースがない持っている場合はこれに基づき、我々は、簡単なプラグインのサブライブラリーサブテーブルを達成するために所有することができます;もちろん、あなたのシステムはより複雑である場合には、大規模なビジネスのボリューム、またはこの問題はより安全に対処するための研究チームからのオープンソースコンポーネントやアセンブリの使用。
まず、原則
サブライブラリーサブテーブルシンプルかつ簡単には非常に複雑な複雑だと言っていることをこれらの事...
単にそのコアがより明確に処理するので。SQL文を解析し、事前に設定に基づいて、書き換えることで、または行くために、実際のデータベーステーブルにルーティングされました。
複雑それらを正しく解釈するためにどのようなページング、重複排除、並べ替え、グループ化、重合、およびクエリに関連する他の操作など複雑かつ柔軟なSQL文、、、、それ。
したがって、でもShardingJDBC
公式サイトにも対応していないサポートアイテムとアイテムは明らかです。
第二に、注釈型の構成
複雑な設定ファイルに関しては、我々は次のように定義され、より軽量な注釈型の構成を採用します:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Sharding {
String tableName(); //逻辑表名
String field(); //分片键
String mode(); //算法模式
int length() default 0; //分表数量
}
复制代码
だから、どこにそれを使用するには?たとえば、私たちはユーザーテーブルポイントテーブルを必要とし、それがユーザーのエンティティオブジェクトにマークされています。
@Data
@Sharding(tableName = "user",field = "id",mode = "hash",length = 16)
public class User {
private Long id;
private String name;
private String address;
private String tel;
private String email;
}
复制代码
これは私がその位置を計算するハッシュアルゴリズムを使用して、16回のユーザー表、ユーザーIDの合計を持って、説明しています。
もちろん、我々はより多くのハッシュアルゴリズムよりも、あなたはまた、日付範囲を定義することができています。
@Data
@Sharding(tableName = "car",field = "creatTime",mode = "range")
public class Car {
private long id;
private String number;
private String brand;
private String creatTime;
private long userId;
}
复制代码
第三に、断片化アルゴリズム
ここで、私は2つの断片化の方法です実現しましたHashAlgorithm和RangeAlgorithm
。
図1に示すように、スライスの範囲
お使いのシステムは、ホットとコールドデータの分離を使用している場合、我々は別のテーブルに日付データの異なるヶ月に従うことができます。
このような車両の作成時間などで2019-12-10 15:30:00
データの断片が割り当てされる、car_201912
移動するには、このテーブル。
私たちは、その後、論理テーブル名を追加し、年間のパートタイムによってインターセプト。
public class RangeAlgorithm implements Algorithm {
@Override
public String doSharding(String tableName, Object value,int length) {
if (value!=null){
try{
DateUtil.parseDateTime(value.toString());
String replace = value.toString().substring(0, 7).replace("-", "");
String newName = tableName+"_"+replace;
return newName;
}catch (DateException ex){
logger.error("时间格式不符合要求!传入参数:{},正确格式:{}",value.toString(),"yyyy-MM-dd HH:mm:ss");
return tableName;
}
}
return tableName;
}
}
复制代码
2、ハッシュフラグメント
スライス内のハッシュアルゴリズムは、まず、テーブルの数が2のべき乗でないかを決定することができます。ない場合は、それがある場合、あなたは下付き動作モードによってビットを取得し、インデックス算術方法を介して取得します。もちろん、これはHashMapのはああ学んだソースです。
public class HashAlgorithm implements Algorithm {
@Override
public String doSharding(String tableName, Object value,int length) {
if (this.isEmpty(value)){
return tableName;
}else{
int h;
int hash = (h = value.hashCode()) ^ (h >>> 16);
int index;
if (is2Power(length)){
index = (length - 1) & hash;
}else {
index = Math.floorMod(hash, length);
}
return tableName+"_"+index;
}
}
}
复制代码
第四に、インターセプター
設定と断片化アルゴリズム持って、次はメインイベントです。ここでは、使用Mybatis拦截器
便利になるためにそれらを。
私たちの多年生CRUDは、ビジネスはSQLがその範囲を逃れる確かに知っています。その中で、我々の削除機能は、一般的なビジネスに墓石されているので、基本的にはDELETE操作はありません。
比較では、新規および更新SQLはSQLクエリは、より柔軟かつ複雑になる傾向があり、比較的簡単かつ固定フォーマットです。だから、ここで著者は、2つのインターセプタを定義します。
SQL構文パーサとスライスアルゴリズム処理:しかし、迎撃の導入前に、我々は2つの他の事を知っている理由を持っています。
1、JSqlParser
JSqlParser
SQL文を解析するための責任、およびJavaクラス階層に変換します。我々はそれを理解するために簡単な例を見ることができます。
public static void main(String[] args) throws JSQLParserException {
String insertSql = "insert into user (id,name,age) value(1001,'范闲',20)";
Statement parse = CCJSqlParserUtil.parse(insertSql);
Insert insert = (Insert) parse;
String tableName = insert.getTable().getName();
List<Column> columns = insert.getColumns();
ItemsList itemsList = insert.getItemsList();
System.out.println("表名:"+tableName+" 列名:"+columns+" 属性:"+itemsList);
}
输出: 表名:user 列名:[id, name, age] 属性:(1001, '范闲', 20)
复制代码
私たちは、それを見ることができますJSqlParser
SQLの構文情報を解析することができます。したがって、我々はまた、SQLステートメントを変更するの目的を達成するように、オブジェクトの内容を変更することができます。
2、アルゴリズムプロセッサ
当社のアルゴリズムは、より具体的な、1つは、実行中の期間であるかを決定するために呼ばれるべき、断片化されています。そこで、我々は、最初に登録された地図のアルゴリズムを使用して、スライスモードに応じてそれを呼び出します。これは、戦略パターンに反映されます。
@Component
public class AlgorithmHandler {
private Map<String, Algorithm> algorithm = new HashMap<>();
@PostConstruct
public void init(){
algorithm.put("range",new RangeAlgorithm());
algorithm.put("hash",new HashAlgorithm());
}
public String handler(String mode,String name,Object value,int length){
return algorithm.get(mode).doSharding(name, value,length);
}
}
复制代码
3、インターセプター
私たちは知っている、MyBatisのマッピングプロセスがインターセプト呼び出しにポイントのステートメントの実行を持っていることができます。
あなたはその原則に慣れていない場合は、記事の著者を見てみることができます。MyBatisの原則インターセプタを。
次のように全体的に、そのプロセスは次のとおりです。
Mybatis
実行されるSQLをインターセプトします。JSqlParser
SQLを解析する、などの論理表をフェッチします。- テーブルの本当の名前を取得するためのアルゴリズムをスライス呼び出します。
- 変更SQL、および修正
BoundSql
。 Mybatis
目的を達成するために、SQLが改訂実行します。
例えば、のためにinsert
次のようにそのコアコードの文です:
String sql = boundSql.getSql();
Statement statement = CCJSqlParserUtil.parse(sql);
Insert insert = (Insert) statement;
Table table = insert.getTable();
String newName = this.handler.handler(mode, table.getName(), value,length);
table.setName(newName);
ReflectionUtil.setField(boundSql,"sql",insert.toString());
复制代码
V.のお問い合わせやページネーション
実際には、新しい、比較的簡単な修正、より複雑なクエリ文。
しかし、我々は、クエリのすべてを満たすためにそれを接続していないが、実際のビジネスシナリオに基づいて修正拡張することができます。
しかし、ページング機能は、基本的には逃げられないです。TakeはPageHelper
、例えば、その原理はであるMybatis
達成するインターセプタ。それは一緒に私たちのテーブルとプラグを指している場合は、競合を作成することができます。
ポイントテーブルのプラグので、著者はまた、改ページを統合して、基本的にはPageHelper
同じですが、それを直接使用していませんでした。また、クエリによって、クエリはシャードキーと条件かどうか、また、非常に重要です。
1、クエリ
アルゴリズムの範囲では、ビジネスで私たちは、ここ数カ月の間にのみ、月または特定のクエリデータを必要とすることができ、ハッシュアルゴリズムでは、我々は主キーを持つたびに必要になります。
しかし、第2の条件は、多くの場合、ビジネス面では、プライマリキーを持つあらゆるニーズを満たすことができない、確立することはできません。
このような状況に鑑み、我々ができる唯一の反復すべてのテーブル、データの条件を満たし、その後、要約を返すクエリ。
for (int i=0;i<sharding.length();i++){
Statement parse = CCJSqlParserUtil.parse(boundSql.getSql());
sqlParser.processSelect(parse,i);
cacheKey.update(new Object());
List<E> query = ExecutorUtil.query(parse.toString());
result.addAll(query);
}
复制代码
この方法の欠点は、見かけ上のパフォーマンスの低下になります。もう一つの一般的な方法は、あなたがシャードキーに基づいてクエリ、その後、最初のクエリの検索基準に基づいてシャードキーフィールドの値を見つけ、シャードキーとのマッピング関係を照会することができるということです。
2、ページ
上述したように、プラグイン統合ページネーション、プロセスとは達成PageHelper
同じ、しかし、競合、直接ではなくを考えます。
private <E> List<E> queryPage(){
Long count = this.getCount();
page.setTotal(count.intValue());
page.setCountPage();
String limitSql = getLimitSql(page,sql);
List<E> query = ExecutorUtil.query();
page.addAll(query);
return page;
}
复制代码
他の6つ
実際に、私はこの記事のタイトルを考えるとき、比較は本当に悩まさ。ので分库分表
、業界は言葉ですが、この記事では、サブライブラリーのプラグインを必要としないが、唯一の唯一のサブテーブルの操作が、この記事の焦点はアイデアです、最終的に呼ば分库分表
忠実な、してください流域は、私を許して、パーティーを見出し私を呼び出すことはありません〜
限られたスペースのために、テキスト内のコードのわずかな量は、流域の友人興味を持っている場合は、行くことができhttps://github.com/taoxun/sharding
、完全なデモのために。
コード、テストケースの著者であり、構築されたフォームのSQLの数が含まれているが、あなたはテーブルの完了後にプロジェクトの直接実行を作成することができます。