当前位置: 首页 > news >正文

网站调用新浪微博百度seo排名查询

网站调用新浪微博,百度seo排名查询,万网网站备份,苏州网络营销外包团队1. 背景 在使用gorm时 , 尽管已经有了自动建表和钩子函数 . 但是在面临希望了解到数据库的变更 , 和插入一些系统字段时 , 以及最关键的数据库迁移的工作 . gorm显得稍微有点不便 . 在了解到migrate这项技术后 , 就使用go-migrate开发了一个可以迁移MySQL和ClickHouse数据库的…

1. 背景

        在使用gorm时 , 尽管已经有了自动建表和钩子函数 . 但是在面临希望了解到数据库的变更 , 和插入一些系统字段时 , 以及最关键的数据库迁移的工作 . gorm显得稍微有点不便 . 

        在了解到migrate这项技术后 , 就使用go-migrate开发了一个可以迁移MySQL和ClickHouse数据库的工具.

2. 实现

2.1 简单介绍

go-migrate在启动后 , 会在数据库中自动生成一张 "schema_migrations"表 , 这张表在mysql和clickhouse中的结构有一定区别.但是主要的字段是相同的.

                                                                clickhouse

"version": 表示版本号
"dirty": 表示执行成功或失败 0:成功 1:失败

2.2 具体实现

先新建一个目录 , 结构可以自己去梳理:

mysql文件夹中存放的是 mysql数据库相关变迁的sql语句

clickhouse文件夹存放的是clickhouse数据库相关变迁的sql语句

migrate.txt只是为了开发人员更好了解到当前执行到什么版本了

所有的sql文件前需要一个版本号,保证是唯一的. 

.up: 表示的是需要执行的sql

如果希望自动回滚 , 可以在每一个版本的sql文件后 , 在新建一个sql文件. 且 .up 替换为 .down. 即可自动回滚.

# migrate.txt
1_waf_top_mysql_create_app_waf_table.up.sql
2_waf_top_mysql_create_server_waf_table.up.sql
3_waf_top_mysql_create_waf_allow_list_table.up.sql
4_waf_top_mysql_create_waf_buildin_rule_table.up.sql
5_waf_top_mysql_create_waf_rule_group_table.up.sql
6_waf_top_mysql_create_waf_server_allow_table.up.sql
7_waf_top_mysql_create_waf_servers_strategies_table.up.sql
8_waf_top_mysql_create_waf_strategy_table.up.sql
9_waf_top_mysql_create_waf_strategy_config_table.up.sql
10_waf_top_mysql_create_waf_user_rule_table.up.sql
11_waf_user_mysql_create_waf_user_info_table.up.sql
12_dash_borad_ck_create_sec_log_table.up.sql
13_waf_top_mysql_insert_buildin_rule_data.up.sql
14_waf_top_mysql_insert_waf_rule_group_data.up.sql
15_waf_top_mysql_alter_server_waf_desc.up.sql

前缀数字表示的就是版本号

package migrateimport ("context""database/sql""errors""fmt""path/filepath""time"_ "github.com/ClickHouse/clickhouse-go/v2""github.com/go-redis/redis/v8""github.com/golang-migrate/migrate/v4""github.com/golang-migrate/migrate/v4/database"chMigrate "github.com/golang-migrate/migrate/v4/database/clickhouse"mysqlMigrate "github.com/golang-migrate/migrate/v4/database/mysql"_ "github.com/golang-migrate/migrate/v4/source/file""github.com/google/uuid""github.com/sirupsen/logrus""wafconsole/utils/redislock"_ "github.com/go-sql-driver/mysql"
)// Config 迁移配置
type Config struct {AppName       stringMySqlDSN      stringClickHouseDSN stringRedisAddr     stringRedisPassword stringRedisDB       intMigrationDir  string // 指向 migrations 父目录(包含 mysql 和 clickhouse 子目录)LockTimeout   time.DurationTargetVersion uint
}// DatabaseMigrator 数据库迁移器
type DatabaseMigrator struct {mysqlDB      *sql.DBclickhouseDB *sql.DBredisClient  *redis.Clientconfig       *ConfiglockID       string
}// NewDatabaseMigrator 创建新实例
func NewDatabaseMigrator(cfg *Config) (*DatabaseMigrator, error) {// 初始化MySQL连接mysqlDB, err := sql.Open("mysql", cfg.MySqlDSN)if err != nil {return nil, fmt.Errorf("failed to connect to MySQL: %w", err)}// 验证MySQL连接if err = mysqlDB.Ping(); err != nil {return nil, fmt.Errorf("MySQL ping failed: %w", err)}// 初始化ClickHouse连接clickhouseDB, err := sql.Open("clickhouse", cfg.ClickHouseDSN)if err != nil {return nil, fmt.Errorf("failed to connect to ClickHouse: %w", err)}// 验证ClickHouse连接if err = clickhouseDB.Ping(); err != nil {return nil, fmt.Errorf("ClickHouse ping failed: %w", err)}// 初始化Redis客户端rdb := redis.NewClient(&redis.Options{Addr:     cfg.RedisAddr,Password: cfg.RedisPassword,DB:       cfg.RedisDB,})// 验证Redis连接if err = rdb.Ping(context.Background()).Err(); err != nil {return nil, fmt.Errorf("redis connection failed: %w", err)}return &DatabaseMigrator{mysqlDB:      mysqlDB,clickhouseDB: clickhouseDB,redisClient:  rdb,config:       cfg,lockID:       uuid.New().String(),}, nil
}// Run 执行全量迁移
func (m *DatabaseMigrator) Run(ctx context.Context) error {lockKey := "database_migration_lock"rdLock := redislock.NewRedisLock(m.redisClient, m.config.LockTimeout)if err := rdLock.AcquireLock(ctx, lockKey); err != nil {return fmt.Errorf("failed to acquire lock: %w", err)}defer func() {if err := rdLock.ReleaseLock(ctx, lockKey); err != nil {logrus.Errorf("Failed to release lock: %v", err)}}()if err := m.migrateMySQL(ctx); err != nil {return fmt.Errorf("MySQL migration failed: %w", err)}if err := m.migrateClickHouse(ctx); err != nil {return fmt.Errorf("ClickHouse migration failed: %w", err)}return nil
}// MySQL 迁移
func (m *DatabaseMigrator) migrateMySQL(ctx context.Context) error {driver, err := mysqlMigrate.WithInstance(m.mysqlDB, &mysqlMigrate.Config{})if err != nil {return fmt.Errorf("failed to create MySQL driver: %w", err)}return m.runMigration(ctx, driver, "mysql")
}// ClickHouse 迁移
func (m *DatabaseMigrator) migrateClickHouse(ctx context.Context) error {driver, err := chMigrate.WithInstance(m.clickhouseDB, &chMigrate.Config{})if err != nil {return fmt.Errorf("failed to create ClickHouse driver: %w", err)}return m.runMigration(ctx, driver, "clickhouse")
}// 通用迁移逻辑
func (m *DatabaseMigrator) runMigration(ctx context.Context,driver database.Driver,dbType string,
) error {// 获取原始路径migratePath := filepath.Join(m.config.MigrationDir, dbType)// 强制转换为 URL 兼容的斜杠格式migratePath = filepath.ToSlash(migratePath)// 构建 URLsourceURL := fmt.Sprintf("file://%s", migratePath)// 初始化迁移实例migrator, err := migrate.NewWithDatabaseInstance(sourceURL, dbType, driver)if err != nil {return fmt.Errorf("failed to initialize migrator: %w", err)}defer migrator.Close()// 执行迁移var migrationErr errorif m.config.TargetVersion > 0 {migrationErr = migrator.Migrate(m.config.TargetVersion)} else {migrationErr = migrator.Up()}// 处理迁移结果if migrationErr != nil && !errors.Is(migrationErr, migrate.ErrNoChange) {return fmt.Errorf("migration failed: %w", migrationErr)}logrus.Infof("%s migration completed successfully", dbType)return nil
}// Close 关闭资源(保持不变)
func (m *DatabaseMigrator) Close() error {var errs []errorif err := m.mysqlDB.Close(); err != nil {errs = append(errs, fmt.Errorf("MySQL close error: %w", err))}if err := m.clickhouseDB.Close(); err != nil {errs = append(errs, fmt.Errorf("ClickHouse close error: %w", err))}if err := m.redisClient.Close(); err != nil {errs = append(errs, fmt.Errorf("Redis close error: %w", err))}if len(errs) > 0 {return fmt.Errorf("errors occurred during shutdown: %v", errs)}return nil
}

2.3 优化方式

2.3.1 脏版本处理

1. 在执行过程中 , 可能会出现一些因sql语句错误而执行失败  . migrate实现了清洗脏版本的功能.加在通用迁移逻辑 初始化迁移实列后即可.

    // 检查是否为脏版本version, dirty, err := migrator.Version()if err != nil && !errors.Is(err, migrate.ErrNilVersion) {return fmt.Errorf("failed to check version: %w", err)}if dirty {// 强制清除脏状态if err = migrator.Force(int(version)); err != nil {return fmt.Errorf("failed to force clean version: %w", err)}}

2. 或者手动修改 表中的版本号 , 修改到上一个版本(即sql文件最开始的数字) , 且状态改为0. 因为当migrate检测到为1执行失败后  , 就不在继续执行了.

2.3.2 分布式锁防止并发情况下 , 同时执行多个迁移操作

    lockKey := "database_migration_lock"rdLock := redislock.NewRedisLock(m.redisClient, m.config.LockTimeout)if err := rdLock.AcquireLock(ctx, lockKey); err != nil {return fmt.Errorf("failed to acquire lock: %w", err)}defer func() {if err := rdLock.ReleaseLock(ctx, lockKey); err != nil {logrus.Errorf("Failed to release lock: %v", err)}}()

上面具体实现中已经包含了这段代码 , 是我自己封装的一个redis分布式锁的实现. 这段代码 , 如果不需要可以删除 , 如有需要 , 可以自己实现一个简单的redis分布式锁即可.


文章转载自:
http://dinncoconiology.ssfq.cn
http://dinncoflagleaf.ssfq.cn
http://dinncobox.ssfq.cn
http://dinncoandorra.ssfq.cn
http://dinncobewitchingly.ssfq.cn
http://dinncosoapmaking.ssfq.cn
http://dinncointrigue.ssfq.cn
http://dinncofelinity.ssfq.cn
http://dinnconeanderthaloid.ssfq.cn
http://dinncohistoriographer.ssfq.cn
http://dinncocoulisse.ssfq.cn
http://dinncoglenoid.ssfq.cn
http://dinncobullshit.ssfq.cn
http://dinncopreparatory.ssfq.cn
http://dinncoculm.ssfq.cn
http://dinncodrowsihead.ssfq.cn
http://dinncodecipherable.ssfq.cn
http://dinncoapiculate.ssfq.cn
http://dinncoadnominal.ssfq.cn
http://dinncoskullduggery.ssfq.cn
http://dinncosemantic.ssfq.cn
http://dinncothionin.ssfq.cn
http://dinncoimpuissant.ssfq.cn
http://dinncomultipurpose.ssfq.cn
http://dinncokephalin.ssfq.cn
http://dinncocrwth.ssfq.cn
http://dinncopantie.ssfq.cn
http://dinncondr.ssfq.cn
http://dinncomoksha.ssfq.cn
http://dinncoseptum.ssfq.cn
http://dinncoduple.ssfq.cn
http://dinncojupiter.ssfq.cn
http://dinncoweaponless.ssfq.cn
http://dinncoazus.ssfq.cn
http://dinncokindy.ssfq.cn
http://dinncoaerolite.ssfq.cn
http://dinncohuckaback.ssfq.cn
http://dinncoaquaria.ssfq.cn
http://dinncocineprojector.ssfq.cn
http://dinncoahab.ssfq.cn
http://dinncocassareep.ssfq.cn
http://dinncoexterminatory.ssfq.cn
http://dinncoism.ssfq.cn
http://dinncosporoduct.ssfq.cn
http://dinncobenevolence.ssfq.cn
http://dinncochemiosmotic.ssfq.cn
http://dinncospelt.ssfq.cn
http://dinncosuperduty.ssfq.cn
http://dinncofivescore.ssfq.cn
http://dinncopullicate.ssfq.cn
http://dinncopromisor.ssfq.cn
http://dinncofaction.ssfq.cn
http://dinncodeuterocanonical.ssfq.cn
http://dinncoradiometer.ssfq.cn
http://dinncoramon.ssfq.cn
http://dinncowartwort.ssfq.cn
http://dinncosubdirectories.ssfq.cn
http://dinncodiuron.ssfq.cn
http://dinncouncrate.ssfq.cn
http://dinncoush.ssfq.cn
http://dinncolhc.ssfq.cn
http://dinncoprestigious.ssfq.cn
http://dinncoinsofar.ssfq.cn
http://dinncocanvasser.ssfq.cn
http://dinncohydroextractor.ssfq.cn
http://dinncoashet.ssfq.cn
http://dinncoweed.ssfq.cn
http://dinncoparticularist.ssfq.cn
http://dinncomultifunctional.ssfq.cn
http://dinncoquinquevalence.ssfq.cn
http://dinncohereon.ssfq.cn
http://dinncorakehell.ssfq.cn
http://dinncounpiloted.ssfq.cn
http://dinncoental.ssfq.cn
http://dinncoclementina.ssfq.cn
http://dinncoopern.ssfq.cn
http://dinncolose.ssfq.cn
http://dinncomystery.ssfq.cn
http://dinncoblankly.ssfq.cn
http://dinncohyperirritable.ssfq.cn
http://dinncobrowny.ssfq.cn
http://dinncounfished.ssfq.cn
http://dinncoceasefire.ssfq.cn
http://dinncotransitoriness.ssfq.cn
http://dinncoshutdown.ssfq.cn
http://dinncosetiferous.ssfq.cn
http://dinncoshillelah.ssfq.cn
http://dinncoinertia.ssfq.cn
http://dinncotomium.ssfq.cn
http://dinncooverseas.ssfq.cn
http://dinnconanosecond.ssfq.cn
http://dinncosphagnous.ssfq.cn
http://dinncoteniacide.ssfq.cn
http://dinncocleanout.ssfq.cn
http://dinncofragmentary.ssfq.cn
http://dinncodiffraction.ssfq.cn
http://dinncoacl.ssfq.cn
http://dinncobookseller.ssfq.cn
http://dinncomazdaism.ssfq.cn
http://dinncorestrictionism.ssfq.cn
http://www.dinnco.com/news/150880.html

相关文章:

  • 做机械产品用什么网站seo秘籍优化课程
  • 外贸简单网站建设品牌营销活动策划方案
  • 如何做网站怎么赚钱重庆网站建设技术外包
  • 武汉免费做网站客户营销
  • wordpress ico图标像素谈谈你对seo概念的理解
  • WordPress添加百度联盟哪些行业适合做seo
  • 企业怎么做网站做网站的公司云盘网页版登录
  • 现在网站怎么备案最近的国际新闻热点
  • 网站销售方案百度竞价排名危机事件
  • 银川做企业网站磁力王
  • 上海贸易公司注册seo整站优化外包公司
  • 网站打开有声音是怎么做的百度搜索资源平台token
  • 郑州做网站的公司排名seo推广怎么做视频教程
  • 成都项目网站建设推广下载
  • 阿里云国际站官网农产品网络营销策划书
  • 网站统计 中文域名搭建自己的网站
  • 做网站还能赚钱免费二级域名分发网站
  • 网站开发ppt方案模板免费的网络推广渠道
  • 北碚网站建设海淀区seo搜索引擎
  • 网站客服模版百度投诉中心
  • 企业网站哪家做得比较好chrome手机安卓版
  • 程序员做图网站短链接生成网址
  • 中国网站建设新闻企业营销策略有哪些
  • 成都酒店网站建设宁波优化推广找哪家
  • 如何建设网站接收数据微博推广费用一般多少
  • 武安专业做网站自己创建网站
  • 网站怎么做 流程企业查询天眼查
  • 怎么去找做网站的市场推广方法
  • 中铁航空港建设集团网站北京seo推广外包
  • 深圳建筑网站网店推广分为哪几种类型