sql >> データベース >  >> RDS >> PostgreSQL

Spring、Hibernate、C3P0を使用してマルチテナントWebアプリで接続プールを管理する

    接続ポーリングに影響を与える3つの異なる戦略から選択できます。いずれの場合も、MultiTenantConnectionProviderの実装を提供する必要があります 。もちろん、選択した戦略は実装に影響を与えます。

    MultiTenantConnectionProvider.getAnyConnection()に関する一般的な注意

    getAnyConnection() メタデータを収集してSessionFactoryをセットアップするためにHibernateが必要とします。通常、マルチテナントアーキテクチャでは、どのテナントも使用しない特別な/マスターデータベース(またはスキーマ)があります。これは一種のテンプレートデータベース(またはスキーマ)です。このメソッドがこのデータベース(またはスキーマ)への接続を返す場合は問題ありません。

    戦略1:各テナントには独自のデータベースがあります。 (したがって、それは独自の接続プールです)

    この場合、各テナントにはC3POによって管理される独自の接続プールがあり、MultiTenantConnectionProviderの実装を提供できます。 AbstractMultiTenantConnectionProviderに基づく

    すべてのテナントには独自のC3P0ConnectionProviderがあります 、したがって、selectConnectionProvider(tenantIdentifier)で行う必要があるのはすべてです 正しいものを返すことです。マップを保持してそれらをキャッシュすることができ、C3POConnectionProviderを次のようなもので遅延初期化することができます:

    private ConnectionProvider lazyInit(String tenantIdentifier){
        C3P0ConnectionProvider connectionProvider = new C3P0ConnectionProvider();
        connectionProvider.configure(getC3POProperties(tenantIdentifier));
        return connectionProvider;
    }
    
    private Map getC3POProperties(String tenantIdentifier){
        // here you have to get the default hibernate and c3po config properties 
        // from a file or from Spring application context (there are good chances
        // that those default  properties point to the special/master database) 
        // and alter them so that the datasource point to the tenant database
        // i.e. : change the property hibernate.connection.url 
        // (and any other tenant specific property in your architecture like :
        //     hibernate.connection.username=tenantIdentifier
        //     hibernate.connection.password=...
        //     ...) 
    }
    

    戦略2:各テナントには独自のスキーマがあり、単一のデータベースに独自の接続プールがあります

    このケースは、ConnectionProviderに関する最初の戦略と非常によく似ています。 AbstractMultiTenantConnectionProviderも使用できるため実装 MultiTenantConnectionProviderを実装するための基本クラスとして

    実装は、c3po構成のデータベースの代わりにスキーマを変更する必要があることを除いて、戦略1の推奨実装と非常に似ています

    戦略3:各テナントは単一のデータベースに独自のスキーマを持っていますが、共有接続プールを使用しています

    すべてのテナントが同じ接続プロバイダーを使用するため、このケースは少し異なります(したがって、接続プールは共有されます)。場合:接続プロバイダーは、接続を使用する前に、使用するスキーマを設定する必要があります。つまり、MultiTenantConnectionProvider.getConnection(String tenantIdentifier)を実装する必要があります (つまり、AbstractMultiTenantConnectionProviderによって提供されるデフォルトの実装 動作しません)。

    postgresqlを使用すると、次のように実行できます:

     SET search_path to <schema_name_for_tenant>;
    

    またはエイリアスを使用する

     SET schema <schema_name_for_tenant>;
    

    これがgetConnection(tenant_identifier);です。 次のようになります:

    @Override
    public Connection getConnection(String tenantIdentifier) throws SQLException {
        final Connection connection = getAnyConnection();
        try {
            connection.createStatement().execute( "SET search_path TO " + tenanantIdentifier );
        }
        catch ( SQLException e ) {
            throw new HibernateException(
                    "Could not alter JDBC connection to specified schema [" +
                            tenantIdentifier + "]",
                    e
            );
        }
        return connection;
    }
    

    役立つリファレンスはこちら(公式ドキュメント)

    その他の便利なリンクC3POConnectionProvider.java

    実装で戦略1と戦略2を組み合わせることができます。現在のテナントの正しい接続プロパティ/接続URLを見つける方法が必要です。

    編集

    戦略2と戦略3のどちらを選択するかは、アプリのトラフィックとテナントの数に依存すると思います。個別の接続プールを使用すると、1つのテナントで使用できる接続の量が大幅に少なくなるため、正当な理由で1つのテナントが突然多くの接続を必要とする場合、この特定のテナントで見られるパフォーマンスは大幅に低下します(他のテナントは低下しません)。影響を受ける)。

    一方、戦略3では、何らかの正当な理由で1つのテナントが突然多くの接続を必要とする場合、すべてのテナントで見られるパフォーマンスが低下します。

    一般に、戦略2はより柔軟で安全だと思います。すべてのテナントは、指定された量を超える接続を消費することはできません(必要に応じて、この量をテナントごとに構成できます)



    1. MySQL Galeraクラスターレプリケーションを使用した地理分散クラスターの作成:パート2

    2. MariaDB JSON_LOOSE()の説明

    3. ODBC呼び出しがストアドプロシージャで失敗しました-クエリをパススルーします

    4. ストアドプロシージャへのパラメータとしてのデータテーブルの受け渡し