Tính kích thước hàng và kích thước hàng tối đa cho một bảng


10

Vấn đề:

Có cách nào để tính số byte bị chiếm dụng khi tạo bảng không, tôi biết rằng bạn có thể nhận được một số thông tin từ information_schema.tables nhưng thông tin đó không đủ chính xác.

Điều thực sự cần là số byte theo định nghĩa của bảng chỉ dành cho innodb và đối chiếu cũng có thể được coi là utf-8-general-ci

Ví dụ: kiểm tra bảng như sau

tạo bảng kiểm tra
(
col1 varchar (25),
col2 int,
col3 varchar (3),
col4 char (15),
col5 datetime
);

Bây giờ sẽ yêu cầu biết tổng kích thước hàng có thể được tích lũy trong một hàng theo các loại cột trong bảng.

Tìm thấy một số loại giải pháp tương tự trong MSSQL nhưng cần phiên bản MySQL của nó

Tập lệnh để ước tính kích thước hàng cho bất kỳ bảng nào

Bất kỳ sự giúp đỡ nào cũng được đánh giá cao.


Nó có thể phụ thuộc vào định dạng động cơ và hàng của bảng để MySQL có thể không lưu trữ ở bất cứ đâu (và thậm chí có thể không biết).
jkavalik

Chỉ cần thêm một liên kết đến chính xác những gì tôi đang tìm kiếm ... có nhưng nên có một số cách để kiểm tra một bảng và nói rằng nó sẽ chiếm nhiều byte này tùy thuộc vào cấu trúc của nó
Nawaz Sohail

Câu trả lời:


2

Sau nhiều suy nghĩ và nghiên cứu đã tìm thấy một câu trả lời thực sự hữu ích trong việc đạt được những gì được yêu cầu. Nó là một tập lệnh perl và liên kết tham chiếu là

http://dev.mysql.com/doc/refman/5.6/en/st Storage-request.html

#!/usr/bin/perl
use strict;
$| = 1;

my %DataType = (
"TINYINT"=>1, "SMALLINT"=>2, "MEDIUMINT"=>3, "INT"=>4, "INTEGER"=>4, "BIGINT"=>8,
"FLOAT"=>'$M<=24?4:8', "DOUBLE"=>8,
"DECIMAL"=>'int(($M-$D)/9)*4+int(((($M-$D)%9)+1)/2)+int($D/9)*4+int((($D%9)+1)/2)',
"NUMERIC"=>'int(($M-$D)/9)*4+int(((($M-$D)%9)+1)/2)+int($D/9)*4+int((($D%9)+1)/2)',
"BIT"=>'($M+7)>>3',
"DATE"=>3, "TIME"=>3, "DATETIME"=>8, "TIMESTAMP"=>4, "YEAR"=>1,
"BINARY"=>'$M',"CHAR"=>'$M*$CL',
"VARBINARY"=>'$M+($M>255?2:1)', "VARCHAR"=>'$M*$CL+($M>255?2:1)',
"ENUM"=>'$M>255?2:1', "SET"=>'($M+7)>>3',
"TINYBLOB"=>9, "TINYTEXT"=>9,
"BLOB"=>10, "TEXT"=>10,
"MEDIUMBLOB"=>11, "MEDIUMTEXT"=>11,
"LONGBLOB"=>12, "LONGTEXT"=>12
);

my %DataTypeMin = (
"VARBINARY"=>'($M>255?2:1)', "VARCHAR"=>'($M>255?2:1)'
);

my ($D, $M, $S, $C, $L, $dt, $dp ,$bc, $CL);
my $fieldCount = 0;
my $byteCount = 0;
my $byteCountMin = 0;
my @fields = ();
my $fieldName;
my $tableName;
my $defaultDbCL = 1;
my $defaultTableCL = 1;
my %charsetMaxLen;
my %collationMaxLen;

open (CHARSETS, "mysql -B --skip-column-names information_schema -e 'select CHARACTER_SET_NAME,MAXLEN from CHARACTER_SETS;' |");
%charsetMaxLen = map ( ( /^(\w+)/ => /(\d+)$/ ), <CHARSETS>);
close CHARSETS;

open (COLLATIONS, "mysql -B --skip-column-names information_schema -e 'select COLLATION_NAME,MAXLEN from CHARACTER_SETS INNER JOIN COLLATIONS USING(CHARACTER_SET_NAME);' |");
%collationMaxLen = map ( ( /^(\w+)/ => /(\d+)$/ ), <COLLATIONS>);
close COLLATIONS;

open (TABLEINFO, "mysqldump -d --compact ".join(" ",@ARGV)." |");

while (<TABLEINFO>) {
chomp;
if ( ($S,$C) = /create database.*?`([^`]+)`.*default\scharacter\sset\s+(\w+)/i ) {
$defaultDbCL = exists $charsetMaxLen{$C} ? $charsetMaxLen{$C} : 1;
print "Database: $S".($C?" DEFAULT":"").($C?" CHARSET $C":"")." (bytes per char: $defaultDbCL)\n\n";
next;
}
if ( /^create table\s+`([^`]+)`.*/i ) {
$tableName = $1;
@fields = ();
next;
}
if ( $tableName && (($C,$L) = /^\)(?:.*?default\scharset=(\w+))?(?:.*?collate=(\w+))?/i) ) {
$defaultTableCL = exists $charsetMaxLen{$C} ? $charsetMaxLen{$C} : (exists $collationMaxLen{$L} ? $collationMaxLen{$L} : $defaultDbCL);
print "Table: $tableName".($C||$L?" DEFAULT":"").($C?" CHARSET $C":"").($L?" COLLATION $L":"")." (bytes per char: $defaultTableCL)\n";
$tableName = "";
$fieldCount = 0;
$byteCount = 0;
$byteCountMin = 0;
while ($_ = shift @fields) {
if ( ($fieldName,$dt,$dp,$M,$D,$S,$C,$L) = /\s\s`([^`]+)`\s+([a-z]+)(\((\d+)(?:,(\d+))?\)|\((.*)\))?(?:.*?character\sset\s+(\w+))?(?:.*?collate\s+(\w+))?/i ) {
$dt = uc $dt;
if (exists $DataType{$dt}) {
if (length $S) {
$M = ($S =~ s/(\'.*?\'(?!\')(?=,|$))/$1/g);
$dp = "($M : $S)"
}
$D = 0 if !$D;
$CL = exists $charsetMaxLen{$C} ? $charsetMaxLen{$C} : (exists $collationMaxLen{$L} ? $collationMaxLen{$L} : $defaultTableCL);
$bc = eval($DataType{$dt});
$byteCount += $bc;
$byteCountMin += exists $DataTypeMin{$dt} ? $DataTypeMin{$dt} : $bc;
} else {
$bc = "??";
}
$fieldName.="\t" if length($fieldName) < 8;
print "bytes:\t".$bc."\t$fieldName\t$dt$dp".($C?" $C":"").($L?" COLL $L":"")."\n";
++$fieldCount;
}
}
print "total:\t$byteCount".($byteCountMin!=$byteCount?"\tleast: $byteCountMin":"\t\t")."\tcolumns: $fieldCount\n\n";
next;
}
push @fields, $_;
}
close TABLEINFO;

Cảm ơn tất cả sự giúp đỡ tuyệt vời.


Tôi không nhận được bất kỳ đầu ra nào khi chạy tập lệnh đó. Tôi đang thiếu gì?
srcritical

thêm -uUser -pPassvào các dòng lệnh mysql và mysqldump trong tập lệnh (hoặc --defaults-extra-file=/etc/mysql/debian.cnfthay vào đó thử trên Ubuntu / Debian) và chạy nó với cơ sở dữ liệu như là đối số đầu tiên nhưperl test.pl mydatabase
dw1

0

Bạn cần biết kích thước tính theo byte của từng trường theo loại dữ liệu ( tham chiếu MySQL tại đây ), sau đó tổng hợp các giá trị này lại với nhau.


3
không thể thực hiện bằng cách sử dụng một số truy vấn động để kiểm tra các cột có độ dài và tổng hợp nó? .. đó là lý do tại sao tôi yêu cầu nó..nếu bạn có thể chia sẻ nó sẽ rất hữu ích
Nawaz Sohail

0

Bước 1:

col1 varchar(25),  2 + avg_byte_len
col2 int,          4
col4 char(15),     1*15 or 3*15 or...
col5 datetime      Pre-5.6: 8; then 5

SELECT AVG(LENGTH(col1)) as avg_byte_len,
       AVG(CHAR_LENGTH(col1) as avg_num_chars FROM ...;

20 ký tự tiếng Anh: 2 + 1 * 20
20 ký tự Trung Đông / Slav: 2 + 2 * 20
20 ký tự châu Á: 2 + 3 * 20
20 ký tự Emoji: 2 + 4 * 20 (và bạn cần utf8mb4)

Bước 2: Thêm chúng lên.

Bước 3: Nhân với một nơi nào đó giữa 2 và 3 để cho phép sử dụng InnoDB. Tôi đã thấy rằng yếu tố đó thường hoạt động. (Nhưng không phải cho các bảng nhỏ và không nhất thiết phải tốt cho các bảng được phân đoạn.)

Tôi thấy không có lý do để lấy kích thước tối đa của mỗi cột.

Bạn có thể đến gần hơn SHOW TABLE STATUShoặc information_schemadữ liệu tương đương :

Bước 1: SELECT COUNT(*)- chúng tôi thay thếRows

Bước 2: Nhận Data_length + Index_length + Data_free

Bước 3: Chia.


cảm ơn vì sự giúp đỡ tuyệt vời của bạn, nhưng nếu một bảng có hơn 100 cột với các kiểu dữ liệu biến thể thì làm thế nào chúng ta có được ước tính kích thước hàng?
Nawaz Sohail

SELECT AVG(LENGTH(varchar_col))- Lưu ý: LENGTHđã là byte ; không cần nhân với 2/3/4. ( CHAR_LENGTHcó độ dài tính cách.)
Rick James

0

Tôi đã tạo một tập lệnh bash thô để tính kích thước hàng và cảnh báo nếu nó vượt qua giới hạn dựa trên lược đồ:

#!/bin/bash

#
# usage: mysqldump --no-data | check_row_size.sh
#

#
#
# https://dev.mysql.com/doc/refman/8.0/en/column-count-limit.html#row-size-limits
#
# The maximum row size for an InnoDB table, which applies to data stored locally within a database page, is slightly less than half a page for 4KB, 8KB, 16KB, and 32KB innodb_page_size settings.
# For example, the maximum row size is slightly less than 8KB for the default 16KB InnoDB page size.
#
#
# MariaDB [(none)]> show variables like 'innodb_page_size';
#+------------------+-------+
#| Variable_name    | Value |
#+------------------+-------+
#| innodb_page_size | 16384 |
#+------------------+-------+
#1 row in set (0.00 sec)
#
#
# Options:
# 1. Change default innodb_page_size to 32k
# 2. Change storage engine to DYNAMIC for tables
# 3. ?
#

#===========================================================================================
# Functions
#===========================================================================================
RETVAL=0

calc_row_size() {
    local -n TABLE_FIELDS=$1
    local -n TABLE_CHARSET=$2
    local FIELD_TYPE=""
    local FIELD_SIZE=""
    local FIELD=""
    local ROW_SIZE=0
    local IFS=$'|' # To split the vars using set
    for FIELD in "${TABLE_FIELDS[@]}"  
    do  
        set $FIELD
        FIELD_NAME=$1
        FIELD_TYPE=$2
        FIELD_SIZE=$3        
        calc_field_size_in_bytes $FIELD_TYPE $FIELD_SIZE $TABLE_CHARSET
        ROW_SIZE=$((ROW_SIZE + RETVAL))
        [ $DEBUG -gt 0 ] && echo "DEBUG1: Field name: $FIELD_NAME type: $FIELD_TYPE lenght: $FIELD_SIZE size: $RETVAL bytes Row size: $ROW_SIZE"
    done  
    RETVAL=$ROW_SIZE
}

calc_field_size_in_bytes() {
    local TYPE=$1
    local SIZE=$2
    local CHARSET=$3

    case $FIELD_TYPE in
        varchar)
            # https://adayinthelifeof.nl/2010/12/04/about-using-utf-8-fields-in-mysql/
            # Max 3 bytes per utf-8 chat in mysql
            case $CHARSET in
                utf8)
                    RETVAL=$((SIZE * 3))  # 3 bytes per character for utf8 
                ;;
                latin1)
                    RETVAL=$((SIZE))  # 1 byte per character for latin1
                ;;
                *)
                    echo "Unknown charset ($CHARSET), please fix the script"
                    exit 1
                ;;
            esac
        ;;
        smallint|int|bigint|tinyint|varbinary)
            RETVAL=$SIZE
        ;;
        blob)
            # https://dev.mysql.com/doc/refman/8.0/en/column-count-limit.html#row-size-limits
            # BLOB and TEXT columns only contribute 9 to 12 bytes toward the row size limit because their contents are stored separately from the rest of the row.
            RETVAL=9
        ;;
        text)
            RETVAL=12
        ;;
        timestamp)
            RETVAL=4 
        ;; 
        decimal)
            # https://dev.mysql.com/doc/refman/8.0/en/storage-requirements.html#data-types-storage-reqs-numeric
            # Each multiple of nine digits requires four bytes, and the leftover digits require some fraction of four bytes. 
            if [[ $SIZE =~ ([0-9]+),([0-9]+) ]] 
            then
              INTEGER_PART=${BASH_REMATCH[1]}
              FRACTIONAL_PART=${BASH_REMATCH[2]}

              INTEGER_BYTES=$((INTEGER_PART / 9 * 4))
              REMAINDER=$((INTEGER_PART % 9))
              case $REMAINDER in
                  0) INTEGER_BYTES=$((INTEGER_BYTES + 0)); ;;
                  1) INTEGER_BYTES=$((INTEGER_BYTES + 1)); ;;
                  2) INTEGER_BYTES=$((INTEGER_BYTES + 1)); ;;
                  3) INTEGER_BYTES=$((INTEGER_BYTES + 2)); ;;
                  4) INTEGER_BYTES=$((INTEGER_BYTES + 2)); ;;
                  5) INTEGER_BYTES=$((INTEGER_BYTES + 3)); ;;
                  6) INTEGER_BYTES=$((INTEGER_BYTES + 3)); ;;
                  7) INTEGER_BYTES=$((INTEGER_BYTES + 4)); ;;
                  8) INTEGER_BYTES=$((INTEGER_BYTES + 4)); ;;
              esac

              FRACTIONAL_BYTES=$((FRACTIONAL_PART / 9 * 4))
              REMAINDER=$((FRACTIONAL_PART % 9))
              case $REMAINDER in
                  0) FRACTIONAL_BYTES=$((FRACTIONAL_BYTES + 0)); ;;
                  1) FRACTIONAL_BYTES=$((FRACTIONAL_BYTES + 1)); ;;
                  2) FRACTIONAL_BYTES=$((FRACTIONAL_BYTES + 1)); ;;
                  3) FRACTIONAL_BYTES=$((FRACTIONAL_BYTES + 2)); ;;
                  4) FRACTIONAL_BYTES=$((FRACTIONAL_BYTES + 2)); ;;
                  5) FRACTIONAL_BYTES=$((FRACTIONAL_BYTES + 3)); ;;
                  6) FRACTIONAL_BYTES=$((FRACTIONAL_BYTES + 3)); ;;
                  7) FRACTIONAL_BYTES=$((FRACTIONAL_BYTES + 4)); ;;
                  8) FRACTIONAL_BYTES=$((FRACTIONAL_BYTES + 4)); ;;
              esac
              [ $DEBUG -gt 0 ] && echo "DEBUG1: Calulation of decimal: SIZE: $SIZE INTEGER_PART:$INTEGER_PART FRACTIONAL_PART:$FRACTIONAL_PART TOTAL = INTEGER_BYTES($INTEGER_BYTES) + FRACTIONAL_BYTES($FRACTIONAL_BYTES)"
              RETVAL=$((INTEGER_BYTES + FRACTIONAL_BYTES)) 
            else
                echo "Seems like SIZE ($SIZE) for a decimal field doesn't match pattern ([0-9]+),([0-9]+). Please investigate"
                exit 1
            fi
        ;;
        *)
            echo "Found a field type that is not handled: $TYPE. Please fix before proceeding."
            exit 1
        ;;
    esac
}


#===========================================================================================
# INIT
#===========================================================================================
INSIDE_CREATE_TABLE_STATEMENT=false # True if we are within a create table statement
TABLE_NAME=''  # Current table name
ROW_SIZE=0 # Current row size being calculated
DEBUG=0
VERBOSE=0
MAX_SIZE=8126 # Default
declare -a FIELDS # List of fields from the current CREATE TABLE statement

#===========================================================================================
# Parameters
#===========================================================================================
OPTIND=1         # Reset in case getopts has been used previously in the shell.

while getopts "hvdt:" opt; do
    case "$opt" in
    h)
        echo "Usage: mysqldump --no-data | ./check_row_size [-v|-d] [-t threshold]"
        exit 0
        ;;
    v) VERBOSE=1
        ;;
    d) DEBUG=2
        ;;
    t) MAX_SIZE=$OPTARG
        ;;
    esac
done


#===========================================================================================
# MAIN Loop - parses schema then calc row_size based on charset
#===========================================================================================
while IFS= read -r LINE
do
    [ $DEBUG -gt 1 ] && echo "DEBUG2: Read: $LINE"
    # Are we within a CREATE TABLE statement?
    if [ $INSIDE_CREATE_TABLE_STATEMENT == "false" ]
    then
        # Nope, is the current line a 'CREATE TABLE' statement?
        if [[ $LINE =~ ^"CREATE TABLE \`"([^\`]+) ]] 
        then
            [ $DEBUG -gt 0 ] && echo "CREATE TABLE FOUND!: $TABLE_NAME"
            TABLE_NAME=${BASH_REMATCH[1]} # What has been caught between pattern parenthesis
            INSIDE_CREATE_TABLE_STATEMENT='true'
            FIELDS=()
        fi
        continue # Ok, next line 
    fi
    # Is this a create table field definition line?
    if [[ $LINE =~ ^' '+'`'([^'`']+)'` '([a-z]+)'('([^')']+) ]]
    then
        FIELD_NAME=${BASH_REMATCH[1]}
        FIELD_TYPE=${BASH_REMATCH[2]}
        FIELD_SIZE=${BASH_REMATCH[3]}
        FIELDS+=( "$FIELD_NAME|$FIELD_TYPE|$FIELD_SIZE" )
        continue
    fi
    # Have we reached the end of the CREATE TABLE statement?
    if [[ $LINE =~ ^") ENGINE=InnoDB DEFAULT CHARSET="([^ ]+) ]] 
    then
        CHARSET=${BASH_REMATCH[1]}
        [ $DEBUG -gt 0 ] && echo "End of CREATE TABLE statement"
        calc_row_size FIELDS CHARSET
        ROW_SIZE=$RETVAL
        if [ $ROW_SIZE -gt $MAX_SIZE ]
        then
            echo "Table: $TABLE_NAME has a row size: $ROW_SIZE Bytes > $MAX_SIZE Bytes Charset: $CHARSET"
            # and is going to cause problem if the we upgrade to tables in ROW_FORMAT compact. See https://mariadb.com/kb/en/library/troubleshooting-row-size-too-large-errors-with-innodb/ for more details."
        fi
        INSIDE_CREATE_TABLE_STATEMENT='false'
    fi
done 

Hay nói, là một tài tài của, qua, qua, qua một tài khác, qua giữ, qua một tài khác


-1

Có một vài câu hỏi thuộc loại này, ví dụ như câu hỏi này: Cách ước tính / dự đoán kích thước dữ liệu và kích thước chỉ mục của một bảng trong MySQL

Một điểm khác biệt giữa câu hỏi đó và bảng của bạn là sự hiện diện của các chuỗi có độ dài thay đổi trong câu hỏi của bạn - hãy nhớ tính kích thước tối đa mà chúng có thể có.

Ngoài ra, hãy nhớ rằng từ phiên bản 5 trở đi có varchar(25)tối đa 25 ký tự không tối đa 25 byte, vì vậy nếu bạn có thể thấy các ký tự không phải ASCII trong chuỗi của mình, kích thước cột có thể tăng tối đa 100 byte vì một số ký tự mất bốn byte để đại diện - ví dụ: "đống biểu tượng cảm xúc poo" (mà tôi không nói là không tồn tại - nếu trình duyệt + phông chữ hiện tại của bạn hỗ trợ thì có vẻ như:) là 0xF0 0x9F 0x92 0xA9. Trước v5 myQuery đã đếm byte không phải ký tự khi chỉ định độ dài loại chuỗi.

Chỉnh sửa về tự động hóa

Về mặt tự động hóa quy trình, bạn sẽ có thể lấy được tất cả thông tin cần thiết từ các INFORMATION_SCHEMAbảng theo cách tương tự với tập lệnh bạn đã tìm thấy cho MS SQL Server. Xem https://dev.mysql.com/doc/refman/5.0/en/inif-schema.html để biết một số tài liệu về điều đó.


Tôi đã thấy câu trả lời được chia sẻ trước khi đăng câu hỏi này, điều tôi đang tìm kiếm không cần phải chỉ ra information_schema.tables vì ​​nó có thể không chính xác hơn là một giải pháp có thể kiểm tra cấu trúc bảng và cho tôi kích thước hàng phù hợp.
Nawaz Sohail

Bạn không thể nghi ngờ gì khi tạo một phiên bản myQuery của tập lệnh mà bạn đã tìm thấy. Các INFORMATION_SCHEMAbảng nên bao gồm thông tin bạn cần. Xem dev.mysql.com/doc/refman/5.0/en/inatures-schema.html để biết một số tài liệu về điều đó.
David Spillett

@DavidSpillett Các op đang hỏi về kích thước hàng tối đa, lược đồ thông tin chỉ cung cấp thông tin kích thước hàng trung bình aobut.
srcritical
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.