SupCube

  • 主页
  • 随笔
所有文章 友链 关于我

SupCube

  • 主页
  • 随笔

大数据存储之水平分表方案

2021-05-10

      随着互联网的发展潮流,慢慢衍射出需要大数据存储的场景;而且在各行各业中大数据存储场景越来越常见;在面对大数据的读取过程中我们往往会考虑到ES、Redis等非关系型数据库的使用;这些技术的引入的确解决了数据读取问题,然而这些数据也需要在关系型数据库中存储,那么如何有效解决这一问题呢?本文拿mysql为例来阐述对于大数据存储方面的一些方案;

     一,分表场景    

    对于Mysql而言在面对大数据存储的场景中若数据量超过一定量(一般参考500W)之后就要考虑分库或者分表方式来建立关系型数据库;(如果数据量特别大,可以考虑分库方案,本文只考虑分表方案);

    二,分表类型

    Mysql常用分表策略有:垂直拆分(纵向拆分)和水平拆分(横向拆分)

    所谓垂直拆分就是将一个大表拆分成:1:1 的一些小表;它们的拆分原则往往是依据业务不同,即“一失一的”,这样分的目的是减少单个表的存储空间;表与表之间通过业务外键关联;这样拆分不会减少单表的数据量但是可以提升对标的操作性能;

    所谓水平拆分是将一个大表拆分成一系列小表;这些表之间是完全并列的关系;各个表之间的结构完全一致;(本文主要讨论水平分表方案)

   三,水平分表的方法

   水平分表有些数据库中间件可以很方便实现此功能如mycat;但是通过mycat官方指出采用mycat方式来进行分表,因为内部可能考虑复杂情形的联合查询等情况因此性能会降低10%-30%对于性能不是很强劲的mysql来说这些损失也是挺可惜的;因此业界普遍采用路由表方式来进行“手动”维护分表;目前分表一般采用如下几种:1)Hash、取模;2)时间;3)区间;4)路由表;

  1.    所谓的分表方式均只有一个目标即如何确认数据的存放位置。即通过如上算法确定表序号;

   3.1Hash、取模
   该方法是对操作的数据主键进行hash取模追踪取到对应的表序号;
   该算法的核心是找到表的下标,若id为字符串则可以对id进行hash最终取模得到一个下标;如对一个新闻表采用分表方式存储,若拆分为10个表,若需要插入id为20001的数据则该条数据需要存到下标为1的表中(news_1);
   按照id对该表进行读取同样需要计算该id所对应的数据存在于哪个表中;
   3.2时间
   该方法对于日志数据较为普遍,譬如对于大量日志数据的存储则可以按照时间格式命名表的后缀;如同样对于新闻表采用按照时间方式分表(如一个月一张表)可以采用new_202105的方式进行。如果我们需要存储创建时间在2021年五月的数据则存放在后缀为202105的表中;
   读取同样需要指明当前数据存放在哪个时间段内;
   3.3区间
   该方法可以将各个表内的数据均匀分配;譬如规定好每个表中的数据10000000(1KW)则可以根据id计算出当前数据所在的数据表;如id范围为:【1——10000000】存在于new_1中;【10000001-20000000】存在于new_2中。这样分表的优点在于容易计算表的后缀:(id/10000000+1)但是若对于id不为数字类型则实现起来有困难;
   3.4路由表
   该方法是最灵活的实现方式;对于各个分表内部存放的数据范围有明确的标识,我们需要新建一个路由表来存放如上数据;

+-----------------+-------------+------+-----+---------+----------------+
| Field           | Type        | Null | Key | Default | Extra          |
+-----------------+-------------+------+-----+---------+----------------+
| id              | int(11)     | NO   | PRI | NULL    | auto_increment |
| root_table_name | varchar(64) | NO   |     | NULL    |                |
| table_name      | varchar(64) | NO   |     | NULL    |                |
| min_id          | int(11)     | NO   |     | NULL    |                |
| max_id          | int(11)     | NO   |     | NULL    |                |
+-----------------+-------------+------+-----+---------+----------------+


对于操作一条数据可以通过该表查询本数据所在的分表名称;
这样做的优点很明显:
1)可以灵活控制每个分表的数据量;
2)扩展表或者调整每个分表的数据很方便;

+----+-----------------+------------+----------+----------+
| id | root_table_name | table_name | min_id   | max_id   |
+----+-----------------+------------+----------+----------+
|  2 | document        | document_1 | 12000001 | 35000000 |
+----+-----------------+------------+----------+----------+

如定义id范围在:【12000001-35000000】之间的数据存在于document_1中;

    


  • 已发布
  • 取消发布
  • 修改

展开全文 >>

Mysql-常用命令

2020-04-23

   1.数据库操作

create database 数据库名;
create database if not exists 数据库名; -- 如果不存在则创建
show databases; --展示所有数据库
show create database 数据库名; --展示创建数据库脚本
drop database 数据库名称; --删除数据库
use 数据库名; --切换数据库

   2.数据表操作

show tables;-- 展示所有表
desc 表名; --显示表结构
show create table 表名; --查看建表语句
drop table 表名; --删除表
create table 表名(
    id int primary key,
   字段名 类型(长度) 约束,
   字段名 类型(长度) 约束
);--创建数据表
alter TABLE 表名 CHARACTER SET 字符集 --修改表字符集

   3.字段操作

alter table 表名 drop primary key; --删除字段
alter TABLE 表名 DROP 列名; --删除字段
alter TABLE 表名 CHANGE 列名 新列名 列类型; --修改表名
alter table 表名 add 列名 列类型; --新增列

4.索引操作

alter table 表名 add index 索引名 (字段名1[,字段名2 …]); --加索引
alter table 表名 add primary key (字段名);--加关键字索引
alter table 表名 add unique 索引名 (字段名); --加唯一索引
alter table 表名 drop index 索引名; --删除索引
  • 已发布
  • 取消发布
  • 修改

展开全文 >>

Mysql-NULL值探索

2020-03-30

Mysql中NULL和‘’的区别;

在Mysql中字段中可以存储为NULL和‘’;

对于NULL就是在字段中存储NULL值,空值在字段中存在‘’本次主要从存储空间、插入查询方式、count 函数使用区别以及索引字段这四个方面来讲述两者区别;

1.空间占用

在此我想说下mysql中的两个函数:length( )和char_length( )。

length( ) 返回被计算字符串 占用的 字节数;

char_length( )返回被计算字符串占用的字符长度;

我们通过:length来查询 NULL,'',以及‘0’的字节长度;

mysql>  select length(NULL), length(''), length('0');
+--------------+------------+-------------+
| length(NULL) | length('') | length('0') |
+--------------+------------+-------------+
|         NULL |          0 |           1 |
+--------------+------------+-------------+
1 row in set (0.00 sec)

可以发现NULL占用 NULL 字节,''占用0字节,那么NULL到底是多少空间呢?
Mysql 官方文档:NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte
.通过文档翻译大概意思是:一个null字段占用1bit...;

尽管很小但是还是占用空间的

总价如下:‘’字符串不占用空间空间为0.但是NULL占用1bit;

2.插入查询方式

NULL字段查询不能用= 、<> 、或者>等操作。

必须使用  is null  或者is  not null 来查询;

但是‘’字段可以用 = 、<> 、或者>等操作。

3.count 函数使用

 有如下表:

mysql> select * from user;
+----+---------+
| id | name    |
+----+---------+
|  2 | tom     |
|  3 | supcube |
|  4 | NULL    |
+----+---------+
3 rows in set (0.00 sec)

如果按照name查询个数系统会自动过滤掉null值:

mysql> select count(name) from user;
+-------------+
| count(name) |
+-------------+
|           2 |
+-------------+
1 row in set (0.00 sec)

4.索引使用

我在user表中新增索引:index_name (name)

mysql> show keys from user;
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY    |            1 | id          | A         |           2 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | index_name |            1 | name        | A         |           3 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)

再执行查询查询空值:

mysql> explain
    -> select * from user where name is NULL;
+----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key        | key_len | ref   | rows | filtered | Extra                    |
+----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+--------------------------+
|  1 | SIMPLE      | user  | NULL       | ref  | index_name    | index_name | 768     | const |    1 |   100.00 | Using where; Using index |
+----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

由此看见mysql空值不影响索引使用,查询NULL值也不影响索引;

但在sqlserver中:

在SQL SERVER中,定长记录的NULL值占用存储空间,而变长记录的NULL值不占用存储空间。(结论来源:https://blog.csdn.net/h2503652646/article/details/84991240  )
  • 已发布
  • 取消发布
  • 修改

展开全文 >>

ArrayList 与 LinkList 比较

2020-03-22

LinkList 是通过双向链表方式实现的,因此新增,删除效率高;

ArrayList 是通过动态数组方式实现的,如果频繁新增 或者修改会改变数组长度需要重新开辟内存空间复制内部元素因此需要消耗额外资源;

如果是随机查询ArrayList 由于具有数组地址连续的特性因此效率会远远高于LinkList

  • 已发布
  • 取消发布
  • 修改

展开全文 >>

Java两个集合取差集

2020-03-21

在java开发过程中我们往往会遇到对两个集合取交集操作。譬如如下场景:

在权限管理中我们选择一个用户对用户菜单权限进行选择然后统一保存。我们的操作往往是这样的:

第一步:移除当前用户现有权限。

第二步:新增所选择的权限。

但是这样似乎并不是很理想,有时候我们对权限根本没有任何操作或者做很少的改动我们就需要全部移除,然后全部新增。

我推荐操作方法如下:

如果选择的菜单权限集合成为listA,当前用户所有的权限集合为listB。

我们只需删除:listB和listA的差集。新增listA和listB的差集即可。那如何操作呢?

两层循环判断?太繁琐,java8提供的Stream即可很好解决如下:

List<UserPower> listCreate= listA.stream().  filter(a-> listB.stream().allMatch(b-> !a.getPowerId().equals(b.getPowerId()))).collect(Collectors.toList());

如上代码即可获得listA-listB方便,快捷;


  • 已发布
  • 取消发布
  • 修改

展开全文 >>

mysql安全模式(safe_updates)

2020-03-04

mysql数据库在用workbench批量更新时有时候会出现:

    You are using safe update mode and you tried to update a table without a WHERE clause that uses a KEY column.” 

原因:

safe mode下,要强制安全点,update只能跟where了

解决方案:

 SET SQL_SAFE_UPDATES=0;


  • 已发布
  • 取消发布
  • 修改

展开全文 >>

sqlserver阻止保存要求重新创建表的更改

2019-07-10

数据库修修改之后保存时提示“阻止保存要求重新创建表的更改"

这种情况一般是数据库安全所致,解决办法如下:

依次展开数据库管理工具中的。

【工具】/【选项】展开“设计器=>"表设计器和数据库设计器"

取消右侧“阻止保存要求重新创建表的更改”选项即可

  • 已发布
  • 取消发布
  • 修改

展开全文 >>

sqlserver时间格式转化

2019-06-30

最近在开发信息管理系统经常经常遇到在数据库中数据转化。特别是时间格式的转化;为了后期方便查找现收集整合如下以备后期使用:
语句及查询结果:
SELECT CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AM

SELECT CONVERT(varchar(100), GETDATE(), 1): 05/16/06

SELECT CONVERT(varchar(100), GETDATE(), 2): 06.05.16

SELECT CONVERT(varchar(100), GETDATE(), 3): 16/05/06

SELECT CONVERT(varchar(100), GETDATE(), 4): 16.05.06

SELECT CONVERT(varchar(100), GETDATE(), 5): 16-05-06

SELECT CONVERT(varchar(100), GETDATE(), 6): 16 05 06

SELECT CONVERT(varchar(100), GETDATE(), 7): 05 16, 06

SELECT CONVERT(varchar(100), GETDATE(), 8): 10:57:46

SELECT CONVERT(varchar(100), GETDATE(), 9): 05 16 2006 10:57:46:827AM

SELECT CONVERT(varchar(100), GETDATE(), 10): 05-16-06

SELECT CONVERT(varchar(100), GETDATE(), 11): 06/05/16

SELECT CONVERT(varchar(100), GETDATE(), 12): 060516

SELECT CONVERT(varchar(100), GETDATE(), 13): 16 05 2006 10:57:46:937

SELECT CONVERT(varchar(100), GETDATE(), 14): 10:57:46:967

SELECT CONVERT(varchar(100), GETDATE(), 20): 2006-05-16 10:57:47

SELECT CONVERT(varchar(100), GETDATE(), 21): 2006-05-16 10:57:47.157

SELECT CONVERT(varchar(100), GETDATE(), 22): 05/16/06 10:57:47 AM

SELECT CONVERT(varchar(100), GETDATE(), 23): 2006-05-16

SELECT CONVERT(varchar(100), GETDATE(), 24): 10:57:47

SELECT CONVERT(varchar(100), GETDATE(), 25): 2006-05-16 10:57:47.250

SELECT CONVERT(varchar(100), GETDATE(), 100): 05 16 2006 10:57AM

SELECT CONVERT(varchar(100), GETDATE(), 101): 05/16/2006

SELECT CONVERT(varchar(100), GETDATE(), 102): 2006.05.16

SELECT CONVERT(varchar(100), GETDATE(), 103): 16/05/2006

SELECT CONVERT(varchar(100), GETDATE(), 104): 16.05.2006

SELECT CONVERT(varchar(100), GETDATE(), 105): 16-05-2006

SELECT CONVERT(varchar(100), GETDATE(), 106): 16 05 2006

SELECT CONVERT(varchar(100), GETDATE(), 107): 05 16, 2006

SELECT CONVERT(varchar(100), GETDATE(), 108): 10:57:49

SELECT CONVERT(varchar(100), GETDATE(), 109): 05 16 2006 10:57:49:437AM

SELECT CONVERT(varchar(100), GETDATE(), 110): 05-16-2006

SELECT CONVERT(varchar(100), GETDATE(), 111): 2006/05/16

SELECT CONVERT(varchar(100), GETDATE(), 112): 20060516

SELECT CONVERT(varchar(100), GETDATE(), 113): 16 05 2006 10:57:49:513

SELECT CONVERT(varchar(100), GETDATE(), 114): 10:57:49:547

SELECT CONVERT(varchar(100), GETDATE(), 120): 2006-05-16 10:57:49

SELECT CONVERT(varchar(100), GETDATE(), 121): 2006

SELECT CONVERT(varchar(100), GETDATE(), 126): 2006-05-16T10:57:49.827

  • 已发布
  • 取消发布
  • 修改

展开全文 >>

C#多线程Thread-Task-Parallel用法

2019-06-30

大家在开发过程中,往往会为了提高用户体验,某些功能会采用异步进行。譬如:一个定时服务,以及用户登录日志等。这些操作在执行过程中用户不需要要等待其执行结束。对此引入了多线程处理方式。在此小编就之前用过的几种方式总结下;

1.Thread用法:
Thread是多线程最基本的操作方式;

using System;
using System.Threading;
namespace TestSupCube
{
class TestSuper
{
  static void TestThread()
  {
   //执行一些列操作  
  }
  static void Main(string[] args)
  {
      Thread mthread = new Thread(TestTrhead);
      mthread.Start();
      Console.WriteLine("SupCube Test Thread");
      Console.ReadLine();
  }
}
}

1.需要首先定义方法。
2.实例化线程引用该方法;
3.启动线程;

2.Task用法:Task是Thread的封装因此用法 几乎一样:

using System;
using System.Threading.Tasks;
namespace TestSupCube
{
class TestSuper
{
    static void TestTask()
    {
     //执行一些列操作  
    }
    static void Main(string[] args)
    {
    Task mtask = new Task(TestTask);
    mtask.Start();
    Console.WriteLine("SupCube Test Task");
    Console.ReadLine();
    }
}
}

=============================缺省方法如下:=====================================

   static void Main(string[] args)
{
    Task mtask = new Task(() =>
        {
            //执行一些列操作  
        });
    mtask.Start();
    Console.WriteLine("SupCube Test Thread");
    Console.ReadLine();
}

Task是支持多核的因此如果是多核cpu效率很高很多;
3.Parallel用法:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace TestSupCube
{
class TestSuper
{
    static void TestTask(int num)
    {
        //执行一些列操作  
    }
    static void Main(string[] args)
    {
        List<string> listtask = new List<string>() { 1, 2, 3, 4, 5 };
        Parallel.ForEach(listtask, new ParallelOptions
        {
            MaxDegreeOfParallelism = 3//开启线程数
        },
        x =>
        {
            TestTask(x);
        });
        Console.WriteLine("SupCube Test Parallel");
        Console.ReadLine();
    }
}
}

Parallel适用于:
并行处理多个作业,内部支持线程数控制;

  • 已发布
  • 取消发布
  • 修改

展开全文 >>

C#扩展方法

2019-06-30

在开发过程中,经常需要对相同的某个类型的数据进行处理。譬如:字符串自定义转化、时间格式的自定义转化等。
按照正常的开发我们需要定义一个方法,用原始的数据作为参数实体,然后在方法内部处理,然后返回处理之后的参数;
如:需要对一个字符串处理处理成int类型,如果字符串合法则返回数据,如果不合法则返回0。开发代码如下:

public class Program
{
    /// <summary>
    /// 方法如下
    /// </summary>
    /// <param name="inputNum"></param>
    /// <returns></returns>
    private static int GetNum(string inputNum)
    {
        int result;
        int.TryParse(inputNum, out result);
        return result;
    }
    /// <summary>
    /// 测试
    /// </summary>
    /// <param name="args"></param>
    static void Main(string[] args)
    {
        string test1 = "123";
        Console.WriteLine(GetNum(test1));
    }
}

但是能否有其他更“高大上”的方法呢:我们可以扩展方法。对字符串String扩展方法。代码如下:

/// <summary>
/// 扩展类
/// </summary>
public static class SupExtString
{
    public static int ToInt(this string inputNum)
    {
        int result=0;
        int.TryParse(inputNum, out result);
        return result;
    }
}

然后调用类如下:

public class Program
{

    /// <summary>
    /// 测试
    /// </summary>
    /// <param name="args"></param>
    static void Main(string[] args)
    {
        string test1 = "123";
        Console.WriteLine(test1.ToInt());
    }
}

在扩展方法的时候需要注意事项如下:
1.扩展方法必须在静态类中。
2.扩展方法必须是静态方法。
3.需要扩展的数据类型应用this关键字修饰并作为第一个参数传递。
4.如果扩展方法有参数时:参数要放到this修饰的参数之后。(上述例子是0参的实现方式);
5.引用的时候需要应用到扩展类的命名空间即可。

  • 已发布
  • 取消发布
  • 修改

展开全文 >>

加载更多
京公网安备11011102002544 © 京ICP备2024068849号-1
智汇魔方:http://www.supcube.com
  • 所有文章
  • 友链
  • 关于我
  • 切水果
  • json格式化
  • 自动分组
  • 集合差集