Los datos de la base de datos en el proyecto recientemente lanzado se acercan a la saturación. Los datos de tabla más grandes son cercanos a 3000W, y hay varias tablas con millones de datos. El proyecto requiere que el tiempo de lectura de datos no pueda exceder 0.05 segundos, pero la situación real no cumple con los requisitos. Explique la indexación y el uso de la tecnología Redis y Ehcache Cache ya no puede cumplir con los requisitos. Por lo tanto, comenzamos a usar tecnología de separación de lectura y escritura. Tal vez cuando el volumen de datos excede los 100 millones o más en el futuro, debemos considerar la implementación de bases de datos distribuidas. Sin embargo, en la actualidad, la separación de lectura y escritura de lectura + caché + índice + partición de tabla + optimización SQL + equilibrio de carga puede cumplir con el trabajo de consulta de 100 millones de volúmenes de datos. Echemos un vistazo a los pasos para usar el resorte para lograr la separación de lectura y escritura:
1. Antecedentes
Nuestra aplicación general es "leer más y escribir menos" para bases de datos, lo que significa que la presión sobre la base de datos para leer datos es relativamente alta. Una idea es usar una solución de clúster de base de datos.
Una de ellas es la biblioteca principal, que es responsable de escribir datos, que llamamos: biblioteca de escritura;
Los otros son todos de la biblioteca, que es responsable de leer datos, que llamamos: Lectura de la biblioteca;
Entonces, los requisitos para nosotros son:
1. Los datos de la biblioteca de lectura y la biblioteca de escritura son consistentes; (Este es un tema muy importante. El procesamiento de la lógica comercial debe procesarse en la capa de servicio, y no a nivel DAO o mapeador)
2. Al escribir datos, debe escribirlos en la biblioteca de escritura;
3. Debe ir a la biblioteca de lectura para leer los datos;
2. Plan
Hay dos soluciones para resolver la separación de lectura y escritura: solución de capa de aplicación y solución de middleware.
2.1. Solución de la capa de aplicación:
ventaja:
1. múltiples fuentes de datos son fáciles de cambiar y son completadas automáticamente por el programa;
2. No se requiere un middleware;
3. En teoría, apoye cualquier base de datos;
defecto:
1. Completado por programadores, y la operación y el mantenimiento no están involucrados;
2. No se pueden lograr las fuentes de datos dinámicamente que no se pueden lograr;
2.2. Solución de middleware
Pros y contras:
ventaja:
1. El programa fuente puede lograr la separación de lectura y escritura sin ningún cambio;
2. Agregar dinámicamente fuentes de datos no requiere reiniciar el programa;
defecto:
1. Los programas dependen del middleware, lo que dificulta cambiar las bases de datos;
2. El middleware se usa como agente de tránsito, y el rendimiento ha disminuido;
3. Use el resorte para implementar en función de la capa de aplicación
3.1. Principio
Antes de ingresar al servicio, use AOP para juzgar, si usar una biblioteca de escritura o una biblioteca de lectura, la base del juicio se puede juzgar en función del nombre del método, como el que comienza con consulta, búsqueda, get, etc. y otra biblioteca de escritura.
3.2. DynamicDataSource
importar org.springframework.jdbc.dataSource.lookup.AbstracTroutingDataSource;/*** Definir fuentes de datos dinámicos e implementar AbstrutingDataSource proporcionado por Integration Spring. Solo necesita implementar el método DetermineCurrentLoPlookUpkey * * Dado que DynamicDataSource es un singleton y hilo de inicio, ThreadLocal se usa para garantizar la seguridad de hilos, que es completada por DynamicDataSourceholder. * * @author zhijun * */public class DynamicDataSource extiende AbstracTroutingDataSource {@Override Objeto protegido DetetEnECRENTLOPOPKEPKEY () {// Use DynamicDataSourceHolder para garantizar la seguridad de los subprocesos y obtener la tecla de origen de datos en el retorno de hilo actual DynamicDataSourceHolder.getDataSourceKey (); }} 3.3. DynamicDataSourceholder
/** * * Use la tecnología ThreadLocal para registrar la clave de la fuente de datos en el hilo actual * * @author zhijun * */public class DynamicDataSourceHolder {// Escriba la fuente de datos correspondiente a la biblioteca String final estática privada Master = "maestro"; // Lea la fuente de datos correspondiente a la biblioteca privada estática final de cadena esclava = "esclavo"; // use ThreadLocal para registrar la fuente de datos del hilo actual está el hilo privado final de threadlocal <string> holder = new ThreadLocal <String> (); / ** * Establezca la tecla de origen de datos * @param clave */ public static void putDataSourceKey (tecla de cadena) {Holder.set (clave); } / ** * Obtenga la clave de origen de datos * @return * / public static String getDataSourceKey () {return Holder.get (); } / *** Biblioteca de escritura de marcado* / public static void markmaster () {putDataSourceKey (maestro); } / *** Biblioteca de lectura de marcado* / public static void markslave () {putDataSourceKey (esclavo); }} 3.4. DataSourCeSpect
importar org.apache.commons.lang3.StringUtils; import org.spectj.lang.joinpoint;/** * Defina la sección AOP de la fuente de datos y juzga si es hora de leer la biblioteca o escribir la biblioteca a través del nombre del método del servicio * @author zhijun * */pública class dataSourceSceSpect {/** * Ejecutar antes de entrar en el método de servicio * * @author Zhijun * */Public Class dataSourceSceSpect {/** * Execute antes de entrar en el método de servicio * * @author zhijun * */pública class dataSourceSceSpect {/** * Execute antes de entrar en el Método de servicio * * * * @Author Object SECT void antes (punto de unión) {// Obtener el nombre de método ejecutado actualmente String MethodName = Point.getSignature (). GetName (); if (isslave (métodeName)) {// marque como lea la biblioteca dynamicDataSourceHolder.markSlave (); } else {// Marque como biblioteca de escritura DynamicDataSourceHolder.markmaster (); }} / ** * Determine si es una biblioteca de lectura * * @param MethodName * @return * / private boolean isslave (string metodName) {// El nombre del método comienza con Query, Buscar, obtener, devolver stringUtils.Startswithany (MethodName, "Query", "Buscar", "Get"); }}3.5. Configurar 2 fuentes de datos
3.5.1. JDBC.Properties
jdbc.master.driver = com.mysql.jdbc.driverjdbc.master.url = jdbc: mysql: //127.0.0.1: 3306/mybatis_1128? UseUnicode = true & ch aracteriCoding = UTF8 & Autoreconnect = True & PeaTMultIliqueries = trueJDBC.Master.Username = rootJdbc.Master.Password = 123456JD bc.slave01.driver = com.mysql.jdbc.driverjdbc.slave01.Url = jdbc: mysql: //127.0.0.1: 3307/mybatis_1128? Useunicode = true & ch aracteriCoding = UTF8 & Autoreconnect = True & PeaTMultiqueries = trueJdbc.Slave01.USername = rootJdbc.Slave01.Password = 123456
3.5.2. Definir el grupo de conexión
< Nombre de usuario de la base de datos-> <Property Name = "Username" value = "$ {jdbc.master.username}" /> <!-La contraseña de la base de datos-> <propiedad name = "contraseña" value = "$ {jdbc.master.password}" /> <!-Verifique el tiempo de intervalo de las conexiones inactivas en el grupo de conexión de la base de datos. La unidad es una fracción. El valor predeterminado es 240. Si desea cancelar, establezca en 0.-> <Property Name = "IdleconnectionTperiod" value = "60" /> <!-El número máximo de conexiones que no se usan en el grupo de conexión. La unidad es una fracción. El valor predeterminado es 60. Si desea sobrevivir para siempre, establezca en 0.-> <propiedad name = "idlemaxage" valor = "30" /> <!-El número máximo de conexiones por partición-> <name de propiedad = "maxconnectionsperpartition" valor = "150" /> <!-Número mínimo de conexiones por partición por separación-<name de propiedad = "mAxconnectionsperPartition" value "value" "" ". Número mínimo de conexiones por partición-> <propiedad name = "maxConnectionsPerPartition" valor = "150" /> <!-El número mínimo de conexiones por partición-> <propiedad name = "minconnectionsperpartition" value = "5" /> < /bean> <!-Configure el grupo de conexión-> <bean id = "slove01dataSource" destruye-method = "Close" <!-dataBase--database->>> <!-database-> <! name = "DriverClass" Value = "$ {jdbc.slave01.driver}" /> <!-jdbcurl para el controlador correspondiente-> <propiedad name = "jdbcurl" valor = "$ {jdbc.slave01.url}" /> <!-dataS UserName-> <nombre de propiedad = "username" value = "$ {jdbc.slave01.username}" /> <!-La contraseña de la base de datos-> <propiedad name = "contraseña" value = "$ {jdbc.slave01.password}" /> <!-verifique el tiempo de intervalo de las conexiones inactivas en el grupo de conexiones de dataBase. La unidad es una fracción. El valor predeterminado es 240. Si desea cancelar, establezca en 0.-> <Property Name = "IdleconnectionTestperiod" valor = "60" /> <!-El tiempo máximo de supervivencia de los enlaces no utilizados en el grupo de conexión. La unidad es una fracción. El valor predeterminado es 60. Si desea sobrevivir para siempre, establezca en 0.-> <propiedad name = "idlemaxage" value = "30" /> <!-El número máximo de conexiones por partición-> <propiedad name = "maxconnectionsPerPartition" valor = "150" /> <!-Número mínimo de conexiones por partición-> <name de propiedad = "minconnectionsperPartition" value "5" 5 "5" 3.5.3. Definir dataSource
<!-Defina la fuente de datos y use la fuente de datos que implementa-> <bean id = "dataSource"> <!-Establezca múltiples fuentes de datos-> <propiedad name = "targetDataSources"> <map key-type = "java.lang.string"> <!-Esta clave debe ser consistente con la clave en el programa-> <Intering Key = "Master" Value-SRF = "MasterDataSource key = "Slave" value-ref = "Slave01DataSource"/> </s map> </property> <!-Establezca la fuente de datos predeterminada, aquí la biblioteca de escritura predeterminada-> <Property name = "defaultTargetDataSource" ref = "masterDataSource"/> </bean>
3.6. Configurar la gestión de transacciones y cambiar dinámicamente las superficies de origen de datos
3.6.1. Definición del administrador de transacciones
<!-Definition Transaction Manager-> <Bean ID = "TransactionManager"> <Property Name = "DataSource" ref = "DataSource" /> </lebre>
3.6.2. Definir políticas de transacción
< /> <!-La biblioteca principal realiza operaciones, y el comportamiento de propagación de transacciones se define como el comportamiento predeterminado-> <tx: name de método = "salvar*" propagation = "requerido" /> <tx: name de método = "update*" propagation = "requerido" /> <tx: método name = "deletee*" propagation = "requerido" /> <!-Otro método use la política de transacción predeterminada-> <tx name = "delete*" /" /" /> ". </tx: atributos> </tx: consejo>
3.6.3. Defina la faceta
< consejo-ref = "txadvice" pointcut-ref = "txpointcut" /> <!-Aplique la sección a un procesador de sección personalizado, -9999 asegura que la sección tenga la ejecución de prioridad más alta-> <aop: aspecto ref = "dataSourCeAspect" orden = "-9999"> <aop: antes del método = "antes de" punto-ref = "tx-tourcut" / /" /> />>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /" TXPointCut /" </aop: aspecto> </aop: config>
4. Mejore la implementación de la sección y utilice la coincidencia de la regla de la política de transacciones
En la implementación anterior, coincidiremos con el nombre del método en lugar de usar la definición en la política de transacción, y utilizaremos la coincidencia de reglas en la política de gestión de transacciones.
4.1. Configuración mejorada
<
4.2. Implementación mejorada
import java.lang.reflect.field; import java.util.arrayList; import java.util.list; import java.util.map; import org.apache.commons.lang3.stringutils; importar org.aspectj.lang.joinpoint; import org.springframework.transaction.interceptor.transactionAttribute; import org.springframework.transaction.interceptor.transactionattributeSource; import og.springframework.transaction.interceptor.transactioninterceptor; import og.springframework.util.patternmatchutils; importación; importación; org.springframework.util.reflectionUtils;/*** Define la sección AOP de la fuente de datos, que controla si se debe usar maestro o esclavo. * * Si una política de transacción está configurada en la gestión de transacciones, el método para marcar Readonly en la política de transacción configurada es usar esclavo y el otro usa maestro. * * Si no hay una política para configurar la gestión de transacciones, se adopta el principio de coincidencia del nombre del método, y el esclavo se usa como comenzando con consulta, búsqueda y get, y otros métodos se usan como maestro. * * @author zhijun * */public class dataSourCeAspect {private list <string> slaVemethodPattern = new ArrayList <String> (); cadena final estática privada [] defaultSlaVemethodStart = new String [] {"consulta", "encontrar", "obtener"}; cadena privada [] slaVemethodStart; / ** * Lea las políticas en la gestión de transacciones * * @param txadvice * @throws excepción */ @suppleswarnings ("no verked") public void settxadvice (TransactionInterceptor txAdVice) lanza excepción {if (txadvice == null) {// La política de gestión de transacciones no está configurada de regreso; } // Obtener información de configuración de política de TXAdVice TransactionATtributesOurce TransactionATtributesOurce = txAdVice.getTransactionAttributeSource (); if (! (transaccionAttributeurce instancia de namEmatchTransactionAttributeurce)) {return; } // Use la tecnología de reflexión para obtener el valor del atributo NamEmap en el NamematchTransactionAttributesurce Object NamEmatchTransactionAttributesurce MatchTransactionAttributeSource = (namatchTRANSACTIONTTRIBUTSOURCE) TRANSACTIONATTRIVEUTUSEURCE; Campo namEmapfield = reflexCleUtils.findfield (namematchTransactionAttributesource.class, "namEmap"); namEmapfield.setAccessible (verdadero); // Establecer este campo para acceder // Obtener el valor de NamEmap <String, TransActionAttribute> MAP = (MAP <String, TransactionAttribute>) namEmapfield.get (MatchTRANSACTIONATTRIBUTSOURCE); // transaccionAttribute> Entry: map.entryset ()) {if (! Entry.getValue (). IsReadonly ()) {// Después del juicio, la política de lectura se define antes de agregarla a SlaVemethodPattern continúa; } slavemethodpattern.add (entry.getKey ()); }} / *** Ejecutar antes de ingresar el método de servicio* @param Point Face Object* / public void antes (punto unePoint) {// Obtenga el nombre de método ejecutado actualmente String MethodName = Point.getSignature (). GetName (); boolean isslave = false; if (slaVemethodpattern.isEmpty ()) {// No hay una política de transacción configurada en el contenedor Spring actual, y el método de coincidencia del nombre del método isSlave = isSlave (método); } else {// Use reglas de política para que coincidan para (String MappedName: SlavemethodPattern) {if (isMatch (MethodName, MappedName)) {isSlave = true; romper; }}}} if (isslave) {// marque como lea la biblioteca dynamicDataSourceHolder.markSlave (); } else {// Marque como biblioteca de escritura DynamicDataSourceHolder.markmaster (); }} / ** * Determine si es una biblioteca de lectura * * @param MethodName * @return * / private boolean isslave (string metodName) {// El nombre del método comienza con consultas, busca, obtiene return stringUtils.startswithany (metodName, getSlaVemethodtart ()); } /** * Compañía comodín * * Return si el nombre del método dado coincide con el nombre asignado. *<p>*La implementación predeterminada verifica "xxx*", "*xxx" y "*xxx*", así como la igualdad directa*. Se puede anular en subclases. * * @param methodName the method name of the class * @param mappedName the name in the descriptor * @return if the names match * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) */ protected boolean isMatch(String methodName, String mappedName) { return PatternMatchUtils.simpleMatch(mappedName, MethodName); } / *** El prefijo de nombre del método del esclavo especificado por el usuario* @param slaVemethodStart* / public void setSlaVemethodStart (string [] slaVemethodStart) {this.SlavemethodStart = slaVemethodStart; } public String [] getSlaVemethodStart () {if (this.slaVemethodStart == null) {// no especificado, use el retorno predeterminado defaultsLaveMethodStart; } return slaVemethodStart; }}5. Implementación de un maestro y múltiples esclavos
En muchos escenarios de uso práctico, utilizamos la arquitectura "One Master, Múltiples esclavos", por lo que ahora apoyamos esta arquitectura, y actualmente solo necesitamos modificar el DynamicDataSource.
5.1. Implementación
import java.lang.reflect.field; import java.util.arrayList; import java.util.list; import java.util.map; import java.util.concurrent.atomic.atomicInteger; import javax.sql.dataSource; import og.slf4j.logger; import og.slf4j org.springframework.jdbc.dataSource.lookup.AbstracTroutingDataSource; import org.springframework.util.reflectionUtils;/** * Define las fuentes de datos dinámicas e implementan la dynamicTatAsource proporcionada por la primavera a través de la integración, solo debe implementar el método de teclado de determinación de la determinación de la tecla de determinación * *. Hild-Insecure, ThreadLocal se usa para garantizar la seguridad de hilos, que es completada por DynamicDataSourceHolder. * * @author zhijun * */public class DynamicDataSource extiende AbstrutingDataSource {private static final logger = loggerFactory.getLogger (DynamicDataSource.class); Integer privado SlaVecount; // recuento de encuestas, inicialmente -1, AtomicInteger es un contador privado de AtomicInteger privado de hilo = nuevo AtomicInteger (-1); // Registre la lista privada clave <S Object> SlaVedataSources = new ArrayList <ject> (0); @Override Objeto protegido DetetInECRENTLOVEWPKEY () {// Use DynamicDataSourceHolder para garantizar la seguridad de los subprocesos y obtener la clave de origen de datos en el subproceso actual if (DynamicDataSourceHolder.ismaster ()) {Object Key = DynamicDataSourceHolder.getDataSourceKey (); if (logger.isDebugeNabled ()) {logger.debug ("La clave del plato de datos actual es:" + clave); } clave de retorno; } Key de objeto = GetSlaveKey (); if (logger.isDebugeNabled ()) {logger.debug ("La clave del plato de datos actual es:" + clave); } clave de retorno; } @SupplesSwarnings ("sin verificar") @Override public void AfterPropertIesset () {super.afterPropertiesSet (); // Dado que la propiedad resuelveDataSources de la clase principal es una subclase privada que no se puede obtener, debe usar la reflexión para obtener campo de campo = ReflectionUtils.findfield (abstracTroutingDataSource.class, "resueldDataSources"); campo.setAccessible (verdadero); // Establecer accesibilidad prueba {map <object, dataSource> resueledDataSources = (map <object, dataSource>) field.get (this); // La cantidad de datos en la biblioteca de lectura es igual al número total de fuentes de datos menos el número de bibliotecas de escritura this.Slavecount = resueledDataSources.size () - 1; para (MAP.Entry <Object, DataSource> Entrada: resuelveDataSources.EntrySet ()) {if (DynamicDataSourceHolder.master.equals (Entry.getKey ())) {continuar; } slavedataSources.add (Entry.getKey ()); }} catch (Exception e) {logger.error ("¡Error AfterPropertIesset!", E); }} / ** * Implementación del algoritmo de votación * * @return * / public object getSlaveKey () {// Los subíndices resultantes son: 0, 1, 2, 3 ... Integer index = contunt.incrementandget () % slaVeCount; if (contar.get ()> 9999) {// para evitar exceder el rango de entero contar.set (-1); // restaurar} return slavedataSources.get (índice); }}6. Replicación MySQL Master-Slave
6.1. Principio
El principio de copiar MySQL Master (llamado maestro) esclavo (llamado esclavo):
1. El maestro registra los cambios de datos en el registro binario, es decir, el archivo especificado por el archivo de registro de archivo de configuración (estos registros se denominan eventos de registro binario, eventos de registro binario)
2. Slave Copy Binary logvents binary a su registro de retransmisión (registro de relé)
3. Los eventos de Reworks en el registro de retransmisión cambiarán los datos que se reflejan (repeticiones de datos)
6.2. A qué se debe prestar atención cuando la configuración de Mach
1. Las versiones del servidor DB primario y la base de datos del servidor DB Slave son las mismas
2. Los datos de la base de datos del servidor DB maestro y el servidor DB de esclavo son los mismos [aquí puede restaurar la copia de seguridad del maestro en el esclavo, o puede copiar directamente el directorio de datos del maestro al directorio de datos del esclavo correspondiente]
3. El servidor DB principal habilita los registros binarios, y el servidor DB principal y el servidor Slave DB del servidor deben ser únicos.
6.3. Configuración de la biblioteca principal (similar a Windows, Linux)
Es posible que algunos amigos no tengan una dirección IP muy clara, nombre de usuario y configuración de la cuenta de la base de datos maestro y esclavo. La siguiente es la configuración maestra y esclava que probé. Los IP son todos 127.0.0.1. Después de terminar mi ejemplo, lo escribiré.
Una IP maestra-esclavo es un ejemplo de diferentes configuraciones. Puede usar este ejemplo para comprender el método de configuración de manera más intuitiva.
Modificar bajo my.ini [mysqld] (también de la biblioteca):
#Enable replicación maestra-esclavo, la configuración de la biblioteca principal log-bin = mysql3306-bin#especificar la biblioteca principal serveridserver-id = 101#Especifique la base de datos sincronizada. Si no se especifica, todas las bases de datos están sincronizadas binlog-do-db = mybatis_1128
(Los comandos ingresados en my.ini deben tener una línea de espacio a continuación, de lo contrario mysql no lo reconocerá)
Ejecutar el estado de la consulta de la instrucción SQL: Mostrar el estado maestro
El valor de la posición debe registrarse y el valor de inicio de sincronización debe establecerse en la biblioteca.
Déjame decir una cosa más. Si ejecuta el estado de Show Master en MySQL y encuentra que el contenido configurado en My.ini no ha funcionado. Puede ser que no haya elegido el archivo my.ini, o puede ser que no reinicie el servicio. Es muy probable que sea causado por este último.
Para hacer que la configuración surta efecto, debe apagar el servicio MySQL y reiniciarla.
Cómo cerrar el servicio:
Abra la clave de ganar, ingrese Services.MSC para llamar al servicio:
Inicie Sqlyog nuevamente y encuentre que la configuración ha entrado en vigencia.
6.4. Crear un usuario sincrónico en la biblioteca principal
#Authorized User Slave01 utiliza la contraseña 123456 para iniciar sesión en MySQLgrant Replication Slave en *. * A 'slave01'@'127.0.0.1' identificado por '123456'; privilegios de descarga;
6.5. Configuración de la biblioteca
Modificar en my.ini:
#Specify ServerId, siempre que no se repita, solo hay una configuración de la biblioteca, y las otras se operan en la instrucción SQL Server-ID = 102
El siguiente ejecuta SQL (se ejecuta usando la cuenta raíz del esclavo):
Changematertomater_hot = '127.0.0.1', // La dirección IP del host material_uer = 'lave01', // El usuario del host (la cuenta recién creada en el host a través de ql) mater_paword = '123456', mater_port = 3306, mater_log_file = 'myql3306-bin.000006', // filemate_log_po = 1120; //
#Star Slave Syncronization Start Slave; #View Syncronization Status show status;
Aquí están los métodos de configuración maestro y esclavo para dos computadoras IP diferentes:
El sistema operativo donde reside la base de datos principal: Win7
Versión de la base de datos primaria: 5.0
La dirección IP de la base de datos principal: 192.168.1.111
Desde el sistema operativo donde reside la base de datos: Linux
De la versión de los datos: 5.0
Dirección IP de la base de datos: 192.168.1.112
Después de introducir el entorno, hablemos sobre los pasos de configuración:
1. Asegúrese de que la base de datos maestra sea exactamente la misma que la base de datos de esclavos.
Por ejemplo: la base de datos de A en la base de datos principal tiene las Tablas B, C y D, por lo que la base de datos de A y Tablas B, C y D debe estar grabada con un molde.
2. Cree una cuenta sincrónica en la base de datos principal.
La copia del código es la siguiente:
Grant Replication Slave, archivo en *. * A 'mstest'@'192.168.1.112' identificado por '123456';
192.168.1.112: es la dirección IP que se ejecuta con el usuario
MSTEST: es el nombre de usuario recién creado
123456: es la contraseña del nombre de usuario recién creado
La explicación detallada del comando anterior se hace mejor en Baidu. Si escribe demasiado, lo hará más poco claro.
3. Configure my.ini de la base de datos principal (porque está debajo de la ventana, es my.ini no my.cnf).
[mysqld] server-id = 1log-bin = logbinlog-do-db = mstest // Para sincronizar la base de datos MSTEST, si desea sincronizar varias bases de datos, agregue algunas más binlog-do-db = nombre de datos Binlog-anignore-db = mysql // para ignorar la base de datos de la base de datos.
4. Configure my.cnf de la base de datos.
[mysqld]server-id=2master-host=192.168.1.111master-user=mstest //Step 1. Create the username of the account master-password=123456 //Step 1. Create the password of the account master-port=3306master-connect-retry=60replicate-do-db=mstest //To synchronize the mstest database, to synchronize multiple Bases de datos, agregue algunos más replicados-do-db = nombre de la base de datos replicate-annegore-db = mysql // La base de datos se ignorará
5. Verifique si es exitoso
Ingrese MySQL e ingrese el comando: Mostrar estado de esclavo/g. Se mostrará la siguiente imagen. Si tanto slave_io_running como esclave_sql_running son sí, significa que la sincronización puede ser correctamente
6. Prueba de datos sincrónicos.
Ingrese la base de datos principal e ingrese el comando: inserte en uno (nombre) valores ('beijing');
Luego ingrese el comando de entrada de la base de datos: seleccione * de uno;
Si los datos se recuperan de la base de datos en este momento, significa que la sincronización ha sido exitosa y se implementará el maestro y el esclavo.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.