Réaliser une segmentation horizontale des données basée sur JSPGenFire: sous-table de base de données interne, sous-table de sous-base de données

Contexte

La base de données relationnelle elle-même est relativement facile à devenir un goulot d'étranglement du système, et la capacité de stockage, le nombre de connexions et la capacité de traitement d'une seule machine sont limités. Lorsque le volume de données d'une seule table atteint 1000 W ou 100 Go, en raison du grand nombre de dimensions de requête, même si la base de données est ajoutée et que l'index est optimisé, les performances continueront de chuter gravement lors de nombreuses opérations. À ce stade, il est nécessaire d'envisager la segmentation. Le but de la segmentation est de réduire la charge de la base de données et de raccourcir le temps de requête.
Selon son type de segmentation, il peut être divisé en deux manières: la segmentation verticale (longitudinale) et la segmentation horizontale (horizontale).

1. Division verticale

Sous-base de données verticale: selon le couplage d'entreprise, différentes tables à faible corrélation sont stockées dans différentes bases de données. L'approche est similaire à la division d'un grand système en plusieurs petits systèmes, qui sont divisés indépendamment par classification d'entreprise. Semblable à l'approche de «gouvernance des microservices», chaque microservice utilise une base de données distincte.
Fractionnement vertical de table: il est basé sur les "colonnes" de la base de données. Si une table comporte de nombreux champs, vous pouvez créer une table étendue et diviser les champs qui ne sont pas fréquemment utilisés ou avec des longueurs de champ plus grandes dans la table étendue. Dans le cas de nombreux champs, la "grande table divise la petite table" est plus pratique pour le développement et la maintenance, et elle peut également éviter une surcharge de performances supplémentaire.
Lorsqu'une application est difficile à segmenter verticalement à granularité fine, ou que le nombre de lignes de données après la segmentation est énorme, il existe un goulot d'étranglement des performances de lecture-écriture et de stockage dans une seule base de données, une segmentation horizontale est nécessaire.

2. Segmentation horizontale

Sous-table dans la base de données: résout uniquement le problème du volume de données excessif dans une seule table, mais ne distribue pas les tables aux bibliothèques de différentes machines, il n'est donc pas très utile de réduire la pression sur la base de données MySQL. Tout le monde est toujours en concurrence pour le même physique Le processeur, la mémoire et les E / S réseau de la machine sont mieux résolus par une sous-base de données et une table.
Sous-base de données et sous-table: selon la relation logique interne des données, la même table est distribuée à plusieurs bases de données ou à plusieurs tables dans des conditions différentes. Chaque table ne contient qu'une partie des données, de sorte que la quantité de données dans une seule table diminue. Effet distribué.
Les avantages de la segmentation horizontale:
il n'y a pas de goulot d'étranglement des performances causé par un volume de données excessif et une concurrence élevée dans une seule base de données, la stabilité du système et la capacité de charge
sont améliorées, et le volume de données de la table est petit, et l'efficacité d'exécution SQL unique est élevée, ce qui réduit naturellement la charge sur le
côté application du processeur Petite transformation, pas besoin de fractionner les modules métier
. Inconvénients de la segmentation horizontale: la
cohérence des transactions entre partitions est difficile à garantir
les performances des requêtes d'association de jointure entre bases de données sont médiocres,
l'expansion des données est difficile et la maintenance est importante

Concentrez-vous sur la méthode d'implémentation JSPGenFire de la segmentation horizontale et démontrez les sous-tables de la bibliothèque et les sous-tables de la bibliothèque.

Introduction à JSPGenFire

JSPGenFire se positionne comme un cadre d'exploitation de base de données Java léger, fournissant des services sous la forme de packages jar au niveau de la couche JDBC de Java sans déploiement ni dépendances supplémentaires. La segmentation des données consiste à stocker les données dans plusieurs bases de données, de manière à réduire la quantité de données dans une seule base de données et à atténuer les problèmes de performances d'une seule base de données en augmentant le nombre d'hôtes, de manière à atteindre l'objectif d'améliorer les performances de fonctionnement de la base de données.

Concept clé

1. Tableau logique

Terme général désignant les tables ayant la même logique et la même structure de données qu'une base de données (table) fractionnée horizontalement. Exemple: Les données de commande sont divisées en 10 tables selon la mantisse de la clé primaire, qui vont de jspgen_login_0 à jspgen_login_9, et leur table logique est nommée jspgen_login.

2. Table physique

Une table physique qui existe réellement dans une base de données fragmentée. Autrement dit, jspgen_login_0 à jspgen_login_9 dans l'exemple précédent.

3. Nœud de données

La plus petite unité de fragmentation des données. Il se compose du nom de la source de données et de la table de données, par exemple: ds_0. Jspgen_login_0.

Stratégie de sharding

Après la division horizontale, la même table apparaîtra dans plusieurs bases de données / tables, et le contenu de chaque base de données / table est différent. Plusieurs règles typiques de fragmentation des données sont:

1. Selon la plage numérique

Divisez en fonction de l'intervalle de temps ou de l'intervalle d'identification. Par exemple: distribuez les données de différents mois ou même jours à différentes bibliothèques par date; divisez les enregistrements avec l'ID 1 ~ 9999 dans la première bibliothèque, divisez les enregistrements avec 10000 ~ 20000 dans la deuxième bibliothèque, et ainsi de suite.

2. Prenez le module basé sur la valeur

Généralement, une méthode de fractionnement modulaire est adoptée. Par exemple, la table de connexion est divisée en 2 bibliothèques en fonction du champ id, le reste de 0 est placé dans la première bibliothèque, le reste de 1 est placé dans la deuxième bibliothèque, etc. .

Application dans le projet

1. Structure des données

-- ----------------------------
-- Table structure for jspgen_login
-- ----------------------------
DROP TABLE IF EXISTS `jspgen_login`;
CREATE TABLE `jspgen_login` (
  `id` varchar(32) NOT NULL,
  `uid` varchar(32) default NULL,
  `name` varchar(50) default NULL,
  `score` int(20) default '0',
  `time` bigint(13) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for jspgen_login_0
-- ----------------------------
DROP TABLE IF EXISTS `jspgen_login_0`;
CREATE TABLE `jspgen_login_0` (
  `id` varchar(32) NOT NULL,
  `uid` varchar(32) default NULL,
  `name` varchar(50) default NULL,
  `score` int(20) default '0',
  `time` bigint(13) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for jspgen_login_1
-- ----------------------------
DROP TABLE IF EXISTS `jspgen_login_1`;
CREATE TABLE `jspgen_login_1` (
  `id` varchar(32) NOT NULL,
  `uid` varchar(32) default NULL,
  `name` varchar(50) default NULL,
  `score` int(20) default '0',
  `time` bigint(13) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for jspgen_user
-- ----------------------------
DROP TABLE IF EXISTS `jspgen_user`;
CREATE TABLE `jspgen_user` (
  `id` varchar(32) NOT NULL,
  `name` varchar(50) default NULL,
  `time` bigint(13) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2. Fichier de configuration

Réaliser une segmentation horizontale des données basée sur JSPGenFire: sous-table de base de données interne, sous-table de sous-base de données

3. Mise en œuvre de la stratégie

package fire.sub.provider;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fire.sub.SubProvider;
/**
 * 数据库分库实现
 * @author JSPGen
 * @copyright (c) JSPGen.com
 * @created 2020年03月
 * @email [email protected]
 * @address www.jspgen.com
 */
public class DbProvider implements SubProvider {
    // 日志工具
    private static Logger logger = LoggerFactory.getLogger(DbProvider.class);
    /**
     * 获取名称
     * @return String
     */
    public String getSuffix(Map<String, Object> paramMap){
        String name = "";
        try {
            name = name + (Integer.parseInt((String) paramMap.get("id"))%2);
            logger.info("db_name:" + name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return name;
    }
}
package fire.sub.provider;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fire.sub.SubProvider;
/**
 * 数据库分表实现
* @author JSPGen
 * @copyright (c) JSPGen.com
 * @created 2020年03月
 * @email [email protected]
 * @address www.jspgen.com
 */
public class TableProvider implements SubProvider {
    // 日志工具
    private static Logger logger = LoggerFactory.getLogger(TableProvider.class);
    /**
     * 获取名称
     * 
     * @return String
     */
    public String getSuffix(Map<String, Object> paramMap){
        String name = "_";
        try {
            /*
            // 一天一张表
            Long time = (Long) paramMap.get("time");
            if(time == null) time = Dates.getTimeMillis();
            String year = Dates.getDateTime(time, "yyyy");
            String mon  = Dates.getDateTime(time, "MM");
            String day  = Dates.getDateTime(time, "dd");
            name = name + year + mon + day;
            */
            name = name + (Integer.parseInt((String) paramMap.get("id"))%2);
            logger.info("table_name:" + name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return name;
    }
}

4. Test des données

package jspgen.action;
import java.util.HashMap;
import java.util.Map;
import fire.FireAccess;
import fire.FireBuild;
import grapes.Dates;
import grapes.Grapes;
/**
 * Action类:分库分表测试
 * @author JSPGen
 * @copyright (c) JSPGen.com
 * @created 2020年03月
 * @email [email protected]
 * @address www.jspgen.com
 */
public class DemoSubAction extends Action {
    /**
     * 默认方法
     */
    @Override
    public String execute() {
        return text("分库分表测试");
    }

    // 分库测试
    public String user() {
        long start = Dates.getTimeMillis(); // 开始时间
        FireAccess fa = FireBuild.getInstance().getAccess("JSPGen");
        String sql = "insert into `"+FireAccess.getTable("user")+"` (`id`,`name`,`time`) values (:id, :name, :time)";
        Map<String, Object> paramMap = null;
        for (int i =1 ; i< 10 ; i++){
            paramMap = new HashMap<String, Object>();
            paramMap.put("id", i+"");
            paramMap.put("name", 100+i);
            paramMap.put("time", Dates.getTimeMillis());
            fa.createSQL(sql).setParameter(paramMap).executeUpdate();
        }
        fa.close();
        long end = Dates.getTimeMillis();   // 结束时间 
        long count = end-start;
        return text("总共用了:" + Dates.getUnitTime(count, true) + " ("+count+"毫秒)");
    }
    // 分库查询测试
    public String userfind() {
        FireAccess fa = FireBuild.getInstance().getAccess("JSPGen");
        String sql = "select * from `"+FireAccess.getTable("user")+"` where `id`=:id";
        Map<String, Object> paramMap = new HashMap<String, Object>();
        //paramMap.put("id", getParameter("id"));
        paramMap.put("id", Grapes.rand(1,9)+"");
        fa.createSQL(sql).setParameter(paramMap);
        Map<String, Object> map = fa.unlist();
        fa.close();
        return text(map.toString());
    }

    // 分表测试
    public String login() {
        long start = Dates.getTimeMillis(); // 开始时间
        FireAccess fa = FireBuild.getInstance().getAccess("JSPGen");
        String sql = "insert into `"+FireAccess.getTable("login")+"` (`id`,`name`,`time`) values (:id, :name, :time)";
        Map<String, Object> paramMap = null;
        for (int i =1 ; i< 10 ; i++){
            paramMap = new HashMap<String, Object>();
            paramMap.put("id", i+"");
            paramMap.put("name", 100+i);
            paramMap.put("time", Dates.getTimeMillis());
            fa.createSQL(sql).setParameter(paramMap).executeUpdate();
        }
        fa.close();
        long end = Dates.getTimeMillis();   // 结束时间 
        long count = end-start;
        return text("总共用了:" + Dates.getUnitTime(count, true) + " ("+count+"毫秒)");
    }
    // 分表查询测试
    public String loginfind() {
        FireAccess fa = FireBuild.getInstance().getAccess("JSPGen");
        String sql = "select * from `"+FireAccess.getTable("login")+"` where `id`=:id";
        Map<String, Object> paramMap = new HashMap<String, Object>();
        //paramMap.put("id", getParameter("id"));
        paramMap.put("id", Grapes.rand(1,9)+"");
        fa.createSQL(sql).setParameter(paramMap);
        Map<String, Object> map = fa.unlist();
        fa.close();
        return text(map.toString());
    }
}

5. Journal de test

Réaliser une segmentation horizontale des données basée sur JSPGenFire: sous-table de base de données interne, sous-table de sous-base de données

6. Enregistrement des données

A. Lorsque la base de données n'est pas divisée en table
Réaliser une segmentation horizontale des données basée sur JSPGenFire: sous-table de base de données interne, sous-table de sous-base de données
B, une fois la base de données divisée en table
Réaliser une segmentation horizontale des données basée sur JSPGenFire: sous-table de base de données interne, sous-table de sous-base de données
Réaliser une segmentation horizontale des données basée sur JSPGenFire: sous-table de base de données interne, sous-table de sous-base de données
C, une fois la base de données divisée en table
Réaliser une segmentation horizontale des données basée sur JSPGenFire: sous-table de base de données interne, sous-table de sous-base de données
Réaliser une segmentation horizontale des données basée sur JSPGenFire: sous-table de base de données interne, sous-table de sous-base de données

Ecrire à la fin

Tous les tableaux n'ont pas besoin d'être segmentés, cela dépend principalement du taux de croissance des données. Après la segmentation, la complexité de l'entreprise sera augmentée dans une certaine mesure. Outre le stockage et l'interrogation des données portées par la base de données, c'est aussi l'une de ses tâches importantes d'aider l'entreprise à mieux répondre à ses besoins.
Il n'est pas recommandé d'utiliser la sous-base de données et la sous-table en dernier recours pour éviter une «conception excessive» et une «optimisation prématurée». Avant la sous-table de la sous-base de données, ne divisez pas pour des raisons de sous-division, essayez d'abord de faire ce que vous pouvez, par exemple: mettre à niveau le matériel, mettre à niveau le réseau, séparation de lecture et d'écriture, optimisation d'index, etc. Lorsque la quantité de données atteint le goulot d'étranglement d'une seule table, considérez la sous-table de la sous-base de données.

Je suppose que tu aimes

Origine blog.51cto.com/jspgen/2572415
conseillé
Classement