21

Re: Швидке сховище даних

Усе це легко може зробити БД. Достатньо просто записати у неї усі початкові дані з CSV-файлу. Але доведеться читати, що таке індекси, які вони бувають, та як працюють.

22

Re: Швидке сховище даних

Dim fs As FileStream = File.Open('big.csv', FileMode.Open)

Public Class FileSorter Implements IComparer

      Public Function Compare( ByVal x As Object, ByVal y As Object) As Integer _ Implements IComparer.Compare
         fs.Seek(x, SeekOrigin.Begin)
         //читаємо потрібну змінну в x_value
         fs.Seek(y, SeekOrigin.Begin)
         //читаємо потрібну змінну в x_value
         Return x<y
      End Function

End Class

Якось так

23

Re: Швидке сховище даних

Почитайте про incremental sorting

24

Re: Швидке сховище даних

iovchynnikov написав:

Почитайте про incremental sorting

І як це допоможе? Чекаю на відповідь із попкорном.

25

Re: Швидке сховище даних

Я тут подумав що можна з самого початку орієнтуватися не по номерам рядків а по їх зміщенням.

26 Востаннє редагувалося javascriptIsLife (25.05.2018 10:18:38)

Re: Швидке сховище даних

Вся ця затія зі зміщенням в байтах тепер видається мені дуже сумнівною. Використовую клас TextFieldParser. Для обрахунку довжини рядка використовую метод ReadLine який читає рядок як є не розбиваючи його по символам–розділювачам (; в моєму випадку). В документації вказано що «an end-of-line character within a delimited field is interpreted as the actual end of the line.
», отже якщо символ нового рядка є в тексті якогось 3 стовпчика з описом (ASDASD;123333;"Джукаа трьохдюймова йцвфілвофілволо \n asdsadsada";12.3;190\n), то текст зчитаються тільки до цього символу в третьому стовпчику і всі оці зміщення–адреси початку рядків будуть невірними і ніякої гарантії що зміщення 1029084 відповідає саме рядку №13450 а не його частині до першого символу нового рядка немає. Ще є метод PeekChars який читає задану кількість символів і повертає як рядок, але порожні рядки пропускаються і символи кінця рядку всередині полів інтерпретуються як кінець рядка (тобто така ж поведінка як у ReadLine). Для того щоб зчитати весь рядок невідомої довжини є хитрість: потрібно передати методу число яке є більшим ніж найдовший рядок.

Мені потрібно писати власний парсер або знайти такий який дозволяє зчитати рядок до останнього символу нового рядку який не не знаходиться всередині поля з даними всередині.

27

Re: Швидке сховище даних

То що, ви вирішили проблему?
Взагалі csv - не найскладніший формат у світі, парсер не так складно зробити.

28

Re: Швидке сховище даних

Ні. Схоже замовнику не особливо потрібно. А за початкову ціну в 300 гривень за сортування в алфавітному порядку в мене щось охота пропала. Створювати каталоги з файлами (яких виявилося близько 26 мільйонів) дуже довго (годин 7). І читати вміст усіх цих файлів теж дуже довго. Видалити це все добро теж виявилося проблемно. rm -rf в GNU/Linux виїдає всю оперативку після чого її вбивають. oMega commander довго рахує кількість файлів після чого починає довго видаляти. Якщо пробувати через командний рядок в Windows, то через деякий час виконання команди просто припиняється. Видалив написавши програму на Visual basic.NET

        Console.WriteLine("Start {0}", System.DateTime.Now.ToLongTimeString())
        System.IO.Directory.Delete("D:\\source\\duplicates remover\\duplicates remover\\bin\\Debug\\WPFRemover.offsets", True)
        Console.ReadLine()
        Console.WriteLine("End {0}", System.DateTime.Now.ToLongTimeString())

Це зайняло трохи більше трьох годин.

29

Re: Швидке сховище даних

javascriptIsLife написав:

Потрібно зберегти структуру виду

1: 84,
2: 120,
456903: 587411

до якої потім можна буде звертатися по ключу. Для цього в .NET підходить словник (ключі типу Int64 і значення теж Int64), але записів може бути декілька мільйонів і в оперативну пам'ять воно не поміщається. Запис в таблицю RDBMS SQLite триває занадто довго. Можна якось так це все записати щоб потім не потрібно було весь файл завантажувати до ОП або шукати в файлі рядок з потрібним ключем? Щоб можна було просто вказати ключ і отримати потрібне значення?

Раджу використати msql з індексами. І все буде пучком. І з нею працювати.

30

Re: Швидке сховище даних

Alchimic
Для цього треба знати, що таке індекси, та як ними користуватися. Автор цей варіант навіть не розглядає, я уже пропонував.

31 Востаннє редагувалося Alchimic (05.07.2018 16:32:37)

Re: Швидке сховище даних

Швидке сховище це ліст в оператвці.  Або бд на рам диску.Потрібен сервер з 64 гб. Або бд на сд диску. Це дешевше.

32 Востаннє редагувалося Senseye (26.07.2018 02:35:57)

Re: Швидке сховище даних

Функція sortAndStore читаю з файлу в якому є два поля через кому, з сортуванням по першому полю, та записує в новий файл з сортуванням вже по другому полю, файли source.csv, destination.csv

використав приклад для відображення пам'яті https://golangcode.com/print-the-current-memory-usage/

Для 1 000 000 рядків:

Alloc = 28 MiB  TotalAlloc = 199 MiB    Sys = 60 MiB    NumGC = 30
PASS
ok      _/home/senseye/develop/projects/BigSort 4.806s

Для 5 000 000 рядків:

Alloc = 151 MiB TotalAlloc = 976 MiB    Sys = 299 MiB   NumGC = 43
PASS
ok      _/home/senseye/develop/projects/BigSort 25.129s

Код з тестом, щоб без помилок:

package bigsort

import (
    "bufio"
    "fmt"
    "math/rand"
    "os"
    "runtime"
    "sort"
    "strconv"
    "strings"
    "testing"
)

type (
    item struct {
        member int
        score  int
    }
)

const (
    sourceFileName      = "source.csv"
    destinationFileName = "destination.csv"
    size                = 1e6
)

func Test(t *testing.T) {
    err := fillFileOnce(sourceFileName, size)
    assertSuccess(t, err)

    err = sortAndStore(sourceFileName, destinationFileName)
    assertSuccess(t, err)

    printMemUsage()
}

func assertSuccess(t *testing.T, err error) {
    if err != nil {
        t.Fatal("Expect success, but got:", err)
    }
}

func fillFileOnce(fileName string, count int) error {
    if f, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0600); err == nil {
        f.Close()

        return nil
    }

    f, err := os.Create(fileName)
    if err != nil {
        return err
    }
    f.Close()

    f, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0600)
    if err != nil {
        return err
    }
    defer f.Close()

    perm := rand.Perm(count)
    for i, v := range perm {
        f.Write([]byte(fmt.Sprintf("%d,%d\n", i, v)))
    }

    return nil
}

func sortAndStore(sourceFileName, destinationFileName string) error {
    if f, err := os.OpenFile(destinationFileName, os.O_APPEND|os.O_WRONLY, 0600); err == nil {
        f.Close()

        return nil
    }

    sourceFile, err := os.OpenFile(sourceFileName, os.O_RDONLY, 0600)
    if err != nil {
        return err
    }
    defer sourceFile.Close()

    f, err := os.Create(destinationFileName)
    if err != nil {
        return err
    }
    f.Close()

    destinationFile, err := os.OpenFile(destinationFileName, os.O_APPEND|os.O_WRONLY, 0600)
    if err != nil {
        return err
    }
    defer destinationFile.Close()

    items := make([]item, 0)
    scanner := bufio.NewScanner(sourceFile)
    for scanner.Scan() {
        parts := strings.Split(scanner.Text(), ",")

        items = append(items, item{
            member: parseInt(parts[0]),
            score:  parseInt(parts[1]),
        })
    }

    sort.Slice(items, func(i, j int) bool { return items[i].score < items[j].score })

    for i := range items {
        destinationFile.Write([]byte(fmt.Sprintf("%d,%d\n", items[i].member, items[i].score)))
    }

    return nil
}

func parseInt(s string) int {
    r, _ := strconv.Atoi(s)

    return r
}

func printMemUsage() {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    // For info on each, see: https://golang.org/pkg/runtime/#MemStats
    fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
    fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
    fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
    fmt.Printf("\tNumGC = %v\n", m.NumGC)
}

func bToMb(b uint64) uint64 {
    return b / 1024 / 1024
}