1、列举常见的关系型数据库和非关系型都有那些?

  1. 关系型:Mysql / Oracle / SQL Server
  2. 非关系型:redis / MongoDB

2、MySQL常见数据库引擎及比较?

  1. MySQL数据库中的表引擎一般常用两种:MyISAMInnoDB
  2. 区别:
  3. MyISAM类型的数据文件有三个1.frm(结构)、2.MYD(数据)、3.MYI(索引)
  4. MyISAM类型中的表数据增 改速度快,不支持事务,没有InnoDB安全。
  5. InnoDB类型的数据文件只有一个 .frm
  6. InnoDB类型的表数据增 改速度没有MyISAM的快,但支持事务,相对安全。

3、简述数据三大范式?

  1. 第一范式(1NF):原子性 字段不可再分,否则就不是关系数据库;
  2. 第二范式(2NF):唯一性 一个表只说明一个事物;
  3. 第三范式(3NF):每列都与主键有直接关系,不存在传递依赖;
  4. 1NF:列表字段不可分;
  5. 2NF:有主键且非主键依赖主键;
  6. 3NF:非主键字段不能相互依赖;
  1. 注意事项:
  2. 1.第二范式与第三范式的本质区别:在于有没有分出两张表。
  3. 第二范式是说一张表中包含了多种不同实体的属性,那么必须要分成多张表,第三范式是要求已经分好了多张表的话,一张表中只能有另一张标的ID,而不能有其他任何信息,(其他任何信息,一律用主键在另一张表中查询)。
  4. 2.必须先满足第一范式才能满足第二范式,必须同时满足第一第二范式才能满足第三范式。

4、什么是事务?MySQL如何支持事务?

  1. 同时对一组数据进行操作的语句就成为事务
  2. MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。

6、如何基于数据库实现商城商品计数器?

  1. 设置一个商品计数的列 自动递增为1

8、简述触发器、函数、视图、存储过程?

  1. 触发器:制定一个sql条件和要做的事当满足的时候自动触发并执行要做的事
  2. 函数(存储过程):Mysql储存过程简而言之就是一组已经好的命令,需要使用的时候拿出来用就可以
  3. 视图:将一个写好的查询语句封装起来 当调用的时看到的数据就是满足条件的数据 不用每次都写同样的代码

9、MySQL索引种类

  1. 唯一索引
  2. 主键索引
  3. 普通索引
  4. 全文索引

10、索引在什么情况下遵循最左前缀的规则?

  1. 联合索引

11、主键和外键的区别?

企业微信截图_16451729301026.png

12、MySQL常见的函数?

  1. *concat(s1,s2,…Sn) 连接s1,s2..Sn为一个字符串
  2. *length(str) 返回值为字符串str 的长度
  3. *datediff(expr,expr2) 返回起始时间和结束时间的间隔天数

14、如何开启慢日志查询?

  1. 1.查看慢查询是否开启
  2. show variables like slow_query%’;
  3. show variables like long_query_time’;
  4. 2.打开慢查询
  5. set global slow_query_log=’ON’;
  6. 3.设置慢查询日志记录文件
  7. set global slow_query_log_file=’/var/lib/mysql/test-10-226-slow.log’;
  8. 4.指定慢查询事件
  9. set global long_query_time=1;

17、char和varchar的区别?

  1. char的长度是不可变的,而varchar的长度是可变的,也就是说,定义一个char[10]和varchar[10],如果存进去的是‘csdn’,那么char所占的长度依然为10,除了字符‘csdn’外,后面跟六个空格,而varchar就立马把长度变为4

18、简述MySQL的执行计划?

  1. 数据库的执行计划通俗点说就是,数据库服务器在执行sql语句的时候,会准备几套方案,最后选择消耗资源最小的那个方案。就是执行计划。

19、在对name做了唯一索引前提下,简述以下区别:

  1. select * from tb where name = twiss ----—–取出所有name= twiss
  2. select * from tb where name = twiss limit 1 ---—–只取出第一条 name= twiss

20、1000w条数据,使用limit offset 分页时,为什么越往后翻越慢?如何解决?

  1. 当一个数据库表过于庞大,LIMIT offset, length中的offset值过大,则SQL查询语句会非常缓慢,增加order by,并且order by字段需要建立索引。

21、什么是索引合并?

  1. 1、索引合并是把几个索引的范围扫描合并成一个索引。
  2. 2、索引合并的时候,会对索引进行并集,交集或者先交集再并集操作,以便合并成一个索引。
  3. 3、这些需要合并的索引只能是一个表的。不能对多表进行索引合并。

22、什么是覆盖索引?

  1. 就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖。

23、简述数据库读写分离?

  1. 对于数据存储层高并发问题,最先想到的可能就是读写分离,在网站访问量大并且读写不平均的情况下,将存储分为master,slave两台,所有的写都路由到master上,所有的读都路由到slave上,然后masterslave同步。如果一台salve不够,可以加多台,比如一台master3slave

24、简述数据库分库分表?(水平、垂直)

  1. 垂直分表是基于数据库中的"列"进行,某个表字段较多,可以新建一张扩展表,将不经常用或字段长度较大的字段拆分出去到扩展表中。在字段很多的情况下(例如一个大表有100多个字段),通过"大表拆小表",更便于开发与维护,也能避免跨页问题,MySQL底层是通过数据页存储的,一条记录占用空间过大会导致跨页,造成额外的性能开销。另外数据库以行为单位将数据加载到内存中,这样表中字段长度较短且访问频率较高,内存能加载更多的数据,命中率更高,减少了磁盘IO,从而提升了数据库性能。
  2. 垂直切分的优点:
  3. 解决业务系统层面的耦合,业务清晰
  4. 与微服务的治理类似,也能对不同业务的数据进行分级管理、维护、监控、扩展等
  5. 高并发场景下,垂直切分一定程度的提升IO、数据库连接数、单机硬件资源的瓶颈
  6. 缺点:
  7. 部分表无法join,只能通过接口聚合方式解决,提升了开发的复杂度
  8. 分布式事务处理复杂
  9. 依然存在单表数据量过大的问题(需要水平切分)
  10. 水平切分分为库内分表和分库分表,是根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果。
  11. 水平切分的优点:
  12. 不存在单库数据量过大、高并发的性能瓶颈,提升系统稳定性和负载能力
  13. 应用端改造较小,不需要拆分业务模块
  14. 缺点:
  15. 跨分片的事务一致性难以保证
  16. 跨库的join关联查询性能较差
  17. 数据多次扩展难度和维护量极大
  1. 分区的主要目的是将数据按照一个较粗的粒度分在不同的表中,这样可以将相关的数据存放在一起,而且如果想一次性的删除整个分区的数据也很方便。
  2. 通过一些HASH算法或者工具实现将一张数据表垂直或者水平进行物理切分

25、redis和memcached比较?

  1. 1RedisMemcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等;
  2. 2Redis不仅仅支持简单的k/v类型的数据,同时还提供listsethash等数据结构的存储;
  3. 3、虚拟内存–Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘;
  4. 4、过期策略–memcacheset时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10
  5. 5、分布式–设定memcache集群,利用magent做一主多从;redis可以做一主多从。都可以一主一从;
  6. 6、存储数据安全–memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化);
  7. 7、灾难恢复–memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复;

26、redis中数据库默认是多少个db 及作用?

  1. redis默认有十六个db

27、python操作redis的模块?

  1. import redis
  2. r = redis.Redis(host='127.0.0.1', port=6379,decode_responses=True)
  3. r.set('name', 'OK')
  4. print(r.get('name'))

28、如果redis中的某个列表中的数据量非常大,如果实现循环显示每一个值?

  1. 通过scan_iter分片取,减少内存压力
  2. scan_iter(match=None, count=None)增量式迭代获取redis里匹配的的值
  3. # match,匹配指定key
  4. # count,每次分片最少获取个数
  5. r = redis.Redis(connection_pool=pool)
  6. for key in r.scan_iter(match='PREFIX_*', count=100000):
  7. print(key)

20、redis如何实现主从复制?以及数据同步机制?

  1. MasterSlave互通之后,首先,Slave会发送sync同步指令,当Master收到指令后,将在后台启动存盘进程,同时收集所有来自Slave的修改数据集的指令信息,当后台进程完成之后,Master将发送对应的数据库文件到对应的Slave中,以完成一次完整的同步工作。其次Slave在接受到数据库文件之后,将其存盘并加载到内存中。最后,Master继续收集修改命令和新增的修改指令,并依次发送给Slave,其将在本次执行这些数据的修改命令,从而最终达到数据同步的实现。

30、redis中的sentinel的作用?

  1. Redis Sentinel Redis提供了高可用的实现。通俗来说就是你可以部署一套无需人为干预即可防灾的Redis环境。
  2. RS同时为客户端提供了其他诸如监控,通知的功能。
  3. 帮助我们自动在主从之间进行切换
  4. 检测主从中 主是否挂掉,且超过一半的sentinel检测到挂了之后才进行进行切换。
  5. 如果主修复好了,再次启动时候,会变成从。
  6. 启动主redis:
  7. redis-server /etc/redis-6379.conf 启动主redis
  8. redis-server /etc/redis-6380.conf 启动从redis
  9. linux中:
  10. 找到 /etc/redis-sentinel-8001.conf 配置文件,在内部:
  11. - 哨兵的端口 port = 8001
  12. - redisIP,哨兵个数的一半/1
  13. 找到 /etc/redis-sentinel-8002.conf 配置文件,在内部:
  14. - 哨兵的端口 port = 8002
  15. - redisIP, 1
  16. 启动两个哨兵

31、如何实现redis集群?

  1. redis集群、分片、分布式redis
  2. redis-py-cluster
  3. 集群方案:
  4. - redis cluster 官方提供的集群方案。
  5. - codis,豌豆荚技术团队。
  6. - tweproxyTwiter技术团队。
  7. redis cluster的原理?
  8. - 基于分片来完成。
  9. - redis将所有能放置数据的地方创建了 16384 个哈希槽。
  10. - 如果设置集群的话,就可以为每个实例分配哈希槽:
  11. - 192.168.1.200-5000
  12. - 192.168.1.215001-10000
  13. - 192.168.1.2210001-16384
  14. - 以后想要在redis中写值时,
  15. set k1 123
  16. k1通过crc16的算法,将k1转换成一个数字。然后再将该数字和16384求余,如果得到的余数 3000,那么就将该值写入到 192.168.1.20 实例中。

32、redis中默认有多少个哈希槽?

  1. 2**14个哈希槽 16384

33、简述redis的有哪几种持久化策略及比较?

  1. rdb:快照形式是直接把内存中的数据保存到一个dump文件中,定时保存,保存策略
  2. aof:把所有的对redis的服务器进行修改的命令都存到一个文件里,命令的集合
  3. RDB:每隔一段时间对redis进行一次持久化。
  4. - 缺点:数据不完整
  5. - 优点:速度快
  6. AOF:把所有命令保存起来,如果想到重新生成到redis,那么就要把命令重新执行一次。
  7. - 缺点:速度慢,文件比较大
  8. - 优点:数据完整

34、列举redis支持的过期策略。

  1. 定时删除
  2. 含义:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除
  3. 惰性删除
  4. 含义:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null
  5. 定期删除
  6. 含义:每隔一段时间执行一次删除(在redis.conf配置文件设置hz1s刷新的频率)过期key操作
  7. voltile-lru 从已设置过期时间的数据集(server.db[i].expires)中挑选最近频率最少数据淘汰
  8. volatile-ttl 从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  9. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  10. allkeys-lru 从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  11. allkeys-random 从数据集(server.db[i].dict)中任意选择数据淘汰
  12. no-enviction(驱逐):禁止驱逐数据

35、MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中都是热点数据?

  1. LRU(最近少用的淘汰)
  2. redis的缓存每命中一次,就给命中的缓存增加一定ttl(过期时间)(根据具体情况来设定, 比如10分钟).一段时间后, 热数据的ttl都会较大, 不会自动失效, 而冷数据基本上过了设定的ttl就马上失效了.
  3. 相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。redis 提供 6种数据淘汰策略:
  4. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  5. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  6. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  7. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  8. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  9. no-enviction(驱逐):禁止驱逐数据

36、写代码,基于redis的列表实现 先进先出、后进先出队列、优先级队列。

  1. from . import picklecompat
  2. class Base(object):
  3. """Per-spider base queue class"""
  4. def __init__(self, server, spider, key, serializer=None):
  5. """Initialize per-spider redis queue.
  6. Parameters
  7. ----------
  8. server : StrictRedis
  9. Redis client instance.
  10. spider : Spider
  11. Scrapy spider instance.
  12. key: str
  13. Redis key where to put and get messages.
  14. serializer : object
  15. Serializer object with ``loads`` and ``dumps`` methods.
  16. """
  17. if serializer is None:
  18. # Backward compatibility.
  19. # TODO: deprecate pickle.
  20. serializer = picklecompat
  21. if not hasattr(serializer, 'loads'):
  22. raise TypeError("serializer does not implement 'loads' function: %r"
  23. % serializer)
  24. if not hasattr(serializer, 'dumps'):
  25. raise TypeError("serializer '%s' does not implement 'dumps' function: %r"
  26. % serializer)
  27. self.server = server
  28. self.spider = spider
  29. self.key = key % {'spider': spider.name}
  30. self.serializer = serializer
  31. def _encode_request(self, request):
  32. """Encode a request object"""
  33. obj = request_to_dict(request, self.spider)
  34. return self.serializer.dumps(obj)
  35. def _decode_request(self, encoded_request):
  36. """Decode an request previously encoded"""
  37. obj = self.serializer.loads(encoded_request)
  38. return request_from_dict(obj, self.spider)
  39. def __len__(self):
  40. """Return the length of the queue"""
  41. raise NotImplementedError
  42. def push(self, request):
  43. """Push a request"""
  44. raise NotImplementedError
  45. def pop(self, timeout=0):
  46. """Pop a request"""
  47. raise NotImplementedError
  48. def clear(self):
  49. """Clear queue/stack"""
  50. self.server.delete(self.key)
  51. class FifoQueue(Base):
  52. """Per-spider FIFO queue"""
  53. def __len__(self):
  54. """Return the length of the queue"""
  55. return self.server.llen(self.key)
  56. def push(self, request):
  57. """Push a request"""
  58. self.server.lpush(self.key, self._encode_request(request))
  59. def pop(self, timeout=0):
  60. """Pop a request"""
  61. if timeout > 0:
  62. data = self.server.brpop(self.key, timeout)
  63. if isinstance(data, tuple):
  64. data = data[1]
  65. else:
  66. data = self.server.rpop(self.key)
  67. if data:
  68. return self._decode_request(data)
  69. class PriorityQueue(Base):
  70. """Per-spider priority queue abstraction using redis' sorted set"""
  71. def __len__(self):
  72. """Return the length of the queue"""
  73. return self.server.zcard(self.key)
  74. def push(self, request):
  75. """Push a request"""
  76. data = self._encode_request(request)
  77. score = -request.priority
  78. # We don't use zadd method as the order of arguments change depending on
  79. # whether the class is Redis or StrictRedis, and the option of using
  80. # kwargs only accepts strings, not bytes.
  81. self.server.execute_command('ZADD', self.key, score, data)
  82. def pop(self, timeout=0):
  83. """
  84. Pop a request
  85. timeout not support in this queue class
  86. """
  87. # use atomic range/remove using multi/exec
  88. pipe = self.server.pipeline()
  89. pipe.multi()
  90. pipe.zrange(self.key, 0, 0).zremrangebyrank(self.key, 0, 0)
  91. results, count = pipe.execute()
  92. if results:
  93. return self._decode_request(results[0])
  94. class LifoQueue(Base):
  95. """Per-spider LIFO queue."""
  96. def __len__(self):
  97. """Return the length of the stack"""
  98. return self.server.llen(self.key)
  99. def push(self, request):
  100. """Push a request"""
  101. self.server.lpush(self.key, self._encode_request(request))
  102. def pop(self, timeout=0):
  103. """Pop a request"""
  104. if timeout > 0:
  105. data = self.server.blpop(self.key, timeout)
  106. if isinstance(data, tuple):
  107. data = data[1]
  108. else:
  109. data = self.server.lpop(self.key)
  110. if data:
  111. return self._decode_request(data)
  112. # TODO: Deprecate the use of these names.
  113. SpiderQueue = FifoQueue
  114. SpiderStack = LifoQueue
  115. SpiderPriorityQueue = PriorityQueue

37、如何基于redis实现消息队列?

  1. Redis中五大数据结构之一—列表,其PUSHPOP命令遵循FIFO先进先出原则。当我们需要发布消息的时候执行LPUSH(消息从左边进入队列),消息接收端执行RPOP获得消息(消息从右侧弹出)。对于列表,Redis提供了带有阻塞的命令(命令前加B)。因此,生产者lpush消息,消费者brpop(从列表中弹出最右端元素,如无元素则一直阻塞到timeout)消息,并设定超时时间timeout,可以减少redis的压力。
  2. 不要使用redis去做消息队列,这不是redis的设计目标。
  3. 但实在太多人使用redis去做去消息队列,redis的作者看不下去,另外基于redis的核心代码,另外实现了一个消息队列disquehttps://github.com/antirez/disque

38、如何基于redis实现发布和订阅?以及发布订阅和消息队列的区别?

  1. 创建一个频道 客户端加入频道 等待频道发布订阅
  2. 发布者:
  3. import redis
  4. conn = redis.Redis(host='127.0.0.1',port=6379)
  5. conn.publish('104.9MH', "hahaha")
  6. 订阅者:
  7. import redis
  8. conn = redis.Redis(host='127.0.0.1',port=6379)
  9. pub = conn.pubsub()
  10. pub.subscribe('104.9MH')
  11. while True:
  12. msg= pub.parse_response()
  13. print(msg)

39、什么是codis及作用?

  1. Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有明显的区别 (不支持的命令列表), 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务.

40、什么是twemproxy及作用?

  1. Twemproxy是一种代理分片机制,由Twitter开源。Twemproxy作为代理,可接受来自多个程序的访问,按照路由规则,转发给后台的各个Redis服务器,再原路返回。该方案很好的解决了单个Redis实例承载能力的问题。
  2. Twtter 开源的一个 Redis Memcache 代理服务器,主要用于管理 Redis Memcached 集群,减少与Cache 服务器直接连接的数量。

41、写代码实现redis事务操作。

  1. import redis
  2. pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
  3. conn = redis.Redis(connection_pool=pool)
  4. # pipe = r.pipeline(transaction=False)
  5. pipe = conn.pipeline(transaction=True)
  6. # 开始事务
  7. pipe.multi()
  8. pipe.set('name', 'bendere')
  9. pipe.set('role', 'sb')
  10. # 提交
  11. pipe.execute()
  12. 注意:咨询是否当前分布式redis是否支持事务

42、redis中的watch的命令的作用?

  1. Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务执行失败。
  2. 面试题:你如何控制剩余的数量不会出问题?
  3. - 通过rediswatch实现
  4. import redis
  5. conn = redis.Redis(host='127.0.0.1',port=6379)
  6. # conn.set('count',1000)
  7. val = conn.get('count')
  8. print(val)
  9. with conn.pipeline(transaction=True) as pipe:
  10. # 先监视,自己的值没有被修改过
  11. conn.watch('count')
  12. # 事务开始
  13. pipe.multi()
  14. old_count = conn.get('count')
  15. count = int(old_count)
  16. print('现在剩余的商品有:%s',count)
  17. input("问媳妇让不让买?")
  18. pipe.set('count', count - 1)
  19. # 执行,把所有命令一次性推送过去
  20. pipe.execute()
  21. - 数据库的锁

43、基于redis如何实现商城商品数量计数器?

  1. Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们
  2. 称之为“user_scores”,我们只需要像下面一样执行即可:
  3. 当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
  4. ZRANGE user_scores 0 10 WITHSCORES
  5. Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到

44 、简述redis分布式锁和redlock的实现机制。

  1. 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段。
  2. 一个Client想要获得一个锁需要以下几个操作:
  3. 得到本地时间Client使用相同的key和随机数,按照顺序在每个Master实例中尝试获得锁。在获得锁的过程中,为每一个锁操作设置一个快速失败时间(如果想要获得一个10秒的锁, 那么每一个锁操作的失败时间设为5-50ms)。
  4. 这样可以避免客户端与一个已经故障的Master通信占用太长时间,通过快速失败的方式尽快的与集群中的其他节点完成锁操作。
  5. 客户端计算出与master获得锁操作过程中消耗的时间,当且仅当Client获得锁消耗的时间小于锁的存活时间,并且在一半以上的master节点中获得锁。才认为client成功的获得了锁。
  6. 如果已经获得了锁,Client执行任务的时间窗口是锁的存活时间减去获得锁消耗的时间。
  7. 如果Client获得锁的数量不足一半以上,或获得锁的时间超时,那么认为获得锁失败。客户端需要尝试在所有的master节点中释放锁, 即使在第二步中没有成功获得该Master节点中的锁,仍要进行释放操作。

45、什么是一致性哈希?Python中是否有相应模块?

  1. 对节点和数据,都做一次哈希运算,然后比较节点和数据的哈希值,数据取和节点最相近的节点做为存放节点。这样就保证当节点增加或者减少的时候,影响的数据最少。
  2. hash_ring python中做一致性哈希的模块
  3. Python模块--hash_ring,即Python中的一致性hash

46、如何高效的找到redis中所有以twiss开头的key?

  1. key twiss*
  2. redis 有一个keys命令。
  3. 语法:KEYS pattern
  4. 说明:返回与指定模式相匹配的所用的keys
  5. 该命令所支持的匹配模式如下:
  6. 1)?:用于匹配单个字符。例如,h?llo可以匹配hellohallohxllo等;
  7. 2)*:用于匹配零个或者多个字符。例如,h*llo可以匹配hlloheeeello等;
  8. 3)[]:可以用来指定模式的选择区间。例如h[ae]llo可以匹配hellohallo,但是不能匹配hillo
  9. 同时,可以使用“/”符号来转义特殊的字符