IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 使用MySQL/Tidb数据库的一些经验【缓慢更新中...】 -> 正文阅读

[大数据]使用MySQL/Tidb数据库的一些经验【缓慢更新中...】

使用MySQL和Tidb作为存储数据库时遇到的一些经验总结

插入

批量插入

  • 拼接sql语句,一次性发送多条(注意拼接的sql大小,数据库一般都有限制sql语句的大小)

REPLACE INTO

主键会被更新,本质上是先删除再新增

INSERT INTO xxx ON DUPLICATE KEY

没有记录则新增,有则在原有行上更新对应列

插入重复数据

package main

import (
    "database/sql"

    "github.com/sirupsen/logrus"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/schema"
)


type Camera struct {
    ID   uint   `gorm:"primaryKey"`
    Name string `gorm:"not null;type:varchar(32)"`
}

func main() {

    db, err := gorm.Open(mysql.Open("root:root@tcp(127.0.0.1:3306)/testDB?charset=utf8mb4&parseTime=true&loc=Local"), &gorm.Config{
        NamingStrategy: schema.NamingStrategy{
            SingularTable: false,
        },
    })
    if err != nil {
        return
    }

    db.AutoMigrate(&Camera{})

    ch := make(chan struct{})
    for i := 0; i < 5; i++ {
        go func(index int) {
            db.Debug().Transaction(func(tx *gorm.DB) error {
                ca := []Camera{}
                if tx.Raw("SELECT * FROM cameras WHERE name='camera_TEST'").Scan(&ca).RowsAffected == 0 {
                    return tx.Create(&Camera{
                        Name: "camera_TEST",
                    }).Error
                }
                return nil
            }, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
            ch <- struct{}{}
        }(i)
    }

    for i := 0; i < 5; i++ {
        <-ch
    }
}

插入的数据没有unique字段和主键,在高并发场景下,只通过先select查询是否存在,不存在则insert的事务插入,很大概率会出现插入了重复的数据,此时就算加排他锁也没用,因为数据库中根本没有记录,没有记录则不会上锁。解决方法:
1、数据库事务隔离级别提升到串行级别——(基本不考虑
2、给插入的数据建立unique约束(需要处理duplicate entry 错误

type Camera struct {
    ID   uint   `gorm:"primaryKey"`
    Name string `gorm:"not null;type:varchar(32);uniqueIndex"`
}

3、应用层来解决,例如插入逻辑统一为队列插入;或者加锁,锁的key为unique的字段,如下就是一个简单的加锁示例

type Camera struct {
    ID   uint   `gorm:"primaryKey"`
    Name string `gorm:"not null"`
}

func main() {

    db, err := gorm.Open(mysql.Open("root:root@tcp(127.0.0.1:3306)/testDB?charset=utf8mb4&parseTime=true&loc=Local"), &gorm.Config{
        NamingStrategy: schema.NamingStrategy{
            SingularTable: false,
        },
    })
    if err != nil {
        return
    }

    rdb := redis.NewFailoverClient(&redis.FailoverOptions{
        MasterName:    "mymaster",
        SentinelAddrs: []string{"127.0.0.1:26379"},
    })
    if err := rdb.Ping().Err(); err != nil {
        panic(err)
    }

    db.AutoMigrate(&Camera{})

    ch := make(chan struct{})

    for i := 0; i < 5; i++ {
        go func(index int) {
            defer func() {
                ch <- struct{}{}
            }()

            if lock := rdb.SetNX("c1", "random", 5*time.Second).Val(); !lock {
                return
            }
            db.Debug().Transaction(func(tx *gorm.DB) error {
                ca := []Camera{}

                if tx.Raw("SELECT * FROM cameras WHERE name IN ('c1')").Scan(&ca).RowsAffected == 0 {
                    var cs []Camera
                    cs = append(cs, Camera{Name: "c1"})
                    return tx.Create(&cs).Error
                }
                return nil
            }, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
            rdb.Del("c1")
        }(i)
    }

    for i := 0; i < 5; i++ {
        <-ch
    }
}

删除

tidb大数据的删除

在删除大量数据的时候,建议使用 Delete from t where xx limit 5000(xx 建议在满足业务过滤逻辑下,尽量加上强过滤索引列或者直接使用主键选定范围,如 id >= 5000n+m and id <= 5000(n+1)+m 这样的方案,通过循环来删除,用 Affected Rows == 0 作为循环结束条件,这样避免遇到事务大小的限制(否则Tidb会报Transaction too large)。如果一次删除的数据量非常大,这种循环的方式会越来越慢,因为每次删除都是从前向后遍历,前面的删除之后,短时间内会残留不少删除标记(后续会被 GC 掉),影响后面的 Delete 语句。如果有可能,建议把 Where 条件细化。例如如下删除一天的数据:

for i from 0 to 23:
    while affected_rows > 0:
        delete * from t where insert_time >= i:00:00 and insert_time < (i+1):00:00 limit 5000;
        affected_rows = select affected_rows()

软删除和硬删除

目前软删除的做法

  • 在表内加 isDeleted bool
  • 在表内添加 deleted_at timestamp (可以为null) (gorm这类orm框架支持,gorm会自动为其加上索引,可以结合业务考虑删除掉此索引)
  • 将删除数据存储到另一个表内 (看过一些软件是这样实现的,感觉效率高,开发稍微费事点)

设计软删除原则

  • 考虑是否一定需要软删除 ,软删除主要是为了数据恢复已经后续统计等,不是真正意义上的删除(例如产品下架)
  • 考虑数据业务量,读和写的比例
  • 读条件针对where条件复杂 并且业务量较大的建议不要使用deleted_at,可以考虑使用做法3

查询

索引

MySQL的索引

mysql的索引,这里主要指innodb,使用b+树作为底层数据结构,主键索引的b+树中,key为主键值,所有的数据都存储于b+树的叶子节点的value中;而其他非主键的索引,也叫作二级索引(辅助索引),其数据结构也是一个b+树,但是叶子节点存储的值为主键值,所以二级索引查询数据分两步,第一步查到主键值,第二步通过主键查找具体的数据

Tidb的索引

tidb索引的底层数据结构为 LSM-Tree

索引的使用

  • 合理使用索引,不要让索引失效,也不要建立过多索引(索引失效的情况很多,不多赘述
  • 合理覆盖索引进行查询,减少二次寻址
  • 表一定要建主键,通过主键查询速度是最快的
  • 索引过多可能会影响优化器对索引的选择,非必要情况下可以强制使用索引查询force index(idx_name)

其他

  • 不需要的列就不用返回了,能不*就不*
  • join尽量从小表驱动大表进行连接查询
  • 能limit就limit,减少不必要的数据返回

更新

for update

对于数据一致性要求比较高的场景下,要使用好排他锁,尽量对主键/unique索引/普通索引所在字段建立条件查询,这样锁的粒度能控制在行级别。

排查分析

慢日志

tidb慢日志

select query_time, query, user
from information_schema.slow_query
where is_internal = false  -- 排除 TiDB 内部的慢查询 SQL
order by query_time desc;

Explain

查看是否使用了索引

优化

针对mysql

  • 分区表
  • 垂直/水平分表
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:32:03  更:2022-03-30 18:32:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:53:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码