Пакетът rust за libmdbx

Обвивката rust за базата данни libmdbxОтваряне в нов прозорец.


Директория :


Цитати

Докато пишех 'rmw.linkОтваряне в нов прозорец ', почувствах, че имам нужда от вградена база данни.

Поради пропускателната способност на мрежата, свързана с честото записване, четене и писане, sqlite3 беше твърде напреднал за целите на производителността.

Така че по-подходяща беше база данни от по-ниско ниво ключ-стойност (lmdb е 10 пъти по-бърза от sqliteОтваряне в нов прозорец ).

В крайна сметка избрах магическата версия на lmdb - mdbx.

Понастоящем съществуващият пакет rust на mdbx-rs (mdbx-sysОтваряне в нов прозорец ) от mdbx не поддържа WindowsОтваряне в нов прозорец, така че се заех да създам пакет с версия, поддържаща Windows.

Поддръжка за съхраняване на потребителски типове ръжда. Поддържа многонишков достъп.

Базата данни може да бъде дефинирана в модул с помощта на lazy_static и след това просто да бъде въведена и използвана с нещо като :

use db::User;

let id = 1234;
let user = r!(User.get id);

Какво е libmdbx?

mdbxОтваряне в нов прозорец е вторична база данни, базирана на lmdb, дело на руския Леонид Юрьев (Леонид ЮОтваряне в нов прозорец риев).

lmdbОтваряне в нов прозорец е свръхбърза вградена база данни ключ-стойност.

Пълнотекстовата търсачка MeiliSearchОтваряне в нов прозорец се основава на lmdb.

Рамката за дълбоко обучение caffe също използва lmdb като хранилище за данниОтваряне в нов прозорец.

mdbx е с 30% по-бърз от lmdb в бенчмарка за вградени тестове за производителност ioarenaОтваряне в нов прозорец.




В същото време mdbx подобрява много от недостатъците наОтваряне в нов прозорец lmdb, така че Erigon (следващото поколение клиент на ethereum) наскоро премина от LMDB към MDBX [1].

Уроци

Как да стартирате примера

Първо клонирайте базата данни git clone git@github.com:rmw-lib/mdbx.git --depth=1 && cd mdbx

След това стартирайте cargo run --example 01 и той ще работи examples/01.rs

Ако това е ваш собствен проект, първо го стартирайте:

cargo install cargo-edit
cargo add mdbx lazy_static ctor paste

Пример 1 : Писане set(key,val) и четене .get(key)

Нека разгледаме един прост пример/01.rsОтваряне в нов прозорец

Код

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}

Изпълнение на изхода

mdbx file path /Users/z/rmw/mdbx/target/debug/examples/01.mdb
mdbx version https://github.com/erthink/libmdbx/releases/tag/v0.11.2
test1 get Ok(Some(Bin([6])))
[6]

Описание на кода

env_rw! Дефиниране на базата данни

Кодът започва с макрос env_rw, който има 4 параметъра.

  1. Името на променливата на средата на базата данни

  2. Връща обект, mdbx:: env:: ConfigОтваряне в нов прозорец.

Използваме конфигурацията по подразбиране, тъй като Env имплементира From<Into<PathBuf>>, така че пътят до базата данни into() ще бъде достатъчен, а конфигурацията по подразбиране е следната.

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
MDBX,
{
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
},
r,
w
);

mdbx! {
MDBX // Име на променлива на базата данни Env
Test // Тест на базата данни
}

fn main() -> Result<()> {
// Извеждане на номера на версията на libmdbx
unsafe {
  println!(
    "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
    mdbx_version.major, mdbx_version.minor, mdbx_version.release
  );
}

// Многонишково четене и писане
let t = std::thread::spawn(|| {
  let tx = w!();
  let test = tx | Test;
  test.set([1, 2], [6])?;
  println!("test1 get {:?}", test.get([1, 2]));

  match test.get([1, 2])? {
    Some(val) => {
      let t: &[u8] = &val;
      println!("{:?}", t);
    }
    None => unreachable!(),
  }
  Ok(())
});

t.join().unwrap()?;

Ok(())
}
#[derive(Clone, Debug)]
pub struct Config {
  path: PathBuf,
  mode: ffi::mdbx_mode_t,
  flag: flag::ENV,
  sync_period: u64,
  sync_bytes: u64,
  max_db: u64,
  pagesize: isize,
}

lazy_static! {
  pub static ref ENV_CONFIG_DEFAULT: Config = Config {
    path:PathBuf::new(),
    mode: 0o600,
    //https://github.com/erthink/libmdbx/issues/248
    sync_period : 65536, // За 1/65536 част от секундата
    sync_bytes : 65536,
    max_db : 256,
    flag : (
        flag::ENV::MDBX_EXCLUSIVE
      | flag::ENV::MDBX_LIFORECLAIM
      | flag::ENV::MDBX_COALESCE
      | flag::ENV::MDBX_NOMEMINIT
      | flag::ENV::MDBX_NOSUBDIR
      | flag::ENV::MDBX_SAFE_NOSYNC
      // | flag::ENV::MDBX_SYNC_DURABLE
    ),
    pagesize:-1
  };
}

max_db Тази настройка може да се нулира всеки път, когато сеОтваряне в нов прозорец отваря базата данни, но твърде високата ѝ стойност ще се отрази на производителността, просто я задайте според нуждите.

Вижте документацията на libmdbxОтваряне в нов прозорец за значението на другите параметри.

  1. Името на макроса за транзакция за четене на база данни, стойността по подразбиране е r

  2. Името на макроса за транзакция за запис в базата данни, стойността по подразбиране е w

Параметрите 3 и 4 могат да бъдат пропуснати, за да се използват стойностите по подразбиране.

Разширяване на макросистемата

Ако искате да видите какво прави магическият макрос, можете да използвате макроса cargo expand --example 01, за да го разширите, който трябва да бъде инсталиран преди това. cargo install cargo-expand

Снимка на екрана на разширения код е показана по-долу.

PDzEtT

anyhow и lazy_static

От разширената екранна снимка можете да видите, че се използват lazy_static и anyhow.

anyhowОтваряне в нов прозорец е библиотеката за обработка на грешки за rust.

lazy_staticОтваряне в нов прозорец е статична променлива с отложено инициализиране.

Тези две библиотеки са много разпространени и няма да ги разглеждам.

Макросът mdbx!

mdbx!Отваряне в нов прозорец е макрос на процедураОтваряне в нов прозорец.

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}

Първият ред е името на променливата на средата на базата данни

Вторият ред е името на базата данни

Може да има повече от една база данни, по един ред за всяка

Нишки и транзакции

Горният код демонстрира многонишково четене и писане.

Важно е да се отбележи, че в една и съща нишка може да има само една транзакция във всеки един момент, ако в дадена нишка има повече от една отворена транзакция, програмата ще се срине.

Транзакцията ще бъде извършена в края на обхвата.

Четене и записване на двоични данни
use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}
let tx = w!();
let test = tx | Test;
test.set([1, 2], [6])?;
println!("test1 get {:?}", test.get([1, 2]));

match test.get([1, 2])? {
 Some(val) => {
  let t:&[u8] = &val;
  println!("{:?}",t);
 },
 None => unreachable!()
}

set е запис, get е четене, а всеки обект, който реализира AsRef<[u8]>Отваряне в нов прозорец може да бъде записан в базата данни.

get Получава се Ok(Some(Bin([6]))), който може да се преобразува в &[u8].

Пример 2: Типове данни, флагове на базата данни, изтриване, обхождане

Нека разгледаме втория пример/02.rsОтваряне в нов прозорец:

В този пример е пропуснат env_rw!, а третият и четвъртият аргумент ( r, w) са пропуснати.

Код

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}
let tx = w!();
let test = tx | Test;
test.set([1, 2], [6])?;
println!("test1 get {:?}", test.get([1, 2]));

match test.get([1, 2])? {
 Some(val) => {
  let t:&[u8] = &val;
  println!("{:?}",t);
 },
 None => unreachable!()
}
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX // Имена на променливи за базата данни ENV
  Test1
  Test2
    key Str
    val Str
  Test3
    key i32
    val u64
  Test4
    key u64
    val u16
    flag DUPSORT
}

fn main() -> Result<()> {
  // Бързо писане
  w!(Test1.set [2, 3],[4, 5]);

  // Бързо четене
  match r!(Test1.get [2, 3]) {
    Some(r) => {
      println!(
        "\nu16::from_le_bytes({:?}) = {}",
        r,
        u16::from_le_bytes((*r).try_into()?)
      );
    }
    None => unreachable!(),
  }

  // Множество операции върху няколко бази данни в една и съща транзакция
  {
    let tx = w!();
    let test1 = tx | Test1;

    test1.set(&[9], &[10, 12])?;
    test1.set([8, 1], [9])?;
    test1.set("rmw.link", "Down with Data Hegemony")?;
    test1.set(&"abc", &"012")?;

    println!("\n-- loop test1");
    for (k, v) in test1 {
      println!("{} = {}", k, v);
    }

    dbg!(test1.del_val([8, 1], [3])?);
    dbg!(test1.get([8, 1])?.unwrap());
    dbg!(test1.del_val([8, 1], [9])?);
    dbg!(test1.get([8, 1])?);

    dbg!(test1.del([9])?);
    dbg!(test1.get([9])?);
    dbg!(test1.del([9])?);

    let test2 = tx | Test2;
    test2.set("rmw.link", "Down with Data Hegemony")?;
    test2.set(&"abc", &"012")?;
    println!("\n-- loop test2");
    for (k, v) in test2 {
      println!("{} = {}", k, v);
    }

    let test3 = tx | Test3;

    test3.set(13, 32)?;
    test3.set(16, 32)?;
    test3.set(-15, 6)?;
    test3.set(-10, 6)?;
    test3.set(-12, 6)?;
    test3.set(0, 6)?;
    test3.set(10, 5)?;

    println!("\n-- loop test3");
    for (k, v) in test3 {
      println!("{:?} = {:?}", k, v);
    }

    let test4 = tx | Test4;
    test4.set(10, 5)?;
    test4.set(10, 0)?;
    test4.set(13, 32)?;
    test4.set(16, 2)?;
    test4.set(16, 1)?;
    test4.set(16, 3)?;
    test4.set(0, 6)?;
    test4.set(10, 5)?;
    test4.set(0, 2)?;

    dbg!(test4.del_val(0, 2)?);
    dbg!(test4.del_val(0, 2)?);

    println!("\n-- loop test4 rev");
    for (k, v) in test4.rev() {
      println!("{:?} = {:?}", k, v);
    }

    for i in test4.dup(16) {
      println!("dup(16) {:?}", i);
    }

    // Транзакцията ще бъде извършена в края на обхвата
  }

  Ok(())
}

Изпълнение на изхода

mdbx file path /Users/z/rmw/mdbx/target/debug/examples/02.mdb

u16::from_le_bytes(Bin([4, 5])) = 1284

-- loop test1
[2] = [3]
[2, 3] = [4, 5]
[8, 1] = [9]
[9] = [10, 12]
[97, 98, 99] = [48, 49, 50]
[114, 109, 119, 46, 108, 105, 110, 107] = [68, 111, 119, 110, 32, 119, 105, 116, 104, 32, 68, 97, 116, 97, 32, 72, 101, 103, 101, 109, 111, 110, 121]
[examples/02.rs:57] test1.del_val([8, 1], [3])? = false
[examples/02.rs:58] test1.get([8, 1])?.unwrap() = Bin(
    [
        9,
    ],
)
[examples/02.rs:59] test1.del_val([8, 1], [9])? = true
[examples/02.rs:60] test1.get([8, 1])? = None
[examples/02.rs:62] test1.del([9])? = true
[examples/02.rs:63] test1.get([9])? = None
[examples/02.rs:64] test1.del([9])? = false

-- loop test2
abc = 012
rmw.link = Down with Data Hegemony

-- loop test3
0 = 6
10 = 5
13 = 32
16 = 32
-15 = 6
-12 = 6
-10 = 6
[examples/02.rs:100] test4.del_val(0, 2)? = true
[examples/02.rs:101] test4.del_val(0, 2)? = false

-- loop test4 rev
16 = 3
16 = 2
16 = 1
13 = 32
10 = 5
10 = 0
0 = 6
dup(16) 1
dup(16) 2
dup(16) 3

Бързо четене и писане

Ако искаме просто да прочетем или запишем един ред данни, можем да използваме синтактичната захар на макроса.

Прочетени данни

r!(Test1.get [2, 3])

Записване на данни

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}
let tx = w!();
let test = tx | Test;
test.set([1, 2], [6])?;
println!("test1 get {:?}", test.get([1, 2]));

match test.get([1, 2])? {
 Some(val) => {
  let t:&[u8] = &val;
  println!("{:?}",t);
 },
 None => unreachable!()
}
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX // Имена на променливи за базата данни ENV
  Test1
  Test2
    key Str
    val Str
  Test3
    key i32
    val u64
  Test4
    key u64
    val u16
    flag DUPSORT
}

fn main() -> Result<()> {
  // Бързо писане
  w!(Test1.set [2, 3],[4, 5]);

  // Бързо четене
  match r!(Test1.get [2, 3]) {
    Some(r) => {
      println!(
        "\nu16::from_le_bytes({:?}) = {}",
        r,
        u16::from_le_bytes((*r).try_into()?)
      );
    }
    None => unreachable!(),
  }

  // Множество операции върху няколко бази данни в една и съща транзакция
  {
    let tx = w!();
    let test1 = tx | Test1;

    test1.set(&[9], &[10, 12])?;
    test1.set([8, 1], [9])?;
    test1.set("rmw.link", "Down with Data Hegemony")?;
    test1.set(&"abc", &"012")?;

    println!("\n-- loop test1");
    for (k, v) in test1 {
      println!("{} = {}", k, v);
    }

    dbg!(test1.del_val([8, 1], [3])?);
    dbg!(test1.get([8, 1])?.unwrap());
    dbg!(test1.del_val([8, 1], [9])?);
    dbg!(test1.get([8, 1])?);

    dbg!(test1.del([9])?);
    dbg!(test1.get([9])?);
    dbg!(test1.del([9])?);

    let test2 = tx | Test2;
    test2.set("rmw.link", "Down with Data Hegemony")?;
    test2.set(&"abc", &"012")?;
    println!("\n-- loop test2");
    for (k, v) in test2 {
      println!("{} = {}", k, v);
    }

    let test3 = tx | Test3;

    test3.set(13, 32)?;
    test3.set(16, 32)?;
    test3.set(-15, 6)?;
    test3.set(-10, 6)?;
    test3.set(-12, 6)?;
    test3.set(0, 6)?;
    test3.set(10, 5)?;

    println!("\n-- loop test3");
    for (k, v) in test3 {
      println!("{:?} = {:?}", k, v);
    }

    let test4 = tx | Test4;
    test4.set(10, 5)?;
    test4.set(10, 0)?;
    test4.set(13, 32)?;
    test4.set(16, 2)?;
    test4.set(16, 1)?;
    test4.set(16, 3)?;
    test4.set(0, 6)?;
    test4.set(10, 5)?;
    test4.set(0, 2)?;

    dbg!(test4.del_val(0, 2)?);
    dbg!(test4.del_val(0, 2)?);

    println!("\n-- loop test4 rev");
    for (k, v) in test4.rev() {
      println!("{:?} = {:?}", k, v);
    }

    for i in test4.dup(16) {
      println!("dup(16) {:?}", i);
    }

    // Транзакцията ще бъде извършена в края на обхвата
  }

  Ok(())
}
w!(Test1.set [2, 3],[4, 5])

Всичко това е на един ред, както е записано в examples/02.rsОтваряне в нов прозорец.

Типове данни

В examples/02. rsОтваряне в нов прозорец дефиницията на базата данни изглежда по следния начин :

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}
let tx = w!();
let test = tx | Test;
test.set([1, 2], [6])?;
println!("test1 get {:?}", test.get([1, 2]));

match test.get([1, 2])? {
 Some(val) => {
  let t:&[u8] = &val;
  println!("{:?}",t);
 },
 None => unreachable!()
}
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX // Имена на променливи за базата данни ENV
  Test1
  Test2
    key Str
    val Str
  Test3
    key i32
    val u64
  Test4
    key u64
    val u16
    flag DUPSORT
}

fn main() -> Result<()> {
  // Бързо писане
  w!(Test1.set [2, 3],[4, 5]);

  // Бързо четене
  match r!(Test1.get [2, 3]) {
    Some(r) => {
      println!(
        "\nu16::from_le_bytes({:?}) = {}",
        r,
        u16::from_le_bytes((*r).try_into()?)
      );
    }
    None => unreachable!(),
  }

  // Множество операции върху няколко бази данни в една и съща транзакция
  {
    let tx = w!();
    let test1 = tx | Test1;

    test1.set(&[9], &[10, 12])?;
    test1.set([8, 1], [9])?;
    test1.set("rmw.link", "Down with Data Hegemony")?;
    test1.set(&"abc", &"012")?;

    println!("\n-- loop test1");
    for (k, v) in test1 {
      println!("{} = {}", k, v);
    }

    dbg!(test1.del_val([8, 1], [3])?);
    dbg!(test1.get([8, 1])?.unwrap());
    dbg!(test1.del_val([8, 1], [9])?);
    dbg!(test1.get([8, 1])?);

    dbg!(test1.del([9])?);
    dbg!(test1.get([9])?);
    dbg!(test1.del([9])?);

    let test2 = tx | Test2;
    test2.set("rmw.link", "Down with Data Hegemony")?;
    test2.set(&"abc", &"012")?;
    println!("\n-- loop test2");
    for (k, v) in test2 {
      println!("{} = {}", k, v);
    }

    let test3 = tx | Test3;

    test3.set(13, 32)?;
    test3.set(16, 32)?;
    test3.set(-15, 6)?;
    test3.set(-10, 6)?;
    test3.set(-12, 6)?;
    test3.set(0, 6)?;
    test3.set(10, 5)?;

    println!("\n-- loop test3");
    for (k, v) in test3 {
      println!("{:?} = {:?}", k, v);
    }

    let test4 = tx | Test4;
    test4.set(10, 5)?;
    test4.set(10, 0)?;
    test4.set(13, 32)?;
    test4.set(16, 2)?;
    test4.set(16, 1)?;
    test4.set(16, 3)?;
    test4.set(0, 6)?;
    test4.set(10, 5)?;
    test4.set(0, 2)?;

    dbg!(test4.del_val(0, 2)?);
    dbg!(test4.del_val(0, 2)?);

    println!("\n-- loop test4 rev");
    for (k, v) in test4.rev() {
      println!("{:?} = {:?}", k, v);
    }

    for i in test4.dup(16) {
      println!("dup(16) {:?}", i);
    }

    // Транзакцията ще бъде извършена в края на обхвата
  }

  Ok(())
}
w!(Test1.set [2, 3],[4, 5])
Test2
  key Str
  val Str
Test3
  key i32
  val u64
Test4
  key u64
  val u16
  flag DUPSORT

където key и val определят типовете данни съответно за ключовете и стойностите.

Ако се опитате да запишете тип данни, който не съответства на дефинирания, ще бъде отчетена грешка, както е показано на снимката по-долу :

Типът данни по подразбиране е BinОтваряне в нов прозорец , могат да се записват всякакви данни, които се използват в AsRef<[u8]>.

Ако ключът или стойността е низ от utf8, типът данни може да бъде зададен на StrОтваряне в нов прозорец .

Ако премахнете цитирането наОтваряне в нов прозорец Str, ще върнете низ, подобен на let k:&str = &k;.

Освен това в Str се прилагат и std::fmt::DisplayОтваряне в нов прозорец , println!("{}",k) ще изведе четлив низ.

Предварително зададени типове данни

В допълнение към Str и Bin обвивката се предлага и с поддръжка на данни за usize, u128, u64, u32, u16, u8, isize, i128, i64, i32, i16, i8, f32, f64Отваряне в нов прозорец.

Флагове на базата данни

Можете да видите флаговете на базата данни, добавени към данните в examples/02.rsОтваряне в нов прозорец на адрес Test4 flag DUPSORT

Базата данни libmdbx има редица флагове ( MDBX_db_flags_tОтваряне в нов прозорец ), които могат да бъдат зададени.

  • REVERSEKEY използва обратно сравнение на низове за ключове. (полезно при използване на малки числа с краен код като ключове)
  • DUPSORT използва сортирани дубликати, т.е. позволява множество стойности за един ключ.
  • INTEGERKEY Роден цифров ключ с байтова подредба uint32_t или uint64_t. Ключовете трябва да са с еднакъв размер и да са подравнени, когато се предават като аргументи.
  • DUPFIXED Размерът на стойностите на данните трябва да бъде същият, ако се използва DUPSORT (позволява бързо преброяване на броя на стойностите).
  • За INTEGERDUP се изискват DUPSORT и DUPFIXED; стойностите са цели числа (подобно на INTEGERKEY). Всички стойности на данните трябва да имат еднакъв размер и да бъдат подравнени, когато се предават като параметри.
  • REVERSEDUP използва DUPSORT; за стойностите на данните се използва обратно сравнение на низове.
  • CREATE създава БД, ако тя не съществува (добавя се по подразбиране).
  • DB_ACCEDE Отваря съществуваща подбаза данни, създадена с помощта на флага unknown.
    Този флаг DB_ACCEDE е предназначен за отваряне на съществуващи подбази данни, създадени с неизвестни флагове (REVERSEKEY, DUPSORT, INTEGERKEY, DUPFIXED, INTEGERDUP и REVERSEDUP).
    В този случай подбазата данни няма да върне грешка INCOMPATIBLE, а ще бъде отворена с флаговете, използвани за нейното създаване, и приложението ще може да определи действителните флагове с mdbx_dbi_flags().
DUPSORT : Един ключ отговаря на повече от една стойност

DUPSORT, което означава, че на един ключ може да съответства повече от една стойност.

Ако искате да зададете няколко флага, напишете следното flag DUPSORT | DUPFIXED

.dup(key) итератор, който връща всички стойности, съответстващи на даден ключ

Тази функция е налична само за бази данни, маркирани с DUPSORT, където на един ключ може да съответства повече от една стойност.

За базите данни DUPSORT``get връща само първата стойност за този ключ. За да получите всички стойности, използвайте dup.

Автоматично добавени флагове на базата данни по подразбиране

Когато типът данни е u32 / u64 / usize, флагът за база данни се добавя автоматично. INTEGERKEYОтваряне в нов прозорец .

При машини с кодиране на малкия край автоматично се добавят други типове числа. REVERSEKEYОтваряне в нов прозорец Флагът за база данни се добавя автоматично, когато типът данни е / / .

Изтриване на данни

.del(key) Изтриване на клавиш

.del(val) Изтрива стойността, съответстваща на даден ключ.

Ако базата данни има флаг DUPSORT, всички стойности под този ключ ще бъдат изтрити.

Връща true, ако са изтрити някакви данни, и false, ако не са.

.del_val(key,val) Изтриване на точно съвпадение

.del_val(key,val) Изтрива двойки ключ-стойност, които отговарят точно на входните параметри.

Връща true, ако са изтрити някакви данни, и false, ако не са.

Обхождане

последователно обхождане

Поради прилагането на std::iter::IntoIteratorОтваряне в нов прозорец . можете да преминавате директно по следния начин:

for (k, v) in test1

.rev() Обхождане в обратен ред

for (k, v) in test4.rev()

Сортиране

Ключовете на libmdbx са подредени в речников редОтваряне в нов прозорец.

  • За числа без знак

    са подредени от най-малкия към най-големия, тъй като флаговете на базата данни се добавят автоматично ( u32/ u64/ usize се добавят към INTEGERKEY, а други се добавят към REVERSEKEY в зависимост от машинния код).

  • За знакови числа

    редът е следният: първо 0, след това всички положителни числа от най-малкото до най-голямото, след това всички отрицателни числа от най-малкото до най-голямото.

Интервални итератори

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}
let tx = w!();
let test = tx | Test;
test.set([1, 2], [6])?;
println!("test1 get {:?}", test.get([1, 2]));

match test.get([1, 2])? {
 Some(val) => {
  let t:&[u8] = &val;
  println!("{:?}",t);
 },
 None => unreachable!()
}
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX // Имена на променливи за базата данни ENV
  Test1
  Test2
    key Str
    val Str
  Test3
    key i32
    val u64
  Test4
    key u64
    val u16
    flag DUPSORT
}

fn main() -> Result<()> {
  // Бързо писане
  w!(Test1.set [2, 3],[4, 5]);

  // Бързо четене
  match r!(Test1.get [2, 3]) {
    Some(r) => {
      println!(
        "\nu16::from_le_bytes({:?}) = {}",
        r,
        u16::from_le_bytes((*r).try_into()?)
      );
    }
    None => unreachable!(),
  }

  // Множество операции върху няколко бази данни в една и съща транзакция
  {
    let tx = w!();
    let test1 = tx | Test1;

    test1.set(&[9], &[10, 12])?;
    test1.set([8, 1], [9])?;
    test1.set("rmw.link", "Down with Data Hegemony")?;
    test1.set(&"abc", &"012")?;

    println!("\n-- loop test1");
    for (k, v) in test1 {
      println!("{} = {}", k, v);
    }

    dbg!(test1.del_val([8, 1], [3])?);
    dbg!(test1.get([8, 1])?.unwrap());
    dbg!(test1.del_val([8, 1], [9])?);
    dbg!(test1.get([8, 1])?);

    dbg!(test1.del([9])?);
    dbg!(test1.get([9])?);
    dbg!(test1.del([9])?);

    let test2 = tx | Test2;
    test2.set("rmw.link", "Down with Data Hegemony")?;
    test2.set(&"abc", &"012")?;
    println!("\n-- loop test2");
    for (k, v) in test2 {
      println!("{} = {}", k, v);
    }

    let test3 = tx | Test3;

    test3.set(13, 32)?;
    test3.set(16, 32)?;
    test3.set(-15, 6)?;
    test3.set(-10, 6)?;
    test3.set(-12, 6)?;
    test3.set(0, 6)?;
    test3.set(10, 5)?;

    println!("\n-- loop test3");
    for (k, v) in test3 {
      println!("{:?} = {:?}", k, v);
    }

    let test4 = tx | Test4;
    test4.set(10, 5)?;
    test4.set(10, 0)?;
    test4.set(13, 32)?;
    test4.set(16, 2)?;
    test4.set(16, 1)?;
    test4.set(16, 3)?;
    test4.set(0, 6)?;
    test4.set(10, 5)?;
    test4.set(0, 2)?;

    dbg!(test4.del_val(0, 2)?);
    dbg!(test4.del_val(0, 2)?);

    println!("\n-- loop test4 rev");
    for (k, v) in test4.rev() {
      println!("{:?} = {:?}", k, v);
    }

    for i in test4.dup(16) {
      println!("dup(16) {:?}", i);
    }

    // Транзакцията ще бъде извършена в края на обхвата
  }

  Ok(())
}
w!(Test1.set [2, 3],[4, 5])
Test2
  key Str
  val Str
Test3
  key i32
  val u64
Test4
  key u64
  val u16
  flag DUPSORT
use anyhow::Result;
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX
  Test0
  Test1
    key u16
    val u64
    flag DUPSORT
  Test2
    key u32
    val u64
}

macro_rules! range_rev {
  ($var:ident, $range:expr) => {
    println!("\n# {}.rev_range({:?})", stringify!($var), $range);
    for i in $var.range_rev($range) {
      println!("{:?}", i);
    }
  };
}

macro_rules! range {
  ($var:ident, $range:expr) => {
    println!("\n# {}.range({:?})", stringify!($var), $range);
    for i in $var.range($range) {
      println!("{:?}", i);
    }
  };
}

fn main() -> Result<()> {
  {
    println!("\n> Test0");
    let tx = &MDBX.w()?;
    let test0 = tx | Test0;
    test0.set([0], [0, 1])?;
    test0.set([1], [1, 2])?;
    test0.set([2], [2, 3])?;
    test0.set([1, 1], [1, 3])?;
    test0.set([1, 2], [1, 3])?;
    test0.set([3], [])?;

    range!(test0, [1]..);
    let begin: &[u8] = &[1, 1];
    range!(test0, begin..=&[2]);
  }

  {
    let tx = &MDBX.w()?;

    let test1 = tx | Test1;
    test1.set(2, 9)?;
    test1.set(2, 4)?;
    test1.set(9, 7)?;
    test1.set(3, 0)?;
    test1.set(3, 8)?;
    test1.set(5, 3)?;
    test1.set(5, 8)?;
    test1.set(9, 1)?;
    println!("-- all");
    for i in test1 {
      println!("{:?}", i);
    }
    range!(test1, 1..3);
    range!(test1, 5..2);
    range!(test1, 1..=3);
    range!(test1, ..3);
    range!(test1, 3..);
    range_rev!(test1, ..1);
    range_rev!(test1, ..=1);
  }

  {
    println!("\n> Test2");
    let tx = &MDBX.w()?;
    let test2 = tx | Test2;
    test2.set(2, 9)?;
    test2.set(1, 2)?;
    test2.set(2, 4)?;
    test2.set(1, 5)?;
    test2.set(9, 7)?;
    test2.set(9, 1)?;
    test2.set(0, 0)?;

    range!(test2, 1..3);
    range!(test2, 1..=3);
    range!(test2, ..3);
    range!(test2, 2..);
    range_rev!(test2, ..1);
    range_rev!(test2, 2..);
    range_rev!(test2, ..=1);
  }

  Ok(())
}

Изпълнете изхода на

mdbx file path /Users/z/rmw/mdbx/target/debug/examples/range.mdb

> Test0

# test0.range([1]..)
(Bin([1]), Bin([1, 2]))
(Bin([1, 1]), Bin([1, 3]))
(Bin([1, 2]), Bin([1, 3]))
(Bin([2]), Bin([2, 3]))
(Bin([3]), Bin([]))

# test0.range([1, 1]..=[2])
(Bin([1, 1]), Bin([1, 3]))
(Bin([1, 2]), Bin([1, 3]))
(Bin([2]), Bin([2, 3]))
-- all
(2, 4)
(2, 9)
(3, 0)
(3, 8)
(5, 3)
(5, 8)
(9, 1)
(9, 2)
(9, 7)

# test1.range(1..3)
(2, 4)
(2, 9)

# test1.range(5..2)
(5, 8)
(5, 3)
(3, 8)
(3, 0)

# test1.range(1..=3)
(2, 4)
(2, 9)
(3, 0)
(3, 8)

# test1.range(..3)
(2, 4)
(2, 9)

# test1.range(3..)
(3, 0)
(3, 8)
(5, 3)
(5, 8)
(9, 1)
(9, 2)
(9, 7)

# test1.rev_range(..1)
(9, 7)
(9, 2)
(9, 1)
(5, 8)
(5, 3)
(3, 8)
(3, 0)
(2, 9)
(2, 4)

# test1.rev_range(..=1)
(9, 7)
(9, 2)
(9, 1)
(5, 8)
(5, 3)
(3, 8)
(3, 0)
(2, 9)
(2, 4)

> Test2

# test2.range(1..3)
(1, 5)
(2, 4)

# test2.range(1..=3)
(1, 5)
(2, 4)

# test2.range(..3)
(0, 0)
(1, 5)
(2, 4)

# test2.range(2..)
(2, 4)
(9, 1)

# test2.rev_range(..1)
(9, 1)
(2, 4)

# test2.rev_range(2..)
(2, 4)
(1, 5)
(0, 0)

# test2.rev_range(..=1)
(9, 1)
(2, 4)
(1, 5)

.range(begin..end) Итерация на интервала

За числата интервалът е числов интервал.

За двоичен код може да се конструира същият интервал, напр.

let begin : &[u8] = &[1,1];
for (k,v) in test0.range(begin..=&[2]) {}

Ако begin е по-голям от end, ще се извърши итерация назад.

Например, test1.range(5..2) ще изведе следното :

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}
let tx = w!();
let test = tx | Test;
test.set([1, 2], [6])?;
println!("test1 get {:?}", test.get([1, 2]));

match test.get([1, 2])? {
 Some(val) => {
  let t:&[u8] = &val;
  println!("{:?}",t);
 },
 None => unreachable!()
}
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX // Имена на променливи за базата данни ENV
  Test1
  Test2
    key Str
    val Str
  Test3
    key i32
    val u64
  Test4
    key u64
    val u16
    flag DUPSORT
}

fn main() -> Result<()> {
  // Бързо писане
  w!(Test1.set [2, 3],[4, 5]);

  // Бързо четене
  match r!(Test1.get [2, 3]) {
    Some(r) => {
      println!(
        "\nu16::from_le_bytes({:?}) = {}",
        r,
        u16::from_le_bytes((*r).try_into()?)
      );
    }
    None => unreachable!(),
  }

  // Множество операции върху няколко бази данни в една и съща транзакция
  {
    let tx = w!();
    let test1 = tx | Test1;

    test1.set(&[9], &[10, 12])?;
    test1.set([8, 1], [9])?;
    test1.set("rmw.link", "Down with Data Hegemony")?;
    test1.set(&"abc", &"012")?;

    println!("\n-- loop test1");
    for (k, v) in test1 {
      println!("{} = {}", k, v);
    }

    dbg!(test1.del_val([8, 1], [3])?);
    dbg!(test1.get([8, 1])?.unwrap());
    dbg!(test1.del_val([8, 1], [9])?);
    dbg!(test1.get([8, 1])?);

    dbg!(test1.del([9])?);
    dbg!(test1.get([9])?);
    dbg!(test1.del([9])?);

    let test2 = tx | Test2;
    test2.set("rmw.link", "Down with Data Hegemony")?;
    test2.set(&"abc", &"012")?;
    println!("\n-- loop test2");
    for (k, v) in test2 {
      println!("{} = {}", k, v);
    }

    let test3 = tx | Test3;

    test3.set(13, 32)?;
    test3.set(16, 32)?;
    test3.set(-15, 6)?;
    test3.set(-10, 6)?;
    test3.set(-12, 6)?;
    test3.set(0, 6)?;
    test3.set(10, 5)?;

    println!("\n-- loop test3");
    for (k, v) in test3 {
      println!("{:?} = {:?}", k, v);
    }

    let test4 = tx | Test4;
    test4.set(10, 5)?;
    test4.set(10, 0)?;
    test4.set(13, 32)?;
    test4.set(16, 2)?;
    test4.set(16, 1)?;
    test4.set(16, 3)?;
    test4.set(0, 6)?;
    test4.set(10, 5)?;
    test4.set(0, 2)?;

    dbg!(test4.del_val(0, 2)?);
    dbg!(test4.del_val(0, 2)?);

    println!("\n-- loop test4 rev");
    for (k, v) in test4.rev() {
      println!("{:?} = {:?}", k, v);
    }

    for i in test4.dup(16) {
      println!("dup(16) {:?}", i);
    }

    // Транзакцията ще бъде извършена в края на обхвата
  }

  Ok(())
}
w!(Test1.set [2, 3],[4, 5])
Test2
  key Str
  val Str
Test3
  key i32
  val u64
Test4
  key u64
  val u16
  flag DUPSORT
use anyhow::Result;
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX
  Test0
  Test1
    key u16
    val u64
    flag DUPSORT
  Test2
    key u32
    val u64
}

macro_rules! range_rev {
  ($var:ident, $range:expr) => {
    println!("\n# {}.rev_range({:?})", stringify!($var), $range);
    for i in $var.range_rev($range) {
      println!("{:?}", i);
    }
  };
}

macro_rules! range {
  ($var:ident, $range:expr) => {
    println!("\n# {}.range({:?})", stringify!($var), $range);
    for i in $var.range($range) {
      println!("{:?}", i);
    }
  };
}

fn main() -> Result<()> {
  {
    println!("\n> Test0");
    let tx = &MDBX.w()?;
    let test0 = tx | Test0;
    test0.set([0], [0, 1])?;
    test0.set([1], [1, 2])?;
    test0.set([2], [2, 3])?;
    test0.set([1, 1], [1, 3])?;
    test0.set([1, 2], [1, 3])?;
    test0.set([3], [])?;

    range!(test0, [1]..);
    let begin: &[u8] = &[1, 1];
    range!(test0, begin..=&[2]);
  }

  {
    let tx = &MDBX.w()?;

    let test1 = tx | Test1;
    test1.set(2, 9)?;
    test1.set(2, 4)?;
    test1.set(9, 7)?;
    test1.set(3, 0)?;
    test1.set(3, 8)?;
    test1.set(5, 3)?;
    test1.set(5, 8)?;
    test1.set(9, 1)?;
    println!("-- all");
    for i in test1 {
      println!("{:?}", i);
    }
    range!(test1, 1..3);
    range!(test1, 5..2);
    range!(test1, 1..=3);
    range!(test1, ..3);
    range!(test1, 3..);
    range_rev!(test1, ..1);
    range_rev!(test1, ..=1);
  }

  {
    println!("\n> Test2");
    let tx = &MDBX.w()?;
    let test2 = tx | Test2;
    test2.set(2, 9)?;
    test2.set(1, 2)?;
    test2.set(2, 4)?;
    test2.set(1, 5)?;
    test2.set(9, 7)?;
    test2.set(9, 1)?;
    test2.set(0, 0)?;

    range!(test2, 1..3);
    range!(test2, 1..=3);
    range!(test2, ..3);
    range!(test2, 2..);
    range_rev!(test2, ..1);
    range_rev!(test2, 2..);
    range_rev!(test2, ..=1);
  }

  Ok(())
}
(5, 8)
(5, 3)
(3, 8)
(3, 0)

Не се поддържа интервална итерация RangeFullОтваряне в нов прозорец , т.е. използването на ..не се поддържа, моля, използвайте вместо това посочения по-горе обходен път.

.rev_range Инвертирани интервали

Ако искате да получите инвертиран интервал, който е по-малък или равен на дадена стойност, можете да направите следното

test2.rev_range(2..)

Изходът ще бъде

(2, 4)
(1, 5)
(0, 0)

За инвертирания интервал не трябва да се задава едно от следните значения: begin или end; защото ако са зададени и двете, винаги можете да използвате range(end..begin), за да постигнете същия ефект.

Персонализиране на типовете данни

Демонстрационният код е достъпен на адрес github.com/rmw-lib/mdbx-example/01Отваряне в нов прозорец

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}
let tx = w!();
let test = tx | Test;
test.set([1, 2], [6])?;
println!("test1 get {:?}", test.get([1, 2]));

match test.get([1, 2])? {
 Some(val) => {
  let t:&[u8] = &val;
  println!("{:?}",t);
 },
 None => unreachable!()
}
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX // Имена на променливи за базата данни ENV
  Test1
  Test2
    key Str
    val Str
  Test3
    key i32
    val u64
  Test4
    key u64
    val u16
    flag DUPSORT
}

fn main() -> Result<()> {
  // Бързо писане
  w!(Test1.set [2, 3],[4, 5]);

  // Бързо четене
  match r!(Test1.get [2, 3]) {
    Some(r) => {
      println!(
        "\nu16::from_le_bytes({:?}) = {}",
        r,
        u16::from_le_bytes((*r).try_into()?)
      );
    }
    None => unreachable!(),
  }

  // Множество операции върху няколко бази данни в една и съща транзакция
  {
    let tx = w!();
    let test1 = tx | Test1;

    test1.set(&[9], &[10, 12])?;
    test1.set([8, 1], [9])?;
    test1.set("rmw.link", "Down with Data Hegemony")?;
    test1.set(&"abc", &"012")?;

    println!("\n-- loop test1");
    for (k, v) in test1 {
      println!("{} = {}", k, v);
    }

    dbg!(test1.del_val([8, 1], [3])?);
    dbg!(test1.get([8, 1])?.unwrap());
    dbg!(test1.del_val([8, 1], [9])?);
    dbg!(test1.get([8, 1])?);

    dbg!(test1.del([9])?);
    dbg!(test1.get([9])?);
    dbg!(test1.del([9])?);

    let test2 = tx | Test2;
    test2.set("rmw.link", "Down with Data Hegemony")?;
    test2.set(&"abc", &"012")?;
    println!("\n-- loop test2");
    for (k, v) in test2 {
      println!("{} = {}", k, v);
    }

    let test3 = tx | Test3;

    test3.set(13, 32)?;
    test3.set(16, 32)?;
    test3.set(-15, 6)?;
    test3.set(-10, 6)?;
    test3.set(-12, 6)?;
    test3.set(0, 6)?;
    test3.set(10, 5)?;

    println!("\n-- loop test3");
    for (k, v) in test3 {
      println!("{:?} = {:?}", k, v);
    }

    let test4 = tx | Test4;
    test4.set(10, 5)?;
    test4.set(10, 0)?;
    test4.set(13, 32)?;
    test4.set(16, 2)?;
    test4.set(16, 1)?;
    test4.set(16, 3)?;
    test4.set(0, 6)?;
    test4.set(10, 5)?;
    test4.set(0, 2)?;

    dbg!(test4.del_val(0, 2)?);
    dbg!(test4.del_val(0, 2)?);

    println!("\n-- loop test4 rev");
    for (k, v) in test4.rev() {
      println!("{:?} = {:?}", k, v);
    }

    for i in test4.dup(16) {
      println!("dup(16) {:?}", i);
    }

    // Транзакцията ще бъде извършена в края на обхвата
  }

  Ok(())
}
w!(Test1.set [2, 3],[4, 5])
Test2
  key Str
  val Str
Test3
  key i32
  val u64
Test4
  key u64
  val u16
  flag DUPSORT
use anyhow::Result;
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX
  Test0
  Test1
    key u16
    val u64
    flag DUPSORT
  Test2
    key u32
    val u64
}

macro_rules! range_rev {
  ($var:ident, $range:expr) => {
    println!("\n# {}.rev_range({:?})", stringify!($var), $range);
    for i in $var.range_rev($range) {
      println!("{:?}", i);
    }
  };
}

macro_rules! range {
  ($var:ident, $range:expr) => {
    println!("\n# {}.range({:?})", stringify!($var), $range);
    for i in $var.range($range) {
      println!("{:?}", i);
    }
  };
}

fn main() -> Result<()> {
  {
    println!("\n> Test0");
    let tx = &MDBX.w()?;
    let test0 = tx | Test0;
    test0.set([0], [0, 1])?;
    test0.set([1], [1, 2])?;
    test0.set([2], [2, 3])?;
    test0.set([1, 1], [1, 3])?;
    test0.set([1, 2], [1, 3])?;
    test0.set([3], [])?;

    range!(test0, [1]..);
    let begin: &[u8] = &[1, 1];
    range!(test0, begin..=&[2]);
  }

  {
    let tx = &MDBX.w()?;

    let test1 = tx | Test1;
    test1.set(2, 9)?;
    test1.set(2, 4)?;
    test1.set(9, 7)?;
    test1.set(3, 0)?;
    test1.set(3, 8)?;
    test1.set(5, 3)?;
    test1.set(5, 8)?;
    test1.set(9, 1)?;
    println!("-- all");
    for i in test1 {
      println!("{:?}", i);
    }
    range!(test1, 1..3);
    range!(test1, 5..2);
    range!(test1, 1..=3);
    range!(test1, ..3);
    range!(test1, 3..);
    range_rev!(test1, ..1);
    range_rev!(test1, ..=1);
  }

  {
    println!("\n> Test2");
    let tx = &MDBX.w()?;
    let test2 = tx | Test2;
    test2.set(2, 9)?;
    test2.set(1, 2)?;
    test2.set(2, 4)?;
    test2.set(1, 5)?;
    test2.set(9, 7)?;
    test2.set(9, 1)?;
    test2.set(0, 0)?;

    range!(test2, 1..3);
    range!(test2, 1..=3);
    range!(test2, ..3);
    range!(test2, 2..);
    range_rev!(test2, ..1);
    range_rev!(test2, 2..);
    range_rev!(test2, ..=1);
  }

  Ok(())
}
(5, 8)
(5, 3)
(3, 8)
(3, 0)
use anyhow::Result;
use mdbx::prelude::*;
use speedy::{Readable, Writable};

#[derive(PartialEq, Debug, Readable, Writable)]
pub struct City {
  name: String,
  lnglat: (u32, u32),
}

impl FromMdbx for City {
  fn from_mdbx(_: PtrTx, val: MDBX_val) -> Self {
    Self::read_from_buffer(val_bytes!(val)).unwrap()
  }
}

impl ToAsRef<City, Vec<u8>> for City {
  fn to_as_ref(&self) -> Vec<u8> {
    self.write_to_vec().unwrap()
  }
}

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  db_path.into()
});

mdbx! {
  MDBX
  Test
    key u16
    val City
}

fn main() -> Result<()> {
  let city = City {
    name: "BeiJing".into(),
    lnglat: (11640, 3990),
  };

  let tx = w!();
  let test = tx | Test;
  test.set(1, city)?;
  println!("{:?}", test.get(1)?);

  Ok(())
}

Изходът е следният

Some(City { name: "BeiJing", lnglat: (11640, 3990) })

В примера с потребителския тип използваме speedyОтваряне в нов прозорец за извършване на сериализация ( speedy performance reviewОтваряне в нов прозорец ).

Внедряване на потребителски тип FromMdbxОтваряне в нов прозорец и ToAsRefОтваряне в нов прозорец може да се съхранява на адрес mdbx.

Ако използвате конкретна библиотека за сериализация, можете също така да персонализирате макросите наОтваряне в нов прозорец атрибутите, за да опростите процеса.

Опростяване на персонализираните типове с макроси за атрибути

Реализирането на макрос на атрибут е толкова просто, колкото mdbx_speedyОтваряне в нов прозорец Макрокодът на атрибута е следният :

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}
let tx = w!();
let test = tx | Test;
test.set([1, 2], [6])?;
println!("test1 get {:?}", test.get([1, 2]));

match test.get([1, 2])? {
 Some(val) => {
  let t:&[u8] = &val;
  println!("{:?}",t);
 },
 None => unreachable!()
}
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX // Имена на променливи за базата данни ENV
  Test1
  Test2
    key Str
    val Str
  Test3
    key i32
    val u64
  Test4
    key u64
    val u16
    flag DUPSORT
}

fn main() -> Result<()> {
  // Бързо писане
  w!(Test1.set [2, 3],[4, 5]);

  // Бързо четене
  match r!(Test1.get [2, 3]) {
    Some(r) => {
      println!(
        "\nu16::from_le_bytes({:?}) = {}",
        r,
        u16::from_le_bytes((*r).try_into()?)
      );
    }
    None => unreachable!(),
  }

  // Множество операции върху няколко бази данни в една и съща транзакция
  {
    let tx = w!();
    let test1 = tx | Test1;

    test1.set(&[9], &[10, 12])?;
    test1.set([8, 1], [9])?;
    test1.set("rmw.link", "Down with Data Hegemony")?;
    test1.set(&"abc", &"012")?;

    println!("\n-- loop test1");
    for (k, v) in test1 {
      println!("{} = {}", k, v);
    }

    dbg!(test1.del_val([8, 1], [3])?);
    dbg!(test1.get([8, 1])?.unwrap());
    dbg!(test1.del_val([8, 1], [9])?);
    dbg!(test1.get([8, 1])?);

    dbg!(test1.del([9])?);
    dbg!(test1.get([9])?);
    dbg!(test1.del([9])?);

    let test2 = tx | Test2;
    test2.set("rmw.link", "Down with Data Hegemony")?;
    test2.set(&"abc", &"012")?;
    println!("\n-- loop test2");
    for (k, v) in test2 {
      println!("{} = {}", k, v);
    }

    let test3 = tx | Test3;

    test3.set(13, 32)?;
    test3.set(16, 32)?;
    test3.set(-15, 6)?;
    test3.set(-10, 6)?;
    test3.set(-12, 6)?;
    test3.set(0, 6)?;
    test3.set(10, 5)?;

    println!("\n-- loop test3");
    for (k, v) in test3 {
      println!("{:?} = {:?}", k, v);
    }

    let test4 = tx | Test4;
    test4.set(10, 5)?;
    test4.set(10, 0)?;
    test4.set(13, 32)?;
    test4.set(16, 2)?;
    test4.set(16, 1)?;
    test4.set(16, 3)?;
    test4.set(0, 6)?;
    test4.set(10, 5)?;
    test4.set(0, 2)?;

    dbg!(test4.del_val(0, 2)?);
    dbg!(test4.del_val(0, 2)?);

    println!("\n-- loop test4 rev");
    for (k, v) in test4.rev() {
      println!("{:?} = {:?}", k, v);
    }

    for i in test4.dup(16) {
      println!("dup(16) {:?}", i);
    }

    // Транзакцията ще бъде извършена в края на обхвата
  }

  Ok(())
}
w!(Test1.set [2, 3],[4, 5])
Test2
  key Str
  val Str
Test3
  key i32
  val u64
Test4
  key u64
  val u16
  flag DUPSORT
use anyhow::Result;
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX
  Test0
  Test1
    key u16
    val u64
    flag DUPSORT
  Test2
    key u32
    val u64
}

macro_rules! range_rev {
  ($var:ident, $range:expr) => {
    println!("\n# {}.rev_range({:?})", stringify!($var), $range);
    for i in $var.range_rev($range) {
      println!("{:?}", i);
    }
  };
}

macro_rules! range {
  ($var:ident, $range:expr) => {
    println!("\n# {}.range({:?})", stringify!($var), $range);
    for i in $var.range($range) {
      println!("{:?}", i);
    }
  };
}

fn main() -> Result<()> {
  {
    println!("\n> Test0");
    let tx = &MDBX.w()?;
    let test0 = tx | Test0;
    test0.set([0], [0, 1])?;
    test0.set([1], [1, 2])?;
    test0.set([2], [2, 3])?;
    test0.set([1, 1], [1, 3])?;
    test0.set([1, 2], [1, 3])?;
    test0.set([3], [])?;

    range!(test0, [1]..);
    let begin: &[u8] = &[1, 1];
    range!(test0, begin..=&[2]);
  }

  {
    let tx = &MDBX.w()?;

    let test1 = tx | Test1;
    test1.set(2, 9)?;
    test1.set(2, 4)?;
    test1.set(9, 7)?;
    test1.set(3, 0)?;
    test1.set(3, 8)?;
    test1.set(5, 3)?;
    test1.set(5, 8)?;
    test1.set(9, 1)?;
    println!("-- all");
    for i in test1 {
      println!("{:?}", i);
    }
    range!(test1, 1..3);
    range!(test1, 5..2);
    range!(test1, 1..=3);
    range!(test1, ..3);
    range!(test1, 3..);
    range_rev!(test1, ..1);
    range_rev!(test1, ..=1);
  }

  {
    println!("\n> Test2");
    let tx = &MDBX.w()?;
    let test2 = tx | Test2;
    test2.set(2, 9)?;
    test2.set(1, 2)?;
    test2.set(2, 4)?;
    test2.set(1, 5)?;
    test2.set(9, 7)?;
    test2.set(9, 1)?;
    test2.set(0, 0)?;

    range!(test2, 1..3);
    range!(test2, 1..=3);
    range!(test2, ..3);
    range!(test2, 2..);
    range_rev!(test2, ..1);
    range_rev!(test2, 2..);
    range_rev!(test2, ..=1);
  }

  Ok(())
}
(5, 8)
(5, 3)
(3, 8)
(3, 0)
use anyhow::Result;
use mdbx::prelude::*;
use speedy::{Readable, Writable};

#[derive(PartialEq, Debug, Readable, Writable)]
pub struct City {
  name: String,
  lnglat: (u32, u32),
}

impl FromMdbx for City {
  fn from_mdbx(_: PtrTx, val: MDBX_val) -> Self {
    Self::read_from_buffer(val_bytes!(val)).unwrap()
  }
}

impl ToAsRef<City, Vec<u8>> for City {
  fn to_as_ref(&self) -> Vec<u8> {
    self.write_to_vec().unwrap()
  }
}

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  db_path.into()
});

mdbx! {
  MDBX
  Test
    key u16
    val City
}

fn main() -> Result<()> {
  let city = City {
    name: "BeiJing".into(),
    lnglat: (11640, 3990),
  };

  let tx = w!();
  let test = tx | Test;
  test.set(1, city)?;
  println!("{:?}", test.get(1)?);

  Ok(())
}
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(MdbxSpeedy)]
pub fn mdbx_speedy(ts: TokenStream) -> TokenStream {
  let ast: syn::DeriveInput = syn::parse(ts).unwrap();
  let name = &ast.ident;
  quote! {
    impl mdbx::prelude::FromMdbx for #name {
      fn from_mdbx(_: mdbx::prelude::PtrTx, val: mdbx::prelude::MDBX_val) -> Self {
        Self::read_from_buffer(val_bytes!(val)).unwrap()
      }
    }

    impl mdbx::prelude::ToAsRef<#name, Vec<u8>> for #name {
      fn to_as_ref(&self) -> Vec<u8> {
        self.write_to_vec().unwrap()
      }
    }

  }
  .into()
}

Започнете с cargo add mdbx-speedyв собствения си проект и след това можете бързо да персонализирате типа (вижте github.com/rmw-lib/mdbx-example/02Отваряне в нов прозорец за демонстрационен код).

use db::User;

let id = 1234;
let user = r!(User.get id);
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(
  MDBX,
  {
    let mut db_path = std::env::current_exe().unwrap();
    db_path.set_extension("mdb");
    println!("mdbx file path {}", db_path.display());
    db_path.into()
  },
  r,
  w
);

mdbx! {
  MDBX // Име на променлива на базата данни Env
  Test // Тест на базата данни
}

fn main() -> Result<()> {
  // Извеждане на номера на версията на libmdbx
  unsafe {
    println!(
      "mdbx version https://github.com/erthink/libmdbx/releases/tag/v{}.{}.{}",
      mdbx_version.major, mdbx_version.minor, mdbx_version.release
    );
  }

  // Многонишково четене и писане
  let t = std::thread::spawn(|| {
    let tx = w!();
    let test = tx | Test;
    test.set([1, 2], [6])?;
    println!("test1 get {:?}", test.get([1, 2]));

    match test.get([1, 2])? {
      Some(val) => {
        let t: &[u8] = &val;
        println!("{:?}", t);
      }
      None => unreachable!(),
    }
    Ok(())
  });

  t.join().unwrap()?;

  Ok(())
}
  #[derive(Clone, Debug)]
  pub struct Config {
    path: PathBuf,
    mode: ffi::mdbx_mode_t,
    flag: flag::ENV,
    sync_period: u64,
    sync_bytes: u64,
    max_db: u64,
    pagesize: isize,
  }
  
  lazy_static! {
    pub static ref ENV_CONFIG_DEFAULT: Config = Config {
      path:PathBuf::new(),
      mode: 0o600,
      //https://github.com/erthink/libmdbx/issues/248
      sync_period : 65536, // За 1/65536 част от секундата
      sync_bytes : 65536,
      max_db : 256,
      flag : (
          flag::ENV::MDBX_EXCLUSIVE
        | flag::ENV::MDBX_LIFORECLAIM
        | flag::ENV::MDBX_COALESCE
        | flag::ENV::MDBX_NOMEMINIT
        | flag::ENV::MDBX_NOSUBDIR
        | flag::ENV::MDBX_SAFE_NOSYNC
        // | flag::ENV::MDBX_SYNC_DURABLE
      ),
      pagesize:-1
    };
  }
mdbx! {
 MDBX // Име на променлива на базата данни Env
 Test // Тест на базата данни
}
let tx = w!();
let test = tx | Test;
test.set([1, 2], [6])?;
println!("test1 get {:?}", test.get([1, 2]));

match test.get([1, 2])? {
 Some(val) => {
  let t:&[u8] = &val;
  println!("{:?}",t);
 },
 None => unreachable!()
}
use anyhow::{Ok, Result};
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX // Имена на променливи за базата данни ENV
  Test1
  Test2
    key Str
    val Str
  Test3
    key i32
    val u64
  Test4
    key u64
    val u16
    flag DUPSORT
}

fn main() -> Result<()> {
  // Бързо писане
  w!(Test1.set [2, 3],[4, 5]);

  // Бързо четене
  match r!(Test1.get [2, 3]) {
    Some(r) => {
      println!(
        "\nu16::from_le_bytes({:?}) = {}",
        r,
        u16::from_le_bytes((*r).try_into()?)
      );
    }
    None => unreachable!(),
  }

  // Множество операции върху няколко бази данни в една и съща транзакция
  {
    let tx = w!();
    let test1 = tx | Test1;

    test1.set(&[9], &[10, 12])?;
    test1.set([8, 1], [9])?;
    test1.set("rmw.link", "Down with Data Hegemony")?;
    test1.set(&"abc", &"012")?;

    println!("\n-- loop test1");
    for (k, v) in test1 {
      println!("{} = {}", k, v);
    }

    dbg!(test1.del_val([8, 1], [3])?);
    dbg!(test1.get([8, 1])?.unwrap());
    dbg!(test1.del_val([8, 1], [9])?);
    dbg!(test1.get([8, 1])?);

    dbg!(test1.del([9])?);
    dbg!(test1.get([9])?);
    dbg!(test1.del([9])?);

    let test2 = tx | Test2;
    test2.set("rmw.link", "Down with Data Hegemony")?;
    test2.set(&"abc", &"012")?;
    println!("\n-- loop test2");
    for (k, v) in test2 {
      println!("{} = {}", k, v);
    }

    let test3 = tx | Test3;

    test3.set(13, 32)?;
    test3.set(16, 32)?;
    test3.set(-15, 6)?;
    test3.set(-10, 6)?;
    test3.set(-12, 6)?;
    test3.set(0, 6)?;
    test3.set(10, 5)?;

    println!("\n-- loop test3");
    for (k, v) in test3 {
      println!("{:?} = {:?}", k, v);
    }

    let test4 = tx | Test4;
    test4.set(10, 5)?;
    test4.set(10, 0)?;
    test4.set(13, 32)?;
    test4.set(16, 2)?;
    test4.set(16, 1)?;
    test4.set(16, 3)?;
    test4.set(0, 6)?;
    test4.set(10, 5)?;
    test4.set(0, 2)?;

    dbg!(test4.del_val(0, 2)?);
    dbg!(test4.del_val(0, 2)?);

    println!("\n-- loop test4 rev");
    for (k, v) in test4.rev() {
      println!("{:?} = {:?}", k, v);
    }

    for i in test4.dup(16) {
      println!("dup(16) {:?}", i);
    }

    // Транзакцията ще бъде извършена в края на обхвата
  }

  Ok(())
}
w!(Test1.set [2, 3],[4, 5])
Test2
  key Str
  val Str
Test3
  key i32
  val u64
Test4
  key u64
  val u16
  flag DUPSORT
use anyhow::Result;
use mdbx::prelude::*;

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  println!("mdbx file path {}", db_path.display());
  db_path.into()
});

mdbx! {
  MDBX
  Test0
  Test1
    key u16
    val u64
    flag DUPSORT
  Test2
    key u32
    val u64
}

macro_rules! range_rev {
  ($var:ident, $range:expr) => {
    println!("\n# {}.rev_range({:?})", stringify!($var), $range);
    for i in $var.range_rev($range) {
      println!("{:?}", i);
    }
  };
}

macro_rules! range {
  ($var:ident, $range:expr) => {
    println!("\n# {}.range({:?})", stringify!($var), $range);
    for i in $var.range($range) {
      println!("{:?}", i);
    }
  };
}

fn main() -> Result<()> {
  {
    println!("\n> Test0");
    let tx = &MDBX.w()?;
    let test0 = tx | Test0;
    test0.set([0], [0, 1])?;
    test0.set([1], [1, 2])?;
    test0.set([2], [2, 3])?;
    test0.set([1, 1], [1, 3])?;
    test0.set([1, 2], [1, 3])?;
    test0.set([3], [])?;

    range!(test0, [1]..);
    let begin: &[u8] = &[1, 1];
    range!(test0, begin..=&[2]);
  }

  {
    let tx = &MDBX.w()?;

    let test1 = tx | Test1;
    test1.set(2, 9)?;
    test1.set(2, 4)?;
    test1.set(9, 7)?;
    test1.set(3, 0)?;
    test1.set(3, 8)?;
    test1.set(5, 3)?;
    test1.set(5, 8)?;
    test1.set(9, 1)?;
    println!("-- all");
    for i in test1 {
      println!("{:?}", i);
    }
    range!(test1, 1..3);
    range!(test1, 5..2);
    range!(test1, 1..=3);
    range!(test1, ..3);
    range!(test1, 3..);
    range_rev!(test1, ..1);
    range_rev!(test1, ..=1);
  }

  {
    println!("\n> Test2");
    let tx = &MDBX.w()?;
    let test2 = tx | Test2;
    test2.set(2, 9)?;
    test2.set(1, 2)?;
    test2.set(2, 4)?;
    test2.set(1, 5)?;
    test2.set(9, 7)?;
    test2.set(9, 1)?;
    test2.set(0, 0)?;

    range!(test2, 1..3);
    range!(test2, 1..=3);
    range!(test2, ..3);
    range!(test2, 2..);
    range_rev!(test2, ..1);
    range_rev!(test2, 2..);
    range_rev!(test2, ..=1);
  }

  Ok(())
}
(5, 8)
(5, 3)
(3, 8)
(3, 0)
use anyhow::Result;
use mdbx::prelude::*;
use speedy::{Readable, Writable};

#[derive(PartialEq, Debug, Readable, Writable)]
pub struct City {
  name: String,
  lnglat: (u32, u32),
}

impl FromMdbx for City {
  fn from_mdbx(_: PtrTx, val: MDBX_val) -> Self {
    Self::read_from_buffer(val_bytes!(val)).unwrap()
  }
}

impl ToAsRef<City, Vec<u8>> for City {
  fn to_as_ref(&self) -> Vec<u8> {
    self.write_to_vec().unwrap()
  }
}

env_rw!(MDBX, {
  let mut db_path = std::env::current_exe().unwrap();
  db_path.set_extension("mdb");
  db_path.into()
});

mdbx! {
  MDBX
  Test
    key u16
    val City
}

fn main() -> Result<()> {
  let city = City {
    name: "BeiJing".into(),
    lnglat: (11640, 3990),
  };

  let tx = w!();
  let test = tx | Test;
  test.set(1, city)?;
  println!("{:?}", test.get(1)?);

  Ok(())
}
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(MdbxSpeedy)]
pub fn mdbx_speedy(ts: TokenStream) -> TokenStream {
  let ast: syn::DeriveInput = syn::parse(ts).unwrap();
  let name = &ast.ident;
  quote! {
    impl mdbx::prelude::FromMdbx for #name {
      fn from_mdbx(_: mdbx::prelude::PtrTx, val: mdbx::prelude::MDBX_val) -> Self {
        Self::read_from_buffer(val_bytes!(val)).unwrap()
      }
    }

    impl mdbx::prelude::ToAsRef<#name, Vec<u8>> for #name {
      fn to_as_ref(&self) -> Vec<u8> {
        self.write_to_vec().unwrap()
      }
    }

  }
  .into()
}
use anyhow::Result;
use mdbx::prelude::*;
use mdbx_speedy::MdbxSpeedy;
use speedy::{Readable, Writable};

#[derive(PartialEq, Debug, Readable, Writable, MdbxSpeedy)]
pub struct City {
  name: String,
  lnglat: (u32, u32),
}

Разбира се, все още е досадно да се пише многократно #[derive(PartialEq, Debug, Readable, Writable, MdbxSpeedy)], така че можете да използвате derive_aliasОтваряне в нов прозорец за допълнително опростяване на кода.

Бележка относно използването на

Дължината на ключа

  • Минимален 0, максимален ≈ ½ размер на страницата (по подразбиране максималният размер на ключа за страница 4K е 2022 байта), задава се при инициализиране на базата данни pagesize може да бъде конфигуриран на не повече от 65536и трябва да бъде степен 2.

Бележки под линия

Те посочват ползите от преминаването от LMDB към MDBX.

Erigon започна с база данни BoltDB, след това добави поддръжка за BadgerDB и накрая премина изцяло към LMDB. в някакъв момент се сблъскахме с проблеми със стабилността, причинени от използването на LMDB, които не бяха предвидени от създателите. Оттогава разглеждаме добре поддържана производна на LMDB, наречена MDBX, и се надяваме да използваме техните подобрения на стабилността и евентуално да си сътрудничим повече в бъдеще. интеграцията на MDBX вече е завършена и е време за повече тестове и документация.

Предимства на преминаването от LMDB към MDBX.

  1. Растежът "пространство (геометрия)" на файловете с бази данни работи правилно. Това е важно, особено в Windows. В LMDB трябва да се зададе размерът на картата на паметта веднъж предварително (в момента използваме 2 TB по подразбиране) и ако файлът с базата данни нарасне над тази граница, процесът трябва да се рестартира. В Windows задаването на размера на картата на паметта на 2 TB би направило файла с базата данни 2 TB голям в началото, което не е много удобно. В MDBX размерът на картата на паметта се увеличава на стъпки от по 2Gb. Това означава периодично пренастройване, но води до по-добро потребителско изживяване.

  2. MDBX има по-строги проверки за едновременно използване на обработка на транзакции и припокриване на транзакции за четене и запис в една и съща нишка на изпълнение. Това ни позволява да открием някои не очевидни грешки и прави поведението по-предсказуемо.
    За повече от 5 години (откакто е отделен от LMDB) MDBX е натрупал голям брой поправки на сигурността и поправки на грешки, които, доколкото ни е известно, все още съществуват в LMDB. Някои от тях бяха открити по време на нашето тестване, а поддръжниците на MDBX се отнесоха сериозно към тях и ги отстраниха незабавно.

  3. Когато става въпрос за бази данни, които постоянно променят данни, те създават доста голямо пространство за възстановяване (известно също като "свободен списък" в терминологията на LMDB). Наложи се да поправим LMDB, за да отстраним най-сериозните недостатъци при обработката на възстановимото пространство (анализ)Отваряне в нов прозорец. MDBX обръща специално внимание на ефективната обработка на възстановимото пространство и досега не се е налагало да бъде поправянОтваряне в нов прозорец.

  4. Въз основа на нашите тестове MDBX се представи малко по-добре при нашите работни натоварвания.

  5. MDBX разкрива повече вътрешни телеметрични данни - повече показатели за това, което се случва в базата данни. И ние разполагаме с тези данни в Grafana - за да вземаме по-добри решения за дизайна на приложенията. Например след пълното преминаване към MDBX (премахване на поддръжката на LMDB) ще въведем политика на "предаване на наполовина пълна транзакция", за да избегнем препълване/препълване на дисковите контакти. Това ще опрости допълнително кода ни, без да се отрази на производителността.

  6. MDBX поддържа режим "Exclusive open" (Изключително отворен) - използваме го при миграция на бази данни, за да предотвратим достъпа на други читатели до базата данни по време на процеса на миграция.


  1. Erigon (следващото поколение Ethernet клиент) наскоро премина от LMDB към MDBXОтваряне в нов прозорец. ↩︎

Актуализации:
От: gcxfd