MongoDB数据库基础

MongoDB简介

  • MongoDB是由C++语言编写的,是一个基于分布式文件存储的非关系型开源数据库系统。其优势在于可以存放海量数据,具备强大的查询功能,不适用于数据缓存,在高负载的情况下,添加更多的节点,可以保证服务器性能
  • MongoDB旨在为WEB应用提供可扩展的高性能数据存储解决方案
  • MongoDB将数据存储为一个文档,数据结构由键值(key=>value)对,及BSON组成。MongoDB文档类似于JSON对象。字段值可以包含其他文档,数组及文档数组
  • 2007年10月,MongoDB由10gen团队所开发。2009年2月首度推出
  • MongoDB支持Unix、Linux、windows等系统平台
  • 在许多场景下用于代替传统的关系型数据库或键/值存储方式,几乎可以实现类似关系型数据库单表查询的绝大部分功能,而且还支持对数据建立索引。是一个面向集合的,模式自由的文档型数据库

MongoDB特点

  • MongoDB的提供了一个面向文档存储,操作起来比较简单和容易,且安装简单。
  • mongodb支持:索引来实现更快的排序。
  • MongoDB有更强的扩展性,如果负载的增加,可以夯布在计算机网络中的其他i点E这就是所谓的分片。
  • Mongo支持丰富的查询表达式。查询指令使用ISON形式的标记,可轻易查询文档中内嵌的对象及数组。
  • Mongodb使用updatel)命令可以实现替换完成的文档(数据)或者一些指定的数据字段。
  • Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作,Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
  • GridrFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
  • 也可以把函数的定义存储在服务端,下次直接调用即可。
  • MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言

MongoDB结构和数据类型

  • MongoDB适用领域:网站数据、分布式场景、缓存屋、文档格式存储
  • 逻辑结构
    • 文档(document):是mongodb核心概念也是逻辑存储的最小单元,相当于行
      需要注意的是
      • 文档中的键值对是有序的
      • 文档中的值可以使双引号里面的字符串,也可以是其他几种数据类型(甚至可以使整个嵌入的文档)
      • MongoDB区分类型大小写
      • MongoDB的文档不能有重复的键
    • 集合(collection):多个文档组成集合,相当于表,但不同于表的是无固定结构
      • 当一个文档插入时,集合就会被创建
    • 数据库(database);多个集合组成数据席
      • test:默认数据库,该数据库存储在data目录中,要显示它,需要向数据库插入一些数据
      • admin:从权限的角度来看,这是root数据库;如果将一个用户添加到这个数据库,这个用户将自动继承所有数据库的权限
      • local:这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
      • config:当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息
  • 物理结构
    • .ns文件:每个表或索引对应一个命图空间,数据量增加,文件数量增多;
    • 数据文件:存放数据的实体;
    • 预分配空间机制:mongodb在当前数据文件的基础上,公预分配文件(用0进行填充),数据文件每次新生成的文件,大小是上个文件的两倍,这样可以保证数据文件的可用性;内存映射机制:MMAP机制会将所有的数据文件映射到虚拟内存,访问到此块数据时,会将其数据交换到物理内存,加快访问速度;
  • 日志存储结构
    • 系统日志文件
    • journal日志文件:用于mongodb崩溃恢复的保障
    • oplog复制操作日志文件(相当于mysal的binary log文件)
    • 慢查询日志:需要在配置文件profile-1开启慢查询与slowms-200指定记录的老秒数
  • 数据类型
    • BSON 是Binary ISON,是二进制的格式,能将mongdb的所有文档表示为字节字符串.
    • ISON是一种轻量级的数据交换格式,它基于JavaScript的一个子集.
  • BSON的数据类型:null,代表空或者不存在;布尔,只有true和false;数字,64位浮点数:字符串,utf8字符串:数组,值或者列表可表示为数组对象,对象的数据
  • BSON的特点:优点:简单,简洁,容易理解、解析、记忆
  • mongodb的数据库、集合、文档命名规则
    • 支持UTF8的绝大多数字符
    • 不支持如$开头;\0空字符;特殊^.等
    • 集合名不能以“system.”开头,这是为系统保留的前缀
    • 下划线‘_’开头的键是保留的(非严格要求)

搭建MongoDB数据库应用

YUM安装

  • 搭建安装所需yum源

    vim /etc/yum.repos.d/mongodb-org.repo
    [mongodb-org]
    name=MongoDB Repository
    baseurl=http://mirrors.aliyun.com/mongodb/yum/redhat/7Server/mongodb-org/3.4/x86_64/
    gpgcheck=0
    enabled=1
    
  • yum安装

yum update
yum -y install -y mongodb-org
  • 设置内核参数
echo 0 > /proc/sys/vm/zone_reclaim_mode   #当某个节点可用内存不足时,系统会从其他节点分配内存。
echo never > /sys/kernel/mm/transparent_hugepage/enabled      #关闭大页内存
echo never > /sys/kernel/mm/transparent_hugepage/defrag      #关闭大页内存
vi /etc/rc.local( 永久)
echo 0 > /proc/sys/vm/zone_reclaim_mode
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
sh /etc/rc.local
  • 启动
systemctl start mongod
systemctl enable mongod

[root@localhost ~]# curl http://localhost:27017
It looks like you are trying to access MongoDB over HTTP on the native driver port.   #代表安装成功

YUM方式安装MongoDB,其配置文件默认为/etc/mongod.conf

[root@localhost system]# cat /etc/mongod.conf | grep -v ^\# 


systemLog:      日志相关配置
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

storage:		数据文件存储相关
  dbPath: /var/lib/mongo	
  journal:				
    enabled: true

processManagement:	PID进程号相关
  fork: true  # fork and run in background
  pidFilePath: /var/run/mongodb/mongod.pid  # location of pidfile

net:			端口、IP等配置
  port: 27017
  bindIp: 192.168.137.21  # Listen to local interface only, comment to listen on all interfaces.

  • 连接并访问MongoDB
mongo  初始状态下的连接命令
> show dbs
test   默认进入test库

启动MongoDB多实例

在单台服务器资源充分的情况下,可以使用多实例,以便充分使用服务器资源
具体步骤

cp -p /etc/mongod.conf /etc/mongod1.conf
mkdir /var/lib/mongo1
chmod 777 /var/lib/mongo1
touch /var/lib/mongodb/mongod1.log
chmod 777 /var/lib/mongodb/mongod1.log
vim /etc/mongod1.conf
path: /var/log/mongodb/mongod1.log
dbPath: /var/lib/mongo1
 pidFilePath: /var/run/mongodb/mongod1.pid

逻辑结构:

库 database

  • test :默认库 ,只有往里面插入数据,才会看到。
  • admin:系统认证相关的库,存放用户名,密码等。
  • local:系统本地库,存放日志相关。
    MongoDB的常用术语及说明
SQL术语 MongoDB术语 说明
database database 数据库
table collection 数据表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持
primary key primary key 主键,MongoDB自动将_id字段设置为主键

MongoDB中基本操作

基本操作

操作 描述
show dbs 查看当前实例下的数据库列表
show users 显示用户
use db_name 切换当前数据库
db.help() 显示数据库操作命令
show collections 显示当前数据库中的集合
db.foo.help() 显示集合操作命令,foo是当前数据库下的集合
db.foo.find() 对当前数据库中foo集合进行数据查找
> use kgc      切换数据库
> db.mytable.insert({
     id:1,'name':'sary'})  插入文档
> db.mytable.find()    查看文档
> db.mytable.findOne()   查看文档中匹配的信息
>db.mytable.update({
     id:1,"name":"sary"},{
     "name":"lisi",age:17})   修改记录
> db.mytable.remove({
     "name":"lisi"})	删除记录
> db.dropDatabase()   删除数据库
> cls    清屏
[root@mongodb]# mongo --port 27017 --host 192.168.100.101
> help					#查看帮助
>db.help
>show dbs				#查看数据库
admin 0.000GB
config 0.000GB
local 0.000GB
>show databases admin 0.000GB config 0.000GB local 0.000GB
>use local 					#进入数据库,没有且创建
switched to db local					
>show tables 				#查看集合
startup_log
> show collections		#查看集合
startup_log				

>db.user.insert({
     "id":1,"name":"zzq"});			#创建集合user,并添加数据
WriteResult({
      "nInserted" : 1 })
> show dbs
admin  0.000GB
local  0.000GB
> show tables;
startup_log
user
>db.user.find();			#查看集合中的数据记录
{
      "_id" : ObjectId("5f45d32f7e48f4a36a9cb7e0"), "id" : 1, "name" : "zzq" }
>db.user.count();			#统计集合的条目
1
>db.stats();					#查看集合状态
{
     
	"db" : "local",
	"collections" : 2,
	"views" : 0,
	"objects" : 2,
	"avgObjSize" : 887,
	"dataSize" : 1774,
	"storageSize" : 32768,
	"numExtents" : 0,
	"indexes" : 2,
	"indexSize" : 32768,
	"ok" : 1
}
>db.user.insert({
     "id":2,"name":"zeng","isadmin":"true","gender":null,"favorite":["apple","banana","orange",1,2,3],"regtime":new Date()});
#集合中插入数据条目,并设置多种数据类型
WriteResult({
      "nInserted" : 1 })
> db.user.find();
{
      "_id" : ObjectId("5f45d32f7e48f4a36a9cb7e0"), "id" : 1, "name" : "zzq" }
{
      "_id" : ObjectId("5f45d78e17e09ebe1d094426"), "id" : 2, "name" : "zeng", "isadmin" : "true", "gender" : null, "favorite" : [ "apple", "banana", "orange", 1, 2, 3 ], "regtime" : ISODate("2020-08-26T03:31:26.780Z") }
> db.user.findOne({
     "id":2});        #根据KEY值搜索集合中内容
{
     
	"_id" : ObjectId("5f45d78e17e09ebe1d094426"),
	"id" : 2,
	"name" : "zeng",
	"isadmin" : "true",
	"gender" : null,
	"favorite" : [
		"apple",
		"banana",
		"orange",
		1,
		2,
		3
	],
	"regtime" : ISODate("2020-08-26T03:31:26.780Z")
}

MongoDB常用数据类型

数据类型 描述
string 字符串;在MongoDB中UTF-8编码的字符串才是合法的
Integer 整型数值,根据系统分为32位和64位
Boolean 布尔值,用于存储布尔值(真/假)
Double 双精度浮点值
Arrays 用于将数组或列表或多个值存储为一个键
Object 用于内嵌文档
Null 用于创建空值
Date 日期时间,用户可以指定自己的日期时间,创建Date对象,传入年月日信息
Binary Data 二进制数据,用于存储二进制数据
#查看MongoDB的数据类型>
> use zzq;
switched to db zzq
> show collections;
user
> a=db.user.findOne({
     "id":2});
{
      "_id" : ObjectId("5f47562e544998aa19c58d86"), "id" : 2, "name" : "alice" }
> typeof(a.id);
number
> typeof(a.name)
string

MongoDB日常维护

备份与恢复管理

在MongoDB中备份管理包括导入导出、备份与恢复、复制数据库和克隆集合等操作

1.导入导出

可以使用mongoexportmongoimport命令来导出导入MongoDB的数据
mongoexport命令可以把一个collection导出成JSON格式或CSV格式的文件;mongoimport命令可以把一个特定格式的文件的内容导入到指定的collection中,该工具可以导入JSON格式的数据,也可以导入CSV格式的数据


> for(var i=1;i<=1000;i++) db.user.insert({
     "id":i,"name":"ALICE"});   批量插入
WriteResult({
      "nInserted" : 1 })
> db.user.count();    统计行数
1000
> db
zzq
> exit
bye
mongoexport -d zzq -c user -o ./user.json -h 192.168.137.21 --port 27017    导出数据
2020-08-28T09:06:47.984+0800	connected to: 192.168.137.21:27017
2020-08-28T09:06:48.000+0800	exported 1000 records
[root@localhost ~]# ll u*
-rw-r--r-- 1 root root 35001 8月  28 09:05 user.json
 mongoimport -d zzq1 -c user --file user.json -h 192.168.137.21 --port 27018  导入到另一实例
2020-08-28T09:13:13.973+0800	connected to: 192.168.137.21:27018
2020-08-28T09:13:13.988+0800	imported 1000 documents
[root@localhost ~]# mongo 192.168.137.21:27018
> show dbs
admin  0.000GB
local  0.000GB
zzq1   0.000GB
> use zzq1
switched to db zzq1
> show collections
user
> db.user.find()
{
      "_id" : ObjectId("5f48573ecf7246996e681138"), "id" : 1, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e681139"), "id" : 2, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e68113a"), "id" : 3, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e68113b"), "id" : 4, "name" : "ALICE" }
...
Type "it" for more
> db.user.count();
1000
>exit
bye
mongoexport -d zzq1 -c user -q '{"id":{"$gt":500}}' -o user2.json -h 192.168.137.21 --port 27018
2020-08-28T09:18:46.965+0800	connected to: 192.168.137.21:27018
2020-08-28T09:18:46.973+0800	exported 500 records

参数说明
-d:指定数据库的名字
-c:指定collection的名字
-f:指定要导出那些列
-o:指明要导出的文件名
-q:指明导出数据的过滤条件

备份与恢复

在MongoDB中可以使用mongodump命令来备份MongoDB数据,该命令可以导出所有的数据到指定目录中。mongodump命令可以通过参数指定导出的数据量及转存的服务器
mongodump语法:
>mongodump -h dbhost -d dbname -o dbdbdirectory
参数说明

  • -h:MongoDB所在服务器地址,例如:127.0.0.1,也可以指定端口号,如:127.0.0.1:27017
  • -d:需要备份的数据库实例,例如:test
  • -o:备份的数据存放的位置,该目录需要提前建立,在备份完成后,系统自动在dump目录下建立一个test目录,这个目录里面存放该数据库实例的备份数据
[root@localhost ~]# mongodump -d zzq -o ./backup/ -h 192.168.137.21:27017
2020-08-28T09:49:59.228+0800	writing zzq.user to 
2020-08-28T09:49:59.230+0800	done dumping zzq.user (1000 documents)
[root@localhost ~]# ls -lth ./backup/zzq/
总用量 56K
-rw-r--r-- 1 root root 49K 8月  28 09:49 user.bson
-rw-r--r-- 1 root root  80 8月  28 09:49 user.metadata.json

MongoDB使用mongorestore命令来恢复备份的数据
语法 mongorestore -h dbhost -d dbname --directoryperdb dbdirectory

[root@localhost ~]# mongorestore -d zzq1 --dir=./backup/zzq/ -h 192.168.137.21:27017
2020-08-28T09:54:37.961+0800	the --db and --collection args should only be used when restoring from a BSON file. Other uses are deprecated and will not exist in the future; use --nsInclude instead
2020-08-28T09:54:37.961+0800	building a list of collections to restore from backup/zzq dir
2020-08-28T09:54:37.961+0800	reading metadata for zzq1.user from backup/zzq/user.metadata.json
2020-08-28T09:54:37.967+0800	restoring zzq1.user from backup/zzq/user.bson
2020-08-28T09:54:37.977+0800	no indexes to restore
2020-08-28T09:54:37.977+0800	finished restoring zzq1.user (1000 documents)
2020-08-28T09:54:37.977+0800	done
[root@localhost ~]# mongo 192.168.137.21:27017

> show dbs
admin  0.000GB
kgc    0.000GB
kgc1   0.000GB
local  0.000GB
zzq    0.000GB
zzq1   0.000GB
> use zzq1
switched to db zzq1
> show collections
user
> db.user.count()
1000
> db.user.find();
{
      "_id" : ObjectId("5f48573ecf7246996e681138"), "id" : 1, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e681139"), "id" : 2, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e68113a"), "id" : 3, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e68113b"), "id" : 4, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e68113c"), "id" : 5, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e68113d"), "id" : 6, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e68113e"), "id" : 7, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e68113f"), "id" : 8, "name" : "ALICE" }
{
      "_id" : ObjectId("5f48573ecf7246996e681140"), "id" : 9, "name" : "ALICE" }
......

复制数据库

在MongoDB中可以使用db.copyDatabase命令复制数据库


> show dbs
admin  0.000GB
kgc    0.000GB
kgc1   0.000GB
local  0.000GB
zzq    0.000GB
zzq1   0.000GB
> db.copyDatabase("kgc","kgc2","192.168.137.21:27017");
{
      "ok" : 1 }
> show dbs
admin  0.000GB
kgc    0.000GB
kgc1   0.000GB
kgc2   0.000GB
local  0.000GB
zzq    0.000GB
zzq1   0.000GB
> 

克隆集合

在MongoDB中可以将数据库中的集合进行克隆,这里将kgc数据库中的user集合克隆岛另外一个实例


> use kgc
switched to db kgc
> show collections
user
>exit
bye
[root@localhost ~]# mongod -f /etc/mongod1.conf 
about to fork child process, waiting until server is ready for connections.
forked process: 22924
child process started successfully, parent exiting
[root@localhost ~]# netstat -antp | grep mongod
tcp        0      0 192.168.137.21:27017    0.0.0.0:*               LISTEN      13699/mongod        
tcp        0      0 192.168.137.21:27018    0.0.0.0:*               LISTEN      22924/mongod 
[root@localhost ~]# mongo 192.168.137.21:27018
MongoDB shell version v3.4.24
connecting to: mongodb://192.168.137.21:27018/test
MongoDB server version: 3.4.24
......
> show dbs
admin  0.000GB
local  0.000GB
zzq1   0.000GB
> db.runCommand({
     "cloneCollection":"kgc.user","from":"192.168.137.21:27017"});
{
      "ok" : 1 }
> show dbs
admin  0.000GB
kgc    0.000GB
local  0.000GB
zzq1   0.000GB
> show collections;
user

安全管理

MongoDB安全管理主要包括MongoDB的安全访问控制和用户权限分配

限定监听特定端口和IP地址

只绑定内网卡地址:bind_ip=192.168.127.21
只侦听指定的端口:port=27017

vim /etc/mongod.conf
net:
  port: 27017
  bindIp: 192.168.137.21 
root@localhost system]# netstat -antp | grep 27017
tcp        0      0 192.168.137.21:27017    0.0.0.0:*               LISTEN      13699/mongod    
[root@localhost system]# mongo --host 192.168.137.21
MongoDB shell version v3.4.24
connecting to: mongodb://192.168.137.21:27017/
MongoDB server version: 3.4.24

授权启动

可以配置授权用户来访问MongoDB,需在配置文件宗指定authorization: enabled

mongo 192.168.137.21
MongoDB shell version v3.4.24
connecting to: mongodb://192.168.137.21:27017/test
MongoDB server version: 3.4.24
> db.createUser(
...   {
     
...     user: "root",
...     pwd: "root",
...     roles: [ {
      role: "root", db: "admin" } ]
...   }
... )
Successfully added user: {
     
	"user" : "root",
	"roles" : [
		{
     
			"role" : "root",
			"db" : "admin"
		}
	]
}
> exit
bye

> exit
bye
security:
  authorization: enabled
[root@localhost system]# mongo 192.168.137.21:27017
MongoDB shell version v3.4.24
connecting to: mongodb://192.168.137.21:27017/test
MongoDB server version: 3.4.24
> show dbs;
2020-08-28T11:34:11.756+0800 E QUERY    [thread1] Error: listDatabases failed:{
     
	"ok" : 0,
	"errmsg" : "not authorized on admin to execute command { listDatabases: 1.0 }",
	"code" : 13,
	"codeName" : "Unauthorized"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1
shellHelper.show@src/mongo/shell/utils.js:814:19
shellHelper@src/mongo/shell/utils.js:704:15
@(shellhelp2):1:1
> use admin
switched to db admin
> db.auth("root","root")
1
> show dbs
admin  0.000GB
kgc    0.000GB
kgc1   0.000GB
kgc2   0.000GB
local  0.000GB
zzq    0.000GB
zzq1   0.000GB
登录时可指定以授权的用户登录
[root@localhost ~]#  mongo  -uroot -proot 192.168.100.10:27017/admin
删除账号robin
> db.dropUser("robin");
false
> use kgc
switched to db kgc
> db.dropUser("robin");
true

进程管理

管理员可以对MongoDB进程进行管理和控制
查看当前正在运行的进程的命令为db.currentOp()
中止正在运行的高消耗资源的进程命令为db,killOp(opid)

> db.currentOp()
{
     
	"inprog" : [
		{
     
			"desc" : "conn1",
			"threadId" : "140550667011840",
			"connectionId" : 1,
			"client" : "192.168.137.21:53634",
			"appName" : "MongoDB Shell",
			"clientMetadata" : {
     
				"application" : {
     
					"name" : "MongoDB Shell"
				},
				"driver" : {
     
					"name" : "MongoDB Internal Client",
					"version" : "3.4.24"
				},
				"os" : {
     
					"type" : "Linux",
					"name" : "CentOS Linux release 7.8.2003 (Core)",
					"architecture" : "x86_64",
					"version" : "Kernel 3.10.0-1127.18.2.el7.x86_64"
				}
			},
			"active" : true,
			"opid" : 2027,
			"secs_running" : 0,
			"microsecs_running" : NumberLong(90),
			"op" : "command",
			"ns" : "admin.$cmd",
			"query" : {
     
				"currentOp" : 1
			},
			"numYields" : 0,
			"locks" : {
     
				
			},
			"waitingForLock" : false,
			"lockStats" : {
     
				
			}
		}
	],
	"ok" : 1
}
> db.killOp(2027)
{
      "info" : "attempting to kill op", "ok" : 1 }

MongoDB监控

MongoDB提供一些内置工具可以监控数据库的状态

  • 查看数据库实例的状态信息:db.serverStatus()
  • 查看当前数据库的统计信息:db.status()
    此外,还可以通过Web界面查看系统监控信息
    第一步:修改配置文件
    vim /etc/mongod.cong
    bindIp: 192.168.137.21
    第二步:停止进程
    [root@localhost ~]# mongod -f /etc/mongod.conf --shutdown
    第三步:开启监控
    命令行开启,临时,下次重启失效
    [root@localhost ~]# mongod -f /etc/mongod.conf --httpinterface
    验证:在浏览器验证
    http://192.168.137.21:28017/
    MongoDB数据库基础_第1张图片

一些问题

该环境为Centos7.5,使用的MongoDB为YUM安装的3.6版本,我在配置授权用户时,需要将系统服务脚本进行重写,如下:

vim /lib/systemd/system/mongod.service
[Unit]
Description=MongoDB
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/var/lib/mongo/mongod.lock
ExecStart=/usr/bin/mongod --auth -f /etc/mongod.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl start mongod

开启监控时,如已开启用户授权,则访问web页面出现以下情况
MongoDB数据库基础_第2张图片
输入了正确的用户名、密码但页面不能正常显示,只能将认证关闭

你可能感兴趣的:(MongoDB,mongodb,centos,运维)