mysql 海量数据设计:对数据库存储有深入研究

这篇具有很好参考价值的文章主要介绍了mysql 海量数据设计:对数据库存储有深入研究。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

索引:

聚簇索引

二级索引

联合索引:最左匹配原则、自动优化顺序

索引优化方向:

存储空间

主键选择:自增主键、随机主键、业务主键

如何设计一个雪花算法:

正数 + 时间戳 + 机器id(固定) + 服务id + 序号

package util;
 
import java.util.Date;
 
/**
 * @ClassName: SnowFlakeUtil
 */
public class SnowFlakeUtil {
 
    private static SnowFlakeUtil snowFlakeUtil;
    static {
        snowFlakeUtil = new SnowFlakeUtil();
    }
 
    // 初始时间戳(纪年),可用雪花算法服务上线时间戳的值
    // 1650789964886:2022-04-24 16:45:59
    private static final long INIT_EPOCH = 1650789964886L;
 
    // 时间位取&
    private static final long TIME_BIT = 0b1111111111111111111111111111111111111111110000000000000000000000L;
 
    // 记录最后使用的毫秒时间戳,主要用于判断是否同一毫秒,以及用于服务器时钟回拨判断
    private long lastTimeMillis = -1L;
 
    // dataCenterId占用的位数
    private static final long DATA_CENTER_ID_BITS = 5L;
 
    // dataCenterId占用5个比特位,最大值31
    // 0000000000000000000000000000000000000000000000000000000000011111
    private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
 
    // dataCenterId
    private long dataCenterId;
 
    // workId占用的位数
    private static final long WORKER_ID_BITS = 5L;
 
    // workId占用5个比特位,最大值31
    // 0000000000000000000000000000000000000000000000000000000000011111
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
 
    // workId
    private long workerId;
 
    // 最后12位,代表每毫秒内可产生最大序列号,即 2^12 - 1 = 4095
    private static final long SEQUENCE_BITS = 12L;
 
    // 掩码(最低12位为1,高位都为0),主要用于与自增后的序列号进行位与,如果值为0,则代表自增后的序列号超过了4095
    // 0000000000000000000000000000000000000000000000000000111111111111
    private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
 
    // 同一毫秒内的最新序号,最大值可为 2^12 - 1 = 4095
    private long sequence;
 
    // workId位需要左移的位数 12
    private static final long WORK_ID_SHIFT = SEQUENCE_BITS;
 
    // dataCenterId位需要左移的位数 12+5
    private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
 
    // 时间戳需要左移的位数 12+5+5
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
 
    /**
     * 无参构造
     */
    public SnowFlakeUtil() {
        this(1, 1);
    }
 
    /**
     * 有参构造
     * @param dataCenterId
     * @param workerId
     */
    public SnowFlakeUtil(long dataCenterId, long workerId) {
        // 检查dataCenterId的合法值
        if (dataCenterId < 0 || dataCenterId > MAX_DATA_CENTER_ID) {
            throw new IllegalArgumentException(
                    String.format("dataCenterId 值必须大于 0 并且小于 %d", MAX_DATA_CENTER_ID));
        }
        // 检查workId的合法值
        if (workerId < 0 || workerId > MAX_WORKER_ID) {
            throw new IllegalArgumentException(String.format("workId 值必须大于 0 并且小于 %d", MAX_WORKER_ID));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
    }
 
    /**
     * 获取唯一ID
     * @return
     */
    public static Long getSnowFlakeId() {
        return snowFlakeUtil.nextId();
    }
 
    /**
     * 通过雪花算法生成下一个id,注意这里使用synchronized同步
     * @return 唯一id
     */
    public synchronized long nextId() {
        long currentTimeMillis = System.currentTimeMillis();
        System.out.println(currentTimeMillis);
        // 当前时间小于上一次生成id使用的时间,可能出现服务器时钟回拨问题
        if (currentTimeMillis < lastTimeMillis) {
            throw new RuntimeException(
                    String.format("可能出现服务器时钟回拨问题,请检查服务器时间。当前服务器时间戳:%d,上一次使用时间戳:%d", currentTimeMillis,
                            lastTimeMillis));
        }
        if (currentTimeMillis == lastTimeMillis) {
            // 还是在同一毫秒内,则将序列号递增1,序列号最大值为4095
            // 序列号的最大值是4095,使用掩码(最低12位为1,高位都为0)进行位与运行后如果值为0,则自增后的序列号超过了4095
            // 那么就使用新的时间戳
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                currentTimeMillis = getNextMillis(lastTimeMillis);
            }
        } else { // 不在同一毫秒内,则序列号重新从0开始,序列号最大值为4095
            sequence = 0;
        }
        // 记录最后一次使用的毫秒时间戳
        lastTimeMillis = currentTimeMillis;
        // 核心算法,将不同部分的数值移动到指定的位置,然后进行或运行
        // <<:左移运算符, 1 << 2 即将二进制的 1 扩大 2^2 倍
        // |:位或运算符, 是把某两个数中, 只要其中一个的某一位为1, 则结果的该位就为1
        // 优先级:<< > |
        return
                // 时间戳部分
                ((currentTimeMillis - INIT_EPOCH) << TIMESTAMP_SHIFT)
                // 数据中心部分
                | (dataCenterId << DATA_CENTER_ID_SHIFT)
                // 机器表示部分
                | (workerId << WORK_ID_SHIFT)
                // 序列号部分
                | sequence;
    }
 
    /**
     * 获取指定时间戳的接下来的时间戳,也可以说是下一毫秒
     * @param lastTimeMillis 指定毫秒时间戳
     * @return 时间戳
     */
    private long getNextMillis(long lastTimeMillis) {
        long currentTimeMillis = System.currentTimeMillis();
        while (currentTimeMillis <= lastTimeMillis) {
            currentTimeMillis = System.currentTimeMillis();
        }
        return currentTimeMillis;
    }
 
    /**
     * 获取随机字符串,length=13
     * @return
     */
    public static String getRandomStr() {
        return Long.toString(getSnowFlakeId(), Character.MAX_RADIX);
    }
 
    /**
     * 从ID中获取时间
     * @param id 由此类生成的ID
     * @return
     */
    public static Date getTimeBySnowFlakeId(long id) {
        return new Date(((TIME_BIT & id) >> 22) + INIT_EPOCH);
    }
 
    public static void main(String[] args) {
        SnowFlakeUtil snowFlakeUtil = new SnowFlakeUtil();
        long id = snowFlakeUtil.nextId();
        System.out.println(id);
        Date date = SnowFlakeUtil.getTimeBySnowFlakeId(id);
        System.out.println(date);
        long time = date.getTime();
        System.out.println(time);
        System.out.println(getRandomStr());
 
    }
 
}

雪花算法优点:

  • 高并发分布式环境下生成不重复 id,每秒可生成百万个不重复 id。
  • 基于时间戳,以及同一时间戳下序列号自增,基本保证 id 有序递增。
  • 不依赖第三方库或者中间件。
  • 算法简单,在内存中进行,效率高。
  • 不是连续的,找不到规律

雪花算法缺点:

  • 依赖服务器时间,服务器时钟回拨时可能会生成重复 id。算法中可通过记录最后一个生成 id 时的时间戳来解决,每次生成 id 之前比较当前服务器时钟是否被回拨,避免生成重复 id。

怎样建索引最好?

单列索引,如果有业务唯一索引,可不使用自增id

组合索引:尽量保证最左匹配

哪些特殊情况索引不会失效?

索引合并: or查询

什么情况索引会失效?

索引隐式类型转换: varchar字段,传long,不报错,但不走索引,全表扫描

包含计算

索引区分度过低:回表太多次,还不如走全表扫描

条件超出索引范围:走索引没意义了

我的一些经验:

Mysql 为什么默认定义varchar(255) 而不是varchar(256)

char, varchar类型的值,会有一个长度标识位来存值长度。
当定义varchar长度小于等于255时,长度标识位需要一个字节;
当大于255时,长度标识位需要两个字节

真实使用空间是:255 + 1 + 2 = 258字节

1、utf8mb4: 表情符号

2、时间使用long: 兼容数据库切换

3、boolean使用tinyint:: 兼容数据库切换

4、枚举使用tinyint: 兼容数据库切换

5、金额建议使用long: 兼容数据库切换

6、索引数量不要太多

7、单个索引字段不超过5个:有一些算法的,记不大清了,可以百度搜一下,组合索引是否限制长度

8、字符串索引使用前缀索引:最好使用前缀索引

分库分表

啥时候分表?

数据达到千万级

为什么达到千万级要考虑分表?

因为b+树超过三层,查询会变慢

有哪些方案?

垂直:库、表 

如何垂直?

不常用的数据,做垂直分库分表

水平:

如何水平?

取模:

        如何取模?  id % n个库表,写好取模算法

冷热分区:常用语消息历史数据保存。之前做大数据时,用到的。不影响指定时间的数据查询,还能保证定期数据统计PV/UV

基因注入法:包含业务分类

什么是基因注入法? 雪花算法增加业务id

基因注入法优点和缺点?

优点:尽可能的保证同类数据在同一个库表中

缺点:数据拆分不均匀

        

水平分库分表,扩容(一开始分表分少了)怎么做?有哪些方案?

1、简单做法,不做数据迁移

2、

分页怎么做?文章来源地址https://www.toymoban.com/news/detail-423644.html

到了这里,关于mysql 海量数据设计:对数据库存储有深入研究的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【MySQL 数据库】9、存储过程

    🌱 存储过程是事先经过编译并存储在数据库中的 SQL 语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,可以提高数据处理效率 🌱 存储过程思想上很简单:就是数据库 SQL 语言层面的代码封装与重用 🍃 【封装,复用

    2024年02月08日
    浏览(34)
  • 数据库(MySQL)的存储过程

    存储过程是事先经过编译并存储在数据库中的一段SQL 语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。 存储过程思想上很简单,就是数据库SQL 语言层面的代码封装与重用。 特点

    2024年02月10日
    浏览(33)
  • MySQL数据库之存储引擎

    MySQL中的数据用各种不下同的技术存储在文件中,每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力,这些不同的技术以及配套的功能在MySQL中称为存储引擎。 存储引擎是MySQL将数据存储在文件系统中的存储方式或者存储格式。 存储引擎是

    2024年02月03日
    浏览(39)
  • 【SQL Server】数据库开发指南(八)高级数据处理技术 MS-SQL 事务、异常和游标的深入研究

    本系列博文还在更新中,收录在专栏:#MS-SQL Server 专栏中。 本系列文章列表如下: 【SQL Server】 Linux 运维下对 SQL Server 进行安装、升级、回滚、卸载操作 【SQL Server】数据库开发指南(一)数据库设计的核心概念和基本步骤 【SQL Server】数据库开发指南(二)MSSQL数据库开发对

    2024年02月07日
    浏览(64)
  • 【MySql系列】深入解析数据库索引

    MySQL索引是数据库中一个关键的概念,它可以极大地提高查询性能,加快数据检索速度。但是,要充分发挥索引的作用,需要深入理解它们的工作原理和使用方式。 在本文中,我们将深入解析MySQL索引,探讨它们的重要性、类型、创建、维护以及最佳实践。 在数据库中,索引

    2024年02月08日
    浏览(53)
  • MySQL数据库中的存储过程以及存储函数练习

     字段名       数据类型       主键    外键    非空   唯一    自增       id             INT               是      否       是       是           否    name    VARCHAR(50)   否      否       是       否           否    glass   VARCHAR(50)     否   

    2024年02月15日
    浏览(33)
  • 【MySQL数据库 | 第十六篇】存储引擎

    目录  前言:  MySQL体系结构图: 存储引擎简介: 1. InnoDB存储引擎: 2. MyISAM存储引擎: 3. MEMORY存储引擎: 4. NDB Cluster存储引擎: 5. ARCHIVE存储引擎: 存储引擎语法: ACID与行级锁:  总结: 经过前面15篇的学习,我们已经学完了SQL的基本语法内容,大致掌握了数据库的操作

    2024年02月08日
    浏览(34)
  • MySQL数据库——MySQL修改存储过程(ALTER PROCEDURE)

    在实际开发过程中,业务需求修改的情况时有发生,所以修改 MySQL 中的存储过程是不可避免的。 MySQL 中通过 ALTER PROCEDURE 语句来修改存储过程。下面将详细讲解修改存储过程的方法。 MySQL 中修改存储过程的语法格式如下: ALTER PROCEDURE 存储过程名 [ 特征 ... ] 特征 指定了存储

    2024年02月04日
    浏览(32)
  • Mysql数据库的存储引擎——必看教程

    目录 一、什么是存储引擎 二、MySQL支持的存储引擎 三、常见的存储引擎 1.InnoDB存储引擎         2.MyISAM存储引擎         3.MEMORY存储引擎 四、选择存储引擎 总结  ✨✨✨大家好,我是会飞的鱼-blog,今天我来给大家介绍一下Mysql,有不足之处,请大家多多指教。感谢大

    2024年02月03日
    浏览(33)
  • 【后端面经-数据库】MySQL的存储引擎简介

    目录 MySQL的存储引擎 0. 存储引擎的查看和修改 1. MyISAM 2. InnoDB 3. MEMORY 4. MERGE 5. 总结 6. 参考博客 mysql主要有四类存储引擎,目前主要使用InnoDB作为存储引擎。 查看当前数据库的默认存储引擎 查看当前数据库所支持的存储引擎 查看支持事务处理的存储引擎 设置新表的存储引擎

    2024年02月08日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包