Redis入门# Redis入门

一、介绍

redis是键值对的数据库,常用的五种数据类型为

  • 字符串类型(string)
  • 散列类型(hash)
  • 列表类型(list)
  • 集合类型(set)
  • 有序集合类型(zset)

Redis用作缓存,主要两个用途:高性能,高并发,因为内存天然支持高并发

二、安装使用

1.安装 epel仓库

1
2
3
yum search epel
yum -y install epel-release.noarch
yum -y install redis

2.启动

1
2
service redis start
find / -name redis

卸载

1
2
3
rpm -qa | grep redis
redis-3.2.10-2.el7.x86_64
rpm -e redis-3.2.10-2.el7.x86_64

查看6379端口

yum -y install net-tools
netstat -aon | grep 6379

客户端

redis-cli
set 键 值
get 键

修改 /etc/redis.conf

1
bind 192.168.1.238  绑定ip 远程操作

redis的ui客户端

redisstudio

redis-desktop-manager

二、redis简单配置

  1. 设置登录密码(默认没有密码登陆)

    • redis 设置登陆的密码

      1
      requirepass 123456
    • 重启redis

      1
      service redis restart
    • 连接redis 后

      1
      auth 密码 
  2. 设置数据库个数

    • redis所有键值对数据都创建在数据库中 数据库没有名字 只有索引 默认数据 0号数据

      1
      select 数据库下标(从0开始)
    • 如果需要添加数据库的个数 修改 /etc/redis.conf

      1
      databases 具体数据库个数

三、使用

1. redis keys相关命令

redis存储的是键值对 对键进行操作的命令

1
2
3
4
5
6
7
8
9
10
11
12
//获取key对应的值
get key
//删除key对应的值
del key
//判断数据库中是否存在该key对应的数据
exists key
//超时时间 给key设置超时时间 单位是s
expire key
//列表显示所有的键 可以模糊匹配 比如 keys user_* 匹配user_开头所有
keys *
//获取某个键对应的值的类型(默认是string类型)
TYPE key

2. redis所有数据类型(值的类型)

字符串

1
2
3
4
5
6
7
8
9
10
//将 key 中储存的数字值增一。
SET key value
//获取指定 key 的值。
GET key
//返回 key 所储存的字符串值的长度。
STRLEN key
//将 key 中储存的数字值增一。
INCR key
//如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。
APPEND key value

hash(对象 多个属性)

class User{
String id=1;
String name=”zs”;
}

1
2
3
4
5
6
7
8
9
10
11
12
//同时将多个 field-value (域-值)对设置到哈希表 key 中。
HMSET key field1 value1 [field2 value2 ]
//获取所有哈希表中的字段
HKEYS key
//获取在哈希表中指定 key 的所有字段和值
HGETALL key
//获取存储在哈希表中指定字段的值
HGET key field
//删除一个或多个哈希表字段
HDEL key field1 [field2]
//查看哈希表 key 中,指定的字段是否存在。
HEXISTS key field

列表(list 数组 队列 栈)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//将一个或多个值插入到列表头部
LPUSH key value1 [value2]
//在列表中添加一个或多个值
RPUSH key value1 [value2]
//获取列表长度
LLEN key
//通过索引获取列表中的元素
LINDEX key index
//移除列表元素
LREM key count value
//从头往尾移除值为 value 的元素。
count > 0:
//从尾往头移除值为 value 的元素。
count < 0:
//移除所有值为 value 的元素。
count = 0:
//获取列表指定范围内的元素
LRANGE key start stop

set(无重复元素集合)

1
2
3
4
5
6
7
8
9
10
//向集合添加一个或多个成员
SADD key member1 [member2]
// 获取集合的成员数
SCARD key
//判断 member 元素是否是集合 key 的成员
SISMEMBER key member
//获取所有成员
SMEMBERS key
//移除集合中一个或多个成员
SREM key member1 [member2]

zset(排序集合)

1
2
3
4
5
6
7
8
//向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZADD key score1 member1 [score2 member2]
//获取有序集合的成员数
ZCARD key
//通过索引区间返回有序集合成指定区间内的成员
ZRANGE key start stop [WITHSCORES]
//返回有序集中指定区间内的成员,通过索引,分数从高到底
ZREVRANGE key start stop [WITHSCORES]

四、应用场景

分布式锁(string)

setnx key value(),当key不存在时,将 key 的值设为 value ,返回1。若给定的 key 已经存在,则setnx不做任何动作,返回0。
当setnx返回1时,表示获取锁,做完操作以后del key,表示释放锁,如果setnx返回0表示获取锁失败.

SETNX key value
只在键 key 不存在的情况下, 将键 key 的值设置为 value 。
若键 key 已经存在, 则 SETNX 命令不做任何动作。

计数器(string)

如知乎每个问题的被浏览器次数

1
2
3
4
5
set key 0
// incr readcount::{帖子id} 每阅读一次
incr key
// get readcount::{帖子id} 获取阅读量
get key

incr
为键 key 储存的数字值加上一。
如果键 key 不存在, 那么它的值会先被初始化为 0 , 然后再执行 INCR 命令。
如果键 key 储存的值不能被解释为数字, 那么 INCR 命令将返回一个错误。
本操作的值限制在 64 位(bit)有符号数字表示之内。

消息队列(list)

在list里面一边进,一边出即可

实现方式一

  1. 一直往list左边放

    1
    lpush key value 
  2. key这个list有元素时,直接弹出,没有元素被阻塞,直到等待超时或发现可弹出元素为止,上面例子超时时间为10s

    1
    brpop key value 10 

实现方式二

1
2
rpush key value
blpop key value 10

抽奖活动(set)

  1. 参加抽奖活动

    1
    sadd key {userId} 
  2. 获取所有抽奖用户,大轮盘转起来

    1
    smembers key 
  3. 抽取count名中奖者,并从抽奖活动中移除

    1
    spop key count 
  4. 抽取count名中奖者,不从抽奖活动中移除

    1
    srandmember key count

    实现点赞,签到,like等功能(set)

电商商品筛选(set)

每个商品入库的时候即会建立他的静态标签列表如,品牌,尺寸,处理器,内存

  1. 将拯救者y700P-001和ThinkPad-T480这两个元素放到集合brand::lenovo

    1
    2
    3
    sadd brand::lenovo 拯救者y700P-001 ThinkPad-T480
    sadd screenSize::15.6 拯救者y700P-001 机械革命Z2AIR
    sadd processor::i7 拯救者y700P-001 机械革命X8TIPlus
  2. 获取品牌为联想,屏幕尺寸为15.6,并且处理器为i7的电脑品牌(sinter为获取集合的交集)

    1
    sinter brand::lenovo screenSize::15.6 processor::i7 -> 拯救者y700P-001

排行版(zset)

redis的zset天生是用来做排行榜的、好友列表, 去重, 历史记录等业务需求

  1. user1的用户分数为 10

    1
    2
    zadd ranking 10 user1
    zadd ranking 20 user2
  2. 取分数最高的3个用户

    1
    zrevrange ranking 0 2 withscores

五、过期策略

redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的 key。

  • 定期删除策略

    Redis 默认会每秒进行十次过期扫描(100ms一次),过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。
    从过期字典中随机 20 个 key;
    删除这 20 个 key 中已经过期的 key;
    如果过期的 key 比率超过 1/4,那就重复步骤 1;

  • 惰性删除

    除了定期遍历之外,它还会使用惰性策略来删除过期的 key,所谓惰性策略就是在客户端访问这个 key 的时候,redis 对 key 的过期时间进行检查,如果过期了就立即删除,不会给你返回任何东西。

定期删除是集中处理,惰性删除是零散处理。

为什么要采用定期删除+惰性删除2种策略呢?

如果过期就删除。假设redis里放了10万个key,都设置了过期时间,你每隔几百毫秒,就检查10万个key,那redis基本上就死了,cpu负载会很高的,消耗在你的检查过期key上了
但是问题是,定期删除可能会导致很多过期key到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个key的时候,redis会检查一下 ,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。
并不是key到时间就被删除掉,而是你查询这个key的时候,redis再懒惰的检查一下
通过上述两种手段结合起来,保证过期的key一定会被干掉。
所以说用了上述2种策略后,下面这种现象就不难解释了:数据明明都过期了,但是还占有着内存

六、内存淘汰策略

因为Redis将数据放到内存中,内存是有限的,比如redis就只能用10个G,你要是往里面写了20个G的数据,会咋办?当然会干掉10个G的数据,然后就保留10个G的数据了。那干掉哪些数据?保留哪些数据?当然是干掉不常用的数据,保留常用的数据了
Redis提供的内存淘汰策略有如下几种:

  1. noeviction

    不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。

  2. volatile-lru

    尝试淘汰设置了过期时间的 key,最少使用的 key 优先被淘汰。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。(这个是使用最多的)

  3. volatile-ttl

    跟上面一样,除了淘汰的策略不是 LRU,而是 key 的剩余寿命 ttl 的值,ttl 越小越优先被淘汰。

  4. volatile-random

    跟上面一样,不过淘汰的 key 是过期 key 集合中随机的 key。

  5. allkeys-lru

    区别于 volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不只是过期的 key 集合。这意味着没有设置过期时间的 key 也会被淘汰。v

  6. allkeys-random

    跟上面一样,不过淘汰的策略是随机的 key。allkeys-random 跟上面一样,不过淘汰的策略是随机的 key。

七、持久化策略

Redis的数据是存在内存中的,如果Redis发生宕机,那么数据会全部丢失,因此必须提供持久化机制。
Redis 的持久化机制有两种

  • 快照(RDB)

    快照是一次全量备份,AOF 日志是连续的增量备份。快照是内存数据的二进制序列化形式,在存储上非常紧凑,

  • AOF 日志

    而 AOF 日志记录的是内存数据修改的指令记录文本。AOF 日志在长期的运行过程中会变的无比庞大,数据库重启时需要加载 AOF 日志进行指令重放,这个时间就会无比漫长。所以需要定期进行 AOF 重写,给 AOF 日志进行瘦身。
    RDB是通过Redis主进程fork子进程,让子进程执行磁盘 IO 操作来进行 RDB 持久化,AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。

即RDB记录的是数据,AOF记录的是指令

RDB和AOF到底该如何选择?

  1. 不要仅仅使用 RDB,因为那样会导致你丢失很多数据,因为RDB是隔一段时间来备份数据
  2. 也不要仅仅使用 AOF,因为那样有两个问题,第一,通过 AOF 做冷备没有RDB恢复速度快; 第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug
  3. 用RDB恢复内存状态会丢失很多数据,重放AOP日志又很慢。Redis4.0推出了混合持久化来解决这个问题。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

面试题

1. 缓存雪崩和缓存穿透

缓存雪崩

假设有如下一个系统,高峰期请求为5000次/秒,4000次走了缓存,只有1000次落到了数据库上,数据库每秒1000的并发是一个正常的指标,完全可以正常工作,但如果缓存宕机了,每秒5000次的请求会全部落到数据库上,数据库立马就死掉了,因为数据库一秒最多抗2000个请求,如果DBA重启数据库,立马又会被新的请求打死了,这就是缓存雪崩。

如何解决缓存雪崩

  • 事前:redis高可用,主从+哨兵,redis cluster,避免全盘崩溃
  • 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL被打死
  • 事后:redis持久化,快速恢复缓存数据

缓存穿透

假如客户端每秒发送5000个请求,其中4000个为黑客的恶意攻击,即在数据库中也查不到。举个例子,用户id为正数,黑客构造的用户id为负数,
如果黑客每秒一直发送这4000个请求,缓存就不起作用,数据库也很快被打死。

如何解决缓存穿透
查询不到的数据也放到缓存,value为空,如set -999 “”
总而言之,缓存雪崩就是缓存失效,请求全部全部打到数据库,数据库瞬间被打死。缓存穿透就是查询了一个一定不存在的数据,并且从存储层查不到的数据没有写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义

参考资料

Redis不只是缓存,还有n多种你没发现的妙用

理解Redis缓存「穿透」「雪崩」「击穿」以及解决方案和分析

关于Redis,学会这8点就够了

Redis 如何保持和 MySQL 数据一致

oracle表名、字段名等对象的命名长度限制(转)

2020-09-17 14:32:05

今天在为某系统数据库结构整理升级脚本时,遇到了“命名字节过长的错误”,类似于下面的截图语句:img

​ 由于升级的结构中对于字段名的命名根据业务进行了修改,出现了命名过长的情况。

这里想说一下,对于oracle,表名、字段名等对象命名字节个数限制在了30个字节!是无法更改的,这是oracle的固化设置。

​ 我们可以通过查看数据库字典进一步验证一下。先思考一下,哪里可以获得关于这类命名的信息呢?有没有想到USER_TAB_COLUMNS,USER_CONSTRAINTS这一类的视图。我们来做一个验证。

实验:

​ 在sys用户下,查看user_tables这个视图,可以发现在table_name这个字段,类型为varchar,长度为30,这里记录的表的名称,如下:

img

再进一步查看视图的结构,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
create or replace view user_tables
(table_name, tablespace_name, cluster_name, iot_name, status, pct_free, pct_used, ini_trans, max_trans, initial_extent, next_extent, min_extents, max_extents, pct_increase, freelists, freelist_groups, logging, backed_up, num_rows, blocks, empty_blocks, avg_space, chain_cnt, avg_row_len, avg_space_freelist_blocks, num_freelist_blocks, degree, instances, cache, table_lock, sample_size, last_analyzed, partitioned, iot_type, temporary, secondary, nested, buffer_pool, flash_cache, cell_flash_cache, row_movement, global_stats, user_stats, duration, skip_corrupt, monitoring, cluster_owner, dependencies, compression, compress_for, dropped, read_only, segment_created, result_cache)
as
select o.name,
decode(bitand(t.property,2151678048), 0, ts.name,
decode(t.ts#, 0, null, ts.name)),
decode(bitand(t.property, 1024), 0, null, co.name),
decode((bitand(t.property, 512)+bitand(t.flags, 536870912)),
0, null, co.name),
decode(bitand(t.trigflag, 1073741824), 1073741824, 'UNUSABLE', 'VALID'),
decode(bitand(t.property, 32+64), 0, mod(t.pctfree$, 100), 64, 0, null),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(bitand(t.property, 32+64), 0, t.pctused$, 64, 0, null)),
decode(bitand(t.property, 32), 0, t.initrans, null),
decode(bitand(t.property, 32), 0, t.maxtrans, null),
decode(bitand(t.property, 17179869184), 17179869184,
ds.initial_stg * ts.blocksize,
s.iniexts * ts.blocksize),
decode(bitand(t.property, 17179869184), 17179869184,
ds.next_stg * ts.blocksize,
s.extsize * ts.blocksize),
decode(bitand(t.property, 17179869184), 17179869184,
ds.minext_stg, s.minexts),
decode(bitand(t.property, 17179869184), 17179869184,
ds.maxext_stg, s.maxexts),
decode(bitand(ts.flags, 3), 1, to_number(NULL),
decode(bitand(t.property, 17179869184), 17179869184,
ds.pctinc_stg, s.extpct)),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(bitand(o.flags, 2), 2, 1,
decode(bitand(t.property, 17179869184), 17179869184,
ds.frlins_stg, decode(s.lists, 0, 1, s.lists)))),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(bitand(o.flags, 2), 2, 1,
decode(bitand(t.property, 17179869184), 17179869184,
ds.maxins_stg, decode(s.groups, 0, 1, s.groups)))),
decode(bitand(t.property, 32+64), 0,
decode(bitand(t.flags, 32), 0, 'YES', 'NO'), null),
decode(bitand(t.flags,1), 0, 'Y', 1, 'N', '?'),
t.rowcnt,
decode(bitand(t.property, 64), 0, t.blkcnt, null),
decode(bitand(t.property, 64), 0, t.empcnt, null),
decode(bitand(t.property, 64), 0, t.avgspc, null),
t.chncnt, t.avgrln, t.avgspc_flb,
decode(bitand(t.property, 64), 0, t.flbcnt, null),
lpad(decode(t.degree, 32767, 'DEFAULT', nvl(t.degree,1)),10),
lpad(decode(t.instances, 32767, 'DEFAULT', nvl(t.instances,1)),10),
lpad(decode(bitand(t.flags, 8), 8, 'Y', 'N'),5),
decode(bitand(t.flags, 6), 0, 'ENABLED', 'DISABLED'),
t.samplesize, t.analyzetime,
decode(bitand(t.property, 32), 32, 'YES', 'NO'),
decode(bitand(t.property, 64), 64, 'IOT',
decode(bitand(t.property, 512), 512, 'IOT_OVERFLOW',
decode(bitand(t.flags, 536870912), 536870912, 'IOT_MAPPING', null))),
decode(bitand(o.flags, 2), 0, 'N', 2, 'Y', 'N'),
decode(bitand(o.flags, 16), 0, 'N', 16, 'Y', 'N'),
decode(bitand(t.property, 8192), 8192, 'YES',
decode(bitand(t.property, 1), 0, 'NO', 'YES')),
decode(bitand(o.flags, 2), 2, 'DEFAULT',
decode(bitand(decode(bitand(t.property, 17179869184), 17179869184,
ds.bfp_stg, s.cachehint), 3),
1, 'KEEP', 2, 'RECYCLE', 'DEFAULT')),
decode(bitand(o.flags, 2), 2, 'DEFAULT',
decode(bitand(decode(bitand(t.property, 17179869184), 17179869184,
ds.bfp_stg, s.cachehint), 12)/4,
1, 'KEEP', 2, 'NONE', 'DEFAULT')),
decode(bitand(o.flags, 2), 2, 'DEFAULT',
decode(bitand(decode(bitand(t.property, 17179869184), 17179869184,
ds.bfp_stg, s.cachehint), 48)/16,
1, 'KEEP', 2, 'NONE', 'DEFAULT')),
decode(bitand(t.flags, 131072), 131072, 'ENABLED', 'DISABLED'),
decode(bitand(t.flags, 512), 0, 'NO', 'YES'),
decode(bitand(t.flags, 256), 0, 'NO', 'YES'),
decode(bitand(o.flags, 2), 0, NULL,
decode(bitand(t.property, 8388608), 8388608,
'SYS$SESSION', 'SYS$TRANSACTION')),
decode(bitand(t.flags, 1024), 1024, 'ENABLED', 'DISABLED'),
decode(bitand(o.flags, 2), 2, 'NO',
decode(bitand(t.property, 2147483648), 2147483648, 'NO',
decode(ksppcv.ksppstvl, 'TRUE', 'YES', 'NO'))),
decode(bitand(t.property, 1024), 0, null, cu.name),
decode(bitand(t.flags, 8388608), 8388608, 'ENABLED', 'DISABLED'),
case when (bitand(t.property, 32) = 32) then
null
when (bitand(t.property, 17179869184) = 17179869184) then
decode(bitand(ds.flags_stg, 4), 4, 'ENABLED', 'DISABLED')
else
decode(bitand(s.spare1, 2048), 2048, 'ENABLED', 'DISABLED')
end,
case when (bitand(t.property, 32) = 32) then
null
when (bitand(t.property, 17179869184) = 17179869184) then
decode(bitand(ds.flags_stg, 4), 4,
case when bitand(ds.cmpflag_stg, 3) = 1 then 'BASIC'
when bitand(ds.cmpflag_stg, 3) = 2 then 'OLTP'
else decode(ds.cmplvl_stg, 1, 'QUERY LOW',
2, 'QUERY HIGH',
3, 'ARCHIVE LOW',
'ARCHIVE HIGH') end,
null)
else
decode(bitand(s.spare1, 2048), 0, null,
case when bitand(s.spare1, 16777216) = 16777216 -- 0x1000000
then 'OLTP'
when bitand(s.spare1, 100663296) = 33554432 -- 0x2000000
then 'QUERY LOW'
when bitand(s.spare1, 100663296) = 67108864 -- 0x4000000
then 'QUERY HIGH'
when bitand(s.spare1, 100663296) = 100663296 -- 0x2000000+0x4000000
then 'ARCHIVE LOW'
when bitand(s.spare1, 134217728) = 134217728 -- 0x8000000
then 'ARCHIVE HIGH'
else 'BASIC' end)
end,
decode(bitand(o.flags, 128), 128, 'YES', 'NO'),
decode(bitand(t.trigflag, 2097152), 2097152, 'YES', 'NO'),
decode(bitand(t.property, 17179869184), 17179869184, 'NO',
decode(bitand(t.property, 32), 32, 'N/A', 'YES')),
decode(bitand(t.property,16492674416640),2199023255552,'FORCE',
4398046511104,'MANUAL','DEFAULT')
from sys.ts$ ts, sys.seg$ s, sys.obj$ co, sys.tab$ t, sys.obj$ o,
sys.deferred_stg$ ds, sys.obj$ cx, sys.user$ cu, x$ksppcv ksppcv,
x$ksppi ksppi
where o.owner# = userenv('SCHEMAID')
and o.obj# = t.obj#
and bitand(t.property, 1) = 0
and bitand(o.flags, 128) = 0
and t.bobj# = co.obj# (+)
and t.ts# = ts.ts#
and t.file# = s.file# (+)
and t.block# = s.block# (+)
and t.ts# = s.ts# (+)
and t.obj# = ds.obj# (+)
and t.dataobj# = cx.obj# (+)
and cx.owner# = cu.user# (+)
and ksppi.indx = ksppcv.indx
and ksppi.ksppinm = '_dml_monitoring_enabled'
;
comment on table USER_TABLES is 'Description of the user''s own relational tables';
comment on column USER_TABLES.TABLE_NAME is 'Name of the table';
comment on column USER_TABLES.TABLESPACE_NAME is 'Name of the tablespace containing the table';
comment on column USER_TABLES.CLUSTER_NAME is 'Name of the cluster, if any, to which the table belongs';
comment on column USER_TABLES.IOT_NAME is 'Name of the index-only table, if any, to which the overflow or mapping table entry belongs';
comment on column USER_TABLES.STATUS is 'Status of the table will be UNUSABLE if a previous DROP TABLE operation failed,
VALID otherwise';
comment on column USER_TABLES.PCT_FREE is 'Minimum percentage of free space in a block';
comment on column USER_TABLES.PCT_USED is 'Minimum percentage of used space in a block';
comment on column USER_TABLES.INI_TRANS is 'Initial number of transactions';
comment on column USER_TABLES.MAX_TRANS is 'Maximum number of transactions';
comment on column USER_TABLES.INITIAL_EXTENT is 'Size of the initial extent in bytes';
comment on column USER_TABLES.NEXT_EXTENT is 'Size of secondary extents in bytes';
comment on column USER_TABLES.MIN_EXTENTS is 'Minimum number of extents allowed in the segment';
comment on column USER_TABLES.MAX_EXTENTS is 'Maximum number of extents allowed in the segment';
comment on column USER_TABLES.PCT_INCREASE is 'Percentage increase in extent size';
comment on column USER_TABLES.FREELISTS is 'Number of process freelists allocated in this segment';
comment on column USER_TABLES.FREELIST_GROUPS is 'Number of freelist groups allocated in this segment';
comment on column USER_TABLES.LOGGING is 'Logging attribute';
comment on column USER_TABLES.BACKED_UP is 'Has table been backed up since last modification?';
comment on column USER_TABLES.NUM_ROWS is 'The number of rows in the table';
comment on column USER_TABLES.BLOCKS is 'The number of used blocks in the table';
comment on column USER_TABLES.EMPTY_BLOCKS is 'The number of empty (never used) blocks in the table';
comment on column USER_TABLES.AVG_SPACE is 'The average available free space in the table';
comment on column USER_TABLES.CHAIN_CNT is 'The number of chained rows in the table';
comment on column USER_TABLES.AVG_ROW_LEN is 'The average row length, including row overhead';
comment on column USER_TABLES.AVG_SPACE_FREELIST_BLOCKS is 'The average freespace of all blocks on a freelist';
comment on column USER_TABLES.NUM_FREELIST_BLOCKS is 'The number of blocks on the freelist';
comment on column USER_TABLES.DEGREE is 'The number of threads per instance for scanning the table';
comment on column USER_TABLES.INSTANCES is 'The number of instances across which the table is to be scanned';
comment on column USER_TABLES.CACHE is 'Whether the table is to be cached in the buffer cache';
comment on column USER_TABLES.TABLE_LOCK is 'Whether table locking is enabled or disabled';
comment on column USER_TABLES.SAMPLE_SIZE is 'The sample size used in analyzing this table';
comment on column USER_TABLES.LAST_ANALYZED is 'The date of the most recent time this table was analyzed';
comment on column USER_TABLES.PARTITIONED is 'Is this table partitioned? YES or NO';
comment on column USER_TABLES.IOT_TYPE is 'If index-only table, then IOT_TYPE is IOT or IOT_OVERFLOW or IOT_MAPPING else NULL';
comment on column USER_TABLES.TEMPORARY is 'Can the current session only see data that it place in this object itself?';
comment on column USER_TABLES.SECONDARY is 'Is this table object created as part of icreate for domain indexes?';
comment on column USER_TABLES.NESTED is 'Is the table a nested table?';
comment on column USER_TABLES.BUFFER_POOL is 'The default buffer pool to be used for table blocks';
comment on column USER_TABLES.FLASH_CACHE is 'The default flash cache hint to be used for table blocks';
comment on column USER_TABLES.CELL_FLASH_CACHE is 'The default cell flash cache hint to be used for table blocks';
comment on column USER_TABLES.ROW_MOVEMENT is 'Whether partitioned row movement is enabled or disabled';
comment on column USER_TABLES.GLOBAL_STATS is 'Are the statistics calculated without merging underlying partitions?';
comment on column USER_TABLES.USER_STATS is 'Were the statistics entered directly by the user?';
comment on column USER_TABLES.DURATION is 'If temporary table, then duration is sys$session or sys$transaction else NULL';
comment on column USER_TABLES.SKIP_CORRUPT is 'Whether skip corrupt blocks is enabled or disabled';
comment on column USER_TABLES.MONITORING is 'Should we keep track of the amount of modification?';
comment on column USER_TABLES.CLUSTER_OWNER is 'Owner of the cluster, if any, to which the table belongs';
comment on column USER_TABLES.DEPENDENCIES is 'Should we keep track of row level dependencies?';
comment on column USER_TABLES.COMPRESSION is 'Whether table compression is enabled or not';
comment on column USER_TABLES.COMPRESS_FOR is 'Compress what kind of operations';
comment on column USER_TABLES.DROPPED is 'Whether table is dropped and is in Recycle Bin';
comment on column USER_TABLES.READ_ONLY is 'Whether table is read only or not';
comment on column USER_TABLES.SEGMENT_CREATED is 'Whether the table segment is created or not';
comment on column USER_TABLES.RESULT_CACHE is 'The result cache mode annotation for the table';

\我们可以由视图结构中锁定到三个地方,定位到来源,如下:**

\table_name**

\o.name**

\sys.obj$ o**

\可以发现,\*table_name**字段来源于**sys.obj$**中的**name**字段,如下*:**
img

我们尝试修改一下\sys.obj$**中name字段的长度,如下:**

img

​ 可以看到,我们是无法修改数据库系统的字典字段的定义。用同样的方法,我们还可以查看USER_TAB_COLUMNS,USER_CONSTRAINTS等视图,会发现对于oracle,限制了命名的长度,都限制在了30个字节。

​ 之后在网上搜了一下,发现对于不同的数据库原来命名长度限制是不一样的,摘录如下:

数据库 表名长度限制 字段名长度限制
oracle 30 30
mysql 64 64
db2 128 128
access 64 64
sqlserver 128 128

连接参考

oracle表名、字段名等对象的命名长度限制

VUE.JS 组件component### 1. 什么是组件

组件是vue最强大的功能之一,组件可以扩展HTML元素、封装可重用的代码
组件是自定义元素(对象)

2. 组件的定义方式

建议在组件名称中加入:“-”

方式一:先创建组件构造器,然后由构造器来创建组件
方式二:直接创建组件


/**
    方式一:先创建组件构造器,然后由构造器来创建组件
*/
var myCompoent = Vue.extend({
    template:"<h3>hello VUE</h3>"
});

//使用Vue.component(标签名,组件的构造器),根据组件构造器来创建组件
Vue.component("my-hello",myCompoent);


//范式二
Vue.component("v-ps",{
    template:"<h2>你好,VUE</h2>"
})

调用:
    <v-ps></v-ps>
    <my-hello></my-hello>

3. 组件分类

分类:
    局部组件和全局组件

4. 模板引用

将组件的代码写到<template></template>中,并且引用。template必须只有一个直接子元素

5.动态组件

使用keep-alive组件,缓存非活动的组件,可以保留组件状态,避免组件重新渲染(默认每次都会销毁非活动的组件并且重新渲染)

二、组件之间的通讯

1. 父子组件

在一个组件内部定义的另外一个组件,称为:父子组件
子组件只能在父组件的内部使用
默认情况下,父子组件数据无法之间访问,每个组件实例的作用域是独立的

2. 组件之间的数据通信

2.1 子组件访问父组件的数据

a)在调用子组件是,绑定想要接收的父组件的数据
    <my-world :message="msg" :name="name" ></my-world>

b)在子组件内部使用props选项来声明获取数据(接收父组件的数据)
    props:['message','name','user']
    props:{}//也可以传递对象,允许配置高级设置,比如:数据的类型判断,数据的校验,设置默认值等。。。

总结:
    子组件通过props选项来接收父组件的数据
    三种数据形式:data、props、computed

2.2 父组件访问子组件的数据

a)在子组件中使用vm.$emit(事件名,参数1,参数2 ,。。。。)触发一个自定义事件。
    this.$emit("e-world",this.age,this.height);//触发一个事件,向父组件传递数据

b) 在父组件中使用自组件的地方监听子组件触发的事件,并且在父组件中定义方法来获取子组件的数据
    @e-world:监听子组件的事件
    <my-world :message="msg" :name="name" @e-world="getData"></my-world>

3. 单向数据流

父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
不允许直接修改父组件的数据,否则会报错。
解决方式:
    1:
        如果子组件想把传递过来的值,当做局部变量使用,可以将数据存储到另外一个变量中再操作。不影响父组件
    2:
        如果子组件想修改数据,并且同步到父组件中,两种方式
        a.使用.sync指令(1.0支持,2.0不会,2.3版本以后又支持)
            还需要触发一个更新事件 this.$emit('update:name', "唐寅");
        b.将父组件中的数据包装成一个对象,传递给子组件,然后在子组件中修改对象属性值(因为对象是引用类型,指向同一个内存地址),推荐!!!

VUE修改数据后不自动渲染最近写vue写的脑瓜子有点疼,特别今天出现的问题更加头疼。

先说说这个问题,

场景

vue + element

修改表格数据后,不重新加载整个表格的情况下修改局部数据发现数据不能自动渲染。
image.png
wtf???
那就是没得谈咯,好吧,那就使用手动渲染吧。

解决方案

  1. 给表格绑定key值

    1
    <el-table :data="xxx" :key="tableKey">...
  2. 在方法里添加以下代码

    1
    this.tableKey++;

    参考文档

    VUE官网 - 维护状态

Vue父子组件互调方法## 父组件调用子组件方法

父组件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<button @click="getChild">点击调用子组件</button>
<child ref="child"></child>
</template>
<script>
import child from "child";
export default{
components: {child},
methods: {
getChild(){
// 调用子组件方法
this.$ref.child.init();
}
}
}
</script>

子组件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
{{value}}
</template>
<script>
export default{
data(){
return {
value: "我是子组件"
}
},
methods: {
init(){
this.value = "父组件调用了方法";
}
}
}
</script>

子组件调用父组件方法

父组件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
{{value}}
<child @init="init"></child>
</template>
<script>
import child from "child";
export default{
components: {child},
data(){
return {
value: "我是父组件"
}
},
methods: {
init(){
this.value = "子组件调用了方法";
}
}
}
</script>

子组件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<button @child="getFather">调用父组件方法</button>
</template>
<script>
export default{
methods: {
getFather(){
// 调用父组件方法
this.$emit("init");
}
}
}
</script>

mybatis-config.xml 配置文件详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--通过这个配置文件,完成mybatis与数据库的连接 -->
<configuration>
<!-- 环境 -->
<environments default="development">
<!-- 环境变量 -->
<environment id="development">
<!-- 事务管理器,指定事务管理类型,type="JDBC"指直接简单使用了JDBC的提交和回滚设置 -->
<transactionManager type="JDBC"/>
<!-- dataSource指数据源配置,POOLED是JDBC连接对象的数据源连接池的实现。 -->
<dataSource type="POOLED">
<!-- 数据属性 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis_study"/>
<property name="username" value="root"/>
<property name="password" value="ps123456"/>
</dataSource>
</environment>
</environments>
<!-- pojo的映射文件 -->
<mappers>
<!-- 用来告诉MyBatis从哪里去找映射文件,进而找到这些SQL语句。 -->
<mapper resource="org/mybatis/example/stuMapper.xml"/>
</mappers>
</configuration>

git 和 svn 的区别## 使用上

git有本地库的一个概念,而svn是没有的

  • git
    1. 提交代码到本地库
    2. 拉取远程仓库代码
    3. 解决冲突
    4. 一次性提交修改的代码到远程仓库
    5. 拉取远程仓库最新代码
  • svn
    1. 提交代码到远程仓库
    2. 解决冲突
    3. 拉取远程仓库最新代码

通过以上两个步骤可以发现,svn没有本地库这个概念,那么意味着什么呢?

git可以离线也就是不依赖远程仓库去提交代码,你可以在本地库随时提交拉取代码,等到有网络了或者网络好的情况下再提交代码也是没问题的。

而svn则重度依赖远程仓库,因为它只有远程仓库,那么如果没法在没有网络或者网络不好的情况下使用。

流程图

  • git

image20191125100229586.png

  • svn
    image20191125100337069.png

优缺点

git

  • 优点

    离线提交代码,可以有属于自己本地的版本库,提交代码随心所欲。

  • 缺点

    目前使用中还没发现,如果有发现后续再更新

svn

  • 优点

    还未发现

  • 缺点

    必须要有网络才能提交,提交代码时感觉好折磨。

参考资料

SVN与Git比较的优缺点差异

git和svn提交时怎么忽略文件## git

  1. 在项目根目录下创建.gitignore文件
  2. .gitignore文件中添加要屏蔽的文件
    1
    2
    3
    4
    5
    6
    # 指定文件
    xxx.text
    # 指定文件夹
    dir
    # 匹配后缀名为 txt的文件
    *.txt

    svn

  3. 打开SVN的Version Control中的Local Changes下的Configure Ignored Files
    svn1.png
  4. 增加特定文件或文件夹
    SVN
    svn3.png

新版本如下

  1. 打开 Settings→Editor→File Types
  2. 在窗口最下方“Ignore files and folders”一栏中添加如下忽略:
  3. 注意要用英文;符号隔开
    svn4.png

idea 下 springboot 热部署## 依赖

1
2
3
4
5
6
7
<!--热部署 begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--热部署 end-->

配置idea

  1. 找到idea的Preferences -> Build, Execution, Deployment -> Compiler,勾选Build project automatically

  2. 回到idea正常界面,Mac使用快捷键shift+option+command+/,window上的快捷键是Shift+Ctrl+Alt+/,打开Registry,勾选
    compiler.automake.allow.when.app.running

idea自用插件## 开发

  • Alibaba Java Coding Guidelines 阿里巴巴代码规范
  • Free MyBatis plugin 免费的Mybatis插件,可以快速在mapper文件和java代码中切换。
  • Lombok 编制自动生成get,set方法
  • ignore 用来设置git提交时需要过滤的文件
  • Maven Helper 一键查看maven依赖,查看冲突的依赖,一键进行exclude依赖
  • gitee 码云上传代码
  • JRebel 代码热部署

辅助

  • Drumroll 在编译/构建时播放鼓声音。当构建有错误或警告时,以及其他同样有趣的声音。
  • key-promoter-x 快捷键提示
  • Nyan progress bar 加载进度条
  • grep-console 多颜色调试日志
  • Translation 界面翻译