1

I need to create TEMPORARY tables for processing (via JOIN) a large set of data (1M rows), which have varchar columns. If I use ENGINE=MEMORY, it will change varchar to char. Then, the data will not fit into memory (even by increasing tmp_table_size/max_heap_table_size).

I understand that memory mapping need char structure, but is there a workaround to create TEMPORARY tables in MEMORY using varchar to minimise the memory usage?

This is my process:

CREATE TEMPORARY TABLE temp
(
ArticleID int(11) unsigned NOT NULL,
Tag varchar(255),
INDEX(Tag),
PRIMARY KEY(ArticleID,Tag)
) ENGINE=innoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci

LOAD DATA LOCAL INFILE 'file.csv' IGNORE INTO TABLE temp 
  FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n' 
  (ArticleID,Tag);

// Adding missing tags in tag table
INSERT IGNORE INTO Tags (Tag) SELECT DISTINCT Tag FROM temp;

INSERT INTO TagMap (ArticleID,TagID) 
  SELECT a.ArticleID,b.TagID FROM temp a JOIN Tags b ON a.Tag=b.Tag;

In my experience, InnoDB has a better performance as compared with Aria and MyISAM.

6
  • Without MDEV-19 fixed, I don't see a solution using the MEMORY engine. Are you sure that a InnoDB or Aria temporary table won't have the necessary performance? I assume you're avoiding the IO on creation?
    – danblack
    Commented Sep 2, 2021 at 1:15
  • @danblack The performance of InnoDB was much better than Aria and MyISAM (to my surprise). I/O is not my problem per se but the speed. I just create these tables for the data structure of ArticleID,Tag to JOIN with the tag table to get TagID. Therefore, they are short-lived. The bottle-neck is their creation.
    – Googlebot
    Commented Sep 2, 2021 at 5:37
  • LOAD DATA ... TERMINATED BY '\n' (ArticleID,@Tag) SET TagHash = UNHEX(SHA1(@Tag)) and then the TagHash can be a binary(20) if I calculated that right. Then your memory table would be smaller. Your real Tag table would also need the same indexed hash column. Acceptable?
    – danblack
    Commented Sep 2, 2021 at 6:10
  • @danblack your approach is subtle, but I forgot to mention another step in my procedure. I added to the sample code: I have to INSERT possible missing tags before JOIN. Sorry for my failure to clarify!
    – Googlebot
    Commented Sep 2, 2021 at 6:26
  • @danblack of course, your approach is still working if I add another LOAD DATA into the tag table.
    – Googlebot
    Commented Sep 2, 2021 at 6:28

1 Answer 1

1

You will be better off not 'normalizing' tags.

CREATE TABLE Tags (
    ArticleID int(11) unsigned NOT NULL,
    Tag varchar(255),
    INDEX(Tag),
    PRIMARY KEY(ArticleID,Tag)
) ENGINE=innoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci;

LOAD DATA ... INTO Tags ...;

Skip the rest of your steps.

Now simplify the code elsewhere since you don't need to go through TagMap to get to a tag.

And there is no need for MEMORY. (And possibly no advantage, anyway.) Nor a SHA1 kludge.

Note that INDEX(Tag) will be "covering" when Joining from Tag to ArticleID. This is because InnoDB tacks the PK onto the end of each secondary index.

(I did not follow your "missing tags" issue, but it might be simpler now that there is only one new table.)

Example from Comments -- List articles have all of 3 specific tags:

SELECT a.*
    FROM ( SELECT articleID FROM Tags
             WHERE tag IN ('foo', 'bar', thing')
             HAVING COUNT(*) = 3
         ) AS t
    JOIN Articles AS a USING(articleID);
7
  • A reasonable suggestion, but I cannot denormalise the tags table because it is too large (tens of millions of rows) and serves several tag_map tables. My current approach is slow in adding entries but will be faster in reading later on. Surely, by your method, there will be no "missing tags", as there is no tags table, and we use tags directly in tag_maps.
    – Googlebot
    Commented Sep 2, 2021 at 18:09
  • @Googlebot - I think I need to see some typical SELECTs to better understand your point.
    – Rick James
    Commented Sep 2, 2021 at 18:19
  • Are you saying, "There are tags that don't belong to any articles?"
    – Rick James
    Commented Sep 2, 2021 at 18:23
  • No, tags always belong to at least one article. "missing tags" refers to the absence of a tag in Tags table (when we don't have TgaID for a new tag). SELECT is simple like SELECT ArticleID FROM TagMap WHERE TagID IN(1,2,3) GROUP BY ArticleID HAVING COUNT(*)=3.
    – Googlebot
    Commented Sep 2, 2021 at 21:25
  • @Googlebot - I added the query for your example.
    – Rick James
    Commented Sep 3, 2021 at 4:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.