免安裝,還原生產(chǎn)環(huán)境,運(yùn)行中切換版本,這不是我認(rèn)識(shí)的MySQL
MySQL,用了好多年了吧,在你印象里是不是一直都是四平八穩(wěn),做為一個(gè)基礎(chǔ)組件,也不期待啥了。
如果說(shuō)想線下調(diào)度,集成測(cè)試,想用一個(gè)內(nèi)存數(shù)據(jù)庫(kù),你可能會(huì)說(shuō)那H2, Derby吧,不都可以嘛。
但差別是你在自己線下時(shí)跑了多少不說(shuō),但不同的數(shù)據(jù)庫(kù),不同的特性,可能有些地方無(wú)法真正還原線上。為什么不安裝一個(gè)?費(fèi)事,哈哈。
今天咱們介紹的這位,可以理解為嵌入MySQL,免安裝。不同的測(cè)試時(shí)還可以切換不同的版本,Cool。
使用起來(lái)也不費(fèi)勁,加個(gè) Maven 依賴就行,分分鐘的事兒。
就是它:
- <dependency>
- <groupId>com.wix</groupId>
- <artifactId>wix-embedded-mysql</artifactId>
- <version>x.y.z</version>
- <scope>test</scope>
- </dependency>
代碼也簡(jiǎn)單,直接定義你需要的版本,數(shù)據(jù)庫(kù)信息,把要初始化的SQL 給它,走起。
- MysqldConfig config = aMysqldConfig(v5_6_23) //這里是版本
- .withCharset(UTF8)
- .withPort(2215)
- .withUser("user1", "pwd2")
- .withTimeZone("Europe/Vilnius")
- .withTimeout(2, TimeUnit.MINUTES)
- .withServerVariable("max_connect_errors", 666)
- .build();
- EmbeddedMysql mysqld = anEmbeddedMysql(config)
- .addSchema("aschema", ScriptResolver.classPathScript("db/001_init.sql"))
- .start();
- //do work
- mysqld.stop(); //optional, as there is a shutdown hook
這有啥優(yōu)勢(shì):
- 測(cè)試可以跑在和生產(chǎn)環(huán)境基本一致的環(huán)境,同樣的版本,同樣的編碼和配置,database/schema/user settings 等等
- 比安裝一個(gè)更容易,想切換版本,改配置也更輕松;
- 本地每個(gè)項(xiàng)目可以使用不同的版本,不同的配置,啥都不用擔(dān)心;
- 對(duì)于MySQL的多個(gè)版本支持 - 5.5, 5.6, 5.7, 8.0;
- 多種平臺(tái)和環(huán)境都支持。
原理
這背后是怎么實(shí)現(xiàn)的呢?
咱們是「刨根究底」公眾號(hào),一起來(lái)看看。
上面代碼配置之后的 start ,到底 start 了啥?
咱們看下面這幾小段代碼:
- protected EmbeddedMysql(
- final MysqldConfig mysqldConfig,
- final DownloadConfig downloadConfig) {
- this.config = mysqldConfig;
- IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder().defaults(mysqldConfig, downloadConfig).build();
- MysqldStarter mysqldStarter = new MysqldStarter(runtimeConfig);
- localRepository.lock();
- try {
- this.executable = mysqldStarter.prepare(mysqldConfig);
- } finally {
- localRepository.unlock();
- }
- try {
- executable.start();
- getClient(SCHEMA, mysqldConfig.getCharset()).executeCommands(
- format("CREATE USER '%s'@'%%' IDENTIFIED BY '%s';", mysqldConfig.getUsername(), mysqldConfig.getPassword()));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- protected MysqldProcess start(
- final Distribution distribution,
- final MysqldConfig config,
- final IRuntimeConfig runtime) throws IOException {
- logger.info("Preparing mysqld for startup");
- Setup.apply(config, executable, runtime);
- logger.info("Starting MysqldProcess");
- return new MysqldProcess(distribution, config, runtime, this);
- }
其實(shí)這背后依賴了一個(gè)叫embed.process的開(kāi)源項(xiàng)目,
- public AbstractProcess(Distribution distribution, T config, IRuntimeConfig runtimeConfig, E executable)
- throws IOException {
- this.config = config;
- this.runtimeConfig = runtimeConfig;
- this.executable = executable;
- this.distribution = distribution;
- // pid file needs to be set before ProcessBuilder is called
- this.pidFile = pidFile(this.executable.getFile().executable());
- ProcessOutput outputConfig = runtimeConfig.getProcessOutput();
- // Refactor me - to much things done in this try/catch
- String nextCall="";
- try {
- nextCall="onBeforeProcess()";
- onBeforeProcess(runtimeConfig);
- nextCall="newProcessBuilder()";
- ProcessBuilder processBuilder = ProcessControl.newProcessBuilder(
- runtimeConfig.getCommandLinePostProcessor().process(distribution,
- getCommandLine(distribution, config, this.executable.getFile())),
- getEnvironment(distribution, config, this.executable.getFile()), true);
- nextCall="onBeforeProcessStart()";
- onBeforeProcessStart(processBuilder, config, runtimeConfig);
- nextCall="start()";
- process = ProcessControl.start(config.supportConfig(), processBuilder);
- nextCall="writePidFile()";
- if (process.getPid() != null) {
- writePidFile(pidFile, process.getPid());
- }
- nextCall="addShutdownHook()";
- if (runtimeConfig.isDaemonProcess() && !executable.isRegisteredJobKiller()) {
- ProcessControl.addShutdownHook(new JobKiller());
- registeredJobKiller = true;
- }
- nextCall="onAfterProcessStart()";
- onAfterProcessStart(process, runtimeConfig);
- } catch (IOException iox) {
- stop();
- throw iox;
- }
- }
它又操作了什么呢?從名字你也猜到了,它是直接操作進(jìn)程的,實(shí)際在運(yùn)行時(shí),會(huì)下載一個(gè)MySQL,然后通過(guò)腳本啟停。
初次啟動(dòng)的時(shí)候,會(huì)直接下載
有了這些,在測(cè)試的時(shí)候就可以和生產(chǎn)環(huán)境一樣,啟動(dòng)時(shí)加載初始化SQL腳本,開(kāi)始你的工作了。
github地址:https://github.com/wix/wix-embedded-mysql
本文轉(zhuǎn)載自微信公眾號(hào)「Tomcat那些事兒」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Tomcat那些事兒公眾號(hào)。