zoukankan      html  css  js  c++  java
  • 使用Mysql分区表对数据库进行优化

    早期工作中没有做好足够的设计,目前记录表单表数据2000w且无有效索引,表现是分页缓慢,模糊查询拉闸。

    当前业务中,写操作会多于读操作,时不时会遇到慢SQL占用过多的数据连接,导致写操作无法正常进行。作为记录表有着明显的冷热数据,综合考虑下使用数据分区表解决读操作过慢的问题

    下面是问题解决记录:

    1 分离热点数据

    对记录表进行分区,缩小数据筛选范围
    这里我选用的时间字段 create_time[TIMESTAMP]

    ALTER TABLE record PARTITION by RANGE(UNIX_TIMESTAMP(create_time))
    (
    PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-01-01 00:00:00') ),
    PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-02-01 00:00:00') ),
    PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-03-01 00:00:00') ),
    PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-04-01 00:00:00') ),
    PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-05-01 00:00:00') ),
    PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-06-01 00:00:00') ),
    PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-07-01 00:00:00') ),
    PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-08-01 00:00:00') ),
    PARTITION p9 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-09-01 00:00:00') ),
    PARTITION p10 VALUES LESS THAN (UNIX_TIMESTAMP('2020-10-01 00:00:00') )
    )
    

    这里有几个常见的错误

    • A PRIMARY KEY must include all columns in the table's partitioning function
    • A UNIQUE INDEX must include all columns in the table's partitioning function

    意思是表上的每一个唯一索引都必须位于分区表的表达式上,如果我选用create_time作为分区字段,那么这个字段就必须是唯一索引。【PRIMARY KEY或者 UNIQUE INDEX】
    所以删除原有的PRIMARY KEY【主键id】建立联合主键

    ALTER TABLE record DROP PRIMARY KEY, ADD PRIMARY KEY(id,create_time);

    使用下面命令查看各分区记录数量

    SELECT PARTITION_NAME,TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'record';

    分析SQL判断查询是否区分分区

    EXPLAIN PARTITIONS SELECT id,create_time FROM table_name WHERE create_time> '2020-03-01 00:00:00' AND create_time< NOW()

    查询可以区分分区,不再全表查询,优化的初步目的达到。

    2 优化查询效率

    业务中涉及分页操作,最常见的分页语法中包括2条SQL

    • 获取记录总数 SELECT COUNT(*)
    • 对记录进行分页 SELECT * FROM table_name WHERE xxxxxxx LIMIT n , m

    使用分区表后,仅仅是减少了数据筛选范围【2000w数据表只使用最近2月的分区,数据量降低为300w内】,查询效率提升了70%【45s -> 15s】查询耗时还在10s以上并没有完全解决问题

    2.1 选择合适的存储引擎

    在InnoDB存储引擎下,随着表数据的增大 COUNT(*)和LIMIT 都会变得极其耗时。
    MYISAM引擎倒是非常快,但是该引擎并不支持行级锁,读操作是共享锁,写操作是排他锁,支持并发插入,写压力过大情况下可能会遇到表锁情况,长期处于Locked状态。
    综合考虑下使用InnoDB

    2.2 SQL和业务调整

    在业务上做了一定的取舍,去掉了分页的最后一页和输入自定义页码操作,只留下了上下翻页和最近几页跳页面。【参考58同城页面】

    这个有些类似ES中的游标查询【scroll】,前后端配合完成,一页一页的查询,每次需要知道当前的有游标也就是主键ID,上页下页和PageSize

    SELECT * FROM table_name WHERE id > scroll and id < scroll + pageSize

    还见过另一种SQL优化方案,只需要后端即可完成,效率相对低一些 存在limit过大的问题

    SELECT * FROM table_name where id >= (SELECT id FROM table_name LIMIT (pageNo-1) * pageSize, 1) LIMIT pageSize

    2.3 索引的调整

    分区表每一个分区都索引独立存储,记录表涉及到查询,对查询的字段建立索引

    增加记录名索引:CREATE INDEX index_name ON table_name(table_field)
    最终的查询SQL: SELECT id,name,create_time FROM table_name WHERE table_field like 'xxxx%'AND create_time > '2020-03-01 00:00:00' AND create_time < NOW()
    分析SQL:使用Explain ,发现命中索引 查询和分页耗时在0.01-0.04之间,基本满足要求。

    以上是使用分区表对大数据库表进行的优化,也存在一些业务上的妥协和局限,比如为了查询命中索引like必须从前到后匹配查询,分页不能跳到指定页面。

    若想不在业务上做妥协,可以采取ES来做分页,数据库做基础查询,又或者使用Sphinx来做全文搜索。
    业务开发的复杂度,数据的精确程度,以及时效性,三者通常来说指只能满足其二。在不同业务情况下,做不同的取舍,就仁者见仁智者见智了。

  • 相关阅读:
    Oracle的rownum原理和使用
    ehcache 使用笔记
    @PostConstruct 注解
    Dubbo java.io.IOException: Can not lock the registry cache file
    java 学习 todoList
    因为本地没有配置 localhost 导致的 eclipse 的奇葩问题
    cheatsheet——mac 上的一款可以显示软件所有快捷键的小工具
    对象作为 map 的 key 时,需要重写 equals 方法和 hashCode 方法
    eclipse一直报An internal error occurred during: "Building workspace". GC overhead limit exceeded
    如何修改SVN客户端中保存的密码
  • 原文地址:https://www.cnblogs.com/threecha/p/12744080.html
Copyright © 2011-2022 走看看