-
2020-03-27 14:47:20
1. 创建
创建记录:
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()} db.NewRecord(user) // => 主键为空返回`true` db.Create(&user) db.NewRecord(user) // => 创建`user`后返回`false`
设置默认值:
可以在gorm tag中定义默认值,然后插入SQL将忽略具有默认值的这些字段,并且其值为空,并且在将记录插入数据库后,gorm将从数据库加载这些字段的值。type Animal struct { ID int64 Name string `gorm:"default:'galeone'"` Age int64 } var animal = Animal{Age: 99, Name: ""} db.Create(&animal)
INSERT INTO animals("age") values('99'); SELECT name from animals WHERE ID=111; // 返回主键为 111 animal.Name => 'galeone'
2. 查询
获取:第一条记录/ 最后一条记录/ 所有记录
// 获取第一条记录,按主键排序 db.First(&user) SELECT * FROM users ORDER BY id LIMIT 1; // 获取最后一条记录,按主键排序 db.Last(&user) SELECT * FROM users ORDER BY id DESC LIMIT 1; // 获取所有记录 db.Find(&users) SELECT * FROM users; // 使用主键获取记录 db.First(&user, 10) SELECT * FROM users WHERE id = 10;
Where查询条件
// 获取第一个匹配记录 db.Where("name = ?", "jinzhu").First(&user) SELECT * FROM users WHERE name = 'jinzhu' limit 1; // 获取所有匹配记录 db.Where("name = ?", "jinzhu").Find(&users) SELECT * FROM users WHERE name = 'jinzhu'; // IN db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users) // LIKE db.Where("name LIKE ?", "%jin%").Find(&users) // AND db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
注意:当使用struct查询时,GORM将只查询那些具有值的字段
// Struct db.Where(&User{Name: "zhangyang", Age: 20}).First(&user) SELECT * FROM users WHERE name = "zhangyang" AND age = 20 LIMIT 1; // Map db.Where(map[string]interface{}{"name": "zhangyang", "age": 20}).Find(&users) SELECT * FROM users WHERE name = "zhangyang" AND age = 20; // 主键的Slice db.Where([]int64{20, 21, 22}).Find(&users) SELECT * FROM users WHERE id IN (20, 21, 22);
Not条件查询
db.Not("name", "jinzhu").First(&user) SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1; // Not In db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users) SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2"); // Not In slice of primary keys db.Not([]int64{1,2,3}).First(&user) SELECT * FROM users WHERE id NOT IN (1,2,3); db.Not([]int64{}).First(&user) SELECT * FROM users;
Or条件查询
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users) SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin'; // Struct db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users) SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';
Select 指定要从数据库检索的字段,默认情况下,将选择所有字段;
db.Select("name, age").Find(&users) SELECT name, age FROM users; db.Select([]string{"name", "age"}).Find(&users) SELECT name, age FROM users;
Order
db.Order("age desc, name").Find(&users) SELECT * FROM users ORDER BY age desc, name; // Multiple orders db.Order("age desc").Order("name").Find(&users) SELECT * FROM users ORDER BY age desc, name; // ReOrder 从数据库检索记录时指定顺序,将重排序设置为true以覆盖定义的条件 db.Order("age desc").Find(&users1).Order("age", true).Find(&users2) SELECT * FROM users ORDER BY age desc; (users1) SELECT * FROM users ORDER BY age; (users2)
Limit , Offset
指定要检索的记录数db.Limit(3).Find(&users) SELECT * FROM users LIMIT 3;
指定在开始返回记录之前要跳过的记录数
db.Offset(3).Find(&users) SELECT * FROM users OFFSET 3; // Cancel offset condition with -1 db.Offset(10).Find(&users1).Offset(-1).Find(&users2) SELECT * FROM users OFFSET 10; (users1) SELECT * FROM users; (users2)
Count,Group & Having,Join
获取记录数db.Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Find(&users).Count(&count) SELECT * from USERS WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (users) SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count) db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count) SELECT count(*) FROM users WHERE name = 'jinzhu'; (count) //指定表deleted_users db.Table("deleted_users").Count(&count) //
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
3. 更新
更新全部字段
Save将包括执行更新SQL时的所有字段,即使它没有更改db.First(&user) user.Name = "jinzhu 2" user.Age = 100 db.Save(&user) UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
更新指定字段
如果只想更新指定字段,可以使用Update, Updates// 更新单个属性(如果更改) db.Model(&user).Update("name", "hello") UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111; // 使用组合条件更新单个属性 db.Model(&user).Where("active = ?", true).Update("name", "hello") UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true; // 使用`map`更新多个属性,只会更新这些更改的字段 db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false}) UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111; // 使用`struct`更新多个属性,只会更新这些更改的和非空白字段 db.Model(&user).Updates(User{Name: "hello", Age: 18}) UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111; // 警告:当使用struct更新时,FORM将仅更新具有非空值的字段 // 对于下面的更新,什么都不会更新为"",0,false是其类型的空白值 db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})
更新选择的字段
如果您只想在更新时更新或忽略某些字段,可以使用Select, Omitdb.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false}) UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111; db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false}) UPDATE users SET age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
4. 删除
警告:删除记录时,需要确保其主要字段具有值,GORM将使用主键删除记录,如果主要字段为空,GORM将删除模型的所有记录
// 删除存在的记录 db.Delete(&email) DELETE from emails where id=10; // 为Delete语句添加额外的SQL选项 db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email) DELETE from emails where id=10 OPTION (OPTIMIZE FOR UNKNOWN);
批量删除
删除所有匹配记录db.Where("email LIKE ?", "%jinzhu%").Delete(Email{}) DELETE from emails where email LIKE "%jinhu%"; db.Delete(Email{}, "email LIKE ?", "%jinzhu%") DELETE from emails where email LIKE "%jinhu%";
更多相关内容 -
SQL SERVER 安装ORACLE provider for OLE DB 的连接服务驱动
2018-04-19 13:37:18在为SQL SERVER创建ORACLE的连接服务器时,没有ORACLE provider for OLE DB 的连接服务驱动。经网上搜索,把PDF安装说明和驱动程序一起收藏,请各位下载; -
IndexedDB详解
2021-01-11 11:00:49IndexedDB是一种在浏览器端存储数据的方式。既然称之为DB,是因为它丰富了客户端的查询方式,并且因为是本地存储,可以有效的减少网络对页面数据的影响。 有了IndexedDB,浏览器可以存储更多的数据,从而丰富了...简介
IndexedDB是一种在浏览器端存储数据的方式。既然称之为DB,是因为它丰富了客户端的查询方式,并且因为是本地存储,可以有效的减少网络对页面数据的影响。
有了IndexedDB,浏览器可以存储更多的数据,从而丰富了浏览器端的应用类型。
IndexedDB简介
IndexedDB和传统的关系型数据不同的是,它是一个key-value型的数据库。
value可以是复杂的结构体对象,key可以是对象的某些属性值也可以是其他的对象(包括二进制对象)。你可以使用对象中的任何属性做为index,以加快查找。
IndexedDB是自带transaction的,所有的数据库操作都会绑定到特定的事务上,并且这些事务是自动提交了,IndexedDB并不支持手动提交事务。
IndexedDB API大部分都是异步的,在使用异步方法的时候,API不会立马返回要查询的数据,而是返回一个callback。
异步API的本质是向数据库发送一个操作请求,当操作完成的时候,会收到一个DOM event,通过该event,我们会知道操作是否成功,并且获得操作的结果。
IndexedDB是一种 NoSQL 数据库,和关系型数据库不同的是,IndexedDB是面向对象的,它存储的是Javascript对象。
IndexedDB还有一个很重要的特点是其同源策略,每个源都会关联到不同的数据库集合,不同源是不允许访问其他源的数据库,从而保证了IndexedDB的安全性。
IndexedDB的使用
这一节,我们将会以具体的例子来讲解如何使用IndexedDB。
IndexedDB的浏览器支持
不同的浏览器对于IndexedDB有不同的实现,正常来说,我们可以使用window.indexedDB来获取到浏览器的indexedDB对象。但是对于某些浏览器来说,还没有使用标准的window.indexedDB,而是用带前缀的实现。
所以我们在使用过程中通常需要进行判断和转换:
// In the following line, you should include the prefixes of implementations you want to test. window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; // DON'T use "var indexedDB = ..." if you're not in a function. // Moreover, you may need references to some window.IDB* objects: window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: "readwrite"}; // This line should only be needed if it is needed to support the object's constants for older browsers window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange; // (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)
上面我们从window获取了indexedDB,IDBTransaction和IDBKeyRange三个对象。
其中indexedDB表示的是数据库的连接。IDBTransaction表示的是transaction,而IDBKeyRange则是用从数据库的某个特定key range中取出数据。
但是,通常来说带前缀的实现一般都是不稳定的,所以我们通常不建议在正式环境中使用,所以如果不支持标准表达式的话,需要直接报错:
if (!window.indexedDB) { console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available."); }
创建IndexedDB
要使用IndexedDB,我们首先需要open it:
// Let us open our database var request = window.indexedDB.open("MyTestDatabase", 3);
open方法返回一个IDBOpenDBRequest对象,同时这是一个异步操作,open操作并不会立马打开数据库或者开启事务,我们可以通过监听request的事件来进行相应的处理。
open方法传入两个参数,第一个参数是数据库的名字,第二个参数是数据库的版本号。
当你创建一个新的数据库或者升级一个现有的数据库版本的时候,将会触发一个onupgradeneeded事件,并在事件中传入IDBVersionChangeEvent,我们可以通过event.target.result来获取到IDBDatabase对象,然后通过这个对象来进行数据库的版本升级操作。如下所示:
// This event is only implemented in recent browsers request.onupgradeneeded = function(event) { // Save the IDBDatabase interface var db = event.target.result; // Create an objectStore for this database var objectStore = db.createObjectStore("name", { keyPath: "myKey" }); };
注意,这里的版本号是一个整数。如果你传入一个float,那么将会对该float进行取整操作。
有了request,我们可以通过监听onerror或者onsuccess事件来进行相应的处理。
var db; var request = indexedDB.open("MyTestDatabase"); request.onerror = function(event) { console.log("Why didn't you allow my web app to use IndexedDB?!"); }; request.onsuccess = function(event) { db = event.target.result; };
拿到db对象之后,我们可以设置全局的异常处理:
db.onerror = function(event) { // Generic error handler for all errors targeted at this database's // requests! console.error("Database error: " + event.target.errorCode); };
IndexedDB中的table叫做object stores,和关系型数据库中的table一样,object stores中的每一个对象都和一个key相关联,和key相关的有两个概念 key path 和 key generator.
如果存储的是javascript Object对象,那么可以指定该对象中的某一个属性作为key path,那么这个属性将会被作为key。
如果没有指定key path,那么存储的Object可以是任何对象,甚至是基础类型比如数字和String。
而key generator就是key的生成器。
假如我们想要存储这样的数据:
// This is what our customer data looks like. const customerData = [ { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" }, { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" } ];
看一下对应的数据库操作是怎么样的:
const dbName = "the_name"; var request = indexedDB.open(dbName, 2); request.onerror = function(event) { // Handle errors. }; request.onupgradeneeded = function(event) { var db = event.target.result; // Create an objectStore to hold information about our customers. We're // going to use "ssn" as our key path because it's guaranteed to be // unique - or at least that's what I was told during the kickoff meeting. var objectStore = db.createObjectStore("customers", { keyPath: "ssn" }); // Create an index to search customers by name. We may have duplicates // so we can't use a unique index. objectStore.createIndex("name", "name", { unique: false }); // Create an index to search customers by email. We want to ensure that // no two customers have the same email, so use a unique index. objectStore.createIndex("email", "email", { unique: true }); // Use transaction oncomplete to make sure the objectStore creation is // finished before adding data into it. objectStore.transaction.oncomplete = function(event) { // Store values in the newly created objectStore. var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers"); customerData.forEach(function(customer) { customerObjectStore.add(customer); }); }; };
我们需要在onupgradeneeded事件中处理所有的schema相关的操作。
首先使用db.createObjectStore创建了一个customers的ObjectStore,并且使用了对象的keypath作为key。
除了key之外,我们创建了两个index,以提高查询速度。
最后我们监听transaction.oncomplete事件,并在里面加入存储object的操作。
上面的代码中,我们使用了keyPath作为key。
下面是一个使用key Generator的例子:
var objStore = db.createObjectStore("names", { autoIncrement : true });
indexdb中的CURD
indexedDB的所有操作都需要在事务中,我们看一个开启事务的操作:
var transaction = db.transaction(["customers"], "readwrite");
上面的例子中使用readwrite来操作customers ObjectStore。
transaction接收两个参数,第一个参数是一个数组,数组中是这个trans中将会处理的ObjectStores,第二个参数是处理的模式。
有了transaction之后,我们可以监听事务的complete和error操作,然后就可以进行add操作了:
// Do something when all the data is added to the database. transaction.oncomplete = function(event) { console.log("All done!"); }; transaction.onerror = function(event) { // Don't forget to handle errors! }; var objectStore = transaction.objectStore("customers"); customerData.forEach(function(customer) { var request = objectStore.add(customer); request.onsuccess = function(event) { // event.target.result === customer.ssn; }; });
上面的例子中,我们使用了add方法,add的前提是数据库中并不存在相同key的对象。除了add方法之外,我们还可以使用put方法,put方法主要用来进行更新操作。
再看一个删除的操作:
var request = db.transaction(["customers"], "readwrite") .objectStore("customers") .delete("444-44-4444"); request.onsuccess = function(event) { // It's gone! };
现在我们的数据库已经有了数据,我们看下怎么进行查询:
var transaction = db.transaction(["customers"]); var objectStore = transaction.objectStore("customers"); var request = objectStore.get("444-44-4444"); request.onerror = function(event) { // Handle errors! }; request.onsuccess = function(event) { // Do something with the request.result! console.log("Name for SSN 444-44-4444 is " + request.result.name);
这里,我们直接使用了db.transaction,默认情况下是readonly模式的。
下面是一个更新的例子:
var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers"); var request = objectStore.get("444-44-4444"); request.onerror = function(event) { // Handle errors! }; request.onsuccess = function(event) { // Get the old value that we want to update var data = event.target.result; // update the value(s) in the object that you want to change data.age = 42; // Put this updated object back into the database. var requestUpdate = objectStore.put(data); requestUpdate.onerror = function(event) { // Do something with the error }; requestUpdate.onsuccess = function(event) { // Success - the data is updated! }; };
更新我们使用的是put方法。
使用游标cursor
indexedDB支持游标操作,我们可以使用cursor来遍历objectStore的数据:
var objectStore = db.transaction("customers").objectStore("customers"); objectStore.openCursor().onsuccess = function(event) { var cursor = event.target.result; if (cursor) { console.log("Name for SSN " + cursor.key + " is " + cursor.value.name); cursor.continue(); } else { console.log("No more entries!"); } };
openCursor可以接受多个参数,第一个参数可以接受key的查询范围,第二个参数用来指定遍历的方向。如果两个参数都为空的话,默认是所有的数据的以升序的顺序遍历。
如果想遍历下一个游标,则可以调用cursor.continue。
我们看一下两个参数的游标使用:
// Only match "Donna" var singleKeyRange = IDBKeyRange.only("Donna"); // Match anything past "Bill", including "Bill" var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill"); // Match anything past "Bill", but don't include "Bill" var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true); // Match anything up to, but not including, "Donna" var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true); // Match anything between "Bill" and "Donna", but not including "Donna" var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true); // To use one of the key ranges, pass it in as the first argument of openCursor()/openKeyCursor() index.openCursor(boundKeyRange, "prev").onsuccess = function(event) { var cursor = event.target.result; if (cursor) { // Do something with the matches. cursor.continue(); } };
除了openCursor,我们还可以通过使用openKeyCursor来遍历KeyCursor:
// Using a normal cursor to grab whole customer record objects index.openCursor().onsuccess = function(event) { var cursor = event.target.result; if (cursor) { // cursor.key is a name, like "Bill", and cursor.value is the whole object. console.log("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email); cursor.continue(); } }; // Using a key cursor to grab customer record object keys index.openKeyCursor().onsuccess = function(event) { var cursor = event.target.result; if (cursor) { // cursor.key is a name, like "Bill", and cursor.value is the SSN. // No way to directly get the rest of the stored object. console.log("Name: " + cursor.key + ", SSN: " + cursor.primaryKey); cursor.continue(); } };
除此之外,我们还可以直接通过index来进行查询:
var index = objectStore.index("name"); index.get("Donna").onsuccess = function(event) { console.log("Donna's SSN is " + event.target.result.ssn); };
要使用index的前提就是需要在request.onupgradeneeded中创建index。
本文作者:flydean程序那些事
本文链接:http://www.flydean.com/indexeddb-kickoff/
本文来源:flydean的博客
欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
-
中国城市 db文件
2015-06-10 16:04:08中国城市 db文件,可以用到android端的数据库 -
sqlcipher-3.0.1-windows含使用教程,可以直接加密db文件,直接解密db文件,直接查看数据库表数据
2015-09-25 09:45:44sqlcipher-3.0.1-windows含使用教程,可以直接加密db文件,直接解密db文件,直接查看数据库表数据。windows可用!含使用教程!自己用过整理的,很好用,所以2分不亏! -
使用db_bench 对rocksdb进行性能压测
2020-05-30 16:59:16rocksdb提供benchmark工具来对自身性能进行全方位各个纬度的评估,包括顺序读写,随机读写,热点读写,删除,合并,查找,校验等性能的评估,十分好用。 工具编译 这里使用的是cmake的方式,主要是为了在自己用户下...
rocksdb提供benchmark工具来对自身性能进行全方位各个纬度的评估,包括顺序读写,随机读写,热点读写,删除,合并,查找,校验等性能的评估,十分好用。1. 工具编译
这里使用的是cmake的方式,主要是为了在自己用户下指定对应的第三方库的路径(glfags等)以及指定是否开启rocksdb自己的压缩算法编译,否则直接make 原生的Makefile问题较多且 db_bench所依赖的一些库都没法自动链接进去(zstd,snappy等压缩算法 默认是不编译到db_bench中的)
如果你的db_bench工具已经安装好了,可以跳过当前步骤,如果没有编译好,不想这么麻烦,可以直接看下面,会提供一个整体的便捷编译脚本。
基本流程如下:
- 下载rocksdb源码
如果需要指定对应的版本,可以下载好之后执行git clone https://github.com/facebook/rocksdb.git
git branch xxx
切换到对应版本的分支 - 第三方库的编译安装
a. gflags
b. 安装a. git clone https://github.com/gflags/gflags.git b. cd gflags c. mkdir build && cd build #以下DCMAKE_INSTALL_PREFIX 之后的路径为自己想要安装的路径,如果有root权限且可以安装到系统目录下,那么可以不用指定prefix选项,BUILD_SHARED_LIBS选项表示开启编译gflags的动态库,否则默认不编译动态库 d. cmake .. -DCMAKE_INSTALL_PREFIX=/xxx -DBUILD_SHARED_LIBS=1 -DCMAKE_BUILD_TYPE=Release e. make && make install #增加gflags的include 和 lib库的路径到系统库下面,如上面未指定路径,则系统默认安装在 #/usr/local/gflags f. 编辑当前用户下的bashrc,添加如下内容: export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/xxx/gcc-5.3/lib64:/xxx/gflags/lib export LIBRARY_PATH=$LIBRARY_PATH:/xxx/gflags/include
snappy
sudo yum install snappy snappy-devel
c. 安装zlib
yum install zlib zlib-devel
d. 安装bzip2
yum install bzip2 bzip2-devel
e.安装lz4
yum install lz4-devel
f. 安装zstardard
wget https://github.com/facebook/zstd/archive/v1.1.3.tar.gz mv v1.1.3.tar.gz zstd-1.1.3.tar.gz tar zxvf zstd-1.1.3.tar.gz cd zstd-1.1.3 make && sudo make install
- 生成makefile
cd rocksdb && mkdir build #以下的prefix路径需要指定安装gflags的prefix路径,否则编译过程中无法链接到gflags的库 #如果cmake 版本过低,使用cmake3 #DWITH_xxx 表示开启几个压缩算法的编译选项,否则运行db_bench时rocksdb产生数据压缩的时候无法找到对应的库 cmake .. -DCMAKE_PREFIX_PATH=/xxx -DWITH_SNAPPY=1 -DWITH_LZ4=1 -DWITH_ZLIB=1 -DWITH_ZSTD=1 -DCMAKE_BUILD_TYPE=Release=release
- 编译
以上会通过上级目录的CMakeList.txt 在当前目录生成Makefile,最终执行编译make -j
成功后
db_bench
工具会在当前目录下生成。
编译脚本如下,那一些压缩算法的lib还是需要提前装好的。#!/bin/sh set -x # build gflags function build_gflags() { local source_dir local build_dir # dir is exists, there is no need to clone again if [ -e "gflags" ] && [ -e "gflags/build" ]; then return fi git clone https://github.com/gflags/gflags.git if [ $? -ne 0 ]; then echo "git clone gflags failed." fi cd gflags source_dir=$(pwd) build_dir=$source_dir/build mkdir -p "$build_dir"/ \ && cd "$build_dir"/ \ && cmake3 .. -DCMAKE_INSTALL_PREFIX="$build_dir" -DBUILD_SHARED_LIBS=1 -DBUILD_STATIC_LIBS=1 \ && make \ && make install \ && cd ../../ } git submodule update --init --recursive build_gflags SOURCE_DIR=$(pwd) BUILD_DIR="$SOURCE_DIR"/build GFLAGS_DIR=gflags/build # for using multi cores parallel compile NUM_CPU_CORES=$(grep "processor" -c /proc/cpuinfo) if [ -z "${NUM_CPU_CORES}" ] || [ "${NUM_CPU_CORES}" = "0" ] ; then NUM_CPU_CORES=1 fi mkdir -p "$BUILD_DIR"/ \ && cd "$BUILD_DIR" cmake3 "$SOURCE_DIR" -DCMAKE_BUILD_TYPE=Release -DWITH_SNAPPY=ON -DWITH_TESTS=OFF -DCMAKE_PREFIX_PATH=$GFLAGS_DIR make -j $NUM_CPU_CORES
2. 基本性能压测
由于db_bench工具的选项太多了,这里直接提取社区的测试方式
核心是benchmark,它代表本次测试使用的压测方式,benchmark的列表如下fillseq -- write N values in sequential key order in async mode fillseqdeterministic -- write N values in the specified key order and keep the shape of the LSM tree fillrandom -- write N values in random key order in async mode filluniquerandomdeterministic -- write N values in a random key order and keep the shape of the LSM tree overwrite -- overwrite N values in random key order in async mode fillsync -- write N/100 values in random key order in sync mode fill100K -- write N/1000 100K values in random order in async mode deleteseq -- delete N keys in sequential order deleterandom -- delete N keys in random order readseq -- read N times sequentially readtocache -- 1 thread reading database sequentially readreverse -- read N times in reverse order readrandom -- read N times in random order readmissing -- read N missing keys in random order readwhilewriting -- 1 writer, N threads doing random reads readwhilemerging -- 1 merger, N threads doing random reads readrandomwriterandom -- N threads doing random-read, random-write prefixscanrandom -- prefix scan N times in random order updaterandom -- N threads doing read-modify-write for random keys appendrandom -- N threads doing read-modify-write with growing values mergerandom -- same as updaterandom/appendrandom using merge operator. Must be used with merge_operator readrandommergerandom -- perform N random read-or-merge operations. Must be used with merge_operator newiterator -- repeated iterator creation seekrandom -- N random seeks, call Next seek_nexts times per seek seekrandomwhilewriting -- seekrandom and 1 thread doing overwrite seekrandomwhilemerging -- seekrandom and 1 thread doing merge crc32c -- repeated crc32c of 4K of data xxhash -- repeated xxHash of 4K of data acquireload -- load N*1000 times fillseekseq -- write N values in sequential key, then read them by seeking to each key randomtransaction -- execute N random transactions and verify correctness randomreplacekeys -- randomly replaces N keys by deleting the old version and putting the new version timeseries -- 1 writer generates time series data and multiple readers doing random reads on id
-
创建一个db,并写入一些数据
./db_bench --benchmarks="fillseq"
但是这样并不会打印更多有效的元信息DB path: [/tmp/rocksdbtest-1001/dbbench] fillseq : 2.354 micros/op 424867 ops/sec; 47.0 MB/s
-
创建一个db,并打印一些元信息
./db_bench --benchmarks="fillseq,stats"
--benchmarks
表示测试的顺序,支持持续叠加。本次就是顺序写之后打印db的状态信息。
这样会打印db相关的stats信息,包括db的stat信息和compaction的stat信息DB path: [/tmp/rocksdbtest-1001/dbbench] # 测试顺序写的性能信息 fillseq : 2.311 micros/op 432751 ops/sec; 47.9 MB/s ** Compaction Stats [default] ** Level Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- L0 1/0 28.88 MB 0.2 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 60.6 0.48 0.31 1 0.477 0 0 Sum 1/0 28.88 MB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 60.6 0.48 0.31 1 0.477 0 0 Int 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 60.6 0.48 0.31 1 0.477 0 0 ** Compaction Stats [default] ** Priority Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- High 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 60.6 0.48 0.31 1 0.477 0 0 Uptime(secs): 2.3 total, 2.3 interval Flush(GB): cumulative 0.028, interval 0.028 AddFile(GB): cumulative 0.000, interval 0.000 AddFile(Total Files): cumulative 0, interval 0 AddFile(L0 Files): cumulative 0, interval 0 AddFile(Keys): cumulative 0, interval 0 Cumulative compaction: 0.03 GB write, 12.34 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.5 seconds Interval compaction: 0.03 GB write, 12.50 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.5 seconds Stalls(count): 0 level0_slowdown, 0 level0_slowdown_with_compaction, 0 level0_numfiles, 0 level0_numfiles_with_compaction, 0 stop for pending_compaction_bytes, 0 slowdown for pending_compaction_bytes, 0 memtable_compaction, 0 memtable_slowdown, interval 0 total count ** File Read Latency Histogram By Level [default] ** ** DB Stats ** Uptime(secs): 2.3 total, 2.3 interval Cumulative writes: 1000K writes, 1000K keys, 1000K commit groups, 1.0 writes per commit group, ingest: 0.12 GB, 53.39 MB/s Cumulative WAL: 1000K writes, 0 syncs, 1000000.00 writes per sync, written: 0.12 GB, 53.39 MB/s Cumulative stall: 00:00:0.000 H:M:S, 0.0 percent Interval writes: 1000K writes, 1000K keys, 1000K commit groups, 1.0 writes per commit group, ingest: 124.93 MB, 54.06 MB/s Interval WAL: 1000K writes, 0 syncs, 1000000.00 writes per sync, written: 0.12 MB, 54.06 MB/s Interval stall: 00:00:0.000 H:M:S, 0.0 percent
更多的meta operation操作如下
- compact 对整个数据库进行合并
- stats 打印db的状态信息
- resetstats 重置db的状态信息
- levelstats 打印每一层的文件个数以及每一层的占用的空间大小
- sstables 打印sst文件的信息
对应的sstables和levelstats显示信息如下
--- level 0 --- version# 2 --- 7:30286882[1 .. 448148]['00000000000000003030303030303030' \ seq:1, type:1 .. '000000000006D6933030303030303030' seq:448148, type:1](0) --- level 1 --- version# 2 --- --- level 2 --- version# 2 --- --- level 3 --- version# 2 --- --- level 4 --- version# 2 --- --- level 5 --- version# 2 --- --- level 6 --- version# 2 --- Level Files Size(MB) -------------------- 0 1 29 1 0 0 2 0 0 3 0 0 4 0 0 5 0 0 6 0 0
-
单独随机写测试
相关的参数可以自己配置,这里仅仅列出一部分参数
可以通过./db_bench --help
自己查看想要的配置参数,当然配置前需要对各个参数有一定的了解。./db_bench \ --benchmarks="fillrandom,stats,levelstats" \ --enable_write_thread_adaptive_yield=false \ --disable_auto_compactions=false \ --max_background_compactions=32 \ --max_background_flushes=4 \ --write_buffer_size=536870912 \ --min_write_buffer_number_to_merge=2 \ --max_write_buffer_number=6 \ --target_file_size_base=67108864 \ --max_bytes_for_level_base=536870912 \ --compression_type=none \ #关闭压缩 --num=500000000 \ #总共写入的请求个数,如果达不到则写30秒就停止 --duration=30 \ #持续IO的时间是30秒 --threads=1000\ #并发1000个线程 --value_size=8192\ #value size是8K --key_size=16 \ #key size 16B --enable_pipelined_write=true \ --db=./db_bench_test \ #指定创建db的目录 --wal_dir=./db_bench_test \ #指定创建wal的目录 --allow_concurrent_memtable_write=true \ #允许并发写memtable --disable_wal=false \ --batch_size=1 \ --sync=false \ #是否开启sync
在这个workload下,每一次benchmark db_bench都会重新创建db,可能是遗留原因。而有的时候,我们想要在原有db之前追加一定条目的请求,并不希望之前的db被清理掉。可以这样简单更改一下db_bench的代码:
我们使用fillrandom
workload的时候,搭配use_existing_db=1
默认会退出
这个时候我们只需要将,fillrandom workload下的fresh_db 更改为false就可以继续测试了,每一次fillrandom都会在之前的基础上增加条目,而不会destory之前的db。
-
随机读
使用之前fillrandom创建的db,进行随机读取,需要开启use_existing_keys=1
和use_existing_db=1
,否则都会是not-found,被bloom-filter过滤,不会命中之前写入的数据。./db_bench \ --benchmarks="fillseq,stats,readrandom,stats" --enable_write_thread_adaptive_yield=false \ --disable_auto_compactions=false \ --max_background_compactions=32 \ --max_background_flushes=4 \ --write_buffer_size=536870912 \ --min_write_buffer_number_to_merge=2 \ --max_write_buffer_number=6 \ --target_file_size_base=67108864 \ --max_bytes_for_level_base=536870912 \ --use_existing_keys=1 \ #建议使用已存在key进行读,否则就一直被filter过滤掉,打不到磁盘,测试不精确 --use_existing_db=1 \ --cache_size=2147483648 \ #2G的block-cache,默认是8M,实际生产环境,如果read-heavy应该设置为内存大小的三分之一。 --num=500000000 \ #总共写入的请求个数,如果达不到则写30秒就停止 --duration=30 \ #持续IO的时间是30秒 --threads=1000 \ #并发1000个线程 --value_size=8192 \ #value size是8K --key_size=16 \ #key size 16B --enable_pipelined_write=true \ --db=./db_bench_test \ #指定创建db的目录 --wal_dir=./db_bench_test \ #指定创建wal的目录 --allow_concurrent_memtable_write=true \ #允许并发写memtable --disable_wal=false \
如果要测试热点读,可以指定参数
--key_id_range=100000
,表示生成的key的范围是在100000范围内,该测试需要在benchmark中增加timeseries
-
读写混合readwhilewriting
9个线程读,一个线程写./db_bench \ --benchmarks="readwhilewriting,stats" --enable_write_thread_adaptive_yield=false \ --disable_auto_compactions=false \ --max_background_compactions=32 \ --max_background_flushes=4 \ --write_buffer_size=536870912 \ --min_write_buffer_number_to_merge=2 \ --max_write_buffer_number=6 \ --target_file_size_base=67108864 \ --max_bytes_for_level_base=536870912 \ --use_existing_keys=1 \ #建议使用已存在key进行读,否则就一直被filter过滤掉,打不到磁盘,测试不精确 --use_existing_db=1 \ --cache_size=2147483648 \ #2G的block-cache,默认是8M,实际生产环境,如果read-heavy应该设置为内存大小的三分之一。 --num=500000000 \ #总共写入的请求个数,如果达不到则写30秒就停止 --duration=30 \ #持续IO的时间是30秒 --threads=1000\ #并发1000个线程 --value_size=8192\ #value size是8K --key_size=16 \ #key size 16B --enable_pipelined_write=true \ --db=./db_bench_test \ #指定创建db的目录 --wal_dir=./db_bench_test \ #指定创建wal的目录 --allow_concurrent_memtable_write=true \ #允许并发写memtable --disable_wal=false \
-
随机读随机写 ReadRandomWriteRandom
随机读一个请求,随机写一个请求,可以指定读写比,readwritepercent;默认是90%的读,可以降低读的比例
需要注意开启--use_existing_keys=1
和--use_existing_db=1
./db_bench \ --benchmarks="readrandomwriterandom,stats" --enable_write_thread_adaptive_yield=false \ --disable_auto_compactions=false \ --max_background_compactions=32 \ --max_background_flushes=4 \ --write_buffer_size=536870912 \ --min_write_buffer_number_to_merge=2 \ --max_write_buffer_number=6 \ --target_file_size_base=67108864 \ --max_bytes_for_level_base=536870912 \ --use_existing_keys=1 \ #建议使用已存在key进行读,否则就一直被filter过滤掉,打不到磁盘,测试不精确 --use_existing_db=1 \ #建议打开,使用已经存在的db --cache_size=2147483648 \ #2G的block-cache,默认是8M,实际生产环境,如果read-heavy应该设置为内存大小的三分之一。 --readwritepercent=50 \ #指定读写比 1:1,一次随机读,对应一次随机写 --num=500000000 \ #总共写入的请求个数,如果达不到则写30秒就停止 --duration=30 \ #持续IO的时间是30秒 --threads=1000\ #并发1000个线程 --value_size=8192\ #value size是8K --key_size=16 \ #key size 16B --enable_pipelined_write=true \ --db=./db_bench_test \ #指定创建db的目录 --wal_dir=./db_bench_test \ #指定创建wal的目录 --allow_concurrent_memtable_write=true \ #允许并发写memtable --disable_wal=false \
3. 便捷Benchmark.sh 自动匹配workload
ps: 需要注意的是benchmark.sh 中很多参数并不是默认的,而是官方给的一些适配当前benchmark workload 的系列优化之后的参数,所以如果大家想要测试自己的option,这个方法并不推荐,还是使用上面的db_bench方式来测试。
因为db_bench选项太多,而测试纬度很难做到统一(可能一个memtable大小的配置都会导致测试出来的写性能相关的的数据差异很大),所以官方给出了一个benchmark.sh脚本用来对各个workload进行测试。
该脚本能够将db_bench测试结果中的stats信息进行统计汇总打印(qps,),更放方便查看。这个测试需要将编译好的
db_bench
二进制文件和./tools/benchmark.sh
放到同一个目录下即可,测试项可以参考官方给出的workload,Performance Benchmarks-
随机插入
bulkload
,制造好数据集
这里的随机插入是指单纯的随机写,且禁掉自动compaction,将当前请求插入完成之后会再进行手动compaction
NUM_KEYS=900000000 NUM_THREADS=32 CACHE_SIZE=6442450944 benchmark.sh bulkload
总体来说这个随机插入结果相比于默认配置是偏高的,benchmark.sh
中的脚本对memtable相关的配置如下:
很明显性能肯定好于默认配置,好处是官方有一个在指定硬件之下的workload测试结果,可以进行对比参考。 -
随机写,覆盖写
在上一次已有的数据基础上进行测试,会覆盖写9亿条key
NUM_KEYS=900000000 NUM_THREADS=32 CACHE_SIZE=6442450944 DURATION=5400 benchmark.sh overwrite
-
读时写,9个线程读,一个线程写
这里的读是从已经存在的key中进行读
NUM_KEYS=900000000 NUM_THREADS=32 CACHE_SIZE=6442450944 DURATION=5400 benchmark.sh readwhilewriting
-
随机读
NUM_KEYS=900000000 NUM_THREADS=32 CACHE_SIZE=6442450944 DURATION=5400 benchmark.sh readrandom
4. 上限的benchmark及参数
欢迎大家补充各自的上限 workload 。
4.1 随机写
单进程 32个线程,32个db,各自的写吞吐会以秒计形态输出到一个
report.csv
。这里线程数 和 db数可以根据自己环境的cpu核心情况而定,基本不用担心write-stall问题。./db_bench \ --benchmarks=fillrandom,stats \ --readwritepercent=90 \ --num=3000000000 \ --threads=32 \ --db=./db \ --wal_dir=./db \ --duration=3600 \ -report_interval_seconds=1 \ --key_size=16 \ --value_size=128 \ --max_write_buffer_number=16 \ -max_background_compactions=32 \ -max_background_flushes=16 \ -subcompactions=8 \ -num_multi_db=32 \ -compression_type=none
如果想要支持 direct_io 写,可以打开
--use_direct_io_for_flush_and_compaction=true
,这个配置是在写sst时 也就是flush & compaction 生效。
如果想要测试 mmap 写,则可以打开
--mmap_write=true
4.2 完全随机读
随机读想要命中所有的key,需要打开
use_existing_db=1
和use_existing_keys=1
。
需要注意的是 use_existing_keys 开启之后不能直接读多db,只能读单个db,因为它会在真正执行读workload 之前从这一个db内scan 所有的key 到一个数组中,同时 配置的--num
选项是失效的,这里会填充扫描上来的key的个数。
使用这个配置之后 worklaod 不会立即启动,会卡一会扫描完所有的key之后才真正开始随机读(读的过程是生成随机下标来进行访问)。
这个测试是使用默认大小的block_cache (8MB),以及 开启bloom filter,因为我们是use_existing_keys,那bloom filter基本没什么用。$DB_BENCH \ --benchmarks=readrandom,stats \ --num=3000000000 \ --threads=40 \ --db=./db \ --wal_dir=./db \ --duration="$DURATION" \ --statistics \ -report_interval_seconds=1 \ --key_size=16 \ --value_size=128 \ -use_existing_db=1 \ -use_existing_keys=1 \ -compression_type=none \
想要测试 direct 读,添加
-use_direct_reads=true
,那么读就不会用os pagecache了,这里可以搭配-cache_size=1073741824
以及其他block_cache的配置进行测试,来看rocksdb的block_cache 相比于os pagecache的收益。想要测试 mmap 读,添加
-mmap_read=true
即可。4.3 热点读
这里基本是使用之前的配置,主要是增加一个数据倾斜的配置
read_random_exp_range
,它会用来产生倾斜的随机下标。
这个值越大,下标的倾斜越严重(可以理解为key-range 越小)。$DB_BENCH \ --benchmarks=readrandom,stats \ --num=3000000000 \ --threads=40 \ --db=./db \ --wal_dir=./db \ --duration="$DURATION" \ --statistics \ -report_interval_seconds=1 \ --key_size=16 \ --value_size=128 \ -use_existing_db=1 \ -use_existing_keys=1 \ -compression_type=none \ -read_random_exp_range=0.8 \
以上所有的workload 最后的结果
可以通过tail -f report.csv
查看 吞吐secs_elapsed,interval_qps 1,3236083 2,2877314 3,2645623 4,2581939 5,2655481 6,2038635 7,2226018 8,2366941 ...
后面可以通过python 绘图脚本系列简单记录进行曲线绘图。
- 下载rocksdb源码
-
Altium designer--DB接口DB9/DB15/DB25/DB37/DB50
2021-07-23 22:42:16使用Altium designer软件绘制DB接口封装图如下所示: 具体获取方式见文末链接 (1)DB9针 (2)DB15针 (3)DB25针 (4)DB37针 (5)DB50针 AD封装库链接: 链接:... -
DB数据库编辑器(破解版)_3.1.6.54
2014-07-03 21:14:55DB数据库编辑器(破解版)_3.1.6.54 -
dB的相关概念及计算
2021-03-15 13:02:56dB的相关概念及计算 我们在平常工作中经验遇到dB、dBm、DBi和dBd,我也经常会搞错,所以今天我们来系统的学习一下他们各自的计算及概念,加深一下印象,欢迎各位大神指导,希望对新手有用。 一、 dB概念及算法 1、... -
db换算(db和功率的换算)
2021-01-26 11:53:53DB是一个比值,是一个数值,是一个纯计数方法,没有任何...以功率为例:信号功率为X = 100000W = 10^5 基准功率为Y=1W dB的值:Lx(dB) = 10*lg(10^5W/1W) dB= 10*lg(10^5) dB= 50 dB 同理:X = 10^-15 Lx(dB) = 10... -
db与w换算(1db等于多少功率)
2021-01-26 11:53:53dBm是功率的单位,1dbm等于1毫瓦,也就是千分之一瓦。 1、 dBm这是我们接触到....dB的意义其实再简单不过了,就是把一个很大(后面跟一长串0的.DB是一个比值,是一个数值,是一个纯计数方法,没有... -
功率和db换算(功率与db换算表)
2021-02-06 10:42:02dBm是功率的单位,1dbm等于1毫瓦,也就是千分之一瓦。 1、 dBm这是我们接触到比较多的一个单位,也是唯一一...dB的意义其实再简单不过了,就是把一个很大(后面跟一长串0的.DB是一个比值,是一个数值,是一个纯计数方... -
OleDb驱动程序
2012-09-04 23:54:41OleDb驱动程序 -
GaussDB系列数据库简介
2020-08-07 10:45:56GaussDB目前支持TD、Natezza、Oracle、MySQL、DB2、sybase、PG的离线数据迁移, 支持Oracle的全量+增强的在线迁移。 GaussDB版本的区别: GaussDB T(OLTP): 前身是GauussDB 100, 主打OLTP在线事务处理。 用于存储... -
dB(分贝)定义及其应用
2020-11-30 15:17:26目录 1.1 dB的诞生背景 1.2 dB的定义 ...而关于dB的讨论也是一个历久常新的话题,因为每一位从业者都会经历一个“搞懂dB是什么”的阶段,尤其是在声学领域,dB经常用作为表征声压级SPL(Sound Pressure -
TimescaleDB部署
2018-12-03 17:35:231、准备 ...Timescale: timescaledb(只支持pgsql9.x和10.x) Timescale release_tag: 1.0.0 cmake: cmake-3.10.2.tar(Timescale要求CMake 3.4或更高版本) 安装TimescaleDB时序数据库需要... -
RocksDB的一些基本概念及简单应用(扫盲)
2021-02-07 15:50:51本文介绍RocksDB的一些基本概念和术语,及Java程序如何使用RocksDB。 -
通信领域的dB计量单位
2021-01-07 09:20:16在通信技领域,尤其是射频领域,常常碰到dB开头的计量单位,如dBm,dBi,dBc,dBr等等,初学时容易迷糊。这里做一个小结。 dB 首先说说dB的概念。dB是英文单词decible的缩写,译作是分贝;英文还有个单词bel,也... -
RocksDB简介
2019-10-07 18:04:49RocksDB是FaceBook起初作为实验性质开发的,旨在充分实现快存上存储数据的服务能力。由Facebook的Dhruba Borthakur于2012年4月创建的LevelDB的分支,最初的目标是提高服务工作负载的性能,最大限度的发挥闪存和RAM的... -
BerkeleyDB库简介
2019-02-22 19:29:02BerkeleyDB(简称为BDB)是一种以key-value为结构的嵌入式数据库引擎: 嵌入式:bdb提供了一系列应用程序接口(API),调用这些接口很简单,应用程序和bdb所提供的库一起编译/链接成为可执行程序; NOSQL:bdb不... -
【GaussDB】初始GaussDB和GaussDB版本介绍
2020-03-09 16:24:16文章目录初始GaussDBGaussDB的版本GaussDB版本的区别OLTP和OLAP比较GaussDB T介绍GaussDB A 介绍MPP架构介绍架构组件介绍 初始GaussDB 名字的由来:GaussDB是华为数据库产品品牌名,致敬数据加高斯(Gauss) GaussDB... -
史上最全的dB分贝单位合集: dB,dBFS, dB FS, dBTP, dB TP, dBO, dBov, dBu/dBv, dBV, dBm/dBmW, dBW,...
2020-11-11 16:43:39dB 数值范围根据实际测量的参考值有变化。计算能量: 计算场量: 领域 电压 dBu/dBv dBmV dB(1 mVRMS)–电压相对于75 Ω阻抗上的1毫伏。[16]广泛用于有线电视网,其接收端的单路电视信号强度名义为0 dBmV。... -
Microsoft.ACE.OLEDB.12.0
2012-05-17 15:10:24解决 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序。的问题 -
navicat导入db文件_db文件转换为txt
2020-12-22 18:24:33但是对于新手来说(比如说我),Navicat for MySQL 里的导出连接、运行SQL文件、导入向导、还原备份、这些 关键是要弄清楚.db是什么弄明白就好了,用odbc连接.db,然后用navicat的导入导出功能进行数据导入处理。... -
DB数据库文件查看器
2011-10-12 15:24:37绿色 小巧 的DB数据库文件查看器,可以查看和修改DB文件。 -
声音单位dB理解
2021-06-04 17:26:11声音单位dB理解 0、前言 SPL:Sound Presure Level,即声压级。 0.1 有效值(RMS:Root Mean Square,均方根) 正弦信号峰值和有效值换算公式: 其中Up为峰值, 0.2 对数相关计算公式 lgA - lgB = lg(A/B) lgA + ... -
dB的换算
2020-09-12 13:38:02dB的意义 1. dB基本上是一个比例数值,也就是一种倍数的表示单位。也就是 测试数据 与 参考标准的相对差异表示。 2. &... -
SQL Server LocalDB本地文件数据库操作实践
2018-06-20 11:11:42这里以官方的Microsoft SQL Server 2012 Express LocalDB加以说明,VS从2012开始应该都集成了此功能。 Microsoft SQL Server 2012 Express LocalDB 是面向程序开发人员的 SQL Server Express 的执行模式。 LocalDB ... -
图数据库 OrientDB 安装 及 初步使用
2018-08-07 21:56:18图数据库 OrientDB 安装 及 初步使用 -
西门子db数据块详解
2020-12-28 22:39:38西门子PLC从300/400系列开始STEP7软件引入了DB块的概念,DB就是数据块,这个DB区有点象西门子200PLC中的V变量数据区,200中编程使用V区的点,300中就可以使用DB区来存储数据,以及触摸屏上位机这些的通讯也都主要...