数据存储——数据库

2023-11-11

关系型数据库存储

关系型数据库是基于关系模型的数据库,而关系模型是通过二维表保存的,所以它的存储方式就是行列组成的表,每一列就是一个字段,每一行就是一条记录。表可以看作某个实体的集合,而实体之间存在联系,这就需要表与表之间的关联关系来体现,如主键外键的关联关系。多个表组成一个数据库,也就是关系型数据库。

关系型数据库有多种,如SQLite、MySQL、Oracle、SQL Server、DB2等。

MySQL的存储

本节我们主要介绍Python3下MySQL的存储。

在python2中,连接MySQL的库大多是使用MySQLdb,但是此库的官方并不支持Python3,所以这里推荐使用PyMySQL。接下来,我们就来讲解一下使用PyMySQL操作MySQL数据库的方法。

连接数据库

这里,首先尝试连接一下数据库。假设当前的 MySQL 运行在本地,用户名为 root ,密码为 123456,运行端口为 3306。这里利用 PyMySQL 先连 MySQL ,然后创建一个新的数据库,名字叫作 spiders,代码如下:

import pymysql

db = pymysql.connect(host='localhost',user='root',password='123456',port=3306)
cursor = db.cursor()
cursor.execute('SELECT VERSION()')
data = cursor.fetchone()
print('Database version:',data)
cursor.execute('CREATE DATABASE spiders DEFAULT CHARACTER SET utf8')
db.close()

运行结果如下:

Database version: ('5.6.22',)

这里通过 PyMySQL 的connect()方法声明一个 MySQL 连接对象db,此时需要传人 MySQL 运行的host (即 IP)。由于 MySQL 在本地运行,所以传入的是 localhost。如果 MySQL 在远程运行,则传入其公网IP地址。后续的参数 user 即用户名, password 即密码, port 即端口(默认为 3306)。

连接成功后,需要再调用 cursor()方法获得 MySQL 操作游标,利用游标来执行 SQL 语句。这里我们执行了两句 SQL ,直接用execute()方法执行即可。 第一句 SQL 用于获得 MySQL 的当前版本,然后调用 fetchone ()方法获得第一条数据,也就得到了版本号。第二句 SQL 执行创建数据库的操作,数据库名叫作 spiders,默认编码为 UTF-8。由于该语句不是查询语句,所以直接执行后就成功创建了数据库 spider。接着,再利用这个数据库进行后续的操作。

创建表

创建数据库后,在连接时需要额外指定一个参数db。
接下来,新建一个数据表students,此时执行创建表的SQL语句即可。示例代码如下:

import pymysql

db = pymysql.connect(host='localhost',user='root',password='123456',port=3306,db='spiders')
cursor = db.cursor()
sql = 'create table if not exists students(id varchar(20) not null,name varchar(20) not null,age smallint not null,primary key(id))'
cursor.execute(sql)
db.close()

运行之后,我们便创建了一个名为 students 的数据表。
当然,为了演示,这里只指定了最简单的几个字段。实际上,在爬虫过程中,我们会根据爬取结果设计特定的字段。

插入数据

下一步就是向数据库中插入数据了。例如,这里爬取了一个学生信息,学号为 20120001 ,名字为Bob ,年龄为20 ,那么如何将该条数据插入数据库呢?示例代码如下:

import pymysql

id = '20120001'
name = 'Bob'
age = 20
db = pymysql.connect(host='localhost',user='root',password='123456',port=3306,db='spiders')
cursor = db.cursor()
 sql = 'insert into students(id,name,age) values(%s,%s,%s)'
 try:
     cursor.execute(sql,(id,name,age))
     db.commit()
 except:
     db.rollback()
 db.close()

值得注意的是,需要执行 db 对象的 commit() 方法才可实现数据插入,这个方法才是真正将语句提交到数据库执行的方法。对于数据插入、更新、删除操作,都需要调用该方法才能生效。

接下来,我们加了一层异常处理。如果执行失败,则调用 rollback() 执行数据回滚,相当于什么都没有发生过。
在这里插入图片描述
插入、更新和删除操作都是对数据库进行更改的操作,而更改操作都必须为一个事务,所以这些操作的标准写法就是:

 try:
     cursor.execute(sql)
     db.commit()
 except:
     db.rollback()

这样就可以保证数据的一致性。

上面的数据插入操作是通过构造SQL语句实现的,但是很明显,存在一个极不方便的地方,比如突然增加了性别字段gender,此时SQL语句就需要改为:

insert  into students(id,name,age,gender) values(%s,%s,%s,%s)

相应的元组参数需要改为:

(id,name,age,gender)

这显然不是我们想要的。在很多情况下,我们要达到的效果是插入方法无需改动,做成一个通用方法,只需要传入一个动态变化的字典就好了。比如,构造这样一个字典:

{'id':'20120001','name':'Bob','age':20}

然后SQL语句会根据字典动态构造,元组也动态构造,这样才能实现通用的插入方法。

data = {
    'id':'20120001',
    'name':'Bob',
    'age':20
}
table = 'students'
keys = ','.join(data.keys())
values = ','.join(['%s']*len(data))
sql = 'insert into {table} ({keys}) values ({values})'.format(table=table,keys=keys,values=values)
try:
    if cursor.execute(sql,tuple(data.values())):
    print('Successful')
    db.commit()
except:
    print('Failed')
    db.rollback()
db.close()

这里我们传入的数据是字典,并将其定义为 data 变量。表名也定义成变量 table。接下来,就要构造一个动态的 SQL 语句了。

首先,需要构造插入的字段 id、name和age。这里只需要将 data 的键名拿过来,然后用逗号分隔即可。所以’,’ .join(data.keys())的结果就是id,name,age,然后需要构造多个%s 当作占位符,有几个字段构造几个即可。比如,这里有三个字段,就需要构造%s,%s,%s。最后,利用字符串的 format()方法将表名、字段名和占位符构造出来。最终的 SQL 语句就被动态构造成了:

INSERT INTO students(id, name, age) VALUES (%s, %s, %s) 

最后,为 execute()方法第一个参数传入 sql 变量,第二个参数传入 data 的键值构造的元组,就可以成功插入数据了。

如此以来,我们便实现了传入一个字典来插入数据的方法,不需要再去修改 SQL 语句和插入操作了。

更新数据

数据更新操作实际上也是执行 SQL 语句,最简单的方式就是构造一个 SQL 语句,然后执行:

sql = 'update students set age=%s where name=%s'
try:
    cursor.execute(sql,(22,'Bob'))
    db.commit()
except:
    db.rollback()
db.close()

这里同样用占位符的方式构造 SQL 然后执行 execute()方法,传人元组形式的参数,同样执行 commit()方法执行操作。如果要做简单的数据更新的话,完全可以使用此方法。

但是在实际的数据抓取过程中,大部分情况下需要插入数据,但是我们关心的是会不会现重复数据, 如果出现了,我们希望更新数据而不是重复保存一次。另外,就像前面所说的动态构造 SQL 的问题,所以这里可以再实现一种去重的方法,如果数据存在, 则更新数据;如果数据不存在,则插入数据。另外,这种做法支持灵活的字典传值。示例如下:

data = {
    'id':'20120001',
    'name':'Bob',
    'age':21
}
table = 'students'
keys = ','.join(data.keys())
values = ','.join(['%s']*len(data))

sql = 'insert into {table} ({keys}) values ({values}) on duplicate key update'.format(table=table,keys=keys,values=values)
update = ','.join([" {key} = %s".format(key=key) for key in data])
sql += update
try:
    if cursor.execute(sql,tuple(data.values())*2):
        print('Successful')
        db.commit()
except:
    print('Failed')
    db.rollback()
db.close()

这里构造的 SQL 语句其实是插入语句,但是我们在后面加了 ON DUPLICATE KEY UPDATE。这行代码的意思是如果主键已经存在,就执行更新操作。比如,我们传人的数据 id 仍然为 20120001 ,但是年龄有所变化,由 20 变成了 21 ,此时这条数据不会被插入,而是直接更新 id 为 20120001 的数据。
完整的 SQL 构造出来是这样的:

insert into students(id,name,age) values(%s,%s,%s) on duplicate key update id = %s, name = %s, age = %s

这里就变成6个%s,所以在后面的execute()方法的第二个参数元组就需要乘以2变成原来的2倍。

如此一来,我们就可以实现主键不存在便插入数据,存在则更新数据的功能了。

删除数据

删除操作相对简单,直接使用 DELETE 语句即可,只是需要指定要删除的目标表名和删除条件,而且仍然需要使用 db的 commit() 方法才能生效。示例如下:

table = 'students'
condition = 'age > 20'
sql = 'delete from {table} where {condition}'.format(table=table,condition=condition)
try:
    cursor.execute(sql)
    db.commit()
except:
    db.rollback()
db.close()

因为删除条件有多种多样,运算符有大于、小于、等于、 LIKE 等,条件连接符有 AND、OR 等,所以不再继续构造复杂的判断条件。这里直接将条件当作字符串来传递,以实现删除操作。

查询数据

说完插入、修改和删除等操作,还剩下非常重要的一个操作,那就是查询。查询会用到 SELECT语句,示例如下:

sql = 'select * from students where age >= 20'

try:
    cursor.execute(sql)
    print('Count:',cursor.rowcount)
    one = cursor.fetchone()
    print('One:',one)
    results = cursor.fetchall()
    print('Results:',results)
    print('Results Type:',type(results))
    for row in results:
        print(row)
except:
    print('Error')

运行结果如下:

Count: 4
One: ('20120001','Bob',25)
Results: (('20120011','Mary',21),('20120012','Mike',20),('20120013','James',22))
Results Type: <class 'tuple'>
('20120011','Mary',21)
('20120012','Mike',20)
('20120013','James',22)

这里我们构造了一条 SQL 语句,将年龄 20 岁及以上的学生查询出来,然后将其传给 execute()方法。注意,这里不再需要 db 的 commit() 方法。接着,调用 cursor 的rowcount 属性获取查询结果的条数,当前示例中是4条。

然后我们调用了 fetchone() 方法,这个方法可以获取结果的第一条数据,返回结果是元组形式,元组的元素顺序跟字段一一对应,即第一个元素就是第一个字段 id,第二个元素就是第二个字段 name,以此类推。随后,我们又调用了 fetchall() 方法,它可以得到结果的所有数据。然后将其结果和类型打印出来,它是二重元组,每个元素都是一条记录。

但是这里需要注意一个问题,这里显示的是3条数据而不是4条, fetchall() 方法不是获取所有数据吗?这是因为它的内部实现有一个偏移指针用来指向查询结果,最开始偏移指针指向第一条数据,取一次之后,指针偏移到下一条数据,这样再取的话,就会取到下一条数据了。 我们最初调用 fetchone() 方法,这样结果的偏移指针就指向下一条数据,fetchall() 方法返回的是偏移指针指向的数据一直到结束的所有数据,所以该方法获取的结果就只剩3个了。

此外,我们还可以用 while 循环加 fetchone() 方法来获取所有数据,而不是用 fetchall() 全部一起获取出来。fetchall()会将结果以元组形式返回,如果数据量很大,那么占用的开销会非常高。因此,推荐使用如下方法来逐条取数据:

sql ='SELECT * FROM students WHERE age >= 20' 
try:
    cursor.execute(sql)
    print('Count :', cursor.rowcount) 
    row = cursor.fetchone()
    while row: 
        print('Row :', row) 
        row = cursor.fetchone() 
except: 
    print('Error')

这样每循环一次,指针就会偏移一条数据,随用随取,简单高效。

非关系型数据库存储

NoSQL ,全称 Not Only SQL ,意为不仅仅是 SQL ,泛指非关系型数据库。NoSQL 是基于键值对的, 而且不需要经过 SQL 层的解析,数据之间没有耦合性,性能非常高。

非关系型数据库又可细分如下:
键值存储数据库:代表有 Redis、Voldemort 和 Oracle BDB等。
列存储数据库:代表有 Cassandra、HBase 和 Riak等。
文档型数据库:代表有 CouchDB 和 MongoDB 等。
图形数据库:代表有 Neo4J、lnfoGrid 和 Infinite Graph等。

对于爬虫的数据存储来说,一条数据可能存在某些字段提取失败而缺失的情况,而且数据可能随时调整。另外,数据之间还存在嵌套关系。如果使用关系型数据库存储,一是需要提前建表,二是如果存在数据嵌套关系的话,需要进行序列化操作才可以存储,这非常不方便。如果用了非关系型数据库,就可以避免一些麻烦,更简单高效。

本节中,我们主要介绍 MongoDB 和 Redis 的数据存储操。

MongoDB存储

MongoDB 是由 C++ 语言编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统,其内容存储形式类似 JSON 对象,它的字段值可以包含其他文档、数组及文档数组,非常灵活。在这一节中,我们就来看看 Python3 下 MongoDB 的存储操作。

连接 MongoDB

连接 MongoDB 时,我们需要使用 PyMongo 库里面的 MongoClient 。一般来说,传入 MongoDB 的IP及端口即可,其中第一个参数为地址 host ,第二个参数为端口 port (如果不给它传递参数,默认是 27017):

import pymongo

client = pymongo.Mongoclient(host='localhost',port=27017)

另外 MongoClient 第一个参数 host 还可以直接传入 MongoDB 连接字符串,它以 mongodb 开头,例如:

client = MongoClient('mongodb://localhost:27017/')

这也可以达到同样的连接效果。

指定数据库

MongoDB 中可以建立多个数据库,接下来我们需要指定操作哪个数据库。这里我们以 test 数据库为例来说明,下一步需要在程序中指定要使用的数据库:

db = client.test 

这里调用 client 的 test 属性即可返回 test 数据库。当然,我们也可以这样指定:

db = client['test'] 

这两种方式是等价的。

指定集合

MongoDB 每个数据库又包含许多集合( collection ),它们类似于关系型数据库中的表。下一步需要指定要操作的集合,这里指定一个集合名称为 students。与指定数据库类似,指定集合也有两种方式:

collection = db.students 
collection = db['students']

这样我们便声明了 Collection 对象.。

插入数据

接下来,便可以插入数据了。对于 students 这个集合,新建一条学生数据,这条数据以字典形式表示。

student = {
    ’id': '20170101',
    'name': 'Jordan',
    'age': 20,
    'gender: 'male'
}

这里指定了学生的学号、姓名、年龄和性别。接下来,直接调用 collection 的 insert() 方法即可插入数据,代码如下:

result = collection.insert(student) 
print(result) 

在 MongoDB 中,每条数据其实都有一个_id 属性来唯一标识。如果没有显式指明该属性, MongoDB 会自动产生一个 ObjectId 类型的_id 属性。insert() 方法会在执行后返回_id 值。

运行结果如下:

5932a68615c2606814c91f3d

当然,我们也可以同时插入多条数据,只需要以列表形式传递即可,示例如下:

student1 = { 
    'id': '20170101', 
    'name':'Jordan', 
    'age': 20, 
    'gender':'male' 
}
student2 = { 
    'id' : '20170202', 
    'name':'Mike', 
    'age': 21, 
    'gender':'male' 
}
result = collection.insert([student1, student2]) 
print(result) 

运行的结果是对应_id的集合:

[ObjectId(’ 5932a80115c2606a59e8ao48'), ObjectId('5932a80115c2606a59e8ao49 ' )]

实际上,在 PyMongo 3.x 版本中,官方已经不推荐使用 insert() 方法了。当然,继续使用也没有什么问题。官方推荐使用 insert_one() 和 insert_many() 方法来分别插入单条记录和多条记录,示例如下:

result = collections.insert_one(student)
print(result)
print(result.inserted_id)

运行结果如下:

<pymongo.results.InsertOneResult object at 0x10d68b558>
5932a80115c2606a59e8f6c5

与insert() 方法不同,这次返回的是 InsertOneResult 对象,我们可以调用其 inserted_id 属性获取_id。

对于 insert_many()方法,我们可以将数据以列表形式传递。调用inserted_ids属性获取插入数据的_id列表。

查询

插入数据后,我们可以利用find_one() 或 find() 方法进行查询,其中 find_one() 查询得到的是单个结果,find() 则返回一个生成器对象。示例如下:

result = collection.find_one({'name':'Mike'})
print(type(result))
print(result)

我们这里查询name为Mike的数据,它返回的结果就是字典类型,运行结果如下:

<class 'dict'>
{'_id':ObjectId('5932a80115c2606a59e8a265'),'id':'20170202','name':'Mike','age':21,'gender':'male'}

可以发现,它多了_id 属性,这就是 MongoDB 在插入过程中自动添加的。

此外 ,我们也可以根据 ObjectId 查询, 此时需要使用 bson 库里面的 objectid:

from bson.objectid import ObjectId

result = collection.find_one({'_id':ObjectId('5932a80115c2606a59e8a265')})
print(result)

其查询结果依然是字典类型,具体如下:

{'_id':ObjectId('5932a80115c2606a59e8a265'),'id':'20170202','name':'Mike','age':21,'gender':'male'}

当然,如果查询结果不存在,则会返回 None。

对于多条数据的查询,我们可以使用 find() 方法。例如,这里查找年龄为 20 的数据,示例如下:

results = collection.find({'age':20})
print(results)
for result in results:
    print(result)

运行结果如下:

<pymongo.cursor.Cursor object at 0x1032d5128>
{'_id': Objectld( '593278c11Sc2602667ec6bae'), 'id ': '20170101', 'name':'Jordan','age': 20, 'gender':'male'} 
{'_id': Objectld('593278c81sc2602678bb2b8d'),'id':'20170102', ' name ':'Kevin', 'age' : 20, 'gender': 'male' } 
{'_id': Objectld ('593278d81Sc260269d764Sa8'), 'id ' : ' 20170103', ' name ' : 'Harden', ' age ': 20, 'gender ' : 'male '} 

返回结果是 Cursor 类型,它相当于一个生成器,我们需要遍历取到所有的结果,其中每个结果都是字典类型。

如果要查询年龄大于20的数据,则写法如下:

result = collection.find({'age':{'$gt':20}})

这里查询的条件键值已经不是单纯的数字了,而是一个字典,其键名为比较符号$gt ,意思是大于,键值为 20。

这里将比较符号归纳为下图:
在这里插入图片描述
另外,还可以进行正则匹配查询。例如,查询名字以 开头的学生数据,示例如下:

results= collection.find ({'name' : {'$regex' : '^'M .*'}} ) 

这里使用$regex 来指定正则匹配,^M.*代表以M开头的正则表达式。

这里将一些功能符号再归类为下表:
在这里插入图片描述
关于这些操作的更详细用法,可以在 MongoDB 官方文档找到 https://docs.mongodb.com/manual/reference/ operator/query/

计数

要统计查询结果有多少条数据,可以调用 count() 方法。比如,统计所有数据条数:

count = collection.find().count() 
print(count) 

或者统计符合某个条件的数据:

count= collection.find({'age' : 20}) .count() 
print(count) 

运行结果是一个数值,即符合条件的数据条数

排序

排序时,直接调用 sort() 方法,并在其中传入排序的字段及升降序标志即可。示例如下:

results = collection.find().sort('name', pymongo.ASCENDING) 
print([result['name'] for result in results]) 

运行结果如下:

['Harden', 'Jordan','Kevin','Mark','Mike']

这里我们调用 pymongo.ASCENDING 指定升序。如果要降序排列,可以传入 pymongo.DESCENDING

偏移

在某些情况下,我们可能想只取某几个元素,这时可以利用 skip() 方法偏移几个位置 ,比如偏移2,就忽略前两个元素,得到第三个及以后的元素:

results = collection.find().sort('name' ,pymongo.ASCENDING).skip(2) 
print([result['name'] for result in results]) 

运行结果如下:

['Kevin','Mark','Mike'] 

另外,还可以用 limit() 方法指定要取的结果个数,示例如下:

results = collection.find().sort('name' ,pymongo.ASCENDING).skip(2).limit(2)
print ([result['name'] for result in results]) 

运行结果如下:

[’ Kevin' , 'Mark' ] 

如果不使用 limit() 方法,原本会返回三个结果,加了限制后,会截取两个结果返回。

值得注意的是,在数据库数量非常庞大的时候,如千万、亿级别,最好不要使用大的偏移量来查询数据,因为这样很可能导致内存溢出。此时可以使用类似如下操作来查询:

from bson.objectid import ObjectId 
collection.find({'_id ':{$gt':ObjectId('593278c81Sc2602678bb2b8d')}}) 

这时需要记录好上次查询的 _id。

更新

对于数据更新,我们可以使用 update() 方法,指定更新的条件和更新后的数据即可。例如:

condition = {'name':'Kevin'}
student = collection.find_one(condition)
student['age'] = 25
result = collection.update(collection,student)
print(result)

这里我们要更新 name为Kevin 的数据的年龄 :首先指定查询条件,然后将数据查询出来,修改年龄后调用 update() 方法将原条件和修改后的数据传入。

运行结果如下:

{'ok':1,'nModified':1,'n':1,'updatedExisting':True}

返回结果是字典形式, ok 代表执行成功,nModified 代表影响的数据条数。

另外,我们也可以使用$set 操作符对数据进行更新,代码如下:

result = collection.update(condition,{'$set':student})

这样可以只更新 student 字典内存在的字段。如果原先还有其他字段,则不会更新,也不会删除。如果不用$set 的话,则会把之前的数据全部用 student 字典替换;如果原本存在其他字段, 会被删除。

另外, update() 方法其实也是官方不推荐使用的方法。这里也分为 update_one() 方法和
update_many() 方法,用法更加严格,它们的第二个参数需要使用$类型操作符作为字典的键名,示例如下:

condition = {'name':'Kevin'}
student = collection.find_one(condition)
student['age'] = 26
result = collection.update_one(condition,{'$set':student})
print(result)
print(result.matched_count,result.modified_count)

这里调用了 update_one() 方法,第二个参数不能再直接传人修改后的字典,而是需要使用{‘$set’:student }这样的形式,其返回结果是 UpdateResult 类型。然后分别调用 matched_count 和 modified_count属性,可以获得匹配的数据条数和影响的数据条数

运行结果如下:

<pymongo.results.UpdateResult object at 0x10d17b678>
1  1

如果调用 update_many() 方法,则会将所有符合条件的数据都更新,示例如下:

condition = {'age':{'$gt':20}}
result = collection.update_many(condition,{'$inc':{'age':1}})
print(result)
print(result.matched_count,result.modified_count)

运行结果如下:

<pymongo.results.UpdateResult object at 0x10d17b699>
3  3

可以看到,这时所匹配到的数据都会被更新。

删除

删除操作比较简单,直接调用 remove() 方法指定删除的条件即可,此时符合条件的所有数据均被删除。示例如下:

result = collection.remove({'name':'Kevin'})
print(result)

运行结果如下:

{'ok':1,'n':1}

另外,这里依然存在两个新的推荐方法——delete_one()和 delete_many()。示例如下:

result = collection.delete_one({'name':'Kevin'})
print(result)
print(result.deleted_count)
result = collection.delete_many({'age':{'$lt':25}})
print(result.deleted_count)

运行结果如下:

<pymongo.results.DeleteResult object at 0x10e6ba4c8
1
4

delete_one() 即删除第一条符合条件的数据, delete_many() 删除所有符合条件的数据。它们的
返回结果都是 DeleteResult 类型,可以调用 deleted_count 属性获取删除的数据条数。

其他操作

另外,PyMongo 还提供一些组合方法,如 find_one_and_delete()、find_one_and_replace()、和find_one_and_update(),它们是查找后删除、替换和更新操作,其用法与上述方法基本一致。

。。。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据存储——数据库 的相关文章

  • 我当年自学黑客(网络安全)的一些心得!(内附学习笔记)

    前 言 写这篇教程的初衷是很多朋友都想了解如何入门 转行网络安全 实现自己的 黑客梦 文章的宗旨是 1 指出一些自学的误区 2 提供客观可行的学习表 3 推荐我认为适合小白学习的资源 大佬绕道哈 文末有福利 一 自学网络安全学习的误区和陷阱
  • 【一份老网工珍藏多年的网络配置笔记,很重要!】

    01 交换机 路由器的几种配置模式及模式转换 1 用户模式 登录到交换机 路由器 时会自动进入用户模式 提示符为 switchname gt 在该模式下只能够查看相关信息 对 IOS的运行不产生任何影响 2 特权模式 用户模式下 键入 en
  • 拼多多详情API开启运营比价新纪元

    随着互联网的快速发展 电商行业正在迅速崛起 拼多多作为一家新兴的电商平台 凭借其独特的营销策略和创新的商业模式 成为了电商行业的一匹黑马 在拼多多的成功背后 其详情API接口营销起到了至关重要的作用 本文将详细介绍拼多多详情API接口营销的
  • AntDB内存管理之内存上下文之如何使用内存上下文

    5 如何使用内存上下文 使用内存上下文之前 我们需要先对其进行创建 AntDB启动时已经创建并初始化好了部分内存上下文 例如 TopMemoryContext 这个TopMemoryContext是所有内存上下文的父节点或者祖先节点 一般我
  • 【计算机毕业设计】网上拍卖系统

    现代经济快节奏发展以及不断完善升级的信息化技术 让传统数据信息的管理升级为软件存储 归纳 集中处理数据信息的管理方式 本网上拍卖系统就是在这样的大环境下诞生 其可以帮助使用者在短时间内处理完毕庞大的数据信息 使用这种软件工具可以帮助管理人员
  • 【计算机毕业设计】北关村基本办公管理系统

    在如今社会上 关于信息上面的处理 没有任何一个企业或者个人会忽视 如何让信息急速传递 并且归档储存查询 采用之前的纸张记录模式已经不符合当前使用要求了 所以 对北关村基本办公信息管理的提升 也为了对北关村基本办公信息进行更好的维护 北关村基
  • 图解python | 字符串及操作

    1 Python元组 Python的元组与列表类似 不同之处在于元组的元素不能修改 元组使用小括号 列表使用方括号 元组创建很简单 只需要在括号中添加元素 并使用逗号隔开即可 tup1 ByteDance ShowMeAI 1997 202
  • Python爬虫实战:IP代理池助你突破限制,高效采集数据

    当今互联网环境中 为了应对反爬虫 匿名访问或绕过某些地域限制等需求 IP代理池成为了一种常用的解决方案 IP代理池是一个包含多个可用代理IP地址的集合 可以通过该代理池随机选择可用IP地址来进行网络请求 IP代理池是一组可用的代理IP地址
  • 通俗易懂,十分钟读懂DES,详解DES加密算法原理,DES攻击手段以及3DES原理

    文章目录 1 什么是DES 2 DES的基本概念 3 DES的加密流程 4 DES算法步骤详解 4 1 初始置换 Initial Permutation IP置换 4 2 加密轮次 4 3 F轮函数 4 3 1 拓展R到48位 4 3 2
  • 网络安全(黑客)自学启蒙

    一 什么是网络安全 网络安全是一种综合性的概念 涵盖了保护计算机系统 网络基础设施和数据免受未经授权的访问 攻击 损害或盗窃的一系列措施和技术 经常听到的 红队 渗透测试 等就是研究攻击技术 而 蓝队 安全运营 安全运维 则研究防御技术 作
  • 深入了解 Python MongoDB 查询:find 和 find_one 方法完全解析

    在 MongoDB 中 我们使用 find 和 find one 方法来在集合中查找数据 就像在MySQL数据库中使用 SELECT 语句来在表中查找数据一样 查找单个文档 要从MongoDB的集合中选择数据 我们可以使用 find one
  • 【计算机毕业设计】趵突泉景区的智慧导游小程序_5ztvv

    当今社会已经步入了科学技术进步和经济社会快速发展的新时期 国际信息和学术交流也不断加强 计算机技术对经济社会发展和人民生活改善的影响也日益突出 人类的生存和思考方式也产生了变化 传统趵突泉景区的智慧导游采取了人工的管理方法 但这种管理方法存
  • 【计算机毕业设计】二手家电管理平台

    时代在飞速进步 每个行业都在努力发展现在先进技术 通过这些先进的技术来提高自己的水平和优势 二手家电管理平台当然不能排除在外 二手家电管理平台是在实际应用和软件工程的开发原理之上 运用java语言以及前台VUE框架 后台SpringBoot
  • 做测试不会 SQL?超详细的 SQL 查询语法教程来啦!

    前言 作为一名测试工程师 工作中在对测试结果进行数据比对的时候 或多或少要和数据库打交道的 要和数据库打交道 那么一些常用的sql查询语法必须要掌握 最近有部分做测试小伙伴表示sql查询不太会 问我有没有sql查询语法这一块的文档可以学习
  • 独家 | 鸿蒙(HarmonyOS)开发详细学习笔记免费分享

    前言 华为宣布 将在1月18日 在北京 上海 杭州 南京 成都 厦门 武汉 长沙 8 大城市同时召开大会 届时将揭秘鸿蒙生态和 HarmonyOS NEXT 进阶新篇章 简单的来说就是 纯血鸿蒙系统 即将彻底揭晓 鸿蒙系统自推出来以来 就一
  • 数据库 | 面试官:一次到底插入多少条数据合适啊?.....面试连环炮

    数据库 面试官 一次到底插入多少条数据合适啊 面试连环炮 数据库插入操作的基础知识 插入数据是数据库操作中的基础 但是 我们程序员将面临随之而来的问题 如何快速有效地插入数据 并保持数据库 性能 当你向数据库中插入数据时 这些数据直接存储到
  • Redis分布式锁--java实现

    文章目录 Redis分布式锁 方案 SETNX EXPIRE 基本原理 比较好的实现 会产生四个问题 几种解决原子性的方案
  • Mysql中设置只允许指定ip能连接访问(可视化工具的方式)

    场景 Mysql中怎样设置指定ip远程访问连接 Mysql中怎样设置指定ip远程访问连接 navicat for mysql 设置只有某个ip可以远程链接 CSDN博客 前面设置root账户指定ip能连接访问是通过命令行的方式 如果通过可视
  • 网络安全(黑客)自学启蒙

    一 什么是网络安全 网络安全是一种综合性的概念 涵盖了保护计算机系统 网络基础设施和数据免受未经授权的访问 攻击 损害或盗窃的一系列措施和技术 经常听到的 红队 渗透测试 等就是研究攻击技术 而 蓝队 安全运营 安全运维 则研究防御技术 作
  • SAP ERP系统是什么?SAP好用吗?

    A公司是一家传统制造企业 公司曾先后使用过数个管理软件系统 但各部门使用的软件都是单独功能 导致企业日常管理中数据流与信息流相对独立 形成了 信息孤岛 随着公司近年业务规模的快速发展以及客户数量的迅速增加 企业原有的信息系统在销售预测及生产

随机推荐

  • Magento2.x中文语言包及安装使用

    下载 magento2中文语言包 安装 上传到mage2 store app i18n Magento zh Hans CN文件夹 备注 mage2 store vendor magento language zh hans cn 运行脚本
  • c++二分查找—来自编程珠玑

    c 二分查找 来自编程珠玑 二分查找法 Binary search algorithm 是一个很常见的算法 从 编程珠玑 里再次看到时又有新的收获 直接看代码吧 下面是常见的实现代码 int binary search int a int
  • linux vim使用

    读本文之前请注意 1 本文的目标是提供一些vim的使用技巧 利用这些技巧可以提高vim的操作效率 部分技巧在vi上也可以使用 但是现在基本上都是用vim了 2 本文是整理和总结使用技巧 而非讲解vim入门 因此不会涉及最基本的使用 例如如何
  • 笔试

    文章目录 前言 27 时钟抖动jitter 时钟偏移skew 28 EDA开发工具 29 时序约束 30 DMA 31 乒乓buffer 32 BRAM DRAM 33 设计描述方式 34 延迟设计 35 DDR带宽计算 总结 往期精彩 前
  • xml建模包括以下_建模干货,1分钟速学,Photoscan空三结果完美导入CC

    关注公众号 三维前沿 获取更多倾斜摄影 激光点云资讯 相信大家都经历过 有时CC空三总是失败或错误 最崩溃的是 一顿操作猛如虎 定睛一看原地杵 此时 Photoscan空三能力其实可优于CC 头疼的是 又不想放弃用CC来建模 所以今天 为你
  • JAVA题目~由Point类组合成Triangle类 Exp04-1

    问题描述 先定义Point类 再定义Triangle类 在Triangle类中定义三个Point对象来表示一个三角形的三个顶点 然后定义一个方法isTri 对这三个点是否能构成三角形进行判定 构造方法先调用isTri 如果三个点能够构成三角
  • VUE中 created(),mounted()与activated() 方法都是干嘛的?

    created html加载完成之前 执行 执行顺序 父组件 子组件 mounted html加载完成后执行 执行顺序 子组件 父组件 methods 事件方法执行 watch watch是去监听一个值的变化 然后执行相对应的函数 comp
  • Python爬虫-某网酒店评论数据

    前言 本文是该专栏的第6篇 后面会持续分享python爬虫案例干货 记得关注 本文以某网的酒店数据为例 采集对应酒店的评论数据 具体思路和方法跟着笔者直接往下看正文详细内容 附带完整代码 注意 本文的案例 数据集 选用的是本专栏上一篇 Py
  • 教程篇(7.2) 03. 第2层设计 & FortiSwitch ❀ Fortinet网络安全专家 NSE6

    在本课中 你将了解最常见的FortiSwitch拓扑 以及FortiSwitch上可用的不同环路预防协议和方法 在这节课中 你将学习上图显示的主题 通过展示FortiSwitch拓扑的能力 你应该能够确定最适合你的网络设计需求的拓扑 当涉及
  • chisel-book-chinese

    chisel book chinese 蓝奏云地址 https wwu lanzoue com isdim08x8x5i
  • 王爽老师汇编13.3对int iret和栈的深入理解 在屏幕中间显示80个叹号字符

    6666 问题 用7ch中断例程完成loop指令的功能 个人自学思考编写的 不知道和书中的要表达的意思是否相同 先编译运行第一个代码 再编译运行第二个显示代码 下面为写入内存0 200出的中断代码 iret指令为跳转指令 跳转到显示代码处
  • 音频wav mp3 批量转文本

    作为一个业余的软件开发爱好者 我又捣鼓了一个有意思的小东西 使用完全免费哈 使用了目前已有的音频识别的深度学习网络 然后封装成了一个单独的模块 使用的方法就是下载软件 然后打开音频所在的文件夹就行了 会自动扫描wav mp3 后缀结尾的文件
  • OSI七层模型与TCP/IP五层模型

    1 OSI open system interconnection 七层模型 OSI模型为开放式系统互联参考模型 是一个逻辑上的定义和规范 把网络从逻辑上划分为了7层 每一层都有相应的物理设备 OSI模型是一种框架性的设计方法 其主要功能是
  • .net grpc简单使用

    本文提供一个最简单的grpc demo 来模拟一个本地应用使用grpc的方式去调用一个远程的服务 文章目录 首先创建一个grpc项目来模拟远程服务 再创建一个项目来调用服务 如果需要在web api项目而不是控制台程序中调用远程服务 首先创
  • 【Github】GPT2-Chinese:中文的GPT2训练代码

    推荐一个Github项目 Morizeyao GPT2 Chinese Chinese version of GPT2 training code using BERT tokenizer 作者是AINLP交流群里的杜则尧同学 提供了一份中
  • 排阻的封装尺寸

    http arlen opcom blog 163 com blog static 33775037201011144225518 0402封装 0603封装 1 电阻封装尺寸与功率关系 通常来说 0201 1 20W 0402 1 16W
  • MATLAB与当今时代的意义

    对于现在的工科生来说 MATLAB 是必不可少的工具 其必要性可能仅次于 Office 了 如果你想在谷歌工作 确保你会用MATLAB 谷歌产品高级副总裁Jonathan Rosenberg认为 对于学生而言 掌握如何使用MATLAB将为其
  • 内存卡受损文件如何恢复

    现在的手机 你不用担心内存不够用 因为我们都有内存卡 一个8g内存的手机用内存卡扩到32g 64g都不成问题 而且存到内存卡上的资源还不耽误手机运行 简直一举两得 手机内存卡和很多卡片是一样的 u盘文件恢复工具 http mydown ye
  • Spring Boot 笔记

    springBoot 注解示意图 SpringBootApplication EnableAutoConfiguration 导入组件类 Import AutoConfigurationImportSelector class 调用方法 s
  • 数据存储——数据库

    关系型数据库存储 关系型数据库是基于关系模型的数据库 而关系模型是通过二维表保存的 所以它的存储方式就是行列组成的表 每一列就是一个字段 每一行就是一条记录 表可以看作某个实体的集合 而实体之间存在联系 这就需要表与表之间的关联关系来体现